//! Fieldtest c0010 #1 — `aura run` as a downstream CLI consumer (axis a). //! //! Question under test: invoking the REAL `aura` binary the way the deferred //! C18 registry or a shell/jq pipeline would — capture stdout, treat it as the //! single-line canonical JSON report (manifest + metrics) — does the documented //! C14 machine-readable face hold? Specifically: //! - `aura run` -> exit 0, one line of JSON on stdout, nothing on stderr; //! - the JSON parses as an object with {manifest, metrics} nesting (rustdoc: //! "prints the run's metrics + manifest as canonical JSON to stdout"); //! - two runs are byte-identical (C1 determinism, pinned on the surface); //! - missing / unknown subcommand -> exit 2 + `aura: usage: aura run` on //! stderr, NOTHING on stdout (cycle_scope bad-args contract). //! //! This consumer does NOT read crates/*/src. It only runs the binary and //! inspects observable behaviour. The binary path comes from $AURA_BIN (a CI //! consumer would point this at the built artefact); we default to the //! sibling workspace target/debug/aura so `cargo run` works out of the box. use std::process::Command; fn aura_bin() -> String { if let Ok(p) = std::env::var("AURA_BIN") { return p; } // Default: the workspace debug artefact, two levels up from this crate. let here = env!("CARGO_MANIFEST_DIR"); format!("{here}/../../target/debug/aura") } /// A bare-bones JSON value walker — enough for a consumer to confirm the /// documented {manifest, metrics} shape WITHOUT a serde dependency (the /// workspace is deliberately zero-external-dependency; a downstream consumer /// would reach for serde_json or jq, but here we only assert structural facts /// the rustdoc promises). Returns the substring value following `"key":`. fn raw_value_after<'a>(json: &'a str, key: &str) -> Option<&'a str> { let needle = format!("\"{key}\":"); let idx = json.find(&needle)? + needle.len(); Some(&json[idx..]) } fn run_capture(args: &[&str]) -> (String, String, i32) { let out = Command::new(aura_bin()) .args(args) .output() .unwrap_or_else(|e| panic!("failed to spawn {}: {e}", aura_bin())); ( String::from_utf8_lossy(&out.stdout).to_string(), String::from_utf8_lossy(&out.stderr).to_string(), out.status.code().unwrap_or(-1), ) } fn main() { println!("using binary: {}", aura_bin()); // --- `aura run`: exit 0, one JSON line on stdout, clean stderr --- let (stdout, stderr, code) = run_capture(&["run"]); println!("`aura run` exit={code}"); println!("`aura run` stdout = {stdout:?}"); assert_eq!(code, 0, "aura run must exit 0"); assert!(stderr.is_empty(), "aura run must not write stderr, got {stderr:?}"); let line = stdout.trim_end_matches('\n'); assert!(!line.contains('\n'), "report must be a single line of JSON"); assert!(line.starts_with('{') && line.ends_with('}'), "report is a JSON object"); // Documented nesting: a `manifest` object and a `metrics` object. assert!(raw_value_after(line, "manifest").is_some(), "manifest key present"); assert!(raw_value_after(line, "metrics").is_some(), "metrics key present"); // Documented manifest fields (RunManifest rustdoc: commit/params/window/seed/broker). for k in ["commit", "params", "window", "seed", "broker"] { assert!(raw_value_after(line, k).is_some(), "manifest.{k} key present"); } // Documented metrics fields (RunMetrics rustdoc). for k in ["total_pips", "max_drawdown", "exposure_sign_flips"] { assert!(raw_value_after(line, k).is_some(), "metrics.{k} key present"); } // --- determinism: two runs byte-identical (C1) --- let (stdout2, _, _) = run_capture(&["run"]); assert_eq!(stdout, stdout2, "two runs must be byte-identical (C1)"); println!("determinism: two runs byte-identical OK"); // --- bad-args contract: missing subcommand --- let (out, err, code) = run_capture(&[]); println!("`aura` (no args) exit={code} stderr={err:?}"); assert_eq!(code, 2, "no subcommand must exit 2"); assert!(out.is_empty(), "no subcommand must print nothing on stdout, got {out:?}"); assert_eq!(err.trim_end(), "aura: usage: aura run", "exact usage line on stderr"); // --- bad-args contract: unknown subcommand --- let (out, err, code) = run_capture(&["play"]); println!("`aura play` exit={code} stderr={err:?}"); assert_eq!(code, 2, "unknown subcommand must exit 2"); assert!(out.is_empty(), "unknown subcommand must print nothing on stdout"); assert_eq!(err.trim_end(), "aura: usage: aura run", "exact usage line on stderr"); // --- probe: trailing junk after `run`. The cycle_scope says "Any other or // missing subcommand prints usage and exits 2"; `run extra` is neither a // clean `run` nor a different subcommand. What does the binary do? --- let (out, err, code) = run_capture(&["run", "extra-garbage"]); println!("`aura run extra-garbage` exit={code} stdout_len={} stderr={err:?}", out.len()); // We RECORD the behaviour; the reading we assert is the lenient one the // binary actually exhibits, and flag the spec ambiguity in the report. if code == 0 { println!("NOTE: `aura run ` was ACCEPTED (exit 0) — trailing args ignored."); } else { println!("NOTE: `aura run ` was REJECTED (exit {code})."); } println!("c0010_1 OK: aura run -> JSON report; determinism + bad-args contract observed"); }