test(cli): RED -- synthetic member panics are contained like the real path
Milestone-fieldtest bug (source-blind run, 2026-07-24): a member fault on the synthetic walkforward/sweep path escapes as an uncaught panic (exit 101, no class marker, internal source path leaked) while the real path contains the identical fault via the #272 boundary as aura: warning: + exit 3. Cause: the family builders in aura-runner/src/family.rs run members bare, without the catch_unwind containment that lives only in aura-campaign/src/exec.rs. Two RED e2es pin the derived contract (decision log on #278): no exit 101, a warning-marked fault line, no panic text or crates/ path on stderr, exit 3 per the C14 partition. refs #278
This commit is contained in:
@@ -4839,6 +4839,77 @@ fn aura_walkforward_with_trades_emits_no_zero_trade_note() {
|
||||
let _ = std::fs::remove_dir_all(&cwd);
|
||||
}
|
||||
|
||||
/// E2E (#278 decision: same fault class, same verb, same surface => same
|
||||
/// treatment): a member fault on the SYNTHETIC walk-forward path is CONTAINED
|
||||
/// exactly like the real/campaign path contains it (#272) — it surfaces as an
|
||||
/// `aura: warning: ` class-marked stderr line naming the member fault and the
|
||||
/// process takes the deliberate failed-cells exit 3 (C14 partition,
|
||||
/// `exit_on_campaign_result` precedent). It never escapes as an uncaught Rust
|
||||
/// panic: no exit 101, no `panicked at`, no internal `crates/` source path on
|
||||
/// the consumer's stderr.
|
||||
#[test]
|
||||
fn aura_walkforward_synthetic_member_panic_is_contained() {
|
||||
let cwd = temp_cwd("blueprint-walkforward-member-panic-contained");
|
||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
// length=0 poisons the member: `Sma::new` asserts "SMA length must be >= 1"
|
||||
// during member compile — the same fault class the real path records as
|
||||
// "a member panicked: …" and survives.
|
||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=0", "--name", "wfpanic"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura walkforward (poison member)");
|
||||
let stderr = String::from_utf8_lossy(&out.stderr).to_string();
|
||||
assert!(
|
||||
stderr.lines().any(|l| l.contains("aura: warning: ") && l.contains("SMA length must be >= 1")),
|
||||
"the member fault surfaces as one class-marked warning naming the fault: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
!stderr.contains("panicked at"),
|
||||
"no raw panic report reaches the consumer: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
!stderr.contains("crates/"),
|
||||
"no internal source path leaks to the consumer: {stderr}"
|
||||
);
|
||||
assert_eq!(
|
||||
out.status.code(),
|
||||
Some(3),
|
||||
"a contained member fault is the deliberate failed-cells exit 3, never 101: {stderr}"
|
||||
);
|
||||
let _ = std::fs::remove_dir_all(&cwd);
|
||||
}
|
||||
|
||||
/// E2E (#278 decision, sweep twin): the synthetic `aura sweep` shares the
|
||||
/// same bare-member seam (`run_blueprint_member` inside the family builders'
|
||||
/// sweep closures, no #272 fault boundary), so the same poison must be
|
||||
/// contained the same way — warning + exit 3, no panic escape.
|
||||
#[test]
|
||||
fn aura_sweep_synthetic_member_panic_is_contained() {
|
||||
let cwd = temp_cwd("blueprint-sweep-member-panic-contained");
|
||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["sweep", &bp, "--axis", "sma_signal.fast.length=0", "--name", "swpanic"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura sweep (poison member)");
|
||||
let stderr = String::from_utf8_lossy(&out.stderr).to_string();
|
||||
assert!(
|
||||
stderr.lines().any(|l| l.contains("aura: warning: ") && l.contains("SMA length must be >= 1")),
|
||||
"the member fault surfaces as one class-marked warning naming the fault: {stderr}"
|
||||
);
|
||||
assert!(
|
||||
!stderr.contains("panicked at") && !stderr.contains("crates/"),
|
||||
"no raw panic report or internal source path leaks: {stderr}"
|
||||
);
|
||||
assert_eq!(
|
||||
out.status.code(),
|
||||
Some(3),
|
||||
"a contained member fault is the deliberate failed-cells exit 3, never 101: {stderr}"
|
||||
);
|
||||
let _ = std::fs::remove_dir_all(&cwd);
|
||||
}
|
||||
|
||||
/// E2E (#313, Path 1 — the real/sugar walk-forward): the degenerate
|
||||
/// equal-length config over the shared GER40 archive emits the same
|
||||
/// zero-trade note. Gated on the archive; skips cleanly on a data refusal.
|
||||
|
||||
Reference in New Issue
Block a user