From 2e381060fafa4fa189471aa4a405e272bb72dafc Mon Sep 17 00:00:00 2001 From: Brummel Date: Thu, 4 Jun 2026 23:37:49 +0200 Subject: [PATCH] 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 contract (None = unbounded) and asserts an unbounded read equals the finite sane bound bar-for-bar. refs #23 --- crates/aura-ingest/tests/unbounded_window.rs | 56 ++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 crates/aura-ingest/tests/unbounded_window.rs diff --git a/crates/aura-ingest/tests/unbounded_window.rs b/crates/aura-ingest/tests/unbounded_window.rs new file mode 100644 index 0000000..6b7850a --- /dev/null +++ b/crates/aura-ingest/tests/unbounded_window.rs @@ -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` 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" + ); +}