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