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

mikronova/modgunn

tests/build_variants.rs

//! **RED-when-broken: modgunn's declarative build matrix** —
//! `.nornir/build-variants.toml` DISCOVERS, parses and VALIDATES through
//! nornir-build-thing (`cargo xtask build`'s [`BuildVariantSet`]), and every atom it
//! declares is checked against `workspace_nordisk/.nornir/build-matrix-2026-08-02.md`
//! — the five shapes (`<x>-server` · `<x>` · `<x>-cli` · `<x>-ui` · `<x>-solo`), one
//! suite column, `ORIGINAL`.
//!
//! ## THE RULE THIS FILE ENFORCES
//!
//! The owner, 2026-08-03: *"bara de som är markerade suite=ORIGINAL i matrisen skall ha
//! built things"* — **carrying the suite tag is what claims Build-Thing status**, so a
//! tagged atom that cannot build is a promise the repo cannot keep (holger's
//! `archive ⭐`: a starred default in the docs and in no build file). And
//! *"ta bort allt inte stämmer med matrisen"* — anything not a matrix row is gone.
//!
//! modgunn declares **ONE** atom, `modgunn` (client · fat · bin `modgunn`), and the
//! other four shapes are asserted MISSING as positive claims. A gap nothing checks is
//! a phantom waiting to be re-declared by somebody copying a sibling repo's file.
//!
//! ## The four gaps, each measurable from the source rather than a matter of taste
//!
//! * **no server** — the only `TcpListener` in the crate is in `#[cfg(test)]`;
//!   `src/service.rs` (which merely RENDERS a unit) is behind the `framework` feature
//!   that no atom enables; and the unit it would render `ExecStart`s
//!   `modgunn world-delta watch`, a verb main.rs's dispatch does not have.
//! * **no thin cli** — modgunn has no `tonic`/`prost`/`nornir-service`, so there is no
//!   wire for a thin client to talk over; and its only `[[bin]]` carries
//!   `required-features = ["warehouse", "scan"]` where `warehouse = ["dep:skade", …]`,
//!   so the only executable modgunn can produce REQUIRES the dependency that makes it
//!   fat. A thin modgunn binary is impossible by construction, not merely absent.
//! * **no UI** — zero egui/eframe/tauri/iced/slint/wasm/winit/wgpu in the whole
//!   manifest. `nornir-appshell` is the serde-only About-card MODEL, not a card.
//! * **no solo** — `solo` means the UI with the server running inside it. With neither
//!   half there is nothing to embed and nothing to put on screen.
//!
//! ## Removed 2026-08-03 because the matrix has no such row
//!
//! `modgunn-lib` (role `library`), `modgunn-image` (role `client`, kind `container`,
//! and no `bin`) and the `[[build.container]] modgunn-world-delta` descriptor. No
//! source was deleted: a Build Thing is a declared BUILD POINT, and holger's
//! `server/lib`, holger's `server/ui` and nornir all consume modgunn's library as an
//! ordinary cargo dependency with their own feature selection — never through that
//! atom, whose closure (`scan,provenance-verify`) matched none of theirs.

use std::collections::BTreeSet;
use std::path::Path;

use nornir_build_thing::plugin::Role;
use nornir_build_thing::suite_cli;
use nornir_build_thing::variant::{ArtifactKind, BuildVariant, BuildVariantSet, ServiceShape};

fn repo_root() -> &'static Path {
    Path::new(env!("CARGO_MANIFEST_DIR"))
}

fn set() -> BuildVariantSet {
    BuildVariantSet::discover(repo_root())
        .expect("discover .nornir/build-variants.toml")
        .expect("modgunn declares a .nornir/build-variants.toml")
}

/// **The five shapes of `build-matrix-2026-08-02.md`, as data.** `(atom, role,
/// linkage, bin)` — the ONLY rows a modgunn declaration may contain. Written here as
/// the matrix's own five names so the conformance guard below compares a declaration
/// against the matrix and not against itself.
const MATRIX_ROWS: [(&str, Role, ServiceShape, &str); 5] = [
    (
        "modgunn-server",
        Role::Server,
        ServiceShape::FatService,
        "modgunn-server",
    ),
    ("modgunn", Role::Client, ServiceShape::FatService, "modgunn"),
    (
        "modgunn-cli",
        Role::Client,
        ServiceShape::ThinStandalone,
        "modgunn-cli",
    ),
    (
        "modgunn-ui",
        Role::Client,
        ServiceShape::ThinStandalone,
        "modgunn-ui",
    ),
    (
        "modgunn-solo",
        Role::Solo,
        ServiceShape::FatService,
        "modgunn-solo",
    ),
];

