From 1f01badac70d37129a2f48210ae5aad6ef4f3acf Mon Sep 17 00:00:00 2001 From: Brummel Date: Sun, 14 Jun 2026 00:18:04 +0200 Subject: [PATCH] refactor(aura-engine): share the SMA-cross harness test fixtures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collapse the three verbatim-duplicated #[cfg(test)] fixtures — synthetic_prices, sma_cross, composite_sma_cross_harness — that lived in both blueprint.rs's and sweep.rs's test modules into one shared crate-root `#[cfg(test)] mod test_fixtures` (pub(crate) free fns). The two consumers now import from `crate::test_fixtures`; sweep.rs's test-mod `use` block sheds the imports that were only needed by the moved fns. Why: the topology had to move in lockstep across the two copies but no test pinned them equal, so an edit to one silently diverged the other (#53). One definition removes the hazard structurally — better than a pin-them-equal test, which loses its premise once there is a single definition. Test-only; no production code, public API, or runtime path changes. C16 untouched (a #[cfg(test)] module ships in no artifact). The CLI sample is no longer a third peer (cycle 0036 enriched it into a trend+momentum blend), so this is an engine-internal two-copy dedup, not the three-way the issue originally named. graph_model.rs keeps its own intentionally-minimal golden copy, out of scope. Plan glitch fixed in-flight: the plan's literal import list named sma_cross, but neither consumer calls it directly (only transitively via composite_sma_cross_harness), so importing it tripped the plan's own Step-7 clippy -D warnings gate. Trimmed to {composite_sma_cross_harness, synthetic_prices} — within the task's "drop now-unused imports" scope. Verified: cargo test --workspace green (aura-engine 114 passed, count unchanged — behaviour-preserving); the two load-bearing fixture consumers composite_sma_cross_runs_bit_identical_to_hand_wired (C1) and sweep_equals_n_independent_runs (C11/C12) both green; cargo clippy --workspace --all-targets -- -D warnings clean. closes #53 --- crates/aura-engine/src/blueprint.rs | 75 +-------------------- crates/aura-engine/src/lib.rs | 3 + crates/aura-engine/src/sweep.rs | 83 +---------------------- crates/aura-engine/src/test_fixtures.rs | 87 +++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 154 deletions(-) create mode 100644 crates/aura-engine/src/test_fixtures.rs diff --git a/crates/aura-engine/src/blueprint.rs b/crates/aura-engine/src/blueprint.rs index 5113815..edb2bf0 100644 --- a/crates/aura-engine/src/blueprint.rs +++ b/crates/aura-engine/src/blueprint.rs @@ -753,6 +753,7 @@ fn slot_kind(t: Target, flat_signatures: &[NodeSchema]) -> Result Vec<(Timestamp, Scalar)> { - [ - (1_i64, 1.0000_f64), - (2, 1.0010), - (3, 1.0030), - (4, 1.0060), - (5, 1.0040), - (6, 1.0010), - (7, 0.9990), - ] - .iter() - .map(|&(t, p)| (Timestamp(t), Scalar::F64(p))) - .collect() - } - /// Today's flat, hand-wired SMA-cross signal-quality harness (the /// `sample_harness` wiring from `aura-cli`), with two recording sinks. #[allow(clippy::type_complexity)] @@ -1587,63 +1571,6 @@ mod tests { (h, rx_eq, rx_ex) } - /// The SMA-cross signal as a reusable composite: one input role (price), one - /// output (the fast-minus-slow spread). Interior wired with raw local indices. - /// Value-empty: the two SMA lengths are injected at compile, not baked here. - fn sma_cross() -> Composite { - Composite::new( - "sma_cross", - vec![ - Sma::builder().named("fast").into(), - Sma::builder().named("slow").into(), - Sub::builder().into(), - ], - vec![ - Edge { from: 0, to: 2, slot: 0, from_field: 0 }, - Edge { from: 1, to: 2, slot: 1, from_field: 0 }, - ], - vec![Role { - name: "price".into(), - targets: vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }], source: None, }], - vec![OutField { node: 2, field: 0, name: "out".into() }], - ) - } - - /// The same signal-quality harness authored as a composite blueprint. - #[allow(clippy::type_complexity)] - fn composite_sma_cross_harness() -> ( - Composite, - mpsc::Receiver<(Timestamp, Vec)>, - mpsc::Receiver<(Timestamp, Vec)>, - ) { - let (tx_eq, rx_eq) = mpsc::channel(); - let (tx_ex, rx_ex) = mpsc::channel(); - let bp = Composite::new( - "root", - vec![ - BlueprintNode::Composite(sma_cross()), - Exposure::builder().into(), - SimBroker::builder(0.0001).into(), - Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq).into(), - Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex).into(), - ], - vec![ - Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // composite out -> Exposure - Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // exposure -> broker slot 0 - Edge { from: 2, to: 3, slot: 0, from_field: 0 }, // equity -> sink - Edge { from: 1, to: 4, slot: 0, from_field: 0 }, // exposure -> sink - ], - vec![ - Role { name: "src".into(), targets: vec![ - Target { node: 0, slot: 0 }, // price -> sma_cross role 0 - Target { node: 2, slot: 1 }, // price -> SimBroker price slot - ], source: Some(ScalarKind::F64) }, - ], - vec![], // output - ); - (bp, rx_eq, rx_ex) - } - #[test] fn composite_sma_cross_runs_bit_identical_to_hand_wired() { let prices = synthetic_prices(); diff --git a/crates/aura-engine/src/lib.rs b/crates/aura-engine/src/lib.rs index d8c825c..5f53c52 100644 --- a/crates/aura-engine/src/lib.rs +++ b/crates/aura-engine/src/lib.rs @@ -50,6 +50,9 @@ pub use sweep::{sweep, GridSpace, SweepError, SweepFamily, SweepPoint}; // Firing / Timestamp) so a graph builder has one import surface, not two. pub use aura_core::{Firing, NodeSchema, ParamSpec, PortSpec, Scalar, ScalarKind, Timestamp}; +#[cfg(test)] +mod test_fixtures; + #[cfg(test)] mod reexport_tests { // #29: the core scalar vocabulary a Blueprint builder needs is reachable diff --git a/crates/aura-engine/src/sweep.rs b/crates/aura-engine/src/sweep.rs index 9739d15..b1dabb2 100644 --- a/crates/aura-engine/src/sweep.rs +++ b/crates/aura-engine/src/sweep.rs @@ -169,12 +169,9 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{ - f64_field, summarize, BlueprintNode, Composite, Edge, OutField, Role, RunManifest, Target, - }; - use aura_core::{Firing, ParamSpec, Scalar, ScalarKind, Timestamp}; - use aura_std::{Exposure, Recorder, SimBroker, Sma, Sub}; - use std::sync::mpsc; + use crate::test_fixtures::{composite_sma_cross_harness, synthetic_prices}; + use crate::{f64_field, summarize, RunManifest}; + use aura_core::{ParamSpec, Scalar, ScalarKind, Timestamp}; fn i64_space(n: usize) -> Vec { (0..n) @@ -253,80 +250,6 @@ mod tests { assert_eq!(err, SweepError::EmptyAxis { slot: 0 }); } - fn synthetic_prices() -> Vec<(Timestamp, Scalar)> { - [ - (1_i64, 1.0000_f64), - (2, 1.0010), - (3, 1.0030), - (4, 1.0060), - (5, 1.0040), - (6, 1.0010), - (7, 0.9990), - ] - .iter() - .map(|&(t, p)| (Timestamp(t), Scalar::F64(p))) - .collect() - } - - /// The SMA-cross signal as a reusable value-empty composite (duplicated from - /// `blueprint.rs`'s test module — test fixtures stay module-local, the same - /// way `report.rs` copies its harness helper). - fn sma_cross() -> Composite { - Composite::new( - "sma_cross", - vec![ - Sma::builder().named("fast").into(), - Sma::builder().named("slow").into(), - Sub::builder().into(), - ], - vec![ - Edge { from: 0, to: 2, slot: 0, from_field: 0 }, - Edge { from: 1, to: 2, slot: 1, from_field: 0 }, - ], - vec![Role { - name: "price".into(), - targets: vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }], - source: None, - }], - vec![OutField { node: 2, field: 0, name: "out".into() }], - ) - } - - /// The signal-quality harness as a value-empty composite blueprint with two - /// recording sinks (duplicated from `blueprint.rs`'s test module). - #[allow(clippy::type_complexity)] - fn composite_sma_cross_harness() -> ( - Composite, - mpsc::Receiver<(Timestamp, Vec)>, - mpsc::Receiver<(Timestamp, Vec)>, - ) { - let (tx_eq, rx_eq) = mpsc::channel(); - let (tx_ex, rx_ex) = mpsc::channel(); - let bp = Composite::new( - "root", - vec![ - BlueprintNode::Composite(sma_cross()), - Exposure::builder().into(), - SimBroker::builder(0.0001).into(), - Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq).into(), - Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex).into(), - ], - vec![ - Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // composite out -> Exposure - Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // exposure -> broker slot 0 - Edge { from: 2, to: 3, slot: 0, from_field: 0 }, // equity -> sink - Edge { from: 1, to: 4, slot: 0, from_field: 0 }, // exposure -> sink - ], - vec![Role { - name: "src".into(), - targets: vec![Target { node: 0, slot: 0 }, Target { node: 2, slot: 1 }], - source: Some(ScalarKind::F64), - }], - vec![], // output: the root ends in sinks - ); - (bp, rx_eq, rx_ex) - } - /// Build + bootstrap + run + drain + summarize one grid point into a /// `RunReport`. A free `fn` (Copy + Sync) so it serves as the `sweep` closure /// AND a direct reference for the "sweep == N independent runs" comparison. diff --git a/crates/aura-engine/src/test_fixtures.rs b/crates/aura-engine/src/test_fixtures.rs new file mode 100644 index 0000000..f998e1f --- /dev/null +++ b/crates/aura-engine/src/test_fixtures.rs @@ -0,0 +1,87 @@ +//! Shared `#[cfg(test)]` fixtures for the SMA-cross signal-quality harness, +//! consumed by the `blueprint` and `sweep` unit-test modules. Kept in one place +//! so the harness topology cannot silently diverge between them (see #53). +//! Gated test-only at the declaration site in `lib.rs` +//! (`#[cfg(test)] mod test_fixtures;`), matching the `reexport_tests` precedent. + +use crate::{BlueprintNode, Composite, Edge, OutField, Role, Target}; +use aura_core::{Firing, Scalar, ScalarKind, Timestamp}; +use aura_std::{Exposure, Recorder, SimBroker, Sma, Sub}; +use std::sync::mpsc; + +/// Seven synthetic F64 ticks driving the harness; deterministic input fixture. +pub(crate) fn synthetic_prices() -> Vec<(Timestamp, Scalar)> { + [ + (1_i64, 1.0000_f64), + (2, 1.0010), + (3, 1.0030), + (4, 1.0060), + (5, 1.0040), + (6, 1.0010), + (7, 0.9990), + ] + .iter() + .map(|&(t, p)| (Timestamp(t), Scalar::F64(p))) + .collect() +} + +/// The SMA-cross signal as a reusable value-empty composite: one input role +/// (price), one output (fast-minus-slow spread). Lengths injected at compile. +pub(crate) fn sma_cross() -> Composite { + Composite::new( + "sma_cross", + vec![ + Sma::builder().named("fast").into(), + Sma::builder().named("slow").into(), + Sub::builder().into(), + ], + vec![ + Edge { from: 0, to: 2, slot: 0, from_field: 0 }, + Edge { from: 1, to: 2, slot: 1, from_field: 0 }, + ], + vec![Role { + name: "price".into(), + targets: vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }], + source: None, + }], + vec![OutField { node: 2, field: 0, name: "out".into() }], + ) +} + +/// The signal-quality harness as a value-empty composite blueprint with two +/// recording sinks (equity, exposure). +#[allow(clippy::type_complexity)] +pub(crate) fn composite_sma_cross_harness() -> ( + Composite, + mpsc::Receiver<(Timestamp, Vec)>, + mpsc::Receiver<(Timestamp, Vec)>, +) { + let (tx_eq, rx_eq) = mpsc::channel(); + let (tx_ex, rx_ex) = mpsc::channel(); + let bp = Composite::new( + "root", + vec![ + BlueprintNode::Composite(sma_cross()), + Exposure::builder().into(), + SimBroker::builder(0.0001).into(), + Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq).into(), + Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex).into(), + ], + vec![ + Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // composite out -> Exposure + Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // exposure -> broker slot 0 + Edge { from: 2, to: 3, slot: 0, from_field: 0 }, // equity -> sink + Edge { from: 1, to: 4, slot: 0, from_field: 0 }, // exposure -> sink + ], + vec![Role { + name: "src".into(), + targets: vec![ + Target { node: 0, slot: 0 }, // price -> sma_cross role 0 + Target { node: 2, slot: 1 }, // price -> SimBroker price slot + ], + source: Some(ScalarKind::F64), + }], + vec![], // output: the root ends in sinks + ); + (bp, rx_eq, rx_ex) +}