Files
foxhunt/testing/integration/performance/critical_path_tests.rs
jgrusewski 9c3d741a08 refactor: restructure repo — crates/, bin/, testing/ layout
Move 17 library crates into crates/, CLI binary into bin/fxt,
consolidate 10 test crates into testing/, split config crate
from deployment config files.

Root directory reduced from 38+ to ~17 directories.
All Cargo.toml paths and build.rs proto refs updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:56:00 +01:00

892 lines
30 KiB
Rust

//! Critical Path Tests for Foxhunt HFT Trading System
//!
//! This module tests the end-to-end critical trading paths that must work flawlessly
//! in production for the system to be viable for high-frequency trading.
//!
//! # Test Coverage
//!
//! - **Market Data → Signal Generation → Risk Check → Order → Execution** (End-to-End)
//! - **Order Lifecycle Management** (New → Partial Fill → Complete)
//! - **Risk Validation Pipeline** (Position limits, VaR, circuit breakers)
//! - **ML Model Integration** (Feature extraction → Inference → Trading decision)
//! - **Error Recovery Paths** (Market data failure, risk violations, broker issues)
//! - **Latency Performance** (Sub-50μs critical path requirements)
//! - **Financial Safety** (Decimal precision, overflow protection, NaN handling)
//!
//! # Test Philosophy
//!
//! These tests focus on COVERAGE over complexity. Simple tests that run reliably
//! are more valuable than complex tests that don't compile. Each test validates
//! a specific critical path without unnecessary mocking or complexity.
// anyhow not available - using simple Result type
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
use std::time::{Duration, Instant};
use std::collections::HashMap;
use tokio::time::timeout;
// Import unified types from the core prelude
// Import risk management system
// use risk::prelude::*; // REMOVED - prelude does not exist
// Import ML models
use ml::prelude::*;
// Import common test utilities
use crate::common::{*, test_config::*, test_utils::*, assertions::*};
use common::*;
use common::test_config::*;
use common::mock_data::*;
use common::test_utils::*;
use common::assertions::*;
/// Test configuration for critical path tests
#[derive(Debug, Clone)]
struct CriticalPathConfig {
/// Maximum allowed latency for critical operations (microseconds)
max_latency_us: u64,
/// Timeout for async operations (seconds)
timeout_seconds: u64,
/// Enable performance validation
validate_performance: bool,
/// Enable safety checks
validate_safety: bool,
/// Market data simulation parameters
market_data_config: MarketDataConfig,
/// Risk limits for testing
risk_limits: TestRiskLimits,
}
impl Default for CriticalPathConfig {
fn default() -> Self {
Self {
max_latency_us: 50, // 50μs HFT requirement
timeout_seconds: 30,
validate_performance: true,
validate_safety: true,
market_data_config: MarketDataConfig::default(),
risk_limits: TestRiskLimits::default(),
}
}
}
#[derive(Debug, Clone)]
struct MarketDataConfig {
symbol: String,
initial_price: f64,
volatility: f64,
tick_size: f64,
}
impl Default for MarketDataConfig {
fn default() -> Self {
Self {
symbol: "BTCUSD".to_string(),
initial_price: 50000.0,
volatility: 0.02,
tick_size: 0.01,
}
}
}
#[derive(Debug, Clone)]
struct TestRiskLimits {
max_position_size: f64,
max_order_value: f64,
max_daily_loss: f64,
var_limit: f64,
}
impl Default for TestRiskLimits {
fn default() -> Self {
Self {
max_position_size: 10000.0,
max_order_value: 5000.0,
max_daily_loss: 1000.0,
var_limit: 500.0,
}
}
}
/// Market data tick structure for testing
#[derive(Debug, Clone)]
struct TestMarketTick {
symbol: Symbol,
price: Price,
volume: Volume,
timestamp: HftTimestamp,
bid: Price,
ask: Price,
spread: Price,
}
impl TestMarketTick {
fn new(symbol: &str, price: f64, volume: f64) -> Result<Self> {
Ok(Self {
symbol: Symbol::from(symbol),
price: Price::from_f64(price)?,
volume: Volume::from_f64(volume),
timestamp: HftTimestamp::now()?,
bid: Price::from_f64(price - 0.01)?,
ask: Price::from_f64(price + 0.01)?,
spread: Price::from_f64(0.02)?,
})
}
fn create_features(&self) -> Features {
Features::new(
vec![
self.price.to_f64(),
self.volume.to_f64(),
self.bid.to_f64(),
self.ask.to_f64(),
self.spread.to_f64(),
self.timestamp.nanos() as f64,
],
vec![
"price".to_string(),
"volume".to_string(),
"bid".to_string(),
"ask".to_string(),
"spread".to_string(),
"timestamp".to_string(),
],
).with_symbol(self.symbol.as_str().to_string())
}
}
/// Trading signal structure for testing
#[derive(Debug, Clone)]
struct TestTradingSignal {
symbol: Symbol,
side: Side,
strength: f64,
confidence: f64,
timestamp: HftTimestamp,
metadata: HashMap<String, String>,
}
impl TestTradingSignal {
fn new(symbol: Symbol, side: Side, strength: f64, confidence: f64) -> Result<Self> {
Ok(Self {
symbol,
side,
strength,
confidence,
timestamp: HftTimestamp::now()?,
metadata: HashMap::new(),
})
}
fn is_actionable(&self) -> bool {
self.confidence > 0.6 && self.strength.abs() > 0.5
}
}
/// Order execution result for testing
#[derive(Debug, Clone)]
struct TestExecutionResult {
order_id: OrderId,
status: OrderStatus,
filled_quantity: Quantity,
avg_price: Price,
commission: Price,
timestamp: HftTimestamp,
latency_us: u64,
}
impl TestExecutionResult {
fn new(order_id: OrderId, status: OrderStatus) -> Result<Self> {
Ok(Self {
order_id,
status,
filled_quantity: Quantity::ZERO,
avg_price: Price::ZERO,
commission: Price::ZERO,
timestamp: HftTimestamp::now()?,
latency_us: 0,
})
}
fn is_success(&self) -> bool {
matches!(self.status, OrderStatus::Filled | OrderStatus::PartiallyFilled)
}
}
/// Test setup utilities
struct CriticalPathTestSuite {
config: CriticalPathConfig,
risk_engine: Option<RiskEngine>,
position_tracker: Option<PositionTracker>,
ml_registry: Option<std::sync::Arc<ModelRegistry>>,
}
impl CriticalPathTestSuite {
fn new() -> Self {
setup_test_tracing();
Self {
config: CriticalPathConfig::default(),
risk_engine: None,
position_tracker: None,
ml_registry: None,
}
}
async fn setup(&mut self) -> Result<()> {
// Initialize risk management components
let risk_config = RiskConfig {
max_position_size: Price::from_f64(self.config.risk_limits.max_position_size)?,
max_daily_loss: Price::from_f64(self.config.risk_limits.max_daily_loss)?,
var_confidence_level: 0.95,
var_lookback_days: 252,
enable_kill_switch: false, // Disabled for testing
enable_circuit_breakers: true,
redis_url: "redis://localhost:6379".to_string(),
};
self.risk_engine = Some(RiskEngine::new(risk_config).await?);
self.position_tracker = Some(PositionTracker::new());
// Initialize ML model registry
let registry = get_global_registry();
// Register available models (ignore failures for robustness)
if let Ok(tlob_model) = ml::model_factory::create_tlob_wrapper() {
let _ = registry.register(std::sync::Arc::from(tlob_model)).await;
}
if let Ok(dqn_model) = ml::model_factory::create_dqn_wrapper() {
let _ = registry.register(std::sync::Arc::from(dqn_model)).await;
}
self.ml_registry = Some(registry);
Ok(())
}
/// Create test market data
fn create_test_market_data(&self) -> Result<TestMarketTick> {
TestMarketTick::new(
&self.config.market_data_config.symbol,
self.config.market_data_config.initial_price,
1000.0,
)
}
/// Generate trading signal from market data
async fn generate_trading_signal(&self, market_data: &TestMarketTick) -> Result<TestTradingSignal> {
let start_time = Instant::now();
// Use ML models to generate signal if available
let signal = if let Some(registry) = &self.ml_registry {
let features = market_data.create_features();
// Try to get predictions from available models
let models = registry.get_all();
if !models.is_empty() {
let predictions = registry.predict_all(&features).await;
// Aggregate predictions (simple averaging)
let mut total_signal = 0.0;
let mut count = 0;
for prediction_result in predictions {
if let Ok(prediction) = prediction_result {
total_signal += prediction.value;
count += 1;
}
}
if count > 0 {
let avg_signal = total_signal / count as f64;
let side = if avg_signal > 0.0 { Side::Buy } else { Side::Sell };
let strength = avg_signal.abs();
let confidence = 0.8; // Default confidence
TestTradingSignal::new(market_data.symbol.clone(), side, strength, confidence)?
} else {
// Fallback to simple signal generation
self.generate_simple_signal(market_data)?
}
} else {
// No models available, use simple signal
self.generate_simple_signal(market_data)?
}
} else {
// No registry available, use simple signal
self.generate_simple_signal(market_data)?
};
let latency = start_time.elapsed();
// Validate latency if performance checking is enabled
if self.config.validate_performance {
assert_hft_latency(latency, self.config.max_latency_us);
}
Ok(signal)
}
/// Simple signal generation fallback
fn generate_simple_signal(&self, market_data: &TestMarketTick) -> Result<TestTradingSignal> {
// Simple momentum-based signal
let price_change = (market_data.price.to_f64() - self.config.market_data_config.initial_price)
/ self.config.market_data_config.initial_price;
let side = if price_change > 0.001 { Side::Sell } else { Side::Buy }; // Mean reversion
let strength = price_change.abs().min(1.0);
let confidence = 0.7;
TestTradingSignal::new(market_data.symbol.clone(), side, strength, confidence)
}
/// Validate risk for trading signal
async fn validate_risk(&self, signal: &TestTradingSignal) -> Result<bool> {
let start_time = Instant::now();
// Create order info for risk validation
let quantity = Quantity::from_f64(signal.strength * 100.0)?; // Scale by strength
let price = Price::from_f64(self.config.market_data_config.initial_price)?;
let order_info = OrderInfo {
symbol: signal.symbol.clone(),
side: signal.side,
quantity,
price,
};
// Validate with risk engine if available
let risk_approved = if let Some(ref risk_engine) = self.risk_engine {
match risk_engine.validate_order(&order_info).await {
Ok(result) => result.approved,
Err(_) => false, // Risk engine error = rejection
}
} else {
// Basic risk checks without engine
let order_value = quantity.to_f64() * price.to_f64();
order_value <= self.config.risk_limits.max_order_value
};
let latency = start_time.elapsed();
// Validate latency if performance checking is enabled
if self.config.validate_performance {
assert_hft_latency(latency, self.config.max_latency_us);
}
Ok(risk_approved)
}
/// Create order from validated signal
fn create_order_from_signal(&self, signal: &TestTradingSignal) -> Result<Order> {
let symbol = signal.symbol.clone();
let side = signal.side;
let quantity = Quantity::from_f64(signal.strength * 100.0)?;
let price = Price::from_f64(self.config.market_data_config.initial_price)?;
let order = Order::limit(symbol, side, quantity, price);
Ok(order)
}
/// Simulate order execution
async fn simulate_execution(&self, order: &Order) -> Result<TestExecutionResult> {
let start_time = Instant::now();
// Simulate execution latency
tokio::time::sleep(Duration::from_micros(10)).await;
let mut result = TestExecutionResult::new(order.id, OrderStatus::Filled)?;
result.filled_quantity = order.quantity;
result.avg_price = Price::from_f64(self.config.market_data_config.initial_price)?;
result.commission = Price::from_f64(2.50)?; // $2.50 commission
result.latency_us = start_time.elapsed().as_micros() as u64;
// Validate execution latency
if self.config.validate_performance {
assert_hft_latency(start_time.elapsed(), self.config.max_latency_us);
}
Ok(result)
}
}
// ========== CRITICAL PATH TESTS ==========
#[tokio::test]
async fn test_end_to_end_critical_trading_path() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.setup().await?;
// Execute full trading pipeline with timeout
let result = timeout(
Duration::from_secs(test_suite.config.timeout_seconds),
async {
// 1. Simulate market data
let market_data = test_suite.create_test_market_data()?;
assert!(!market_data.symbol.as_str().is_empty(), "Market data should have valid symbol");
assert!(market_data.price.to_f64() > 0.0, "Market data should have positive price");
// 2. Generate trading signal
let signal = test_suite.generate_trading_signal(&market_data).await?;
assert!(signal.confidence > 0.0, "Signal should have positive confidence");
assert!(signal.strength >= 0.0, "Signal strength should be non-negative");
// 3. Risk validation
let risk_approved = test_suite.validate_risk(&signal).await?;
if !risk_approved {
// Risk rejection is a valid outcome, not a test failure
return Ok(());
}
// 4. Create order
let order = test_suite.create_order_from_signal(&signal)?;
assert_eq!(order.symbol, signal.symbol, "Order symbol should match signal symbol");
assert_eq!(order.side, signal.side, "Order side should match signal side");
assert!(order.quantity.to_f64() > 0.0, "Order quantity should be positive");
// 5. Simulate execution
let execution_result = test_suite.simulate_execution(&order).await?;
assert!(execution_result.is_success(), "Execution should be successful");
assert_eq!(execution_result.order_id, order.id, "Execution should match order ID");
// 6. Validate end-to-end latency
if test_suite.config.validate_performance {
assert!(execution_result.latency_us <= test_suite.config.max_latency_us,
"End-to-end execution latency {}μs should be <= {}μs",
execution_result.latency_us, test_suite.config.max_latency_us);
}
Ok::<(), anyhow::Error>(())
}
).await?;
result?;
Ok(())
}
#[tokio::test]
async fn test_order_lifecycle_management() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.setup().await?;
// Test complete order lifecycle
let market_data = test_suite.create_test_market_data()?;
let signal = test_suite.generate_trading_signal(&market_data).await?;
if !signal.is_actionable() {
// Signal not actionable - skip order lifecycle test
return Ok(());
}
let mut order = test_suite.create_order_from_signal(&signal)?;
// Test order states: New -> PartiallyFilled -> Filled
assert_eq!(order.status, OrderStatus::Pending, "New order should be pending");
// Simulate partial fill
order.status = OrderStatus::PartiallyFilled;
let partial_quantity = Quantity::from_f64(order.quantity.to_f64() * 0.5)?;
// Verify partial fill state
assert_eq!(order.status, OrderStatus::PartiallyFilled);
assert!(partial_quantity.to_f64() < order.quantity.to_f64());
// Simulate complete fill
order.status = OrderStatus::Filled;
assert_eq!(order.status, OrderStatus::Filled);
Ok(())
}
#[tokio::test]
async fn test_risk_validation_pipeline() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.setup().await?;
// Test various risk scenarios
let market_data = test_suite.create_test_market_data()?;
// Test 1: Normal order within limits
let normal_signal = TestTradingSignal::new(
market_data.symbol.clone(),
Side::Buy,
0.5, // 50% strength = moderate position
0.8,
)?;
let risk_approved = test_suite.validate_risk(&normal_signal).await?;
// Note: Risk approval depends on risk engine availability - both outcomes are valid
// Test 2: Large order that might exceed limits
let large_signal = TestTradingSignal::new(
market_data.symbol.clone(),
Side::Buy,
2.0, // 200% strength = large position
0.9,
)?;
let large_risk_approved = test_suite.validate_risk(&large_signal).await?;
// Large orders should typically be rejected or approved based on risk limits
// Test 3: Risk validation performance
let start_time = Instant::now();
for _ in 0..10 {
let _ = test_suite.validate_risk(&normal_signal).await?;
}
let avg_latency = start_time.elapsed() / 10;
if test_suite.config.validate_performance {
assert_hft_latency(avg_latency, test_suite.config.max_latency_us);
}
Ok(())
}
#[tokio::test]
async fn test_ml_model_integration() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.setup().await?;
let market_data = test_suite.create_test_market_data()?;
let features = market_data.create_features();
// Test ML model availability and prediction
if let Some(registry) = &test_suite.ml_registry {
let models = registry.get_model_names();
if !models.is_empty() {
// Test parallel prediction across all models
let start_time = Instant::now();
let predictions = registry.predict_all(&features).await;
let prediction_latency = start_time.elapsed();
// Validate that we got some predictions
assert!(!predictions.is_empty(), "Should get predictions from available models");
// Check that at least some predictions succeeded
let successful_predictions: Vec<_> = predictions.into_iter()
.filter_map(|p| p.ok())
.collect();
if !successful_predictions.is_empty() {
// Validate prediction structure
for prediction in &successful_predictions {
assert!(!prediction.model_id.is_empty(), "Prediction should have model ID");
assert!(prediction.confidence >= 0.0 && prediction.confidence <= 1.0,
"Confidence should be between 0 and 1");
}
// Validate prediction latency
if test_suite.config.validate_performance {
assert_hft_latency(prediction_latency, test_suite.config.max_latency_us);
}
}
}
}
Ok(())
}
#[tokio::test]
async fn test_error_recovery_paths() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.setup().await?;
// Test 1: Invalid market data handling
let invalid_market_data = TestMarketTick {
symbol: Symbol::from(""),
price: Price::ZERO,
volume: Volume::from_f64(0.0),
timestamp: HftTimestamp::now()?,
bid: Price::ZERO,
ask: Price::ZERO,
spread: Price::ZERO,
};
// System should handle invalid data gracefully
let signal_result = test_suite.generate_trading_signal(&invalid_market_data).await;
// Either succeeds with fallback or fails gracefully (both are acceptable)
// Test 2: Risk violation handling
let risky_signal = TestTradingSignal::new(
Symbol::from("TESTCOIN"),
Side::Buy,
10.0, // Extremely high strength
0.9,
)?;
let risk_result = test_suite.validate_risk(&risky_signal).await?;
// Should handle risk violations without panicking
// Test 3: Order creation with invalid parameters
let invalid_signal = TestTradingSignal::new(
Symbol::from(""),
Side::Buy,
0.0,
0.0,
)?;
let order_result = test_suite.create_order_from_signal(&invalid_signal);
// Should handle invalid orders gracefully (either succeed with defaults or fail safely)
Ok(())
}
#[tokio::test]
async fn test_latency_performance_validation() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.config.validate_performance = true;
test_suite.setup().await?;
// Measure component latencies
let market_data = test_suite.create_test_market_data()?;
// Test signal generation latency
let signal_start = Instant::now();
let signal = test_suite.generate_trading_signal(&market_data).await?;
let signal_latency = signal_start.elapsed();
// Test risk validation latency
let risk_start = Instant::now();
let _ = test_suite.validate_risk(&signal).await?;
let risk_latency = risk_start.elapsed();
// Test order creation latency
let order_start = Instant::now();
let order = test_suite.create_order_from_signal(&signal)?;
let order_latency = order_start.elapsed();
// Validate individual component latencies
assert_hft_latency(signal_latency, test_suite.config.max_latency_us);
assert_hft_latency(risk_latency, test_suite.config.max_latency_us);
assert_hft_latency(order_latency, test_suite.config.max_latency_us);
// Test batched operations latency
let batch_start = Instant::now();
for _ in 0..10 {
let _ = test_suite.generate_trading_signal(&market_data).await?;
}
let batch_latency = batch_start.elapsed() / 10; // Average per operation
assert_hft_latency(batch_latency, test_suite.config.max_latency_us);
Ok(())
}
#[tokio::test]
async fn test_financial_safety_validation() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.config.validate_safety = true;
test_suite.setup().await?;
// Test 1: Decimal precision handling
let precise_price = Price::from_f64(123.456789)?;
assert_within_percent(precise_price.to_f64(), 123.456789, 0.001);
// Test 2: Overflow protection
let max_price = Price::from_f64(f64::MAX / 2.0)?; // Safe large value
let quantity = Quantity::from_f64(2.0)?;
let product = max_price.to_f64() * quantity.to_f64();
assert!(product.is_finite(), "Large calculations should remain finite");
// Test 3: NaN/Infinity handling
let market_data = test_suite.create_test_market_data()?;
let mut features = market_data.create_features();
// Inject problematic values
features.values[0] = f64::NAN;
features.values[1] = f64::INFINITY;
// System should handle these gracefully
if let Some(registry) = &test_suite.ml_registry {
let predictions = registry.predict_all(&features).await;
// Predictions should either succeed with sanitized values or fail gracefully
for prediction_result in predictions {
if let Ok(prediction) = prediction_result {
assert!(prediction.value.is_finite(), "Predictions should be finite values");
assert!(prediction.confidence.is_finite(), "Confidence should be finite");
}
}
}
// Test 4: Currency and precision consistency
let usd_amount = Money::from_f64(1234.56, Currency::USD);
assert_eq!(usd_amount.currency(), Currency::USD);
assert_within_percent(usd_amount.amount().to_f64(), 1234.56, 0.001);
Ok(())
}
#[tokio::test]
async fn test_concurrent_critical_paths() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.setup().await?;
// Test concurrent execution of critical paths
let market_data = test_suite.create_test_market_data()?;
// Create multiple concurrent trading tasks
let mut tasks = Vec::new();
for i in 0..5 {
let market_data = market_data.clone();
let config = test_suite.config.clone();
let task = tokio::spawn(async move {
// Create a mini test suite for this task
let mut local_suite = CriticalPathTestSuite::new();
local_suite.config = config;
local_suite.setup().await?;
// Execute critical path
let signal = local_suite.generate_trading_signal(&market_data).await?;
let risk_approved = local_suite.validate_risk(&signal).await?;
if risk_approved {
let order = local_suite.create_order_from_signal(&signal)?;
let execution = local_suite.simulate_execution(&order).await?;
Ok::<_, anyhow::Error>(execution.is_success())
} else {
Ok(true) // Risk rejection is a valid outcome
}
});
tasks.push(task);
}
// Wait for all tasks to complete
let results = futures::future::join_all(tasks).await;
// Validate that all tasks completed successfully
for (i, result) in results.into_iter().enumerate() {
match result {
Ok(Ok(success)) => {
// Task completed - success is not required (risk rejections are valid)
}
Ok(Err(e)) => {
return Err(anyhow::anyhow!("Task {} failed: {}", i, e));
}
Err(e) => {
return Err(anyhow::anyhow!("Task {} panicked: {}", i, e));
}
}
}
Ok(())
}
#[tokio::test]
async fn test_system_resource_limits() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.setup().await?;
// Test memory usage stability
let initial_memory = get_memory_usage();
// Perform many operations to test for memory leaks
for _ in 0..100 {
let market_data = test_suite.create_test_market_data()?;
let signal = test_suite.generate_trading_signal(&market_data).await?;
let _ = test_suite.validate_risk(&signal).await?;
// Periodic memory check
if initial_memory > 0 {
let current_memory = get_memory_usage();
let memory_growth = (current_memory as f64 - initial_memory as f64) / initial_memory as f64;
// Allow some memory growth but catch excessive leaks
assert!(memory_growth < 2.0, "Memory usage should not grow excessively");
}
}
Ok(())
}
/// Simple memory usage estimation (placeholder implementation)
fn get_memory_usage() -> usize {
// This is a placeholder - in a real implementation you'd use system APIs
// to get actual memory usage
0
}
#[tokio::test]
async fn test_system_integration_health() -> Result<()> {
let mut test_suite = CriticalPathTestSuite::new();
test_suite.setup().await?;
// Test health check for all major components
let mut health_report = Vec::new();
// Check risk engine health
if let Some(ref risk_engine) = test_suite.risk_engine {
health_report.push(("RiskEngine", "Available"));
} else {
health_report.push(("RiskEngine", "Unavailable"));
}
// Check ML registry health
if let Some(ref registry) = test_suite.ml_registry {
let model_count = registry.get_model_names().len();
health_report.push(("MLRegistry", if model_count > 0 { "Available" } else { "Empty" }));
} else {
health_report.push(("MLRegistry", "Unavailable"));
}
// Check position tracker health
if test_suite.position_tracker.is_some() {
health_report.push(("PositionTracker", "Available"));
} else {
health_report.push(("PositionTracker", "Unavailable"));
}
// Log health report
for (component, status) in &health_report {
tracing::info!("Component {} status: {}", component, status);
}
// Test basic functionality even with limited components
let market_data = test_suite.create_test_market_data()?;
let signal = test_suite.generate_trading_signal(&market_data).await?;
// Should be able to generate signals regardless of component availability
assert!(signal.confidence >= 0.0, "Signal generation should work with available components");
Ok(())
}
// ========== UTILITY FUNCTIONS FOR TESTS ==========
/// Create test environment for isolated testing
async fn create_test_environment() -> Result<CriticalPathTestSuite> {
let mut suite = CriticalPathTestSuite::new();
suite.setup().await?;
Ok(suite)
}
/// Validate test execution metrics
fn validate_execution_metrics(
start_time: Instant,
max_latency_us: u64,
operation_name: &str,
) -> Result<()> {
let latency = start_time.elapsed();
assert_hft_latency(latency, max_latency_us);
tracing::debug!("Operation {} completed in {}μs", operation_name, latency.as_micros());
Ok(())
}
/// Create comprehensive test data set
fn create_test_dataset(size: usize) -> Result<Vec<TestMarketTick>> {
let mut dataset = Vec::with_capacity(size);
for i in 0..size {
let price = 50000.0 + (i as f64 * 0.01); // Incrementing prices
let volume = 1000.0 + (i as f64 * 10.0); // Incrementing volumes
dataset.push(TestMarketTick::new("BTCUSD", price, volume)?);
}
Ok(dataset)
}