/// Every problem with a declaration measured against [`MATRIX_ROWS`]: a variant whose
/// name is not a matrix atom, or whose role / linkage / bin disagrees with its row.
///
/// This is the check that implements *"ta bort allt inte stämmer med matrisen"*, and
/// it is deliberately a function over an arbitrary set so the RED cases below can
/// drive it with the declarations that were REMOVED and watch it report them.
fn matrix_problems(set: &BuildVariantSet) -> Vec<String> {
    let mut out = Vec::new();
    for v in &set.variants {
        let Some(row) = MATRIX_ROWS.iter().find(|(n, ..)| *n == v.name) else {
            out.push(format!(
                "atom '{}' (role {:?}, kind {:?}) is not a row of the build matrix — the five \
                 shapes are {}",
                v.name,
                v.role,
                v.kind,
                MATRIX_ROWS
                    .iter()
                    .map(|(n, ..)| *n)
                    .collect::<Vec<_>>()
                    .join(", ")
            ));
            continue;
        };
        let (name, role, linkage, bin) = row;
        if v.role != Some(*role) {
            out.push(format!(
                "atom '{name}': role {:?} but the matrix says {role:?}",
                v.role
            ));
        }
        if v.shape != *linkage {
            out.push(format!(
                "atom '{name}': linkage {:?} but the matrix says {linkage:?}",
                v.shape
            ));
        }
        if v.bin.as_deref() != Some(*bin) {
            out.push(format!(
                "atom '{name}': bin {:?} but the matrix says '{bin}'",
                v.bin
            ));
        }
    }
    out
}

/// ★ **EVERY DECLARED ATOM IS A MATRIX ROW, AND CARRIES THE SUITE TAG.**
///
/// The suite tag is what claims Build-Thing status, so this test and
/// [`every_declared_bin_is_a_real_cargo_target`] together are the whole contract:
/// what is tagged is in the matrix, and what is in the matrix can actually be built.
#[test]
fn every_declared_atom_is_a_matrix_row() {
    let set = set();

    assert_eq!(
        set.names(),
        vec!["modgunn"],
        "modgunn declares exactly the atoms it can build: the fat client, and nothing else"
    );
    assert!(
        matrix_problems(&set).is_empty(),
        "{:?}",
        matrix_problems(&set)
    );

    // Non-vacuous: the guard must actually have inspected a variant. An empty
    // declaration would pass the line above for the wrong reason.
    assert_eq!(set.variants.len(), 1, "one atom inspected, not zero");

    // The container axis is EMPTY. `[[build.container]]` cannot carry a `suites` tag
    // at all (`BuildVariantSet::suites()` / `declares_suites()` iterate `self.variants`
    // only), so under the rule "the tag is what makes it a Build Thing" a declared
    // container is a cell outside the set. modgunn's `modgunn-world-delta` image
    // descriptor was removed on 2026-08-03 rather than left as an untagged orphan.
    assert!(
        set.container_names().is_empty(),
        "containers are not on the suite axis; declared: {:?}",
        set.container_names()
    );

    // RED-WHEN-BROKEN (a): the two declarations that were REMOVED, driven through the
    // same guard — `modgunn-lib` is a role the matrix does not have, and
    // `modgunn-image` is a client with no bin.
    let removed = BuildVariantSet::from_toml_str(
        r#"
[build]
base_suite = "ORIGINAL"
[[build.variant]]
name = "modgunn"
role = "client"
default = true
suites = ["ORIGINAL"]
package = "modgunn"
bin = "modgunn"
kind = "exe"
linkage = "fat"
profile = "release"
features = ["scan", "warehouse"]
[[build.variant]]
name = "modgunn-lib"
role = "library"
default = true
suites = ["ORIGINAL"]
package = "modgunn"
kind = "lib"
linkage = "not-applicable"
profile = "release"
features = ["scan"]
[[build.variant]]
name = "modgunn-image"
role = "client"
suites = ["ORIGINAL"]
kind = "container"
linkage = "fat"
container_ref = "modgunn-world-delta"
"#,
    )
    .expect("parse the pre-2026-08-03 declaration");
    let problems = matrix_problems(&removed);
    assert_eq!(
        problems.len(),
        2,
        "both off-matrix atoms reported: {problems:?}"
    );
    assert!(
        problems[0].contains("'modgunn-lib' (role Some(Library)"),
        "{}",
        problems[0]
    );
    assert!(
        problems[0].contains("not a row of the build matrix"),
        "{}",
        problems[0]
    );
    assert!(problems[1].contains("'modgunn-image'"), "{}", problems[1]);

    // RED-WHEN-BROKEN (b): a MATRIX-NAMED atom that lies about its own row. This is the
    // case the name check alone cannot see — `modgunn` declared thin would pass a
    // "is it in the list" test and be false.
    let lying = BuildVariantSet::from_toml_str(
        r#"
[build]
base_suite = "ORIGINAL"
[[build.variant]]
name = "modgunn"
role = "server"
default = true
suites = ["ORIGINAL"]
package = "modgunn"
bin = "modgunn-daemon"
kind = "exe"
linkage = "thin"
profile = "release"
features = ["scan"]
"#,
    )
    .expect("parse");
    let problems = matrix_problems(&lying);
    assert_eq!(
        problems.len(),
        3,
        "role + linkage + bin all reported: {problems:?}"
    );
    assert!(
        problems[0].contains("role Some(Server) but the matrix says Client"),
        "{}",
        problems[0]
    );
    assert!(
        problems[1].contains("linkage ThinStandalone but the matrix says FatService"),
        "{}",
        problems[1]
    );
    assert!(
        problems[2].contains("bin Some(\"modgunn-daemon\") but the matrix says 'modgunn'"),
        "{}",
        problems[2]
    );
}

