test(cli): RED — chart cannot open a sweep --trace family by its printed handle

Milestone-fieldtest finding B1: TraceStore::name_kind/read_family
resolve taps exactly one directory below the handle, so the depth-2
per-member layout sweep/walkforward --trace writes (#224,
<name>/<cell>/<member>/index.json) classifies as NotFound and
`aura chart <handle>` exits 1 — with a remedy string that suggests
re-running the very command that produced the data. The new autonomous
test fabricates the depth-2 family via TraceStore and pins: chart on
the printed family handle exits 0 and emits the HTML.

refs milestone fieldtest 09da04f
This commit is contained in:
2026-07-11 14:27:28 +02:00
parent 09da04fe18
commit abb6fbaa87
+82
View File
@@ -568,6 +568,88 @@ fn chart_unknown_name_refuses_with_exit_1_and_message() {
let _ = std::fs::remove_dir_all(&cwd);
}
/// Property (milestone fieldtest B1): a sweep / walk-forward `--trace` family is
/// chartable by the very handle the producing run prints. Those verbs fan a
/// selection-free sweep out per member into the #224 depth-2 layout
/// `runs/traces/<name>/<cell>/<member>/index.json` (one directory level deeper
/// than the campaign path's depth-1 nominee layout `<name>/<cell>/index.json`).
/// `aura chart <handle>` must resolve that fan-out as a family: exit 0 with the
/// chart page on stdout, covering the members' taps. AND the genuinely-absent-
/// handle refusal must not tell the user to re-run a command with that same
/// handle as the `--trace` argument — that command produced the data (it would
/// already be on disk), so pointing back at it is a dead-end remedy.
///
/// The fixture is fabricated inline through `TraceStore` (no archive, no
/// data-server, no sweep run): two members under one cell, exactly the depth-2
/// shape `persist_campaign_traces`' member fan-out writes. On today's tree the
/// depth-1-only `name_kind` / `read_family` classify the handle as NotFound, so
/// `chart` exits 1 with the misleading remedy — RED on both counts.
#[test]
fn chart_opens_a_sweep_trace_family_by_its_printed_handle() {
use aura_core::{Scalar, ScalarKind, Timestamp};
use aura_engine::{ColumnarTrace, RunManifest};
use aura_registry::TraceStore;
let cwd = temp_cwd("chart-sweep-family");
let manifest = || RunManifest {
commit: "c0ffee".to_string(),
params: vec![("fast".to_string(), Scalar::f64(2.0))],
window: (Timestamp(1), Timestamp(2)),
seed: 0,
broker: "sim-optimal(pip_size=1)".to_string(),
selection: None,
instrument: Some("GER40".to_string()),
topology_hash: None,
project: None,
};
let taps = |a: f64, b: f64| {
vec![ColumnarTrace::from_rows(
"equity",
&[ScalarKind::F64],
&[(Timestamp(1), vec![Scalar::f64(a)]), (Timestamp(2), vec![Scalar::f64(b)])],
)]
};
// The #224 sweep fan-out layout: two members under ONE cell, so each member's
// index.json lives at depth 2 (`<name>/<cell>/<member>/index.json`) — not the
// depth-1 nominee layout (`<name>/<cell>/index.json`) the campaign path writes.
// `TraceStore::open` roots at `<runs_dir>/traces`, matching `env.trace_store()`.
let store = TraceStore::open(cwd.join("runs"));
store.write("swp/cell0/m0", &manifest(), &taps(0.0, 0.4)).expect("write member m0");
store.write("swp/cell0/m1", &manifest(), &taps(0.0, 0.7)).expect("write member m1");
// (1) chart the family by the handle the sweep prints: exit 0 + chart page.
let out = Command::new(BIN).args(["chart", "swp"]).current_dir(&cwd).output().expect("spawn chart swp");
assert_eq!(
out.status.code(),
Some(0),
"charting a sweep --trace family by its handle must exit 0; got {:?}, stderr: {}",
out.status.code(),
String::from_utf8_lossy(&out.stderr),
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("window.AURA_TRACES") && stdout.contains("<title>aura chart"),
"the family handle must render a chart page covering its members' taps \
(AURA_TRACES + chart title); stdout len {}",
stdout.len(),
);
// (2) the genuinely-absent-handle refusal must not suggest re-running a
// command with the handle itself as the `--trace` argument (a dead-end remedy).
let ghost = Command::new(BIN).args(["chart", "ghost"]).current_dir(&cwd).output().expect("spawn chart ghost");
assert_eq!(ghost.status.code(), Some(1), "an absent handle still exits 1: {:?}", ghost.status);
let gerr = String::from_utf8_lossy(&ghost.stderr);
assert!(
!gerr.contains("--trace ghost"),
"the not-found remedy must not tell the user to re-run with the handle as --trace \
(that command produced the data / would not create this handle); got: {gerr}",
);
let _ = std::fs::remove_dir_all(&cwd);
}
#[test]
fn no_args_prints_usage_and_exits_two() {
let out = Command::new(BIN).output().expect("spawn aura");