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:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user