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

nordisk/edda

crates/nornir-bench/tests/failed_arm_exits_nonzero.rs

//! The exit status of a bench run, asserted on a **real process**.
//!
//! Until 2026-08-06 `nornir-bench` wrote `"passed": false` into its JSON envelope
//! and exited **0**. The JSON was correct; the status was a lie. Every caller that
//! judges a subprocess the way a shell does — `&&`, `set -e`, a CI step, a
//! `Command::status()` — read a bench run with red arms as a clean pass. It is
//! written down as a standing hazard in the constellation's own `PRATA.md`, and it
//! still bit `gunnar.multi_pack_serve` on the day that arm was added to catch a
//! pack-corruption bug.
//!
//! A unit test on [`nornir_bench::api::verdict`] cannot close this. `verdict`
//! returning `Err` and the process exiting non-zero are two different facts, and
//! the whole defect lived in the gap between them. So this drives the real binary,
//! `nornir-bench-redprobe`, whose arms fail on demand, and reads `ExitStatus`.
//!
//! `CARGO_BIN_EXE_nornir-bench-redprobe` is set by cargo for an integration test
//! in the package that declares the bin, so there is no nested `cargo run` here:
//! no build lock to contend for, and no chance of measuring a stale binary from
//! some other target directory.

use std::process::{Command, Output};

/// Run the probe with `NORNIR_BENCH_REDPROBE=<mode>` (empty ⇒ unset).
fn probe(mode: &str) -> Output {
    let mut cmd = Command::new(env!("CARGO_BIN_EXE_nornir-bench-redprobe"));
    // Neutralise whatever the ambient environment says, so this test measures the
    // probe and not the shell it was started from.
    cmd.env_remove("NORNIR_BENCH_ONLY")
        .env_remove("NORNIR_BENCH_SKIP")
        .env_remove("NORNIR_BENCH_STOP_ON_ERROR")
        .env_remove("NORNIR_BENCH_PREVIEW")
        .env_remove("NORNIR_BENCH_SINGLE_CORE");
    if mode.is_empty() {
        cmd.env_remove("NORNIR_BENCH_REDPROBE");
    } else {
        cmd.env("NORNIR_BENCH_REDPROBE", mode);
    }
    cmd.output().expect("run nornir-bench-redprobe")
}

/// The last non-empty stdout line, parsed as a `BenchRun` — the exact contract
/// `runner::run_bench_example` reads.
fn envelope(out: &Output) -> nornir_bench::BenchRun {
    let stdout = String::from_utf8_lossy(&out.stdout);
    let line = stdout
        .lines()
        .filter(|l| !l.trim().is_empty())
        .next_back()
        .unwrap_or_else(|| {
            panic!(
                "no stdout at all. stderr:\n{}",
                String::from_utf8_lossy(&out.stderr)
            )
        });
    serde_json::from_str(line).unwrap_or_else(|e| panic!("parse `{line}` as BenchRun: {e}"))
}

/// **The green half.** Every arm passes ⇒ exit 0. Without this the test above
/// could be satisfied by a `verdict` that failed unconditionally, which would
/// pass a red-only assertion while breaking every bench in the constellation.
#[test]
fn an_all_green_run_exits_zero() {
    let out = probe("");
    let run = envelope(&out);
    assert_eq!(
        run.tests.len(),
        2,
        "both probe arms must have reported: {:?}",
        run.tests
    );
    assert!(
        run.all_tests_passed(),
        "no arm was told to fail: {:?}",
        run.failed_tests()
    );
    assert_eq!(
        out.status.code(),
        Some(0),
        "an all-green bench must exit 0. stderr:\n{}",
        String::from_utf8_lossy(&out.stderr)
    );
}

/// **The red half — the defect.** One arm fails ⇒ non-zero exit, and the envelope
/// is STILL on stdout so nothing that parses it loses its evidence.
///
/// Seen red on 2026-08-06 by restoring the old ending of `run_main_json`
/// (`println!(…); Ok(())`): this test then reported `exit 0 while
/// redprobe.red carried "passed": false`, and `an_all_green_run_exits_zero`
/// stayed green — i.e. the probe distinguishes the two builds rather than
/// failing on both.
#[test]
fn a_failed_arm_exits_nonzero_and_still_prints_its_envelope() {
    let out = probe("red");
    let run = envelope(&out);

    // The evidence survives the failure. This is the half that makes the fix safe
    // to land: `runner::run_bench_example`, the release gate and the warehouse all
    // read this line, and they must keep reading it on a red run.
    assert_eq!(
        run.tests.len(),
        2,
        "both arms must still be reported: {:?}",
        run.tests
    );
    assert_eq!(
        run.failed_tests(),
        vec!["redprobe.red"],
        "exactly the arm that was told to fail: {:?}",
        run.tests
    );

    // …and the status is now honest.
    assert_ne!(
        out.status.code(),
        Some(0),
        "a bench run carrying `\"passed\": false` MUST NOT exit 0 — that is the whole \
         standing hazard. envelope was: {}",
        String::from_utf8_lossy(&out.stdout)
    );

    let stderr = String::from_utf8_lossy(&out.stderr);
    assert!(
        stderr.contains("redprobe.red"),
        "the failure must name the arm on stderr, or an operator watching a CI log \
         sees a non-zero exit with no subject:\n{stderr}"
    );
}

/// Both arms red ⇒ still non-zero, and both are named. A message that stopped at
/// the first failure would hide how much is broken.
#[test]
fn every_failed_arm_is_named_on_stderr() {
    let out = probe("all");
    let run = envelope(&out);
    assert_eq!(
        run.failed_tests(),
        vec!["redprobe.green", "redprobe.red"],
        "{:?}",
        run.tests
    );
    assert_ne!(out.status.code(), Some(0));
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert!(stderr.contains("redprobe.green"), "{stderr}");
    assert!(stderr.contains("redprobe.red"), "{stderr}");
    assert!(
        stderr.contains("2 of 2"),
        "the count must be a ratio: {stderr}"
    );
}