From 692760d3b06d8878f7535fa169b4ed5131dcc79d Mon Sep 17 00:00:00 2001 From: Brummel Date: Fri, 10 Jul 2026 03:46:30 +0200 Subject: [PATCH] =?UTF-8?q?test(cli):=20RED=20=E2=80=94=20aura=20graph=20w?= =?UTF-8?q?ith=20a=20bad=20blueprint=20arg=20must=20be=20a=20usage=20fault?= =?UTF-8?q?=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The #28 cycle-close audit found the just-shipped `aura graph ` silently renders the embedded sample at exit 0 when the positional names an unreadable blueprint (typo, nonexistent path, wrong extension): is_blueprint_file returns None both for "no positional" (the legit sample default) and for a bad arg, and dispatch_graph's None arm conflates them — re-introducing the exact #28 "I asked for my graph, got the sample" surprise in the error path. This pins the fix: a named-but-unreadable arg exits 2 like its siblings, while bare `aura graph` still renders the sample. refs #28 --- crates/aura-cli/tests/cli_run.rs | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index 8ee4c68..a56864f 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -709,6 +709,65 @@ fn graph_renders_a_consumer_blueprint_file_not_the_embedded_sample() { ); } +/// Property (#28 close-audit): a NAMED-but-unreadable blueprint arg to +/// `aura graph` is a usage fault, not a silent fallback to the embedded sample. +/// `dispatch_graph`'s no-subcommand arm branches on `is_blueprint_file`, which +/// returns `None` for BOTH "no positional given" (→ legitimate sample default) +/// and "a positional was given but it is not a readable `.json` file" (→ a bad +/// arg). The two must not be conflated: a bad arg (typo, nonexistent path, wrong +/// extension) must `eprintln!` a usage error and exit 2, mirroring `dispatch_run` +/// — NOT render the r_sma sample and exit 0, which re-introduces the exact #28 +/// friction ("I asked for my graph, got the sample") in the error path. The +/// bare-`aura graph` default (no positional → sample, exit 0) is guarded in the +/// same test so the fix cannot regress it. +#[test] +fn graph_with_bad_blueprint_arg_is_a_usage_fault_not_a_silent_sample() { + let cwd = temp_cwd("graph-bad-arg"); + // A named .json path that does not exist: `is_blueprint_file` returns None, + // and the buggy arm falls through to the embedded sample instead of refusing. + let out = Command::new(BIN) + .arg("graph") + .arg("does-not-exist.json") + .current_dir(&cwd) + .output() + .expect("spawn aura graph "); + // Headline pin: a named-but-unreadable blueprint arg exits 2 (usage fault), + // mirroring `dispatch_run`'s bad-blueprint branch — not 0. + assert_eq!( + out.status.code(), + Some(2), + "`aura graph ` must be a usage fault (exit 2), not a silent sample fallback; got {:?}\nstderr: {}", + out.status, + String::from_utf8_lossy(&out.stderr), + ); + // ...and it must NOT emit the embedded r_sma sample: the sample's inlined + // model + its Bias node must be absent from stdout. + let stdout = String::from_utf8(out.stdout).expect("utf-8 stdout"); + assert!( + !stdout.contains("\"type\":\"Bias\""), + "a bad blueprint arg silently rendered the embedded r_sma sample (its Bias node is present) instead of refusing" + ); + + // Guard the legitimate default: bare `aura graph` (no positional) still + // renders the embedded sample and exits 0. + let default = Command::new(BIN) + .arg("graph") + .current_dir(&cwd) + .output() + .expect("spawn aura graph"); + assert_eq!( + default.status.code(), + Some(0), + "bare `aura graph` must still render the sample (exit 0); got {:?}", + default.status, + ); + let default_out = String::from_utf8(default.stdout).expect("utf-8 stdout"); + assert!( + default_out.contains("\"type\":\"Bias\""), + "bare `aura graph` must render the embedded r_sma sample (its Bias node), the no-positional default" + ); +} + #[test] fn sweep_with_trailing_token_is_strict_and_exits_two() { // strict-arg reading (#16): an unexpected trailing token is a usage error.