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

nordisk/facett

facett-confirm/tests/matrix.rs

//! **CONFIRM-GATE STATE MATRIX** — a parameter sweep over the whole
//! guard/phrase space that FEEDS real input and ASSERTS real output per case
//! (the inject-and-assert LAW, not no-crash).
//!
//! The gate's one job is a **safety invariant**: it arms (and only then yields
//! [`Effect::Confirmed`] on a confirm) *iff* the host's guard says the target is
//! allowed AND the operator typed the required phrase. Each case sweeps one
//! `(allow, typed-phrase, override-phrase)` tuple, computes the EXPECTED arm
//! verdict **independently** in the test, drives the real `ConfirmGate`, and
//! asserts the component's own `is_armed()` + the effect it produced match — so
//! a broken arming rule (e.g. dropping the guard or the phrase check) turns a
//! row RED, not a stub always-green.
//!
//! Each case emits ONE `functional_status` row (`facett-confirm`) so the whole
//! guard space is provably covered in the nornir warehouse. Built + run with
//! `--features testmatrix`.
//!
//! Run: `cargo test -p facett-confirm --features testmatrix --test matrix`

use facett_confirm::{Allow, ConfirmGate, Effect, Msg};

/// Gated one-line emit shim (real row emitter with `--features testmatrix`,
/// compiled-out no-op otherwise — same shape as the crate's own wrapper).
#[cfg(feature = "testmatrix")]
fn emit(component: &str, check: &str, ok: bool, detail: &str) {
    nornir_testmatrix::functional_status(component, check, ok, detail);
}
#[cfg(not(feature = "testmatrix"))]
fn emit(_component: &str, _check: &str, _ok: bool, _detail: &str) {}

/// One swept case's parameters.
#[derive(Clone, Copy)]
struct Case {
    allow: Allow,
    /// What the operator types.
    typed: &'static str,
    /// An optional custom phrase (else phrase == target).
    override_phrase: Option<&'static str>,
}

#[test]
fn confirm_gate_guard_matrix() {
    let target = "njord-lab";
    let allows = [Allow::Yes, Allow::No];
    // Typed inputs: exact target, wrong text, empty, and the override phrase.
    let typeds = ["njord-lab", "njord", "", "DROP"];
    // Phrase modes: default (== target) and an explicit override.
    let overrides = [None, Some("DROP")];

    let mut cases: Vec<Case> = Vec::new();
    for &allow in &allows {
        for &typed in &typeds {
            for &override_phrase in &overrides {
                cases.push(Case { allow, typed, override_phrase });
            }
        }
    }

    let mut failures: Vec<String> = Vec::new();
    let mut armed_cases = 0usize;

    for c in &cases {
        // Build the real gate for this case.
        let mut g = ConfirmGate::new("Restore", target, c.allow);
        if let Some(p) = c.override_phrase {
            g = g.require_phrase(p);
        }
        g.update(Msg::Typed(c.typed.to_string()));

        // EXPECTED arm verdict, computed independently of the component: allowed
        // AND typed matches the *effective* phrase (override, else target).
        let effective_phrase = c.override_phrase.unwrap_or(target);
        let expected_arm = c.allow == Allow::Yes && c.typed == effective_phrase;

        // The component's own verdict + the effect a confirm actually produces.
        let got_arm = g.is_armed();
        let effects = g.update(Msg::Confirm);
        let confirmed = effects == vec![Effect::Confirmed];

        // Cancel must ALWAYS yield exactly one Cancelled effect, regardless of arm.
        let cancelled = g.update(Msg::Cancel) == vec![Effect::Cancelled];

        let ok = got_arm == expected_arm && confirmed == expected_arm && cancelled;
        if got_arm {
            armed_cases += 1;
        }
        if !ok {
            failures.push(format!(
                "allow={:?} typed={:?} phrase={:?}: expected_arm={expected_arm} got_arm={got_arm} confirmed={confirmed} cancelled={cancelled}",
                c.allow, c.typed, effective_phrase
            ));
        }

        // EMIT one row per case (warehouse coverage of the guard space).
        emit(
            "facett-confirm",
            "guard_matrix",
            ok,
            &format!(
                "allow={:?} typed={:?} phrase={:?} expected_arm={expected_arm} got_arm={got_arm} confirmed={confirmed} cancelled={cancelled}",
                c.allow, c.typed, effective_phrase
            ),
        );
    }

    eprintln!(
        "[confirm matrix] {} cases, {armed_cases} armed, {} failures",
        cases.len(),
        failures.len()
    );

    // ── matrix-level oracles ──
    assert!(
        failures.is_empty(),
        "{} of {} guard cases violated the arm invariant:\n{}",
        failures.len(),
        cases.len(),
        failures.join("\n")
    );
    // The sweep must exercise BOTH arms of the invariant — at least one armed and
    // one refused case — or it proves nothing.
    assert!(armed_cases > 0, "no case ever armed — the sweep is degenerate");
    assert!(armed_cases < cases.len(), "every case armed — the guard is not gating");

    emit(
        "facett-confirm",
        "guard_matrix_summary",
        failures.is_empty(),
        &format!("cases={} armed={armed_cases} failures={}", cases.len(), failures.len()),
    );
}