Gunnar

mikronova/funnel-core

tests/repository_home.rs

//! **Every crate here names this repository's real home** (A28 L17).
//!
//! The old forge is gone. A `repository =` that still points at it, or at
//! another of our own old addresses, sends a reader of crates.io or a
//! `cargo metadata` consumer to a dead or wrong place. This test walks every
//! `Cargo.toml` in the repository (detached workspaces and fixtures too) and
//! every `THIRD_PARTY_LICENSES*.md`, and fails on the first such line.

use std::path::{Path, PathBuf};

/// Where this repository lives.
const HOME: &str = "https://git.gunnar.rs/mikronova/funnel-core";
/// The old forge's host, spelled so a search for it finds only real uses.
const OLD_FORGE: &str = concat!("code", "berg", ".org");
/// Our own hosts and organisations: a `repository =` on one of these must be [`HOME`].
const OURS: &[&str] = &[
    "https://gunnar.rs/",
    "https://git.gunnar.rs/",
    "https://github.com/vetra-automation/",
    "https://github.com/nordisk/",
    "https://github.com/mikronova/",
];

/// The repository root: the first ancestor holding `.git`, else the top-most
/// ancestor that still holds a `Cargo.toml`.
fn repo_root() -> PathBuf {
    let start = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let mut top = start.clone();
    for dir in start.ancestors() {
        if dir.join(".git").exists() {
            return dir.to_path_buf();
        }
        if dir.join("Cargo.toml").is_file() {
            top = dir.to_path_buf();
        }
    }
    top
}

fn walk(dir: &Path, out: &mut Vec<PathBuf>) {
    let Ok(entries) = std::fs::read_dir(dir) else { return };
    for entry in entries.flatten() {
        let path = entry.path();
        let name = entry.file_name();
        let name = name.to_string_lossy();
        let Ok(meta) = std::fs::symlink_metadata(&path) else { continue };
        if meta.is_dir() {
            if name == "target" || name == "node_modules" || name.starts_with('.') {
                continue;
            }
            walk(&path, out);
        } else if meta.is_file() && (name == "Cargo.toml" || (name.starts_with("THIRD_PARTY_LICENSES") && name.ends_with(".md"))) {
            out.push(path);
        }
    }
}

/// The quoted value of a `repository = "…"` line, if this is one.
fn repository_value(line: &str) -> Option<&str> {
    let rest = line.trim_start().strip_prefix("repository")?;
    let rest = rest.trim_start().strip_prefix('=')?.trim_start().strip_prefix('"')?;
    rest.split('"').next()
}

fn offences(path: &Path, text: &str) -> Vec<String> {
    let mut found = Vec::new();
    let manifest = path.file_name().is_some_and(|n| n == "Cargo.toml");
    for (i, line) in text.lines().enumerate() {
        let at = format!("{}:{}", path.display(), i + 1);
        if line.contains(OLD_FORGE) {
            found.push(format!("{at}: names the old forge"));
            continue;
        }
        if manifest {
            if let Some(v) = repository_value(line) {
                if OURS.iter().any(|o| v.starts_with(o)) && v != HOME {
                    found.push(format!("{at}: repository is {v}, not {HOME}"));
                }
            }
        }
    }
    found
}

#[test]
fn every_manifest_names_this_repositorys_real_home() {
    let root = repo_root();
    let mut files = Vec::new();
    walk(&root, &mut files);
    files.sort();
    assert!(files.iter().any(|p| p.ends_with("Cargo.toml")), "no Cargo.toml found under {}", root.display());
    let mut all = Vec::new();
    for f in &files {
        let text = std::fs::read_to_string(f).unwrap_or_default();
        all.extend(offences(f, &text));
    }
    assert!(all.is_empty(), "{} line(s) name an old home:\n{}", all.len(), all.join("\n"));
}

#[test]
fn the_guard_reads_a_repository_line_and_spots_the_old_forge() {
    assert_eq!(repository_value(r#"repository = "https://x/y""#), Some("https://x/y"));
    assert_eq!(repository_value(r#"repository  =  "a""#), Some("a"));
    assert_eq!(repository_value("repository.workspace = true"), None);
    let p = Path::new("Cargo.toml");
    let old = format!("repository = \"https://{OLD_FORGE}/nordisk/x\"");
    assert_eq!(offences(p, &old).len(), 1);
    assert_eq!(offences(p, "repository = \"https://github.com/vetra-automation/elsewhere\"").len(), 1);
    assert!(offences(p, &format!("repository = \"{HOME}\"")).is_empty());
    assert!(offences(p, "repository = \"https://github.com/apache/iceberg-rust\"").is_empty());
}