test(cli): migrate dissolved-verb e2e tests into the project fixture (#218 prep)

The four dissolved verbs (sweep/generalize/walkforward/mc --real) are about to
gain a project gate that refuses outside a project (#218). 25 cli_run.rs tests
today run a well-formed verb to completion in a bare cwd and pin its grade /
store effect — i.e. they pin the pre-gate behaviour. Relocate them into the
built project fixture (fresh_project() over the demo-project cdylib, serialized,
store reset per test) so they still exercise the verb path once the gate lands.
Grades are byte-identical in-project (the fixture Aura.toml overrides no
instrument or data path — DEFAULT_DATA_PATH is absolute), verified against the
exact pins; local run 113 passed / 0 failed / 0 skipped with archive data.

Two new tests pin that the in-project run stamps manifest.project provenance
(the sweep chokepoint and generalize's run_blueprint_member path), a path no
cli_run.rs test previously exercised. The built_project/project_lock helpers are
duplicated into this binary to keep the change inside cli_run.rs; a shared
tests/common extraction is a separate hygiene item.

refs #218
This commit is contained in:
2026-07-09 21:17:45 +02:00
parent 08b0f9c783
commit 2162bd6d97
+218 -70
View File
@@ -1,7 +1,9 @@
//! Integration test: drive the built `aura` binary as a downstream user would, //! Integration test: drive the built `aura` binary as a downstream user would,
//! asserting the `run` subcommand's stdout/exit contract and the bad-args path. //! asserting the `run` subcommand's stdout/exit contract and the bad-args path.
use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use std::sync::{Mutex, MutexGuard, OnceLock};
/// Path to the freshly-built `aura` binary (Cargo sets this env var for the test /// Path to the freshly-built `aura` binary (Cargo sets this env var for the test
/// crate; the binary is named `aura` in `Cargo.toml`). /// crate; the binary is named `aura` in `Cargo.toml`).
@@ -17,6 +19,58 @@ fn temp_cwd(name: &str) -> std::path::PathBuf {
dir dir
} }
/// The demo-project fixture (Aura.toml + a built cdylib present), built once —
/// mirrors research_docs.rs's helper (a separate test binary, so it needs its
/// own `OnceLock`); `cargo build` is idempotent, so a second binary building
/// the same fixture is a no-op.
fn built_project() -> &'static PathBuf {
static BUILT: OnceLock<PathBuf> = OnceLock::new();
BUILT.get_or_init(|| {
let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/demo-project");
let out = std::process::Command::new("cargo")
.arg("build")
.current_dir(&dir)
.output()
.expect("spawn cargo build for the fixture project");
assert!(
out.status.success(),
"fixture build failed:\n{}",
String::from_utf8_lossy(&out.stderr)
);
dir
})
}
/// Serializes every test that runs a verb inside the shared fixture (they reset
/// `<fixture>/runs`, so parallel threads would race). Poison-tolerant.
fn project_lock() -> MutexGuard<'static, ()> {
static LOCK: Mutex<()> = Mutex::new(());
LOCK.lock().unwrap_or_else(|e| e.into_inner())
}
/// Removes `<fixture>/runs` on drop (including mid-panic) so a migrated test never
/// leaks its content-addressed store into the git-tracked fixture tree.
struct RunsCleanup(PathBuf);
impl Drop for RunsCleanup {
fn drop(&mut self) {
std::fs::remove_dir_all(&self.0).ok();
}
}
/// Enter the shared built project fixture with a FRESH `runs/` store, serialized.
/// Returns the fixture dir (has an Aura.toml, so `provenance()` is `Some`) and the
/// guards; bind the guards for the whole test body: `let (dir, _g) = fresh_project();`.
/// The guard tuple orders `RunsCleanup` before the `MutexGuard` deliberately: tuple
/// fields drop in index order, so the `runs/` removal (index 0) runs while the lock
/// (index 1) is still held — the serialization the lock exists to enforce.
fn fresh_project() -> (&'static PathBuf, (RunsCleanup, MutexGuard<'static, ()>)) {
let lock = project_lock();
let dir = built_project();
let runs = dir.join("runs");
std::fs::remove_dir_all(&runs).ok();
(dir, (RunsCleanup(runs), lock))
}
#[test] #[test]
fn run_prints_json_and_exits_zero() { fn run_prints_json_and_exits_zero() {
// `run` is blueprint-only now (#159 cut 4 retired the bare built-in default); // `run` is blueprint-only now (#159 cut 4 retired the bare built-in default);
@@ -662,15 +716,13 @@ fn local_data_present() -> bool {
#[test] #[test]
fn sweep_real_no_geometry_symbol_refuses_with_exit_1() { fn sweep_real_no_geometry_symbol_refuses_with_exit_1() {
// not gated: the pip refusal happens before any data access. // not gated: the pip refusal happens before any data access.
let dir = temp_cwd("sweep_real_refuse"); let (dir, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(BIN) let out = std::process::Command::new(BIN)
.args(["sweep", &fixture, "--axis", "sma_signal.fast.length=2,4", "--real", "NONEXISTENT"]) .args(["sweep", &fixture, "--axis", "sma_signal.fast.length=2,4", "--real", "NONEXISTENT"])
.current_dir(&dir).output().unwrap(); .current_dir(dir).output().unwrap();
assert_eq!(out.status.code(), Some(1)); assert_eq!(out.status.code(), Some(1));
assert!(String::from_utf8_lossy(&out.stderr).contains("no recorded geometry")); assert!(String::from_utf8_lossy(&out.stderr).contains("no recorded geometry"));
let _ = std::fs::remove_dir_all(&dir);
} }
// GER40 Sept-2024 window (inclusive Unix-ms) — the same gated calendar month // GER40 Sept-2024 window (inclusive Unix-ms) — the same gated calendar month
@@ -693,7 +745,7 @@ fn sweep_real_blueprint_member_manifest_stamps_the_default_stop() {
eprintln!("skip: no local data at {}", data_server::DEFAULT_DATA_PATH); eprintln!("skip: no local data at {}", data_server::DEFAULT_DATA_PATH);
return; return;
} }
let dir = temp_cwd("sweep_real_blueprint_no_stop"); let (dir, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(BIN) let out = std::process::Command::new(BIN)
.args([ .args([
@@ -705,7 +757,7 @@ fn sweep_real_blueprint_member_manifest_stamps_the_default_stop() {
"--axis", "sma_signal.slow.length=8", "--axis", "sma_signal.slow.length=8",
"--name", "brp_nostop", "--name", "brp_nostop",
]) ])
.current_dir(&dir) .current_dir(dir)
.output() .output()
.unwrap(); .unwrap();
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr)); assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
@@ -726,6 +778,53 @@ fn sweep_real_blueprint_member_manifest_stamps_the_default_stop() {
} }
} }
/// Property (#218 migration follow-up): running a dissolved `sweep` inside a
/// project (`fresh_project()`, the fixture this migration introduced) stamps
/// `report.manifest.project` on every member with the project's audit-trail
/// provenance (C18/C16) — the `demo` namespace and a well-formed 64-hex-char
/// dylib sha256 — rather than the `None` a bare-cwd run (no `Aura.toml`)
/// produces. Before this migration no cli_run.rs sweep test ran inside a real
/// project, so this stamping path was never exercised end-to-end. Gated on the
/// local GER40 archive; skips cleanly when absent.
#[test]
fn sweep_real_blueprint_member_manifest_stamps_project_provenance() {
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",
"--axis", "sma_signal.slow.length=8",
"--name", "brp_provenance",
])
.current_dir(dir)
.output()
.unwrap();
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
let stdout = String::from_utf8_lossy(&out.stdout).into_owned();
let lines: Vec<&str> = stdout.lines().collect();
assert_eq!(lines.len(), 2, "one member line per grid point: {stdout}");
for line in &lines {
let v: serde_json::Value = serde_json::from_str(line).expect("member line parses as JSON");
let project = &v["report"]["manifest"]["project"];
assert_eq!(
project["namespace"].as_str(), Some("demo"),
"member manifest carries the fixture project's namespace: {line}"
);
let sha = project["dylib_sha256"].as_str().unwrap_or_else(|| panic!("dylib_sha256 present: {line}"));
assert_eq!(sha.len(), 64, "dylib_sha256 is a full sha256 hex digest: {line}");
assert!(sha.chars().all(|c| c.is_ascii_hexdigit()), "dylib_sha256 is hex: {line}");
}
}
/// Property: the real-data blueprint sweep path (`aura sweep <blueprint.json> /// Property: the real-data blueprint sweep path (`aura sweep <blueprint.json>
/// --real SYM --axis …`, dispatched as sugar over the one campaign executor) /// --real SYM --axis …`, dispatched as sugar over the one campaign executor)
/// prints one member line per grid point, IN AXIS ORDER (odometer: the first /// prints one member line per grid point, IN AXIS ORDER (odometer: the first
@@ -746,7 +845,7 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() {
eprintln!("skip: no local data at {}", data_server::DEFAULT_DATA_PATH); eprintln!("skip: no local data at {}", data_server::DEFAULT_DATA_PATH);
return; return;
} }
let dir = temp_cwd("sweep_real_blueprint"); let (dir, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(BIN) let out = std::process::Command::new(BIN)
.args([ .args([
@@ -758,7 +857,7 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() {
"--axis", "sma_signal.slow.length=8,16", "--axis", "sma_signal.slow.length=8,16",
"--name", "brp", "--name", "brp",
]) ])
.current_dir(&dir) .current_dir(dir)
.output() .output()
.unwrap(); .unwrap();
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr)); assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
@@ -806,7 +905,7 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() {
// exactly one new Sweep family persisted, with the grid's four members. // exactly one new Sweep family persisted, with the grid's four members.
let fams = std::process::Command::new(BIN) let fams = std::process::Command::new(BIN)
.args(["runs", "families"]) .args(["runs", "families"])
.current_dir(&dir) .current_dir(dir)
.output() .output()
.unwrap(); .unwrap();
assert!(fams.status.success(), "families exit: {:?}", fams.status); assert!(fams.status.success(), "families exit: {:?}", fams.status);
@@ -909,7 +1008,7 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() {
"--axis", "sma_signal.slow.length=8,16", "--axis", "sma_signal.slow.length=8,16",
"--name", "brp", "--name", "brp",
]) ])
.current_dir(&dir) .current_dir(dir)
.output() .output()
.unwrap(); .unwrap();
assert!(out2.status.success(), "second run stderr: {}", String::from_utf8_lossy(&out2.stderr)); assert!(out2.status.success(), "second run stderr: {}", String::from_utf8_lossy(&out2.stderr));
@@ -939,8 +1038,6 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() {
} }
assert_eq!(count("processes"), 1, "second identical run dedupes the process doc"); assert_eq!(count("processes"), 1, "second identical run dedupes the process doc");
assert_eq!(count("campaigns"), 1, "second identical run dedupes the campaign doc"); assert_eq!(count("campaigns"), 1, "second identical run dedupes the campaign doc");
let _ = std::fs::remove_dir_all(&dir);
} }
/// Property (#210 c0110 fieldtest, "the wrapped-name refusal mangles the axis /// Property (#210 c0110 fieldtest, "the wrapped-name refusal mangles the axis
@@ -1000,7 +1097,7 @@ fn aura_sweep_real_blueprint_subset_axes_refuses_without_store_litter() {
eprintln!("skip: no local data at {}", data_server::DEFAULT_DATA_PATH); eprintln!("skip: no local data at {}", data_server::DEFAULT_DATA_PATH);
return; return;
} }
let dir = temp_cwd("sweep_real_subset_refusal"); let (dir, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = Command::new(BIN) let out = Command::new(BIN)
.args([ .args([
@@ -1011,7 +1108,7 @@ fn aura_sweep_real_blueprint_subset_axes_refuses_without_store_litter() {
"--axis", "sma_signal.fast.length=2,4", "--axis", "sma_signal.fast.length=2,4",
"--name", "c0110-subset", "--name", "c0110-subset",
]) ])
.current_dir(&dir) .current_dir(dir)
.output() .output()
.expect("spawn aura sweep (subset axes)"); .expect("spawn aura sweep (subset axes)");
assert_ne!( assert_ne!(
@@ -1030,7 +1127,6 @@ fn aura_sweep_real_blueprint_subset_axes_refuses_without_store_litter() {
!runs_log.exists() || std::fs::read_to_string(&runs_log).unwrap().is_empty(), !runs_log.exists() || std::fs::read_to_string(&runs_log).unwrap().is_empty(),
"no realization recorded for a refused sweep" "no realization recorded for a refused sweep"
); );
let _ = std::fs::remove_dir_all(&dir);
} }
/// The synthetic seed-resweep `mc` is undefined over real bars (one realization -> /// The synthetic seed-resweep `mc` is undefined over real bars (one realization ->
@@ -1363,7 +1459,7 @@ fn generalize_strategy_flag_is_removed_from_the_grammar() {
fn generalize_grades_a_candidate_across_two_instruments() { fn generalize_grades_a_candidate_across_two_instruments() {
const FROM_MS: &str = "1725148800000"; const FROM_MS: &str = "1725148800000";
const TO_MS: &str = "1727740799999"; const TO_MS: &str = "1727740799999";
let cwd = temp_cwd("generalize-two"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -1372,7 +1468,7 @@ fn generalize_grades_a_candidate_across_two_instruments() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -1409,7 +1505,7 @@ fn generalize_grades_a_candidate_across_two_instruments() {
fn generalize_real_e2e_pins_the_exact_current_grade() { fn generalize_real_e2e_pins_the_exact_current_grade() {
const FROM_MS: &str = "1725148800000"; const FROM_MS: &str = "1725148800000";
const TO_MS: &str = "1727740799999"; const TO_MS: &str = "1727740799999";
let cwd = temp_cwd("generalize-exact-grade"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -1418,7 +1514,7 @@ fn generalize_real_e2e_pins_the_exact_current_grade() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -1466,7 +1562,7 @@ fn generalize_real_e2e_pins_the_exact_current_grade() {
fn generalize_grades_a_non_r_sma_blueprint() { fn generalize_grades_a_non_r_sma_blueprint() {
const FROM_MS: &str = "1725148800000"; const FROM_MS: &str = "1725148800000";
const TO_MS: &str = "1727740799999"; const TO_MS: &str = "1727740799999";
let cwd = temp_cwd("generalize-non-r-sma"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -1476,7 +1572,7 @@ fn generalize_grades_a_non_r_sma_blueprint() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -1515,7 +1611,7 @@ fn generalize_grades_a_non_r_sma_blueprint() {
fn walkforward_real_e2e_pins_the_exact_current_grade() { fn walkforward_real_e2e_pins_the_exact_current_grade() {
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("walkforward-exact-grade"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -1524,7 +1620,7 @@ fn walkforward_real_e2e_pins_the_exact_current_grade() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -1577,7 +1673,7 @@ fn walkforward_real_e2e_pins_the_exact_current_grade() {
fn walkforward_dissolves_a_non_r_sma_blueprint() { fn walkforward_dissolves_a_non_r_sma_blueprint() {
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("walkforward-non-r-sma"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -1587,7 +1683,7 @@ fn walkforward_dissolves_a_non_r_sma_blueprint() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -1664,7 +1760,7 @@ fn walkforward_dissolved_refuses_select_plateau() {
/// cleanly on a data refusal. /// cleanly on a data refusal.
#[test] #[test]
fn walkforward_dissolved_defaults_a_single_omitted_knob() { fn walkforward_dissolved_defaults_a_single_omitted_knob() {
let cwd = temp_cwd("walkforward-single-knob-default"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -1672,7 +1768,7 @@ fn walkforward_dissolved_defaults_a_single_omitted_knob() {
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12", "--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "3", "--stop-length", "3",
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -1807,7 +1903,7 @@ fn walkforward_dissolved_refuses_name_and_trace_together() {
fn walkforward_dissolves_through_the_campaign_path() { fn walkforward_dissolves_through_the_campaign_path() {
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("walkforward-dissolves"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -1816,7 +1912,7 @@ fn walkforward_dissolves_through_the_campaign_path() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -1862,7 +1958,7 @@ fn walkforward_dissolves_through_the_campaign_path() {
let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["runs", "families"]) .args(["runs", "families"])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn families"); .expect("spawn families");
let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned(); let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned();
@@ -1898,7 +1994,7 @@ fn walkforward_dissolves_through_the_campaign_path() {
fn walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2() { fn walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2() {
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("walkforward-stopless-defaults"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
// Stop-less: the two knobs are omitted entirely. This is the headline — today it // Stop-less: the two knobs are omitted entirely. This is the headline — today it
@@ -1910,7 +2006,7 @@ fn walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2() {
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12", "--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if stopless.status.code() == Some(1) { if stopless.status.code() == Some(1) {
@@ -1939,7 +2035,7 @@ fn walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2() {
"--stop-length", "3", "--stop-k", "2.0", "--stop-length", "3", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
assert_eq!( assert_eq!(
@@ -1984,7 +2080,7 @@ fn walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2() {
fn generalize_dissolves_through_the_campaign_path() { fn generalize_dissolves_through_the_campaign_path() {
const FROM_MS: &str = "1725148800000"; const FROM_MS: &str = "1725148800000";
const TO_MS: &str = "1727740799999"; const TO_MS: &str = "1727740799999";
let cwd = temp_cwd("generalize-dissolves"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -1993,7 +2089,7 @@ fn generalize_dissolves_through_the_campaign_path() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -2044,7 +2140,7 @@ fn generalize_dissolves_through_the_campaign_path() {
// per-instrument Sweep family per cell (persisted by the executor). // per-instrument Sweep family per cell (persisted by the executor).
let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["runs", "families"]) .args(["runs", "families"])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn families"); .expect("spawn families");
let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned(); let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned();
@@ -2075,7 +2171,7 @@ fn generalize_dissolves_through_the_campaign_path() {
fn generalize_repeated_identical_invocation_does_not_litter_the_store() { fn generalize_repeated_identical_invocation_does_not_litter_the_store() {
const FROM_MS: &str = "1725148800000"; const FROM_MS: &str = "1725148800000";
const TO_MS: &str = "1727740799999"; const TO_MS: &str = "1727740799999";
let cwd = temp_cwd("generalize-repeat-idempotent"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let invoke = || { let invoke = || {
std::process::Command::new(env!("CARGO_BIN_EXE_aura")) std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
@@ -2085,7 +2181,7 @@ fn generalize_repeated_identical_invocation_does_not_litter_the_store() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura") .expect("spawn aura")
}; };
@@ -2130,7 +2226,7 @@ fn generalize_repeated_identical_invocation_does_not_litter_the_store() {
fn generalize_distinct_invocations_persist_distinct_campaign_documents() { fn generalize_distinct_invocations_persist_distinct_campaign_documents() {
const FROM_MS: &str = "1725148800000"; const FROM_MS: &str = "1725148800000";
const TO_MS: &str = "1727740799999"; const TO_MS: &str = "1727740799999";
let cwd = temp_cwd("generalize-distinct-content"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let invoke = |fast: &str| { let invoke = |fast: &str| {
let fast_axis = format!("sma_signal.fast.length={fast}"); let fast_axis = format!("sma_signal.fast.length={fast}");
@@ -2141,7 +2237,7 @@ fn generalize_distinct_invocations_persist_distinct_campaign_documents() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura") .expect("spawn aura")
}; };
@@ -2179,7 +2275,7 @@ fn generalize_distinct_invocations_persist_distinct_campaign_documents() {
/// unexercised. Gated on local GER40/USDJPY data. /// unexercised. Gated on local GER40/USDJPY data.
#[test] #[test]
fn generalize_without_explicit_window_falls_back_to_the_first_symbols_full_window() { fn generalize_without_explicit_window_falls_back_to_the_first_symbols_full_window() {
let cwd = temp_cwd("generalize-default-window"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -2187,7 +2283,7 @@ fn generalize_without_explicit_window_falls_back_to_the_first_symbols_full_windo
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12", "--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -2197,7 +2293,6 @@ fn generalize_without_explicit_window_falls_back_to_the_first_symbols_full_windo
"exit 1 must be a data refusal, got: {stderr}" "exit 1 must be a data refusal, got: {stderr}"
); );
eprintln!("skip: no local GER40/USDJPY data"); eprintln!("skip: no local GER40/USDJPY data");
let _ = std::fs::remove_dir_all(&cwd);
return; return;
} }
assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status); assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status);
@@ -2205,7 +2300,6 @@ fn generalize_without_explicit_window_falls_back_to_the_first_symbols_full_windo
assert!(stdout.contains("\"generalize\":"), "aggregate object: {stdout}"); assert!(stdout.contains("\"generalize\":"), "aggregate object: {stdout}");
assert!(stdout.contains("\"n_instruments\":2"), "two instruments graded: {stdout}"); assert!(stdout.contains("\"n_instruments\":2"), "two instruments graded: {stdout}");
assert!(stdout.contains("\"family_id\":\"generalize-0\""), "family handle: {stdout}"); assert!(stdout.contains("\"family_id\":\"generalize-0\""), "family handle: {stdout}");
let _ = std::fs::remove_dir_all(&cwd);
} }
/// Property: a single-instrument generalize is refused at parse time (exit 2), /// Property: a single-instrument generalize is refused at parse time (exit 2),
@@ -2362,7 +2456,7 @@ fn generalize_refuses_an_unknown_axis_name() {
fn generalize_persists_a_discoverable_cross_instrument_family() { fn generalize_persists_a_discoverable_cross_instrument_family() {
const FROM_MS: &str = "1725148800000"; const FROM_MS: &str = "1725148800000";
const TO_MS: &str = "1727740799999"; const TO_MS: &str = "1727740799999";
let cwd = temp_cwd("generalize-family"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -2371,7 +2465,7 @@ fn generalize_persists_a_discoverable_cross_instrument_family() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -2381,7 +2475,6 @@ fn generalize_persists_a_discoverable_cross_instrument_family() {
"exit 1 must be a data refusal, got: {stderr}" "exit 1 must be a data refusal, got: {stderr}"
); );
eprintln!("skip: no local GER40/USDJPY data"); eprintln!("skip: no local GER40/USDJPY data");
let _ = std::fs::remove_dir_all(&cwd);
return; return;
} }
assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status); assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status);
@@ -2389,7 +2482,7 @@ fn generalize_persists_a_discoverable_cross_instrument_family() {
// the run persisted the per-instrument members as a discoverable CrossInstrument family. // the run persisted the per-instrument members as a discoverable CrossInstrument family.
let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["runs", "families"]) .args(["runs", "families"])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn families"); .expect("spawn families");
assert!(fams.status.success(), "families exit: {:?}", fams.status); assert!(fams.status.success(), "families exit: {:?}", fams.status);
@@ -2407,8 +2500,6 @@ fn generalize_persists_a_discoverable_cross_instrument_family() {
assert_eq!(cross.len(), 1, "exactly one CrossInstrument family: {fams_out:?}"); assert_eq!(cross.len(), 1, "exactly one CrossInstrument family: {fams_out:?}");
assert!(cross[0].contains("\"family_id\":\"generalize-0\""), "families: {fams_out:?}"); assert!(cross[0].contains("\"family_id\":\"generalize-0\""), "families: {fams_out:?}");
assert!(cross[0].contains("\"members\":2"), "one member per instrument: {fams_out:?}"); assert!(cross[0].contains("\"members\":2"), "one member per instrument: {fams_out:?}");
let _ = std::fs::remove_dir_all(&cwd);
} }
/// Property (#210 T3, dissolution): the persisted `CrossInstrument` family's /// Property (#210 T3, dissolution): the persisted `CrossInstrument` family's
@@ -2427,7 +2518,7 @@ fn generalize_persists_a_discoverable_cross_instrument_family() {
fn generalize_family_members_are_attributed_to_the_right_instrument() { fn generalize_family_members_are_attributed_to_the_right_instrument() {
const FROM_MS: &str = "1725148800000"; const FROM_MS: &str = "1725148800000";
const TO_MS: &str = "1727740799999"; const TO_MS: &str = "1727740799999";
let cwd = temp_cwd("generalize-member-attribution"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -2436,7 +2527,7 @@ fn generalize_family_members_are_attributed_to_the_right_instrument() {
"--stop-length", "14", "--stop-k", "2.0", "--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -2446,14 +2537,13 @@ fn generalize_family_members_are_attributed_to_the_right_instrument() {
"exit 1 must be a data refusal, got: {stderr}" "exit 1 must be a data refusal, got: {stderr}"
); );
eprintln!("skip: no local GER40/USDJPY data"); eprintln!("skip: no local GER40/USDJPY data");
let _ = std::fs::remove_dir_all(&cwd);
return; return;
} }
assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status); assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status);
let fam = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let fam = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["runs", "family", "generalize-0"]) .args(["runs", "family", "generalize-0"])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn runs family"); .expect("spawn runs family");
assert!(fam.status.success(), "runs family exit: {:?}", fam.status); assert!(fam.status.success(), "runs family exit: {:?}", fam.status);
@@ -2481,8 +2571,66 @@ fn generalize_family_members_are_attributed_to_the_right_instrument() {
members[1]["metrics"]["r"]["expectancy_r"].as_f64(), Some(0.005795903617609842), members[1]["metrics"]["r"]["expectancy_r"].as_f64(), Some(0.005795903617609842),
"USDJPY's own expectancy_r, matching the exact-grade anchor: {fam_out:?}" "USDJPY's own expectancy_r, matching the exact-grade anchor: {fam_out:?}"
); );
}
let _ = std::fs::remove_dir_all(&cwd); /// Property (#218 migration follow-up): `generalize`'s per-instrument members
/// route through the shared campaign path (`campaign_run.rs`'s
/// `run_blueprint_member` call, a code path distinct from sweep's in
/// `main.rs`), and that path ALSO stamps `manifest.project` with the running
/// project's provenance (C18/C16) for every persisted member — not just the
/// sweep chokepoint. Before the project-fixture migration this cli_run.rs
/// path only ever ran outside a project, so `project` was always `None` here.
/// Gated on the shared GER40/USDJPY Sept-2024 archive.
#[test]
fn generalize_family_members_stamp_project_provenance() {
const FROM_MS: &str = "1725148800000";
const TO_MS: &str = "1727740799999";
let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"generalize", &fixture, "--real", "GER40,USDJPY",
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS,
])
.current_dir(cwd)
.output()
.expect("spawn aura");
if out.status.code() == Some(1) {
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("no local data") || stderr.contains("no recorded geometry"),
"exit 1 must be a data refusal, got: {stderr}"
);
eprintln!("skip: no local GER40/USDJPY data");
return;
}
assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status);
let fam = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["runs", "family", "generalize-0"])
.current_dir(cwd)
.output()
.expect("spawn runs family");
assert!(fam.status.success(), "runs family exit: {:?}", fam.status);
let fam_out = String::from_utf8(fam.stdout).expect("utf-8");
let members: Vec<serde_json::Value> = fam_out
.lines()
.filter(|l| l.starts_with('{'))
.map(|l| serde_json::from_str(l).expect("member line parses as JSON"))
.collect();
assert_eq!(members.len(), 2, "one member per instrument: {fam_out:?}");
for m in &members {
let project = &m["manifest"]["project"];
assert_eq!(
project["namespace"].as_str(), Some("demo"),
"campaign-path member manifest carries the fixture project's namespace: {fam_out:?}"
);
let sha = project["dylib_sha256"].as_str()
.unwrap_or_else(|| panic!("dylib_sha256 present: {fam_out:?}"));
assert_eq!(sha.len(), 64, "dylib_sha256 is a full sha256 hex digest: {fam_out:?}");
}
} }
/// `aura sweep <blueprint.json> --axis <name>=<csv> …` (#166): sweeps a loaded OPEN /// `aura sweep <blueprint.json> --axis <name>=<csv> …` (#166): sweeps a loaded OPEN
@@ -3373,7 +3521,7 @@ fn exit_codes_partition_usage_two_from_runtime_one() {
fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() { fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() {
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("mc-r-bootstrap-exact-grade"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -3383,7 +3531,7 @@ fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() {
"--block-len", "5", "--resamples", "1000", "--seed", "42", "--block-len", "5", "--resamples", "1000", "--seed", "42",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -3430,7 +3578,7 @@ fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() {
fn mc_dissolves_a_non_r_sma_blueprint() { fn mc_dissolves_a_non_r_sma_blueprint() {
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("mc-non-r-sma"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -3441,7 +3589,7 @@ fn mc_dissolves_a_non_r_sma_blueprint() {
"--block-len", "5", "--resamples", "1000", "--seed", "42", "--block-len", "5", "--resamples", "1000", "--seed", "42",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -3486,7 +3634,7 @@ fn mc_dissolves_a_non_r_sma_blueprint() {
fn mc_dissolves_through_the_campaign_path() { fn mc_dissolves_through_the_campaign_path() {
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("mc-dissolves"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -3496,7 +3644,7 @@ fn mc_dissolves_through_the_campaign_path() {
"--block-len", "5", "--resamples", "1000", "--seed", "42", "--block-len", "5", "--resamples", "1000", "--seed", "42",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -3550,7 +3698,7 @@ fn mc_dissolves_through_the_campaign_path() {
let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["runs", "families"]) .args(["runs", "families"])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn families"); .expect("spawn families");
let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned(); let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned();
@@ -3644,9 +3792,9 @@ fn mc_dissolved_refuses_an_unknown_axis_name() {
fn mc_dissolved_seed_changes_the_bootstrap_draw_not_the_pooled_series() { fn mc_dissolved_seed_changes_the_bootstrap_draw_not_the_pooled_series() {
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let run = |seed: &str| { let run = |seed: &str| {
let cwd = temp_cwd(&format!("mc-seed-{seed}"));
std::process::Command::new(env!("CARGO_BIN_EXE_aura")) std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
"mc", &fixture, "--real", "GER40", "mc", &fixture, "--real", "GER40",
@@ -3655,7 +3803,7 @@ fn mc_dissolved_seed_changes_the_bootstrap_draw_not_the_pooled_series() {
"--block-len", "5", "--resamples", "1000", "--seed", seed, "--block-len", "5", "--resamples", "1000", "--seed", seed,
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura") .expect("spawn aura")
}; };
@@ -3707,7 +3855,7 @@ fn walkforward_campaign_runs_an_arbitrary_blueprint() {
} }
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("walkforward-arbitrary-blueprint"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -3717,7 +3865,7 @@ fn walkforward_campaign_runs_an_arbitrary_blueprint() {
"--stop-length", "3", "--stop-k", "2.0", "--stop-length", "3", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -3759,7 +3907,7 @@ fn mc_campaign_runs_an_arbitrary_blueprint() {
} }
const FROM_MS: &str = "1735689600000"; const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000"; const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("mc-arbitrary-blueprint"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -3770,7 +3918,7 @@ fn mc_campaign_runs_an_arbitrary_blueprint() {
"--block-len", "5", "--resamples", "100", "--seed", "7", "--block-len", "5", "--resamples", "100", "--seed", "7",
"--from", FROM_MS, "--to", TO_MS, "--from", FROM_MS, "--to", TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {
@@ -3808,7 +3956,7 @@ fn generalize_campaign_runs_an_arbitrary_blueprint() {
eprintln!("skip: no local data at {}", data_server::DEFAULT_DATA_PATH); eprintln!("skip: no local data at {}", data_server::DEFAULT_DATA_PATH);
return; return;
} }
let cwd = temp_cwd("generalize-arbitrary-blueprint"); let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_meanrev_open.json", env!("CARGO_MANIFEST_DIR")); let fixture = format!("{}/examples/r_meanrev_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([ .args([
@@ -3819,7 +3967,7 @@ fn generalize_campaign_runs_an_arbitrary_blueprint() {
"--stop-length", "3", "--stop-k", "2.0", "--stop-length", "3", "--stop-k", "2.0",
"--from", GER40_SEPT2024_FROM_MS, "--to", GER40_SEPT2024_TO_MS, "--from", GER40_SEPT2024_FROM_MS, "--to", GER40_SEPT2024_TO_MS,
]) ])
.current_dir(&cwd) .current_dir(cwd)
.output() .output()
.expect("spawn aura"); .expect("spawn aura");
if out.status.code() == Some(1) { if out.status.code() == Some(1) {