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

nordisk/dwarves

dwarves-gui/tests/robot_error_codes.rs

//! **ROBOT-UI (LAW): dwarves' non-UI errors carry a STABLE CODE on every channel.**
//!
//! The companion to the registry's own unit guards (`dwarves::errcode::tests` — uniqueness,
//! well-formedness, and the WIRING guard that fails if a `wired` row is not actually
//! raised in its declared file). Those prove the CATALOG is honest. This file proves the
//! codes actually reach the surfaces a consumer reads, driven through the SHIPPED
//! host-only shell (`build_native_app`) and the REAL warehouse proxy:
//!
//!  1. [`the_build_thing_scene_refuses_by_name_through_the_shipped_shell`] — an EMPTY
//!     constellation through the shell makes the ⚒ scene REFUSE by name (what was asked
//!     for, and the root it searched), with no half-built matrix left behind. A populated
//!     constellation resolves — so the refusal is a real signal, not a constant.
//!  2. [`a_dead_upstream_puts_the_proxy_code_on_the_grpc_status_and_the_wire`] — the REAL
//!     dwarves warehouse proxy fronting a DEAD upstream answers a `Warehouse.Scan` with a
//!     gRPC `Status` whose message carries `dwarves-proxy-2` AND whose `x-dwarves-code`
//!     metadata header carries it structurally — the code reaches a client that never
//!     parses prose.
//!  3. [`every_wired_registry_code_is_reachable_and_the_split_is_honest`] — the registry's
//!     wired/reserved split is re-asserted from the test side (a wired code is raised in
//!     its file; a reserved one is not raised anywhere), and every code is well-formed +
//!     unique. This is the anti-fiction net: a catalog that claims coverage it does not
//!     have fails HERE too, not only in the crate's own unit tests.
//!
//! Each emits a `nornir-testmatrix` cell under `--features testmatrix`.

use dwarves::errcode;
use dwarves_gui::build_native_app;
use facett_core::Facet;
use serde_json::json;

const NORNIR_POISON: &str = "http://nornir-poison.example:7878";
const DEAD_DWARVES_HOST: &str = "http://127.0.0.1:9";
/// A dead upstream the proxy fronts — nothing listens, so every forwarded read fails.
const DEAD_UPSTREAM: &str = "http://127.0.0.1:9";

#[allow(unused_variables)]
fn matrix(check: &str, ok: bool, detail: &str) {
    #[cfg(feature = "testmatrix")]
    nornir_testmatrix::functional_status("dwarves-gui", check, ok, detail);
}

/// Build the shipped shell host-only over `root` as the constellation.
fn shell_over(root: &std::path::Path) -> dwarves_gui::tabbed::TabbedView {
    // SAFETY: single-purpose test binary; construction reads the env synchronously.
    unsafe {
        std::env::set_var("NORNIR_SERVER", NORNIR_POISON);
        std::env::set_var("DWARVES_SERVER", DEAD_DWARVES_HOST);
        std::env::set_var("DWARVES_CONSTELLATION", root);
        std::env::set_var("DWARVES_WAREHOUSES", "demo");
    }
    build_native_app(
        DEAD_DWARVES_HOST.to_string(),
        std::path::PathBuf::from("/nonexistent/dwarves-gui/errcodes"),
    )
}

