test: red executable-spec for #264 cut 1 — aura data coverage reports month gaps

aura data coverage <SYMBOL> must print the archive's first/last present
month and each interior missing-month range collapsed to one
'missing: YYYY-MM..YYYY-MM' line — the Copper failure mode (first/last
check passes while the window start has no data) made visible before a
campaign runs. Hostless RED over a new gapped synthetic-archive fixture
(fresh_project_with_gapped_data); fails on the absent 'data' subcommand
(clap exit 2), not on scaffolding.

refs #264
This commit is contained in:
2026-07-13 21:28:58 +02:00
parent bb0b0aeac2
commit 28984ba73c
2 changed files with 63 additions and 1 deletions
+43 -1
View File
@@ -5,7 +5,7 @@ use std::path::Path;
use std::process::Command;
mod common;
use common::{fresh_project, fresh_project_with_data};
use common::{fresh_project, fresh_project_with_data, fresh_project_with_gapped_data};
/// Path to the freshly-built `aura` binary (Cargo sets this env var for the test
/// crate; the binary is named `aura` in `Cargo.toml`).
@@ -5862,3 +5862,45 @@ fn shipped_r_meanrev_example_runs_end_to_end_via_aura_run() {
assert!(stdout.contains("\"n_trades\":0"), "{stdout}");
assert!(stdout.contains("\"total_pips\":0.0"), "{stdout}");
}
/// Property (#264, coverage cut 1): `aura data coverage <SYMBOL>` surfaces the
/// archive's month COVERAGE from the monthly file index — the first and last
/// present month AND every interior missing-month range (a gap) — so the Copper
/// failure mode is visible up front. Copper had files from 2016-08 but was
/// missing 2018-01..2018-10, so a first/last-bounds check passed while a
/// window opening in the hole had no data, aborting a multi-hour campaign
/// mid-run; a per-symbol gap report would have caught it before the run. Over a
/// hermetic synthetic archive whose `GAPSYM` has 2024-01..02 and 2024-05..06
/// present and 2024-03..04 absent, the verb exits 0, frames the span by its
/// first (2024-01) and last (2024-06) present month, and reports the interior
/// hole as a single collapsed month-range line `missing: 2024-03..2024-04`
/// (one line for the whole contiguous gap, as Copper's ten-month hole would be
/// one line — not one line per missing month).
#[test]
fn data_coverage_reports_month_span_and_interior_gap_range() {
let (cwd, _g) = fresh_project_with_gapped_data();
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["data", "coverage", "GAPSYM"])
.current_dir(&cwd)
.output()
.expect("spawn aura data coverage");
assert_eq!(
out.status.code(),
Some(0),
"exit: {:?}, stderr: {}",
out.status,
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8(out.stdout).expect("utf-8 stdout");
// The interior gap is the headline — the Copper failure the verb exists to
// surface — reported as one collapsed range for the whole contiguous hole.
assert!(
stdout.contains("missing: 2024-03..2024-04"),
"interior gap month-range reported as one line: {stdout}"
);
// The span bounds frame the gap: first and last present month.
assert!(
stdout.contains("2024-01") && stdout.contains("2024-06"),
"first (2024-01) and last (2024-06) present month reported: {stdout}"
);
}
+20
View File
@@ -127,6 +127,26 @@ pub fn fresh_project_with_data() -> (PathBuf, FreshProject) {
(root.clone(), FreshProject { root })
}
/// Like [`fresh_project_with_data`], but the single symbol `GAPSYM` carries a
/// DELIBERATE interior month gap: monthly files exist for 2024-01..2024-02 and
/// 2024-05..2024-06, while 2024-03..2024-04 are absent. This is the Copper
/// failure shape (#264 — files present at both ends while an interior window
/// has no data, so a first/last-bounds check passes but a campaign aborts
/// mid-run). For tests whose property is per-symbol month-gap reporting: the
/// two `write_symbol_archive` calls write disjoint contiguous segments into the
/// same `data/` dir (the second re-writes GAPSYM's identical geometry sidecar),
/// leaving March and April as a real hole in the monthly file index.
#[allow(dead_code)]
pub fn fresh_project_with_gapped_data() -> (PathBuf, FreshProject) {
let root = mint_tempdir();
write_project_files(&root, Some("data"));
let data_dir = root.join("data");
std::fs::create_dir_all(&data_dir).expect("create synthetic gapped archive dir");
synthetic_data::write_symbol_archive(&data_dir, "GAPSYM", (2024, 1), (2024, 2));
synthetic_data::write_symbol_archive(&data_dir, "GAPSYM", (2024, 5), (2024, 6));
(root.clone(), FreshProject { root })
}
/// A scratch filesystem entry a test writes under the git-tracked
/// demo-project fixture root (only `runs/` there is fixture-gitignored),
/// removed on drop — including during a mid-test panic — so a failed