Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

holger-recipe/tests/rows_equal_cargo_install.rs

//! **Every product's Build Thing row IS its `cargo install` line** (lane HOL-PRODUCTS).
//!
//! Holger is four products, one crate each (`holger`, `holger-downstream`, `holger-ui`,
//! `holger-cli`). Each row of [`holger_recipe::twins::PRODUCTS`] must be declared, must build
//! the same package, bin, features, default-feature switch, target and profile as its line,
//! and the READMEs must show the one table of them, rendered from the recipe.

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

use holger_recipe::twins::{
    point_of_install, point_of_row, readme_table, twin_differences, PATHS, PRODUCTS,
};
use holger_recipe::variants::{variants, HOLGER_UI};

fn repo_root() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .parent()
        .unwrap()
        .to_path_buf()
}

/// The one bin a package builds with no required feature, read off its manifest.
fn default_bin_of(dir: &str) -> String {
    let m: toml::Value =
        toml::from_str(&std::fs::read_to_string(repo_root().join(dir).join("Cargo.toml")).unwrap())
            .unwrap();
    let bins: Vec<String> = m["bin"]
        .as_array()
        .unwrap()
        .iter()
        .filter(|b| b.get("required-features").is_none())
        .map(|b| b["name"].as_str().unwrap().to_string())
        .collect();
    assert_eq!(bins.len(), 1, "{dir}: exactly one default bin: {bins:?}");
    bins[0].clone()
}

#[test]
fn the_four_products_are_all_rows() {
    let names: Vec<&str> = PRODUCTS.iter().map(|r| r.name).collect();
    assert_eq!(
        names,
        holger_recipe::variants::PRODUCTS,
        "the table and the declaration name the same four, in order"
    );
    let all = variants();
    let missing: Vec<&str> = names
        .iter()
        .copied()
        .filter(|n| !all.iter().any(|v| v.name == *n))
        .collect();
    assert!(missing.is_empty(), "no Build Thing row for {missing:?}");
}

#[test]
fn every_product_row_builds_what_its_cargo_install_line_builds() {
    let all = variants();
    let mut bad = Vec::new();
    for r in PRODUCTS {
        let v = all
            .iter()
            .find(|v| v.name == r.name)
            .unwrap_or_else(|| panic!("no row `{}`", r.name));
        let install = point_of_install(r.install).unwrap_or_else(|e| panic!("{}: {e}", r.name));
        let row = point_of_row(v);
        let (dir, _) = PATHS
            .iter()
            .find(|(_, p)| row.package.as_deref() == Some(*p))
            .copied()
            .unwrap_or_else(|| {
                panic!(
                    "{}: row builds {:?}, a package with no `--path` in twins::PATHS",
                    r.name, row.package
                )
            });
        assert_eq!(
            row.package.as_deref(),
            Some(r.name),
            "{}: the row builds the crate of its name",
            r.name
        );
        // The console's crate lives in holger-ui; its one bin is held there.
        let default_bin = if r.name == HOLGER_UI {
            r.name.to_string()
        } else {
            default_bin_of(dir)
        };
        for d in twin_differences(&row, &install, Some(&default_bin)) {
            bad.push(format!("{}: {d}", r.name));
        }
        // And the crates.io form names the same package.
        let crates_io = point_of_install(&format!("cargo install {}", r.name)).unwrap();
        assert_eq!(crates_io.package, row.package, "{}", r.name);
    }
    assert!(
        bad.is_empty(),
        "a row is not its `cargo install` line:\n  {}",
        bad.join("\n  ")
    );
}

/// Each product is an executable (what `cargo install` leaves in `~/.cargo/bin`), in the
/// one suite, and judged by the airgap rule: its default dials nothing.
#[test]
fn the_products_are_executables_under_the_airgap_rule() {
    let all = variants();
    for r in PRODUCTS {
        let v = all.iter().find(|v| v.name == r.name).unwrap();
        assert_eq!(v.suites, ["MVP"], "{}", r.name);
        assert_eq!(
            v.kind,
            nornir_build_thing::variant::ArtifactKind::Exe,
            "{}",
            r.name
        );
        assert!(
            holger_recipe::rules::links_no_http_client(r.name),
            "{}: the airgap rule judges it",
            r.name
        );
    }
    let defaults: Vec<&str> = all
        .iter()
        .filter(|v| v.is_default && holger_recipe::variants::PRODUCTS.contains(&v.name.as_str()))
        .map(|v| v.name.as_str())
        .collect();
    assert_eq!(
        defaults,
        ["holger", "holger-cli"],
        "the server and the tool are their roles' normal cases"
    );
}

/// **One table of the four products**, generated from the recipe: README.md and the
/// `.nornir/README.md` it is rendered from carry it, and no other install table.
#[test]
fn the_readme_shows_the_one_table_of_the_four_products() {
    let table = readme_table();
    for readme in ["README.md", ".nornir/README.md"] {
        let text = std::fs::read_to_string(repo_root().join(readme)).unwrap();
        assert!(
            text.contains(&table),
            "{readme} lacks the products' table (holger_recipe::twins::readme_table):\n{table}"
        );
        assert_eq!(
            text.matches("| product | install |").count(),
            1,
            "{readme}: one products table"
        );
        for gone in [
            "### The server variants",
            "### The open rows",
            "--bin holger-embedded",
            "--bin holger-mikro",
            "--bin holger-full",
        ] {
            assert!(!text.contains(gone), "{readme} still carries `{gone}`");
        }
    }
    for r in PRODUCTS {
        assert!(!r.is.is_empty(), "{}: says what it is", r.name);
    }
}

/// **Every `cargo install` line the docs give for a product builds that product's row**
/// (owner 2026-10-28, OX-VARIANTS: "make sure ordinary cargo build works for the variants
/// in the docs"). A line naming a product crate, with no feature, must be its row's build
/// point; a line that turns the downstream gateway on in the server crate is refused by
/// name, since the downstream is its own crate.
#[test]
fn every_documented_product_install_is_its_row() {
    let mut bad = Vec::new();
    // docs/v1 is the frozen history of the docs (lane HOL-DOCS-V2); it keeps the old lines.
    for doc in [
        "README.md",
        ".nornir/README.md",
        "docs/v2/02-install.md",
        "docs/v2/03-three-products.md",
    ] {
        let text = std::fs::read_to_string(repo_root().join(doc)).unwrap();
        for line in text
            .split('`')
            .skip(1)
            .step_by(2)
            .filter(|s| s.starts_with("cargo install"))
        {
            let Ok(got) = point_of_install(line) else {
                continue;
            };
            if got.features.contains("downstream") && got.package.as_deref() == Some("holger") {
                bad.push(format!(
                    "{doc}: `{line}`: the downstream is `cargo install holger-downstream`"
                ));
            }
        }
    }
    assert!(bad.is_empty(), "{}", bad.join("\n"));
}