znippy-build-thing/tests/build_gate.rs
//! **⚒ Build Thing's BUILD GATE, asserted where a test run sees it.**
//!
//! The forge's door (`dwarves buildthing` / ⚒ Build) ends every delivery with
//! `nornir_build_thing::buildgate::tally_bins`: every `[[bin]]` cargo can see in this
//! tree must come out of a declared row, or be named in `[build.allow_unbuilt_bins]`
//! with the reason it has no row — and no excuse may name a bin a row builds.
//! MEASURED 2026-10-16 (the build-thing sweep on loki): this repo's declaration was
//! RED on that gate and no test here asked the question, so the first place it
//! showed was the forge, after a whole build. This is the same function over the
//! same declaration, with the bins the declared cargo rows name.
//!
//! A new `[[bin]]` lands RED here until it gets a row or a written reason.
use std::collections::BTreeSet;
use std::path::PathBuf;
use nornir_build_thing::buildgate::tally_bins;
use nornir_build_thing::variant::{ArtifactKind, BuildVariantSet};
/// The checkout this crate lives in: the nearest ancestor that declares a build space.
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.find(|d| BuildVariantSet::conventional_path(d).is_file())
.expect("an ancestor declares .nornir/build-variants.toml")
.to_path_buf()
}
#[test]
fn every_bin_is_built_by_a_row_or_excused_with_a_reason() {
let root = repo_root();
let set = BuildVariantSet::discover(&root)
.expect("the declaration loads")
.expect("this repo declares a build space");
let produced: BTreeSet<String> = set
.variants
.iter()
.filter(|v| matches!(v.kind, ArtifactKind::Exe | ArtifactKind::Service))
.filter_map(|v| v.bin.clone())
.collect();
let (tally, problems) = tally_bins(&root, produced, false).expect("the gate runs");
// NON-VACUOUS: a walker that found no bins would pass any declaration.
assert!(!tally.declared.is_empty(), "the gate saw no [[bin]] at all under {}", root.display());
assert!(problems.is_empty(), "[build.allow_unbuilt_bins] problems: {problems:#?}");
tally.verdict().unwrap_or_else(|e| panic!("{e}\n{}", tally.headline()));
}