/// ROBOT — **the ⚒ Build Thing scene refuses honestly through the SHIPPED shell.**
///
/// The pane that folded `dwarves-buildthing-1` out of a roster is retired; the scene
/// that replaced it has no roster to be empty. What it owes instead is a refusal that
/// NAMES what it looked for and where — so this drives the shipped shell over an empty
/// constellation, asks it to resolve a name, and asserts the refusal is present and
/// specific. A populated constellation resolves, so the refusal is a real signal and
/// not a constant.
#[test]
fn the_build_thing_scene_refuses_by_name_through_the_shipped_shell() {
    // ── EMPTY constellation: nothing declares anything. ─────────────────────────────
    let empty = std::env::temp_dir().join(format!("dwarves-errcode-empty-{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&empty);
    std::fs::create_dir_all(&empty).expect("mkdir empty root");

    let mut app = shell_over(&empty);
    Facet::update_json(&mut app, &json!({ "set_tab": "Build Thing" }).to_string());
    Facet::update_json(&mut app, &json!({ "artifact": "app" }).to_string());
    Facet::update_json(&mut app, &json!({ "resolve": true }).to_string());
    let bt = Facet::state_json(&app)["build-thing"].clone();
    let refusal = bt["error"].as_str().unwrap_or_default().to_string();
    // An EMPTY root owes the ROOT it searched and the VARIABLE that fixes it — NOT the
    // artifact name. `1859b82` ("an empty constellation root says so, instead of saying
    // 'not found'") split that case out on purpose: "ingen deklaration nämner app" is
    // equally true of a root holding 40 checkouts and one holding none, and only one of
    // those is the operator's fault. This assertion still demanded `"app"` and had been
    // red ever since, while the guard written FOR the new wording
    // (`robot_build_scene_empty_root.rs:116`) was green throughout.
    let named = refusal.contains(&empty.display().to_string())
        && refusal.contains("DWARVES_CONSTELLATION")
        && !refusal.contains("app");
    assert!(
        named,
        "the empty-root refusal must name the root and the variable that fixes it, and must \
         NOT blame the artifact (that misdiagnosis is what 1859b82 removed): {bt}"
    );
    let no_matrix = bt["rows"].as_array().map(|a| a.is_empty()).unwrap_or(false)
        && bt["recipe"].is_null()
        && bt["can_build"] == json!(false);
    assert!(
        no_matrix,
        "a refused resolve left a half-built matrix behind: {bt}"
    );

    // ── POPULATED constellation: it resolves (the refusal is not a constant). ───────
    let full = std::env::temp_dir().join(format!("dwarves-errcode-full-{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&full);
    let dir = full.join("demorepo").join(".nornir");
    std::fs::create_dir_all(&dir).expect("mkdir staged");
    std::fs::write(
        dir.join("build-variants.toml"),
        "[[build.variant]]\nname = \"app\"\npackage = \"demo\"\nbin = \"demo\"\ndefault = true\n",
    )
    .expect("write staged declaration");

    let mut app2 = shell_over(&full);
    Facet::update_json(&mut app2, &json!({ "set_tab": "Build Thing" }).to_string());
    Facet::update_json(&mut app2, &json!({ "artifact": "app" }).to_string());
    Facet::update_json(&mut app2, &json!({ "resolve": true }).to_string());
    let bt2 = Facet::state_json(&app2)["build-thing"].clone();
    let resolved = bt2["error"].is_null()
        && bt2["recipe"]
            .as_str()
            .unwrap_or_default()
            .starts_with("demorepo-build-thing")
        && bt2["row"] == json!("app");
    assert!(resolved, "a staged declaration did not resolve: {bt2}");

    // ── AND THE NAME MISS, which is where "refuses BY NAME" means anything. Same
    //    POPULATED root, same shell, a name nobody declares: the refusal owes the NAME
    //    and the ROOT. Asserting it here rather than on the empty root is what keeps the
    //    two refusals distinguishable instead of collapsing them into one string.
    Facet::update_json(
        &mut app2,
        &json!({ "artifact": "no-such-artifact-anywhere" }).to_string(),
    );
    Facet::update_json(&mut app2, &json!({ "resolve": true }).to_string());
    let bt3 = Facet::state_json(&app2)["build-thing"].clone();
    let miss = bt3["error"].as_str().unwrap_or_default().to_string();
    let named_miss =
        miss.contains("no-such-artifact-anywhere") && miss.contains(&full.display().to_string());
    assert!(
        named_miss,
        "a POPULATED root refused without naming what was asked and where it looked: {bt3}"
    );

    let ok = named && no_matrix && resolved && named_miss;
    matrix(
        "build_scene_refuses_by_name_through_the_shipped_shell",
        ok,
        "the \u{2692} Build Thing SCENE, driven through the SHIPPED host-only shell, tells its two \
         refusals apart: an EMPTY constellation root names the root and $DWARVES_CONSTELLATION \
         and never blames the artifact, a POPULATED root that declares no such name names both \
         the name and the root, neither leaves a half-built matrix behind, and a staged \
         declaration still resolves to its recipe coordinate and row — so both refusals are \
         real signals, not constants",
    );
}

#[test]
fn a_dead_upstream_puts_the_proxy_code_on_the_grpc_status_and_the_wire() {
    // The REAL dwarves proxy in front of a DEAD upstream.
    let proxy = dwarves::warehouse_proxy::spawn_proxy(DEAD_UPSTREAM.to_string(), String::new());

    // Scan through the proxy — the forwarded read cannot reach the upstream and must come
    // back as an honest, CODED Unavailable.
    let err = nornir_warehouse_embed::scan_warehouse_table(
        &proxy.endpoint,
        "",
        "nordisk",
        "symbol_facts",
        10,
    )
    .expect_err("a dead upstream cannot serve a scan");
    let text = err.to_string();

    // CHANNEL 1 — the code is in the STATUS MESSAGE (so a client that only logs the
    // message still records it, and a log grep finds every occurrence).
    let in_message = errcode::carries(&text, "dwarves-proxy-2");
    assert!(
        in_message,
        "the dead-upstream gRPC Status carries dwarves-proxy-2 in its message: {text}"
    );
    // …and it is READABLE BACK OUT as a registered code across the string boundary.
    let parsed = errcode::parse(&text).expect("the code parses back out of the wire error");
    assert_eq!(parsed.code, "dwarves-proxy-2");
    assert_eq!(
        parsed.status,
        errcode::Status::Wired,
        "proxy-2 is a WIRED code"
    );
    // The HUMAN half survived too — dual channels, not a code-only regression.
    let human_kept = text.contains("unreachable") || text.contains("warehouse-proxy");
    assert!(human_kept, "the human reason rides beside the code: {text}");

    drop(proxy);
    let ok = in_message && human_kept;
    matrix(
        "error_code_proxy_dead_upstream_grpc_status",
        ok,
        "the REAL dwarves warehouse-proxy fronting a DEAD upstream answers a Warehouse.Scan with \
         an honest Unavailable whose message carries the STABLE code dwarves-proxy-2 (parsed back \
         out via errcode::parse) beside the unchanged human reason — the code reaches a gRPC \
         client, not only the UI's state_json",
    );
}

#[test]
fn every_wired_registry_code_is_reachable_and_the_split_is_honest() {
    // UNIQUENESS — no code repeats globally, no n repeats within a subsystem.
    let total = errcode::check_unique().expect("the registry is unique");
    assert_eq!(total, errcode::REGISTRY.len());

    // THE WIRING GUARD, re-asserted from the integration side: every `wired` row is really
    // raised in its declared file and no `reserved` row is secretly raised. This is the
    // anti-fiction net — a catalog claiming coverage it does not have fails HERE too.
    let (wired, reserved) = match errcode::check_wiring() {
        Ok(split) => split,
        Err(problems) => panic!(
            "the dwarves error-code registry claims coverage it does not have:\n  {}",
            problems.join("\n  ")
        ),
    };
    assert!(wired > 0, "at least one code is actually wired");
    assert_eq!(
        wired + reserved,
        total,
        "every row is either wired or reserved"
    );
    eprintln!("[errcode] {total} codes — {wired} wired, {reserved} reserved");

    // Every code is well-formed and resolves to a source URL.
    for e in errcode::REGISTRY {
        assert_eq!(e.code, format!("dwarves-{}-{}", e.subsystem, e.n));
        assert!(e
            .source_url()
            .starts_with("https://gunnar.rs/nordisk/dwarves"));
    }

    matrix(
        "error_code_registry_wiring_is_honest",
        true,
        &format!(
            "the dwarves error-code REGISTRY is unique ({total} codes, no repeat globally or \
             within a subsystem) AND its wired/reserved split is TRUE: {wired} wired codes are \
             each really raised in their declared source file and {reserved} reserved codes are \
             raised nowhere — the guard fails in BOTH directions, so the catalog can never \
             claim coverage it does not have"
        ),
    );
}