//! Hermetic smoke (Testing strategy §1): synthesize a //! Pepperstone-format `.tick` ZIP, point the REAL `DataServer` at it, //! fold the stream through the embedding `Kernel`, assert the result //! equals a host reference fold. Proves "adapter compiles, links, //! ABI handshake holds" with no `/mnt` dependency. The fixture writer //! dumps `data_server::records::RawTickRecord` bytes (the crate's own //! public on-disk type) into a `.bin` ZIP entry — correct by //! construction, mirroring `data-server/src/loader.rs:110-124`. use std::io::Write; use std::sync::Arc; use ail_embed::Kernel; use data_server::records::RawTickRecord; use data_server::DataServer; /// Writes `recs` as a Pepperstone tick file `_2017_01.tick` /// (ZIP holding one packed-`RawTickRecord` `.bin`) into `dir`. /// Filename satisfies the scanner regex `^(.+)_(\d{4})_(\d{2})\.tick$` /// (`data-server/src/lib.rs:88`); the `.bin` entry name is arbitrary /// as long as it ends in `.bin` (`loader.rs:50-55`). fn write_tick_fixture(dir: &std::path::Path, sym: &str, recs: &[RawTickRecord]) { let path = dir.join(format!("{sym}_2017_01.tick")); let f = std::fs::File::create(&path).unwrap(); let mut zip = zip::ZipWriter::new(f); zip.start_file::("TICK.bin".into(), Default::default()) .unwrap(); for r in recs { // SAFETY: RawTickRecord is #[repr(C, packed)], 24 bytes; this // is exactly data-server's own test serialisation // (loader.rs:117-120). let bytes: &[u8] = unsafe { std::slice::from_raw_parts(r as *const RawTickRecord as *const u8, 24) }; zip.write_all(bytes).unwrap(); } zip.finish().unwrap(); } #[test] fn hermetic_smoke_data_server_roundtrip() { // 10 deterministic ticks. mid = (ask+bid)/2; with ask==bid==i+1, // mid == i+1. Σ_{i=0..9}(i+1) == 55.0 exactly in f64; n == 10. // `time` is any monotonic Delphi-day value (no window ⇒ unused by // the fold; only the filename drives symbol/format scanning). let recs: Vec = (0..10) .map(|i| RawTickRecord { time: 42795.0 + i as f64 * 1e-4, ask: (i + 1) as f64, bid: (i + 1) as f64, }) .collect(); let dir = tempfile::tempdir().unwrap(); write_tick_fixture(dir.path(), "TEST", &recs); let server = Arc::new(DataServer::new(dir.path())); assert!(server.has_symbol("TEST"), "fixture symbol not scanned"); // Collect the mid-price stream in data-server order. let mut it = server.stream_tick("TEST").expect("TEST tick stream"); let mut prices: Vec = Vec::new(); while let Some(chunk) = it.next_chunk() { for r in chunk.iter() { prices.push((r.ask + r.bid) / 2.0); } } assert_eq!(prices.len(), 10, "all ticks streamed"); // Host reference fold, same order, same ops ⇒ bit-exact. let ref_acc: f64 = prices.iter().copied().fold(0.0, |a, p| a + p); let ref_n = prices.len() as i64; let (acc, n) = Kernel::new().run(prices.iter().copied()); assert_eq!(acc, ref_acc, "kernel acc bit-exact vs host reference"); assert_eq!(acc, 55.0, "deterministic fixture sum"); assert_eq!(n, ref_n); assert_eq!(n, 10); }