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

nordisk/gunnar

crates/gunnar-recipe/tests/plain_cargo_cells.rs

//! **The Build Thing's client and server cells are plain cargo builds.**
//!
//! Owner order 2026-10-12: the client (`gunnar-cli` since GUN-PRODUCTS) and core `gunnar-server` must
//! build with ordinary cargo AND as Build Thing cells, from the same code. A
//! cell is `cargo build -p <package> --bin <bin> [--no-default-features]
//! --features …`; this file holds the cells to exactly the commands
//! `crates/gunnar-cli/README.md` ("Building with plain cargo") prints, so the
//! two ways of building cannot drift into two programs. The graph half — what
//! those commands link — is `crates/gunnar-cli/tests/plain_cargo.rs`.

use gunnar_recipe::variants::{by_name, Variant, VARIANTS};

/// The cargo arguments a cell's build passes (target and profile aside).
fn cargo_args(v: &Variant) -> String {
    let mut args = vec![format!(
        "-p {}",
        v.package.expect("a cargo cell names its package")
    )];
    if let Some(bin) = v.bin {
        args.push(format!("--bin {bin}"));
    }
    if v.no_default_features {
        args.push("--no-default-features".into());
    }
    if !v.features.is_empty() {
        args.push(format!("--features {}", v.features.join(",")));
    }
    args.join(" ")
}

#[test]
fn the_client_cells_are_the_plain_cargo_client_build() {
    // GUN-PRODUCTS (2026-10-30): the command-line tool is ONE program, `gunnar-cli`, and
    // every cell that delivers it is the product's build (its defaults, named out loud).
    let documented =
        "-p gunnar-cli --bin gunnar-cli --no-default-features --features client,control";
    for name in ["gunnar-cli", "gunnar-cli-musl"] {
        let v = by_name(name).unwrap_or_else(|| panic!("the {name} cell is declared"));
        assert_eq!(
            cargo_args(v),
            documented,
            "the {name} cell drifted from plain cargo"
        );
    }
    let cli_bins: Vec<&Variant> = VARIANTS
        .iter()
        .filter(|v| v.bin == Some("gunnar-cli"))
        .collect();
    assert!(
        cli_bins.len() >= 2,
        "{} cells deliver `gunnar-cli`",
        cli_bins.len()
    );
    for v in cli_bins {
        assert_eq!(
            cargo_args(v),
            documented,
            "cell {} builds a different `gunnar-cli`",
            v.name
        );
    }
    // No cell delivers a `gunnar` from gunnar-cli any more: that file is the server's.
    assert!(
        !VARIANTS
            .iter()
            .any(|v| v.bin == Some("gunnar") && v.package == Some("gunnar-cli")),
        "a gunnar-cli cell still delivers `gunnar`, the server's file name"
    );
}

#[test]
fn the_server_cells_build_core_gunnar_server_and_only_metered_ones_add_money() {
    let server: Vec<&Variant> = VARIANTS
        .iter()
        .filter(|v| v.bin == Some("gunnar-server"))
        .collect();
    assert!(server.len() >= 3, "{} gunnar-server cells", server.len());
    for v in server {
        assert_eq!(
            v.package,
            Some("gunnar-server"),
            "cell {} builds another package",
            v.name
        );
        let money = v.features.contains(&"metered");
        assert_eq!(
            money,
            v.name.contains("metered"),
            "cell {}: `metered` (monetize) must be on exactly the cells named for it",
            v.name
        );
        for f in v.features {
            assert!(
                !f.contains("ui") && !f.contains("shop") && !f.starts_with("monetize"),
                "cell {} turns on {f}: core gunnar-server has no UI or shop",
                v.name
            );
        }
    }
}