test(cli): RED — sweep --real --trace refuses instead of writing tap series

Pins the #224 delivery headline: the #168 refusal lifts and at least
one non-empty tap-series file lands under runs/traces/. Layout-agnostic
on purpose — the per-member key convention is the GREEN slice's call.

refs #224
This commit is contained in:
2026-07-10 21:13:52 +02:00
parent e44b0cbb3e
commit fe4ea0a08d
+76
View File
@@ -1225,6 +1225,82 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() {
assert_eq!(count("campaigns"), 1, "second identical run dedupes the campaign doc"); assert_eq!(count("campaigns"), 1, "second identical run dedupes the campaign doc");
} }
/// True iff `root` contains, at any depth, a regular file of non-zero length —
/// the layout-agnostic "traces actually landed on disk" probe. #224 does not yet
/// pin the per-member trace-directory naming (the sweep sugar's cell has no
/// nominee and `persist_campaign_traces` keys by `<trace_name>/<cell_key>`), so
/// this walks the tree rather than hard-coding a path, keeping the headline about
/// *that* traces are written, not *where*.
fn any_nonempty_file(root: &Path) -> bool {
let mut stack = vec![root.to_path_buf()];
while let Some(p) = stack.pop() {
let Ok(entries) = std::fs::read_dir(&p) else { continue };
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
stack.push(path);
} else if std::fs::metadata(&path).map(|m| m.len() > 0).unwrap_or(false) {
return true;
}
}
}
false
}
/// Property (#224): `aura sweep <blueprint.json> --real SYM --axis … --trace <fam>`
/// DELIVERS the advertised trace-writing capability instead of the #168
/// refuse-with-pointer. Two things must hold once the capability ships:
/// (1) the run no longer refuses (`aura: --trace is not yet available on sweep;
/// see #224`, exit 2) but succeeds, and (2) the tap series actually land on disk
/// under `runs/traces/` — at least one non-empty tap file is written, the honest
/// inverse of the #168 no-op where the `--real` sugar set `persist_taps: vec![]`
/// (verb_sugar.rs) and `run_blueprint_sweep` did `let _ = persist`, so nothing was
/// ever written. The exact on-disk layout (trace name + per-member/nominee key)
/// is deliberately NOT pinned here: this headline pins that traces are written at
/// all — not how many dirs, nor under what member key — because that naming is
/// the GREEN slice's open design point (the selection-free sweep cell has no
/// nominee for the existing per-cell `persist_campaign_traces` mechanism, #224).
/// Gated on the local GER40 archive (the project skip-on-no-data convention);
/// the refused status quo makes it RED on a data-ful host today.
#[test]
fn sweep_real_with_trace_writes_tap_series_to_disk() {
if !local_data_present() {
eprintln!("skip: no local data at {}", data_server::DEFAULT_DATA_PATH);
return;
}
let (dir, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(BIN)
.args([
"sweep", &fixture,
"--real", "GER40",
"--from", GER40_SEPT2024_FROM_MS,
"--to", GER40_SEPT2024_TO_MS,
"--axis", "sma_signal.fast.length=2,4",
"--trace", "brp",
])
.current_dir(dir)
.output()
.unwrap();
// (1) the #168 refusal is lifted: the advertised flag runs instead of exit 2.
assert!(
out.status.success(),
"sweep --trace must no longer refuse (#224); exit {:?}, stderr: {}",
out.status.code(),
String::from_utf8_lossy(&out.stderr),
);
// (2) the tap series actually land on disk under runs/traces/ (non-empty) —
// the honest inverse of the #168 `persist_taps: vec![]` no-op.
let traces_root = dir.join("runs").join("traces");
assert!(
any_nonempty_file(&traces_root),
"sweep --trace must write at least one non-empty tap file under {} \
(found none — the #168 no-op wrote nothing); stderr: {}",
traces_root.display(),
String::from_utf8_lossy(&out.stderr),
);
}
/// Extract the first persisted family whose `kind` matches `want` from /// Extract the first persisted family whose `kind` matches `want` from
/// `aura runs families` stdout (one JSON object per line), returning its /// `aura runs families` stdout (one JSON object per line), returning its
/// `family_id` — the id `aura reproduce` addresses. Panics if none matches so a /// `family_id` — the id `aura reproduce` addresses. Panics if none matches so a