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

nordisk/gunnar

installtest/tests/medium_census.rs

//! **A RELEASE medium ships an EMPTY `/data`, and this opens the medium to
//! check it.**
//!
//! # The defect, measured
//!
//! On 2026-09-05 a gunnar appliance ISO was composed with
//! `data/control.git/secrets.kek` — a test-minted key-encrypting key —
//! `data/control.redb` and a bootstrapped repository in its initramfs,
//! indistinguishable by name from a release medium. It built green, booted,
//! and served. **No test in the tree could have caught it**, because every
//! check looked at what the build intended; the only thing that found it was
//! extracting the ISO and listing what came out.
//!
//! `korp_installer::forge` now refuses a seeded staging TREE by name, and
//! `iso_output_path` names a fixture medium `-fixture.iso`. This is the third
//! and last point: what the PRODUCED MEDIUM actually carries. A defect has to
//! defeat all three separately, and this one cannot be defeated by any amount
//! of correct intent.
//!
//! # Self-gating
//!
//! `.iso-out/` is not checked in and is written by whichever checkout composed
//! it, so a medium is either there or it is not. Absent media SKIP loudly, by
//! name — never a faked green. The test refuses to pass on an EMPTY
//! population, because "no media were checked" and "every medium was clean"
//! are the same exit code otherwise.
//!
//! ```bash
//! cargo test --manifest-path installtest/Cargo.toml --test medium_census -- --nocapture
//! ```

use std::path::PathBuf;

use gunnar_installtest::medium::{census, MediumCensus};

fn iso_out() -> PathBuf {
    gunnar_installtest::workspace_root().join(".iso-out")
}

/// Every `.iso` present, censused. A medium that cannot be read is a FAILURE,
/// not a skip: an unreadable medium is exactly the state a broken composer
/// leaves behind, and skipping it would hide the thing worth knowing.
fn all_media() -> Vec<MediumCensus> {
    let dir = iso_out();
    let Ok(rd) = std::fs::read_dir(&dir) else {
        return Vec::new();
    };
    let mut paths: Vec<PathBuf> = rd
        .flatten()
        .map(|e| e.path())
        .filter(|p| p.extension().and_then(|x| x.to_str()) == Some("iso"))
        .collect();
    paths.sort();
    paths
        .iter()
        .map(|p| {
            census(p).unwrap_or_else(|e| {
                panic!(
                    "{} is on disk and could not be read: {e}\n\
                     An unreadable medium is not a reason to skip — it is the finding.",
                    p.display()
                )
            })
        })
        .collect()
}

/// **The guard.** No medium whose name does not say `-fixture` carries
/// anything under `data/`, and every medium that DOES carry something says so
/// from the inside.
#[test]
fn a_release_medium_ships_an_empty_data_and_a_fixture_one_says_it_is_one() {
    let media = all_media();
    if media.is_empty() {
        eprintln!(
            "SKIP: no `.iso` in {} — compose one with `dwarves buildthing gunnar --variant \
             gunnar-appliance-iso` (a forge built `--features appliance-iso`). NOT a pass.",
            iso_out().display()
        );
        return;
    }

    let suffix = nornir_build_thing::image_build::FIXTURE_ISO_SUFFIX;
    let mut checked_release = 0usize;
    let mut checked_fixture = 0usize;

    for m in &media {
        eprintln!("\n=== {} ({} B) ===", m.name, m.bytes);
        for pe in &m.pes {
            eprintln!("  {}", pe.line());
        }
        let seeded = m.seeded_data();
        eprintln!(
            "  initramfs: {} entries; data/ content: {}",
            m.initramfs.len(),
            if seeded.is_empty() {
                "none".to_string()
            } else {
                format!("{seeded:?}")
            }
        );

        let is_fixture_name = m.name.contains(suffix);
        if is_fixture_name {
            checked_fixture += 1;
            // The positive control. A fixture medium that carries NOTHING
            // would make the release assertion below vacuous — it would be
            // passing because nothing anywhere has a seed, not because the
            // release media are clean.
            assert!(
                !seeded.is_empty(),
                "{} is named a fixture medium and carries NO data/ seed. Either the e2e did not \
                 graft one, or the reader is not seeing it — and if nothing on this box carries \
                 a seed, the release assertion below proves nothing.",
                m.name
            );
            assert!(
                m.marked_fixture(),
                "{} carries a data/ seed ({seeded:?}) and does NOT carry `{}` — a fixture medium \
                 must say so from the inside, where a booted box and a test can both read it",
                m.name,
                korp_installer::forge::FIXTURE_SEED_MARK
            );
        } else {
            checked_release += 1;
            assert!(
                seeded.is_empty(),
                "{} is a RELEASE-named medium and its initramfs carries {} file(s) under data/: \
                 {seeded:?}. /data ships EMPTY on a release medium; this is the shape a forge \
                 e2e run leaves behind (a fixture control store and a test-minted key), and an \
                 appliance booted from it starts with a KNOWN key.",
                m.name,
                seeded.len()
            );
            assert!(
                !m.marked_fixture(),
                "{} is release-named and carries the fixture mark inside it — the name and the \
                 medium disagree about what it is",
                m.name
            );
        }
    }

    eprintln!(
        "\n{checked_release} release medium/media and {checked_fixture} fixture medium/media \
         checked."
    );
    assert!(
        checked_release > 0,
        "every medium on disk was fixture-named — this guard checked no release medium at all, \
         which is the same exit code as checking many and finding them clean"
    );
}