test(cli): RED — chart cannot resolve a --trace family by its chosen name (#238)
--trace <NAME> lands the name in the generated campaign document, but no
lookup surface consumes it: the trace store knows only the content-derived
handle {campaign8}-{run}, so the user cannot address their own family by
the name they chose.
refs #238
This commit is contained in:
@@ -650,6 +650,125 @@ fn chart_opens_a_sweep_trace_family_by_its_printed_handle() {
|
||||
let _ = std::fs::remove_dir_all(&cwd);
|
||||
}
|
||||
|
||||
/// Property (#238): a sweep / walk-forward `--trace <NAME>` family is chartable by
|
||||
/// the very NAME the user chose — not only by the content-derived handle the run
|
||||
/// prints. `--trace <NAME>` lands `NAME` in the generated campaign document's
|
||||
/// `name` field (content-addressed via `put_campaign`), while the trace directory
|
||||
/// stays keyed by the content-derived handle `{campaign8}-{run}` that
|
||||
/// `append_campaign_run` re-derives (C18, untouched). Today `aura chart <NAME>`
|
||||
/// classifies `NAME` as an unknown trace handle and exits 1, so the chosen name
|
||||
/// has no charting effect. It must instead resolve `NAME` against the recorded
|
||||
/// campaign runs: the ONE record whose stored campaign document carries
|
||||
/// `name == NAME` and a persisted `trace_name` charts that family by its handle —
|
||||
/// exit 0 with the same chart page charting the handle directly renders.
|
||||
///
|
||||
/// The state is fabricated in-process, no archive / sweep run: a campaign
|
||||
/// document carrying a distinctive `name` is stored via `put_campaign`; a
|
||||
/// campaign-run record with a `Some` trace_name sentinel is appended (which
|
||||
/// re-derives the handle `{campaign8}-0`); a family is written under that exact
|
||||
/// handle via `TraceStore` — mirroring the handle-charted sibling above (the
|
||||
/// #224 depth-2 fan-out). The registry roots where `env.registry()` does
|
||||
/// (`<cwd>/runs/runs.jsonl`), so the running binary reads the same store. On
|
||||
/// today's tree `chart <NAME>` hits `emit_chart`'s NotFound arm (no registry
|
||||
/// query), so it exits 1 with the "no recorded run or family" message — RED for
|
||||
/// the right reason (the name-resolution is absent, not a fabrication error).
|
||||
#[test]
|
||||
fn chart_resolves_a_trace_family_by_its_chosen_campaign_name() {
|
||||
use aura_core::{Scalar, ScalarKind, Timestamp};
|
||||
use aura_engine::{ColumnarTrace, RunManifest};
|
||||
use aura_registry::{derive_trace_name, CampaignRunRecord, Registry, TraceStore};
|
||||
|
||||
let cwd = temp_cwd("chart-by-campaign-name");
|
||||
|
||||
// The chosen family name (`--trace <NAME>`) — distinctive, not a trace handle
|
||||
// (no such directory under runs/traces, so `name_kind` classifies NotFound).
|
||||
const CAMPAIGN_NAME: &str = "mom-ger40-sept24";
|
||||
|
||||
// (1) Store a campaign document carrying that name (a valid CampaignDoc, so a
|
||||
// resolver can parse it by any strategy — Value lookup or full deserialize).
|
||||
// `put_campaign` self-keys by content, returning the id the campaign-run
|
||||
// record must reference. The registry roots where `env.registry()` does:
|
||||
// `<cwd>/runs/runs.jsonl` (campaign docs at `<cwd>/runs/campaigns/<id>.json`,
|
||||
// campaign_runs at `<cwd>/runs/campaign_runs.jsonl`).
|
||||
let reg = Registry::open(cwd.join("runs").join("runs.jsonl"));
|
||||
let campaign_json = r#"{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "__NAME__",
|
||||
"data": { "instruments": ["GER40"],
|
||||
"windows": [ { "from_ms": 1, "to_ms": 2 } ] },
|
||||
"strategies": [
|
||||
{ "ref": { "content_id": "9f3a" },
|
||||
"axes": { "fast": { "kind": "I64", "values": [8, 12] } } }
|
||||
],
|
||||
"process": { "ref": { "content_id": "4e2d" } },
|
||||
"seed": 1,
|
||||
"presentation": { "persist_taps": ["equity"], "emit": ["family_table"] }
|
||||
}"#
|
||||
.replace("__NAME__", CAMPAIGN_NAME);
|
||||
let campaign_id = reg.put_campaign(&campaign_json).expect("store campaign document");
|
||||
|
||||
// (2) Append a campaign-run record whose trace_name is Some (the executor's
|
||||
// claim sentinel; `append_campaign_run` assigns the per-campaign run counter
|
||||
// and re-derives the stored trace_name to `{campaign8}-{run}`).
|
||||
let record = CampaignRunRecord {
|
||||
campaign: campaign_id.clone(),
|
||||
process: "4e2d".to_string(),
|
||||
run: 0, // ignored — append assigns the counter
|
||||
seed: 1,
|
||||
cells: vec![],
|
||||
generalizations: vec![],
|
||||
trace_name: Some("claim".to_string()),
|
||||
};
|
||||
let run = reg.append_campaign_run(&record).expect("append campaign run");
|
||||
let handle = derive_trace_name(&campaign_id, run);
|
||||
|
||||
// (3) Write a family under that exact handle via TraceStore — the same
|
||||
// fabrication the handle-charted sibling uses (#224 depth-2: two members under
|
||||
// one cell). Charting `handle` directly is thus known to render a family chart.
|
||||
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)])],
|
||||
)]
|
||||
};
|
||||
let store = TraceStore::open(cwd.join("runs"));
|
||||
store.write(&format!("{handle}/cell0/m0"), &manifest(), &taps(0.0, 0.4)).expect("write member m0");
|
||||
store.write(&format!("{handle}/cell0/m1"), &manifest(), &taps(0.0, 0.7)).expect("write member m1");
|
||||
|
||||
// Chart by the chosen NAME (not the handle): resolution must go through the
|
||||
// campaign-run query and land on `handle`, rendering the same family chart.
|
||||
let out = Command::new(BIN).args(["chart", CAMPAIGN_NAME]).current_dir(&cwd).output().expect("spawn chart <name>");
|
||||
assert_eq!(
|
||||
out.status.code(),
|
||||
Some(0),
|
||||
"charting a --trace family by its chosen campaign name 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 chosen name must render the same family chart as its handle \
|
||||
(AURA_TRACES + chart title); stdout len {}",
|
||||
stdout.len(),
|
||||
);
|
||||
|
||||
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