//! TFT model integration test //! //! Verifies: sequence buffering -> forward pass -> quantile output //! TFT requires sequence_length observations before producing real predictions. //! Uses lightweight config for fast execution (<10s) use ml::ensemble::adapters::TftInferenceAdapter; use ml::ensemble::inference_adapter::{FeatureVector, ModelInferenceAdapter}; use ml::tft::TFTConfig; fn small_tft_config() -> TFTConfig { TFTConfig { input_dim: 20, hidden_dim: 32, num_heads: 2, num_layers: 1, prediction_horizon: 5, sequence_length: 4, num_quantiles: 9, num_static_features: 6, num_known_features: 6, num_unknown_features: 8, dropout_rate: 0.0, ..Default::default() } } const SEQ_LEN: usize = 4; fn make_fv(ts: i64) -> FeatureVector { FeatureVector { values: vec![0.1; 51], timestamp: ts, } } #[test] fn test_tft_adapter_buffers_before_predicting() { let adapter = TftInferenceAdapter::new(small_tft_config(), SEQ_LEN) .expect("TftInferenceAdapter::new should succeed"); assert_eq!(adapter.model_name(), "TFT"); assert!( !adapter.is_ready(), "TFT should not be ready with empty buffer" ); // Feed SEQ_LEN - 1 feature vectors: should return neutral (0.0, 0.0) for i in 0..(SEQ_LEN - 1) { let pred = adapter .predict(&make_fv(1_700_000_000 + i as i64)) .expect("predict should not error during buffering"); assert_eq!( pred.direction, 0.0, "Should return neutral direction while buffering" ); assert_eq!( pred.confidence, 0.0, "Should return zero confidence while buffering" ); } assert!(!adapter.is_ready(), "TFT should still not be ready"); } #[test] fn test_tft_produces_valid_prediction_after_warmup() { let adapter = TftInferenceAdapter::new(small_tft_config(), SEQ_LEN) .expect("TftInferenceAdapter::new should succeed"); // Fill buffer for i in 0..SEQ_LEN { let _ = adapter.predict(&make_fv(1_700_000_000 + i as i64)); } assert!( adapter.is_ready(), "TFT should be ready after filling buffer" ); // Next prediction should produce real values let pred = adapter .predict(&make_fv(1_700_000_000 + SEQ_LEN as i64)) .expect("TFT predict should succeed after warmup"); 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" ); }