diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index d9341f4..0958d93 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -4604,6 +4604,61 @@ fn aura_walkforward_emits_an_unknown_axis_rejection_once() { } } +/// Property (#269): the walkforward verb's unknown-axis rejection reaches a +/// user's stderr as prose — the same register `aura sweep`'s boundary errors +/// adopted in #247 — never wrapped in the `UnknownKnob("…")` Rust `Debug` +/// frame. The walkforward path already carries a fully-prosed payload INSIDE +/// that wrapper (`axis : names no param of this blueprint … — see … +/// --list-axes`), so the fix sheds the frame rather than authoring new prose: +/// the message that reaches stderr is exactly that payload, the axis named as +/// typed, exit still 2. Data-free — the axis is rejected at the resolution +/// pre-flight before any window runs, so no archive and no project are needed. +/// +/// NB: this is the post-#269 contract. The two sibling tests above +/// (`aura_walkforward_over_a_blueprint_rejects_an_unknown_axis`, +/// `aura_walkforward_emits_an_unknown_axis_rejection_once`) still pin the +/// `UnknownKnob` substring — stale pins of the defective frame that the GREEN +/// slice repoints onto the prose fragment. +#[test] +fn walkforward_unknown_axis_rejection_renders_as_prose_not_a_debug_frame() { + let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR")); + let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) + .args(["walkforward", &bp, "--axis", "nope=1,2"]) + .output() + .expect("spawn aura walkforward (unknown axis)"); + assert_eq!( + out.status.code(), + Some(2), + "an unknown axis stays a usage refusal (exit 2, not a panic): status={:?}", + out.status + ); + let stderr = String::from_utf8(out.stderr).expect("utf-8"); + // The headline — the whole defect: no Rust `Debug` frame reaches the user. + assert!( + !stderr.contains("UnknownKnob"), + "must not leak the `UnknownKnob(…)` Debug frame around the prose: {stderr}" + ); + // The prose payload survives intact — the axis named as typed, the + // names-no-param wording, and the `--list-axes` pointer (the sweep + // sibling's register). + assert!( + stderr.contains("nope"), + "the prose names the offending axis exactly as typed: {stderr}" + ); + assert!( + stderr.contains("names no param"), + "the prose carries the boundary payload wording: {stderr}" + ); + assert!( + stderr.contains("--list-axes"), + "the prose points at the discovery surface, like the sweep sibling: {stderr}" + ); + assert!( + !stderr.contains("panicked"), + "must reject cleanly, never panic: {stderr}" + ); +} + /// E2E (acc 2): `aura mc ` is rejected with a named error + exit 2 /// (NOT a panic / exit 101) — MC binds no axis, so a free knob has no binder. #[test]