/// The `.nornir/build-variants.toml` discovers, validates, and materializes the exact
/// cargo argv `cargo xtask build --suite ORIGINAL` runs.
#[test]
fn build_variants_discover_and_validate() {
    let set = set();

    // The whole declared matrix validates (offers / required-features / fat-thin
    // shape + the suite asymmetry graph + kind coherence).
    set.validate_all()
        .expect("modgunn's declared build variants validate");

    // Every ROLE names exactly one normal case — a menu with no default makes the
    // caller guess.
    assert!(
        set.check_defaults().is_empty(),
        "{:?}",
        set.check_defaults()
    );

    // ── ⭐ THE FAT CLIENT: matrix row `ORIGINAL modgunn client fat modgunn`. ────
    let cli = set.get("modgunn").expect("the ⭐ fat-client atom");
    assert_eq!(
        cli.role,
        Some(Role::Client),
        "a batch CLI that exits is driven, not deployed"
    );
    assert_eq!(cli.member.as_deref(), Some("modgunn"));
    assert_eq!(
        cli.kind,
        ArtifactKind::Exe,
        "a plain executable, not a daemon skidbladnir installs"
    );
    assert_eq!(
        cli.shape,
        ServiceShape::FatService,
        "warehouse ⇒ dep:skade is compiled IN — it works with no server running, which is what \
         the matrix means by a fat client"
    );
    assert_eq!(cli.bin.as_deref(), Some("modgunn"));
    assert_eq!(cli.suites, vec!["ORIGINAL".to_string()]);
    assert!(
        cli.features.contains(&"scip".to_string()),
        "Engine B present"
    );
    assert!(
        cli.features.contains(&"warehouse".to_string()),
        "warehouse seam"
    );
    assert!(
        cli.features.contains(&"rustsec".to_string()),
        "advisory-db import"
    );
    // `embeds` is EMPTY, and that is not a retreat from the skade claim. Cargo.toml
    // defines `warehouse = ["dep:skade", …]`, so the feature list two lines up IS
    // the embed, and `linkage = "fat"` is the self-containment claim. `validate_kind`
    // forbids `embeds` on a non-service kind, and the duplicate key was the thing
    // worth losing — not the fact.
    assert!(
        cli.embeds.is_empty(),
        "kind 'exe' composes no service (validate_kind)"
    );
    assert!(
        cli.reaches.is_empty(),
        "modgunn has no tonic/prost/nornir-service — there is no wire to reach over"
    );
    // `provenance-verify` is named EXPLICITLY. It is in modgunn's Cargo `default`,
    // so an older declaration compiled it (and its nornir-build-thing +
    // znippy-common/sign closure) without ever saying so: the declared feature set
    // was a strict subset of what was actually built.
    assert!(
        cli.no_default_features,
        "the closure is stated, not inherited"
    );
    assert!(
        cli.features.contains(&"provenance-verify".to_string()),
        "a default feature that is compiled must be declared"
    );
    // The ArtifactKind seam materializes the exact cargo argv the xtask hands cargo.
    assert_eq!(
        cli.cargo_args(),
        vec![
            "build",
            "-p",
            "modgunn",
            "--bin",
            "modgunn",
            "--features",
            "scan,scip,warehouse,rustsec,provenance-verify",
            "--no-default-features",
            "--profile",
            "release",
        ],
        "the fat client materializes its cargo build argv"
    );

    // RED-WHEN-BROKEN: a `lib` that leaves the linkage unsaid inherits the `fat`
    // default — a positive claim of self-containment nobody checked. `validate`
    // rejects it, which is the machinery that made `modgunn-lib` declarable at all
    // and is still worth having live.
    let mut silent = cli.clone();
    silent.name = "modgunn-lib".into();
    silent.kind = ArtifactKind::Lib;
    silent.bin = None;
    silent.shape = ServiceShape::FatService;
    let err = set
        .validate(&silent)
        .expect_err("a lib may not claim a linkage");
    assert!(err.to_string().contains("not-applicable"), "{err}");

    modgunn::functional_status(
        "modgunn/build-variants",
        "matrix_conformance",
        set.names() == vec!["modgunn"]
            && matrix_problems(&set).is_empty()
            && cli.kind == ArtifactKind::Exe
            && cli.role == Some(Role::Client)
            && cli.shape == ServiceShape::FatService
            && cli.bin.as_deref() == Some("modgunn")
            && cli.embeds.is_empty()
            && set.container_names().is_empty()
            && set.check_defaults().is_empty(),
        "modgunn declares exactly one Build Thing and it is a row of build-matrix-2026-08-02: the \
         ORIGINAL fat client `modgunn`",
    );
}

