//! Test helper utilities and common functions use chrono::{DateTime, Utc}; use foxhunt_core::prelude::TradingOrder; use foxhunt_core::types::prelude::*; use std::collections::HashMap; // Generate a simple test ID instead of using uuid fn generate_test_id() -> String { use std::sync::atomic::{AtomicU64, Ordering}; static COUNTER: AtomicU64 = AtomicU64::new(1); format!("TEST_{}", COUNTER.fetch_add(1, Ordering::SeqCst)) } /// Create a test TradingOrder with all required fields pub fn create_test_order( symbol: &str, side: Side, quantity: Decimal, price: Decimal, ) -> TradingOrder { TradingOrder { id: generate_test_id().into(), symbol: symbol.to_string(), side, order_type: OrderType::Limit, quantity, price, time_in_force: TimeInForce::Day, metadata: HashMap::new(), created_at: Utc::now(), submitted_at: None, executed_at: None, status: OrderStatus::Created, fill_quantity: Decimal::ZERO, average_fill_price: None, } } /// Create a test MarketEvent with all required fields pub fn create_test_market_event(symbol: &str, price: Decimal) -> MarketEvent { MarketEvent::Trade { symbol: Symbol::new(symbol.to_string()), price: Price::from_decimal(price), size: Quantity::from_decimal(Decimal::from(100)) .unwrap_or(Quantity::from_decimal(Decimal::from(1)).unwrap()), timestamp: Utc::now(), side: None, venue: Some("TEST_VENUE".to_string()), trade_id: Some(format!("TRADE_{}", generate_test_id())), } } /// Create test configuration with sensible defaults pub fn create_test_config() -> TestConfig { TestConfig { initial_capital: Decimal::from(100000), risk_free_rate: Decimal::new(2, 2), // 2% enable_logging: false, } } #[derive(Debug, Clone)] pub struct TestConfig { pub initial_capital: Decimal, pub risk_free_rate: Decimal, pub enable_logging: bool, } impl Default for TestConfig { fn default() -> Self { create_test_config() } } /// Mock implementations for testing pub mod mock_implementations { use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; /// Mock performance monitor for testing #[derive(Debug, Clone)] pub struct MockPerformanceMonitor { stats: Arc>, } impl MockPerformanceMonitor { pub fn new() -> Self { Self { stats: Arc::new(Mutex::new(PerformanceStats::default())), } } pub fn record_operation(&self, operation: &str, duration: Duration) { if let Ok(mut stats) = self.stats.lock() { stats.operations_count += 1; stats.total_duration += duration; stats.average_latency = stats.total_duration / stats.operations_count as u32; if duration > stats.max_latency { stats.max_latency = duration; } if duration < stats.min_latency || stats.min_latency == Duration::ZERO { stats.min_latency = duration; } stats .operation_latencies .insert(operation.to_string(), duration); } } pub fn record_metric( &self, metric_name: &str, value: f64, unit: &str, ) -> Result<(), &'static str> { // Convert the metric value to a duration based on the unit let duration = match unit { "ns" => Duration::from_nanos(value as u64), "us" | "μs" => Duration::from_micros(value as u64), "ms" => Duration::from_millis(value as u64), "s" => Duration::from_secs(value as u64), // Non-time units - convert to a mock duration representation "ops/sec" | "orders/sec" | "ratio" | "ns/item" => { // For non-time units, store as microseconds for simplicity Duration::from_micros((value * 1000.0) as u64) } _ => Duration::from_nanos(1000), // Default fallback }; self.record_operation(metric_name, duration); Ok(()) } pub fn get_stats(&self) -> PerformanceStats { self.stats .lock() .unwrap_or_else(|_| panic!("Failed to lock stats")) .clone() } pub fn reset(&self) { if let Ok(mut stats) = self.stats.lock() { *stats = PerformanceStats::default(); } } } impl Default for MockPerformanceMonitor { fn default() -> Self { Self::new() } } /// Performance statistics for testing #[derive(Debug, Clone)] pub struct PerformanceStats { pub operations_count: u64, pub total_duration: Duration, pub average_latency: Duration, pub min_latency: Duration, pub max_latency: Duration, pub operation_latencies: HashMap, } impl Default for PerformanceStats { fn default() -> Self { Self { operations_count: 0, total_duration: Duration::ZERO, average_latency: Duration::ZERO, min_latency: Duration::ZERO, max_latency: Duration::ZERO, operation_latencies: HashMap::new(), } } } impl PerformanceStats { pub fn throughput_per_second(&self) -> f64 { if self.total_duration.as_secs_f64() > 0.0 { self.operations_count as f64 / self.total_duration.as_secs_f64() } else { 0.0 } } pub fn average_latency_micros(&self) -> u64 { self.average_latency.as_micros() as u64 } pub fn max_latency_micros(&self) -> u64 { self.max_latency.as_micros() as u64 } pub fn min_latency_micros(&self) -> u64 { self.min_latency.as_micros() as u64 } } }