test(aura-ingest): RED for unbounded M1 upper bound

load_m1_window cannot express an open upper bound: it hardcodes
Some(from_ms)/Some(to_ms) into stream_m1_windowed, so callers reach for
the i64::MAX sentinel, which panics upstream in chrono's
from_timestamp_millis (data-server records.rs:28). This compile-RED pins
the desired Option<i64> contract (None = unbounded) and asserts an
unbounded read equals the finite sane bound bar-for-bar.

refs #23
This commit is contained in:
2026-06-04 23:37:49 +02:00
parent 4fac529de0
commit 2e381060fa
@@ -0,0 +1,56 @@
//! Gated integration test for the M1-window upper-bound contract (Gitea #23).
//!
//! Property under protection: an *unbounded* upper bound is expressible through
//! `load_m1_window` and is non-panicking — it must yield exactly the bars a
//! finite sane bound covering the same data would. The underlying
//! `data_server::stream_m1_windowed` already takes `Option<i64>` per bound
//! (`None` = unbounded, skipping the coarse `unix_ms_to_year_month` file
//! filter). The aura wrapper must thread that `Option` through so callers can
//! express "to the end of history" without reaching for a sentinel — the
//! natural `i64::MAX` sentinel panics in chrono's `from_timestamp_millis`
//! inside the data-server file filter (`records.rs:28`).
//!
//! Skips with a note where the local Pepperstone archive is absent, so
//! `cargo test --workspace` stays green anywhere; it exercises the real
//! ingestion path where data exists. The trigger is minimized to the latest
//! single AAPL.US month so the unbounded read stays a few thousand bars.
use std::sync::Arc;
use aura_ingest::load_m1_window;
use data_server::{DataServer, DEFAULT_DATA_PATH};
#[test]
fn unbounded_upper_bound_matches_finite_sane_bound() {
let server = Arc::new(DataServer::new(DEFAULT_DATA_PATH));
if !server.has_symbol("AAPL.US") {
eprintln!("skip: no local data at {DEFAULT_DATA_PATH} (symbol AAPL.US absent)");
return; // hermetic elsewhere; exercises the real path where files exist
}
// Minimal trigger: start at the latest archived month so "to the end of
// history" resolves to one small tail file, not the full multi-year set.
// 2026-05-01 00:00:00 UTC in inclusive Unix-ms.
let from_ms = 1_777_996_800_000_i64;
// A finite sane upper bound far past any real bar (2035-01-01), the
// workaround the #23 reporter used in place of the panicking i64::MAX.
let sane_upper = 2_051_222_400_000_i64;
// The reference: a finite bound that covers all remaining bars.
let bounded = load_m1_window(&server, "AAPL.US", Some(from_ms), Some(sane_upper))
.expect("AAPL.US has data from 2026-05 onward");
assert!(!bounded.ts.is_empty(), "tail window resolved to zero bars");
// The behaviour #23 asks for: an unbounded upper bound (`None`) must be
// expressible and must NOT panic — today the wrapper hardcodes `Some(..)`,
// so this is unrepresentable, and the only sentinel a caller can pass
// (`i64::MAX`) panics upstream in `unix_ms_to_year_month`.
let unbounded = load_m1_window(&server, "AAPL.US", Some(from_ms), None)
.expect("unbounded upper bound yields the same data as the sane finite bound");
// Same data, bar-for-bar: an open upper bound is exactly "no upper filter".
assert_eq!(
unbounded, bounded,
"unbounded upper bound must equal the finite sane bound covering the same tail"
);
}