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"));
}