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:
@@ -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"
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user