//! Common test utilities for Foxhunt HFT System //! //! This module provides shared testing infrastructure to eliminate duplication //! across the 80+ test files in the project. //! //! # Usage //! ```rust //! use common::*; //! use common::test_config::*; //! use common::mock_data::*; //! ``` pub mod database_helper; // Test Configuration Module pub mod test_config { /// Unified test configuration for all test types #[derive(Debug, Clone)] pub struct UnifiedTestConfig { pub environment_name: String, pub docker_compose_file: Option, pub cleanup_on_exit: bool, pub persist_data: bool, pub log_level: String, pub test_database_url: String, pub test_redis_url: String, pub test_influxdb_url: String, pub parallel_tests: bool, pub timeout_seconds: u64, pub max_retries: u32, } impl Default for UnifiedTestConfig { fn default() -> Self { // Load test environment variables crate::load_test_env(); Self { environment_name: "test".to_string(), docker_compose_file: Some("docker-compose.test.yml".to_string()), cleanup_on_exit: true, persist_data: false, log_level: "debug".to_string(), test_database_url: std::env::var("TEST_DATABASE_URL").unwrap_or_else(|_| { std::env::var("FOXHUNT_TEST_POSTGRES_URL").unwrap_or_else(|_| { let db_host = std::env::var("DATABASE_HOST") .or_else(|_| std::env::var("POSTGRES_HOST")) .unwrap_or_else(|_| "localhost".to_string()); format!( "postgresql://foxhunt_test:test_password@{}:5433/foxhunt_test", db_host ) }) }), test_redis_url: std::env::var("TEST_REDIS_URL").unwrap_or_else(|_| { std::env::var("FOXHUNT_TEST_REDIS_URL").unwrap_or_else(|_| { let redis_host = std::env::var("REDIS_HOST").unwrap_or_else(|_| "localhost".to_string()); format!( "redis://:{}@{}:6379/0", std::env::var("REDIS_TEST_PASSWORD") .unwrap_or_else(|_| "test_password".to_string()), redis_host ) }) }), test_influxdb_url: std::env::var("TEST_INFLUXDB_URL").unwrap_or_else(|_| { let influx_host = std::env::var("INFLUXDB_HOST").unwrap_or_else(|_| "localhost".to_string()); format!("http://{}:8086", influx_host) }), parallel_tests: true, timeout_seconds: 30, max_retries: 3, } } } } // Mock Data Generation Module pub mod mock_data { use uuid::Uuid; /// Generate mock order data for testing pub fn create_mock_order() -> MockOrder { MockOrder { id: Uuid::new_v4().to_string(), symbol: "BTCUSD".to_string(), side: "Buy".to_string(), quantity: 1.0, price: 50000.0, status: "Pending".to_string(), } } /// Generate mock market data pub fn create_mock_market_tick(symbol: &str) -> MockMarketTick { MockMarketTick { symbol: symbol.to_string(), price: 50000.0, volume: 100.0, timestamp: chrono::Utc::now().timestamp_millis(), } } /// Mock order structure for tests #[derive(Debug, Clone)] pub struct MockOrder { pub id: String, pub symbol: String, pub side: String, pub quantity: f64, pub price: f64, pub status: String, } /// Mock market tick for tests #[derive(Debug, Clone)] pub struct MockMarketTick { pub symbol: String, pub price: f64, pub volume: f64, pub timestamp: i64, } } // Test Utilities Module pub mod test_utils { use std::time::Duration; use tokio::time::timeout; /// Async test helper with timeout pub async fn run_with_timeout(future: F, timeout_secs: u64) -> Result where F: std::future::Future, { timeout(Duration::from_secs(timeout_secs), future) .await .map_err(|_| "Test timed out") } /// Setup tracing for tests pub fn setup_test_tracing() { use tracing_subscriber::EnvFilter; let _ = tracing_subscriber::fmt() .with_test_writer() .with_env_filter(EnvFilter::from_default_env()) .try_init(); } /// Common test assertions pub mod assertions { use std::time::Duration; /// Assert that a value is within a percentage tolerance pub fn assert_within_percent(actual: f64, expected: f64, percent: f64) { let tolerance = expected * (percent / 100.0); let diff = (actual - expected).abs(); assert!( diff <= tolerance, "Value {} is not within {}% of expected {}, difference: {}", actual, percent, expected, diff ); } /// Assert that latency is within HFT requirements pub fn assert_hft_latency(duration: Duration, max_microseconds: u64) { let micros = duration.as_micros() as u64; assert!( micros <= max_microseconds, "Latency {}μs exceeds HFT requirement of {}μs", micros, max_microseconds ); } } } // Async Test Patterns Module pub mod async_patterns { use tokio::sync::broadcast; /// Proper broadcast receiver pattern for tests #[derive(Debug)] pub struct TestBroadcastReceiver { receiver: broadcast::Receiver, } impl TestBroadcastReceiver where T: Clone + Send + 'static, { pub fn new(receiver: broadcast::Receiver) -> Self { Self { receiver } } pub async fn wait_for_shutdown(mut self) -> Result<(), broadcast::error::RecvError> { self.receiver.recv().await.map(|_| ()) } } } // Re-export commonly used items for convenience // DO NOT RE-EXPORT - Use explicit imports at usage sites