/// ★ **THE FOUR GAPS, ASSERTED AS POSITIVE CLAIMS.** modgunn has no daemon, no wire,
/// no UI and therefore no solo build. Four of the matrix's five shapes are fiction for
/// this member, and naming them beats shipping stubs that build and do nothing.
#[test]
fn the_four_missing_atoms_are_declared_missing() {
    let set = set();

    // ── no server ─────────────────────────────────────────────────────────────
    assert_eq!(
        set.default_for_role(Some(Role::Server)),
        None,
        "modgunn declares a SERVER atom, but it has no daemon: no listener outside \
         #[cfg(test)], the service module is behind a feature no atom builds, and the \
         unit it renders ExecStarts a verb that does not exist"
    );
    assert!(
        set.variants.iter().all(|v| v.role != Some(Role::Server)),
        "no modgunn variant may claim the server role: {:?}",
        set.variants
            .iter()
            .filter(|v| v.role == Some(Role::Server))
            .map(|v| &v.name)
            .collect::<Vec<_>>()
    );
    // ── no solo ───────────────────────────────────────────────────────────────
    assert!(
        set.variants.iter().all(|v| v.role != Some(Role::Solo)),
        "solo = UI + server in one binary; modgunn has neither half"
    );
    // ── no thin client of any kind ────────────────────────────────────────────
    assert!(
        set.variants
            .iter()
            .all(|v| v.shape != ServiceShape::ThinStandalone),
        "a thin modgunn binary is impossible by construction: the only [[bin]] requires \
         `warehouse`, and `warehouse = [\"dep:skade\", …]` IS the fat dependency"
    );
    // The one atom that IS there, named as the normal case of its role.
    assert_eq!(
        set.default_for_role(Some(Role::Client))
            .map(|v| v.name.as_str()),
        Some("modgunn"),
        "the fat client is the normal case of the only role modgunn actually fills"
    );

    // ── the manifest is the oracle for the -cli and -ui gaps, read as TEXT so a
    // dependency added tomorrow trips this and forces the atom to be declared ──
    let manifest =
        std::fs::read_to_string(repo_root().join("Cargo.toml")).expect("read Cargo.toml");
    let dep_lines: Vec<&str> = manifest
        .lines()
        .map(str::trim)
        .filter(|l| !l.starts_with('#') && !l.is_empty())
        .collect();
    // Non-vacuous: the filter must have left real dependency lines behind. If the
    // read or the comment-strip silently emptied this, every loop below would pass
    // over nothing — the exact silent-green shape these gaps exist to prevent.
    assert!(
        dep_lines.iter().any(|l| l.starts_with("znippy-zoomies")),
        "the manifest scan found no known dependency line — the oracle is empty: {} lines",
        dep_lines.len()
    );

    for gui in [
        "egui",
        "eframe",
        "tauri",
        "iced",
        "slint",
        "wasm-bindgen",
        "winit",
        "wgpu",
    ] {
        for line in &dep_lines {
            assert!(
                !line.starts_with(gui),
                "modgunn declared a UI dependency ('{gui}') — the `modgunn-ui` gap is no longer a \
                 gap, so declare the atom instead of leaving this test lying: {line}"
            );
        }
    }
    for wire in ["tonic", "prost", "nornir-service"] {
        for line in &dep_lines {
            assert!(
                !line.starts_with(wire),
                "modgunn declared a gRPC dependency ('{wire}') — a THIN client is now possible, so \
                 declare `modgunn-cli` instead of leaving this test lying: {line}"
            );
        }
    }

    // The bin's `required-features` is the structural half of the `-cli` gap: the only
    // executable modgunn ships cannot be built without the dependency that makes it
    // fat. Read from the manifest, not asserted from memory.
    assert!(
        manifest.contains(r#"required-features = ["warehouse", "scan"]"#),
        "the only [[bin]] still requires the fat `warehouse` feature — if that changed, a thin \
         modgunn binary may now be possible and `modgunn-cli` must be reconsidered"
    );
}

/// ★ **No two atoms may write the same file** ([`BuildVariantSet::check_bins`]).
///
/// modgunn is CLEAN on this axis and always was — edda's cross-repo collision
/// sweep (2026-08-01) found `korp` (3 variants → bin `korp`), `nornir` (3 →
/// `nornir`) and `dwarves` (2 → `dwarves`), and modgunn in none of them.
///
/// But modgunn's emptiness is STRUCTURAL, not earned: exactly one atom declares a
/// `bin` at all, so there is nothing that could collide. An assertion that can only
/// ever pass is not a guard, so the negative case below drives the same method on a
/// deliberately-colliding declaration and asserts it REPORTS — otherwise a future
/// second binary could reintroduce the defect under a green test.
#[test]
fn no_two_atoms_emit_the_same_binary() {
    let set = set();
    assert!(set.check_bins().is_empty(), "{:?}", set.check_bins());

    // RED-WHEN-BROKEN: the same declaration with a second atom on the same bin.
    let colliding = BuildVariantSet::from_toml_str(
        r#"
[build]
base_suite = "ORIGINAL"
[[build.variant]]
name = "modgunn"
role = "client"
default = true
suites = ["ORIGINAL"]
package = "modgunn"
bin = "modgunn"
kind = "exe"
profile = "release"
features = ["scan", "warehouse"]
[[build.variant]]
name = "modgunn-lean"
role = "client"
suites = ["ORIGINAL"]
package = "modgunn"
bin = "modgunn"
kind = "exe"
profile = "release"
features = ["scan", "warehouse", "scip"]
"#,
    )
    .expect("parse the colliding twin");
    let problems = colliding.check_bins();
    assert_eq!(
        problems.len(),
        1,
        "one collision, one message: {problems:?}"
    );
    assert!(problems[0].contains("SAME artifact"), "{}", problems[0]);
    assert!(
        problems[0].contains("modgunn, modgunn-lean"),
        "it names both: {}",
        problems[0]
    );
}

/// ★ **Every declared `bin` must be a `[[bin]]` cargo can actually build**
/// ([`BuildVariantSet::check_bins_declared`]) — the phantom-bin guard, and the half of
/// the Build-Thing contract that says a tagged atom is buildable.
///
/// The real target list is READ OUT of the manifests by
/// [`BuildVariantSet::cargo_bin_targets`], never hardcoded here: a hardcoded twin
/// is exactly the drift this guard exists to remove (korp renamed `korp` → `korp-ui`
/// in Cargo.toml and left three variants — and its own test's asserted argv string —
/// pointing at a target that no longer existed).
#[test]
fn every_declared_bin_is_a_real_cargo_target() {
    let set = set();
    let real = BuildVariantSet::cargo_bin_targets(repo_root()).expect("walk the manifests");
    // Non-vacuous: the walk must actually have found modgunn's one `[[bin]]`.
    assert!(
        real.contains(&"modgunn".to_string()),
        "the manifest walk found: {real:?}"
    );
    assert!(
        set.check_bins_declared(&real).is_empty(),
        "{:?}",
        set.check_bins_declared(&real)
    );

    // The four missing shapes are missing FROM THE MANIFEST TOO, which is why they
    // are not declared: had any of them been tagged, this guard would have failed.
    for phantom in [
        "modgunn-server",
        "modgunn-cli",
        "modgunn-ui",
        "modgunn-solo",
    ] {
        assert!(
            !real.contains(&phantom.to_string()),
            "'{phantom}' is now a real cargo target — declare the matrix atom for it"
        );
    }

    // RED-WHEN-BROKEN, twice over. (a) A real target list that does NOT contain the
    // declared bin must be reported…
    let wrong = set.check_bins_declared(&["modgunn-server"]);
    assert_eq!(wrong.len(), 1, "{wrong:?}");
    assert!(wrong[0].contains("declares bin 'modgunn'"), "{}", wrong[0]);
    assert!(
        wrong[0].contains("no `[[bin]]` target exists"),
        "{}",
        wrong[0]
    );
    // …(b) and an EMPTY oracle must be reported rather than passing vacuously, which
    // is what would happen if the manifest walk silently found nothing.
    let empty: [&str; 0] = [];
    let vacuous = set.check_bins_declared(&empty);
    assert_eq!(vacuous.len(), 1, "{vacuous:?}");
    assert!(vacuous[0].contains("VACUOUSLY"), "{}", vacuous[0]);
}

/// ★ **The suite tag** — `suites = ["ORIGINAL"]` on every atom, `base_suite` in
/// `[build]`, and `ORIGINAL` is the only name that exists here.
///
/// [`check_suites`] returns empty here and that is CORRECT, not a pass: below two
/// deliveries there is nothing to be a subset of. The two assertions together are
/// the point — `check_suites().is_empty()` alone cannot tell "checked and fine"
/// from "nothing was declared to check", and [`declares_suites`] is what says
/// which. Asserting only the first is the silent-green shape this pass exists to
/// end.
///
/// [`check_suites`]: BuildVariantSet::check_suites
/// [`declares_suites`]: BuildVariantSet::declares_suites
#[test]
fn every_atom_is_tagged_into_the_one_delivery() {
    let set = set();
    assert!(set.check_suites().is_empty(), "{:?}", set.check_suites());
    assert!(
        set.declares_suites(),
        "check_suites() is empty EITHER because the tags are fine OR because there are \
         none — this is what tells the two apart"
    );
    assert_eq!(
        set.suites(),
        vec!["ORIGINAL"],
        "one delivery, named — and only one"
    );
    assert_eq!(set.base_suite.as_deref(), Some("ORIGINAL"));
    // Every atom, not just some.
    for v in &set.variants {
        assert_eq!(
            v.suites,
            vec!["ORIGINAL".to_string()],
            "atom '{}' is untagged, so it would silently join the repo's UNNAMED delivery \
             and `--suite ORIGINAL` would not build it — and under the rule 'the tag is what \
             makes it a Build Thing' it would not be one at all",
            v.name
        );
    }

    // RED-WHEN-BROKEN: a blank suite name is a delivery with no name, which is
    // indistinguishable from declaring none.
    let blank = BuildVariantSet::from_toml_str(
        "[build]\nbase_suite = \"ORIGINAL\"\n\
         [[build.variant]]\nname = \"modgunn\"\nsuites = [\"\"]\ndefault = true\n",
    )
    .expect("parse");
    let problems = blank.check_suites();
    assert_eq!(problems.len(), 1, "{problems:?}");
    assert!(problems[0].contains("BLANK suite name"), "{}", problems[0]);
}

/// ★ **`--suite` must SELECT.** `--suite ORIGINAL` resolves to exactly the declared
/// atoms; `--suite NOPE` FAILS LOUDLY and names the real delivery. A selector that
/// ignores an unknown name is the silent-green shape — it would keep a
/// "we build the ORIGINAL suite nightly" claim green while building anything at all.
#[test]
fn the_one_build_verb_resolves_this_repos_delivery() {
    let set = set();

    let args = suite_cli::parse(&["--suite", "ORIGINAL"]).expect("parse");
    let (suite, atoms) = suite_cli::resolve(&set, &args).expect("ORIGINAL resolves");
    assert_eq!(suite, "ORIGINAL");
    assert_eq!(
        atoms.iter().map(|v| v.name.as_str()).collect::<Vec<_>>(),
        vec!["modgunn"],
        "exactly the declared atoms, in declaration order"
    );

    // One atom by name, and the argv the xtask will hand cargo.
    let one = suite_cli::parse(&["--suite", "ORIGINAL", "--atom", "modgunn"]).expect("parse");
    let (_, just_cli) = suite_cli::resolve(&set, &one).expect("the ⭐ atom resolves");
    assert_eq!(just_cli.len(), 1);
    assert_eq!(
        just_cli[0].command_line(),
        "cargo build -p modgunn --bin modgunn --features scan,scip,warehouse,rustsec,provenance-verify \
         --no-default-features --profile release"
    );

    // RED-WHEN-BROKEN: an unknown delivery is a hard error that lists the real one.
    // `MVP` is holger's second delivery and is NOT a modgunn suite — asking for it
    // here must fail rather than quietly build the one delivery that exists.
    for unknown in ["NOPE", "MVP"] {
        let bad = suite_cli::parse(&["--suite", unknown]).expect("parse");
        let err = suite_cli::resolve(&set, &bad).expect_err("an unknown suite must not shrug");
        let msg = err.to_string();
        assert!(msg.contains("no such delivery"), "{msg}");
        assert!(
            msg.contains("ORIGINAL"),
            "it must name the declared delivery: {msg}"
        );
    }

    // …and so is an unknown atom. Each of the four fictions must fail HERE rather
    // than resolve to something that builds nothing.
    for ghost in [
        "modgunn-server",
        "modgunn-cli",
        "modgunn-ui",
        "modgunn-solo",
        "modgunn-lib",
        "modgunn-image",
    ] {
        // `--atom` is the deprecated spelling of `--variant`; suite_cli accepts it but
        // NORMALIZES its rejection to the canonical vocabulary, so the phantom comes
        // back as "no such variant" (not "no such atom"). Assert the wording the code
        // actually emits today — a stale "atom" here made this guard red-when-fine.
        let g = suite_cli::parse(&["--suite", "ORIGINAL", "--atom", ghost]).expect("parse");
        let err = suite_cli::resolve(&set, &g).expect_err("a phantom atom must not resolve");
        assert!(err.to_string().contains("no such variant"), "{ghost}: {err}");
    }
}

/// **The headline asymmetry.** modgunn is the cycle-breaker: nornir and holger
/// DEPEND ON it, so they are upstream. A fat modgunn that embedded either would
/// invert the suite dep graph — `validate` MUST reject it. (Red-when-broken: if
/// the `[build.downstream]` allowlist dropped, this stops rejecting.)
///
/// Note this is the ONLY exercise of `[build.downstream]`: no real variant declares
/// `embeds` (kind `exe` forbids it, and `features = [… warehouse …]` already IS the
/// skade embed). The allowlist stays declared and stays checked HERE, which is stated
/// rather than left to be discovered.
#[test]
fn embedding_an_upstream_member_is_rejected() {
    let set = set();

    let inverted = BuildVariant {
        name: "modgunn-inverted".into(),
        member: Some("modgunn".into()),
        kind: ArtifactKind::Service,
        shape: ServiceShape::FatService,
        embeds: BTreeSet::from(["nornir".to_string()]), // nornir is UPSTREAM of modgunn
        profile: nornir_build_thing::variant::Profile::Release,
        ..Default::default()
    };

    let err = set
        .validate(&inverted)
        .expect_err("modgunn must NOT embed nornir (graph inversion)");
    let msg = err.to_string();
    assert!(msg.contains("may not embed 'nornir'"), "message: {msg}");
    assert!(msg.contains("DOWNSTREAM"), "explains the rule: {msg}");
}