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

mikronova/draupnir

crates/draupnir-build-thing/tests/toml_parity.rs

//! **`.nornir/build-variants.toml` IS the code's emission — and this test fails
//! for any reason it is not.**
//!
//! Three properties, in the shape lane AG settled on gunnar (2026-08-30):
//!
//! 1. the committed bytes ARE the emission, byte for byte — the assertion that
//!    fails for ANY reason the two differ: a row added to the code and not
//!    re-emitted, a hand edit, a reordering, a comment typed into the output.
//!    The fix is never to edit the toml; if the emission does not say what you
//!    wanted the file to say, the recipe is missing something and THAT is the
//!    bug;
//! 2. the file is INERT — an edit is visible to a reader and invisible to the
//!    recipe, which is what "never read back as authority" means, measured. The
//!    perturbation is asserted visible first, or this proves nothing;
//! 3. a group at a path the reader would DROP is a REFUSAL that names the path,
//!    not a silence. This is lane AF's measured defect put back: two whole
//!    groups lived in gunnar's toml and not in its code for four pin
//!    generations with parity GREEN, because the loader discarded them before
//!    the comparison ran. R3 (`nornir_build_thing::schema::dropped_keys`, inside
//!    `from_toml_str`) closes it, and this asserts it on THIS repo's file.

use nornir_build_thing::emit::first_difference;
use nornir_build_thing::plugin::BuildThing;
use nornir_build_thing::variant::BuildVariantSet;
use draupnir_build_thing::{emit::repo_root, emitted_toml, DraupnirBuildThing};

fn committed() -> String {
    let path = BuildVariantSet::conventional_path(&repo_root());
    std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()))
}

#[test]
fn the_committed_toml_is_the_codes_emission_byte_for_byte() {
    let emitted = emitted_toml().expect("the recipe emits");
    if let Some(diff) = first_difference(&committed(), &emitted) {
        panic!(
            "{} is not the code's emission — the code is the source. Run \
             `cargo run -p draupnir-build-thing -- emit` and commit the result; never edit the \
             toml by hand. {diff}",
            BuildVariantSet::conventional_path(&repo_root()).display()
        );
    }
    let back = BuildVariantSet::from_toml_str(&emitted).expect("the emission parses back");
    assert_eq!(back, DraupnirBuildThing.variants());
    back.validate_all().expect("and it validates");
}

#[test]
fn an_edited_toml_has_zero_effect_on_what_the_recipe_declares() {
    let declared = DraupnirBuildThing.variants();
    let first = declared
        .variants
        .first()
        .expect("this repo declares at least one variant")
        .name
        .clone();
    let edited = emitted_toml()
        .unwrap()
        .replace(&format!("name = \"{first}\""), "name = \"edited-by-a-human\"");
    let read = BuildVariantSet::from_toml_str(&edited).expect("still parses");
    assert_ne!(
        read, declared,
        "the edit must be visible to a reader, else this test proves nothing"
    );
    assert_eq!(
        DraupnirBuildThing.variants(),
        declared,
        "the recipe read the file — it must not"
    );
}

/// **Nothing this file says is silently discarded** — the third property, and
/// the one lane AF's measurement is about.
///
/// Two mechanisms answer it and the loader owns both, which is why this asserts
/// the OUTCOME rather than either mechanism: on a CLOSED type
/// (`BuildVariantSet`, `BuildVariant`) `deny_unknown_fields` refuses; on an OPEN
/// one (`InstallerBuild`, `IsoBuild`, `ContainerBuild` — open on purpose, so an
/// additive key cannot make a whole recipe unloadable for an older reader) R3
/// refuses, `nornir_build_thing::schema::dropped_keys` inside `from_toml_str`.
/// Before R3 the open half had NOTHING standing in for it, and that is exactly
/// where gunnar lost two whole groups for four pin generations with its parity
/// test green.
///
/// The assertion is that the refusal NAMES the key: an unnamed refusal is not
/// much better than the silence it replaces.
#[test]
fn a_key_no_reader_has_a_field_for_is_refused_by_name() {
    let emitted = emitted_toml().unwrap();
    BuildVariantSet::from_toml_str(&emitted).expect("the real emission loads");
    let with_an_unknown_key =
        format!("{emitted}
[build.a_group_no_reader_has_a_field_for]
k = 1
");
    let err = BuildVariantSet::from_toml_str(&with_an_unknown_key)
        .expect_err("a key the reader cannot read must be a refusal, not a silence");
    let chain = format!("{err:#}");
    assert!(
        chain.contains("a_group_no_reader_has_a_field_for"),
        "the refusal must NAME the key — an unnamed one is not much better than          the silence it replaces: {chain}"
    );
}

/// LAW 2, on the comparisons above: a loader that returned a default would make
/// every one of them green about a file it never read.
#[test]
fn the_loader_is_reading_what_this_test_thinks_it_is() {
    let path = BuildVariantSet::conventional_path(&repo_root());
    let set = BuildVariantSet::load_file(&path)
        .unwrap_or_else(|e| panic!("load {}: {e:#}", path.display()));
    assert_eq!(
        set.variants.len(),
        3,
        "the file parsed to {} variants; this repo declares 3",
        set.variants.len()
    );
    let mut perturbed = set.clone();
    perturbed.variants.pop().expect("a variant to drop");
    assert_ne!(
        perturbed, set,
        "BuildVariantSet compares equal after a variant was removed, so the \
         assertions here can never report red"
    );
}