holger-recipe/tests/mvp_rows_count_consumers.rs
//! **Every MVP server row counts consumers** (lane G2, 2026-10-23; A18 §4 #10, G5).
//!
//! The estate board's subtitle counts the consumers only when server-lib's
//! `admission` is compiled in (`admission_off.rs` says "consumers not counted in this
//! build" otherwise). A18 found that branch reached "only through feature unification".
//! It is not: every MVP server row declares `jobs`, server-cli's `jobs` is
//! `server-lib/jobs`, and server-lib's `jobs` is `["supply-chain", "admission"]`. This
//! test resolves each MVP server row's declared features through the two manifests and
//! holds that `admission` is in the closure, so a row that loses it is red by name.
use std::collections::{BTreeMap, BTreeSet};
use std::path::Path;
fn features(manifest: &Path) -> BTreeMap<String, Vec<String>> {
let text =
std::fs::read_to_string(manifest).unwrap_or_else(|e| panic!("{}: {e}", manifest.display()));
let doc: toml::Table = toml::from_str(&text).unwrap();
doc.get("features")
.and_then(|f| f.as_table())
.map(|t| {
t.iter()
.map(|(k, v)| {
(
k.clone(),
v.as_array()
.unwrap()
.iter()
.map(|s| s.as_str().unwrap().to_string())
.collect(),
)
})
.collect()
})
.unwrap_or_default()
}
/// The server-lib features a row's server-cli feature list turns on, transitively.
fn server_lib_closure(row: &[String], no_default: bool) -> BTreeSet<String> {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
let cli = features(&root.join("server/cli/Cargo.toml"));
let lib = features(&root.join("server/lib/Cargo.toml"));
let mut cli_on = BTreeSet::new();
let mut todo: Vec<String> = row.to_vec();
if !no_default {
todo.push("default".into());
}
let mut lib_todo = Vec::new();
while let Some(f) = todo.pop() {
if let Some(l) = f.strip_prefix("server-lib/") {
lib_todo.push(l.to_string());
continue;
}
if f.contains('/') || f.starts_with("dep:") || !cli_on.insert(f.clone()) {
continue;
}
todo.extend(cli.get(&f).cloned().unwrap_or_default());
}
let mut lib_on = BTreeSet::new();
while let Some(f) = lib_todo.pop() {
if f.contains('/') || f.starts_with("dep:") || !lib_on.insert(f.clone()) {
continue;
}
lib_todo.extend(lib.get(&f).cloned().unwrap_or_default());
}
lib_on
}
#[test]
fn every_mvp_server_row_compiles_the_door_admission_so_the_board_counts_consumers() {
let rows: Vec<_> = holger_recipe::variants::variants()
.into_iter()
// The estate's servers (the appliance marker `mvp-server`): the box whose board
// counts its consumers. A downstream is counted on its upstream's board.
.filter(|v| {
v.package.as_deref() == Some(holger_recipe::variants::SERVER_CLI)
&& v.features.iter().any(|f| f == "mvp-server")
})
.collect();
assert!(
rows.len() >= 4,
"the MVP server rows are found: {:?}",
rows.iter().map(|r| &r.name).collect::<Vec<_>>()
);
for r in &rows {
let closure = server_lib_closure(&r.features, r.no_default_features);
assert!(closure.contains("admission"), "{} does not compile server-lib `admission`, so its board cannot count consumers: {closure:?}", r.name);
}
}