Files
foxhunt/tests/integration/dqn_integration.rs
jgrusewski 0fa7aa41c0 test: add 6 ML integration tests (DQN, PPO, TFT, Mamba2, ensemble, smoke)
- DQN: adapter creation, deterministic inference, varied inputs
- PPO: adapter creation, deterministic inference, short input padding
- TFT: sequence buffering, valid prediction after warmup
- Mamba2: sequence buffering, valid prediction, deterministic SSM
- Ensemble: all 4 models -> EnsembleCoordinator -> trading decision
- Smoke: full pipeline with 10 sequential predictions, stability check

This establishes the production baseline proving the ML pipeline works
end-to-end with all 4 models contributing to ensemble decisions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:13:09 +01:00

95 lines
2.7 KiB
Rust

//! DQN model integration test
//!
//! Verifies: create model -> forward pass -> valid output range
//! Uses lightweight config for fast execution (<10s)
use ml::dqn::dqn::DQNConfig;
use ml::ensemble::adapters::DqnInferenceAdapter;
use ml::ensemble::inference_adapter::{FeatureVector, ModelInferenceAdapter};
fn small_dqn_config() -> DQNConfig {
DQNConfig {
state_dim: 51,
num_actions: 45,
hidden_dims: vec![32, 32],
..Default::default()
}
}
#[test]
fn test_dqn_adapter_produces_valid_prediction() {
let adapter = DqnInferenceAdapter::new(small_dqn_config())
.expect("DqnInferenceAdapter::new should succeed");
assert_eq!(adapter.model_name(), "DQN");
assert!(adapter.is_ready());
let fv = FeatureVector {
values: vec![0.1; 51],
timestamp: 1_700_000_000_000_000,
};
let pred = adapter.predict(&fv).expect("DQN predict should succeed");
assert!(
pred.direction >= -1.0 && pred.direction <= 1.0,
"direction {} out of [-1,1]",
pred.direction
);
assert!(
pred.confidence >= 0.0 && pred.confidence <= 1.0,
"confidence {} out of [0,1]",
pred.confidence
);
assert!(pred.direction.is_finite(), "direction must not be NaN/Inf");
assert!(pred.confidence.is_finite(), "confidence must not be NaN/Inf");
assert!(
pred.metadata.q_values.is_some(),
"DQN should include Q-values in metadata"
);
}
#[test]
fn test_dqn_deterministic_inference() {
let adapter = DqnInferenceAdapter::new(small_dqn_config())
.expect("DqnInferenceAdapter::new should succeed");
let fv = FeatureVector {
values: vec![0.5; 51],
timestamp: 1_700_000_000_000_000,
};
let pred1 = adapter.predict(&fv).expect("predict 1");
let pred2 = adapter.predict(&fv).expect("predict 2");
assert_eq!(
pred1.direction, pred2.direction,
"DQN inference should be deterministic"
);
assert_eq!(
pred1.confidence, pred2.confidence,
"DQN confidence should be deterministic"
);
}
#[test]
fn test_dqn_different_inputs_different_outputs() {
let adapter = DqnInferenceAdapter::new(small_dqn_config())
.expect("DqnInferenceAdapter::new should succeed");
let fv_low = FeatureVector {
values: vec![0.0; 51],
timestamp: 1_700_000_000_000_000,
};
let fv_high = FeatureVector {
values: vec![1.0; 51],
timestamp: 1_700_000_000_000_000,
};
let pred_low = adapter.predict(&fv_low).expect("predict low");
let pred_high = adapter.predict(&fv_high).expect("predict high");
assert!(pred_low.direction.is_finite());
assert!(pred_high.direction.is_finite());
}