#![allow(unused_crate_dependencies)] use std::collections::HashMap; use std::sync::Arc; use std::time::{Duration, Instant}; use tokio::time::timeout; use crate::framework::{TestOrchestrator, TestFrameworkConfig, PerformanceThresholds, IntegrationTestResult}; use crate::framework::mocks::MockServiceRegistry; use config::{ConfigManager, TradingConfig, RiskConfig, MLConfig}; use common::Order; use common::OrderType; use common::OrderStatus; use common::Position; use common::MarketData; use common::Tick; use trading_engine::services::trading::{TradingService, OrderExecutor, PositionManager}; use risk::safety::KillSwitchController; use trading_engine::events::{OrderEvent, PositionEvent, RiskEvent}; use trading_engine::events::EventBus; /// Comprehensive Trading Service Integration Tests /// /// Tests the core trading service functionality including: /// - Order lifecycle management (creation, execution, cancellation) /// /// - Position management and tracking /// - Risk validation integration /// /// - Market data processing pipeline /// - Emergency shutdown (kill switch) integration /// /// - Performance validation for HFT requirements pub struct TradingServiceTests { orchestrator: TestOrchestrator, mock_registry: MockServiceRegistry, trading_config: TradingConfig, risk_config: RiskConfig, } impl TradingServiceTests { /// Initialize Trading Service test suite with HFT performance thresholds pub async fn new() -> Result> { let config = TestFrameworkConfig { performance_thresholds: PerformanceThresholds { max_e2e_latency_us: 50, // 50μs end-to-end max_order_latency_us: 20, // 20μs order processing max_risk_latency_us: 10, // 10μs risk validation max_ml_latency_ms: 50, // 50ms ML inference max_config_reload_ms: 100, // 100ms config reload min_throughput_ops_sec: 10000, // 10k ops/sec minimum }, database_url: std::env::var("TEST_DATABASE_URL") .unwrap_or_else(|_| "postgresql://test:test@localhost:5432/foxhunt_test".to_string()), service_ports: { let mut ports = HashMap::new(); ports.insert("trading".to_string(), 50051); ports.insert("backtesting".to_string(), 50052); ports.insert("ml_training".to_string(), 50053); ports }, test_timeout_secs: 30, }; let orchestrator = TestOrchestrator::new(config).await?; let mock_registry = MockServiceRegistry::new().await?; // Load trading and risk configurations let config_manager = ConfigManager::new().await?; let trading_config = config_manager.get_trading_config().await?; let risk_config = config_manager.get_risk_config().await?; Ok(Self { orchestrator, mock_registry, trading_config, risk_config, }) } /// Test Suite 1: Order Lifecycle Management /// /// Validates complete order processing pipeline: /// - Order creation and validation /// /// - Risk checks and position sizing /// - Market execution and fill handling /// /// - Order status updates and event propagation pub async fn test_order_lifecycle_management(&self) -> IntegrationTestResult { println!("🔄 Testing Trading Service - Order Lifecycle Management"); let start_time = Instant::now(); let mut test_results = IntegrationTestResult::new("Trading Service Order Lifecycle"); // Start trading service self.orchestrator.start_service("trading").await .map_err(|e| format!("Failed to start trading service: {}", e))?; // Wait for service readiness tokio::time::sleep(Duration::from_millis(500)).await; // Test Case 1: Market Order Creation and Execution let market_order = Order { id: uuid::Uuid::new_v4(), symbol: "EURUSD".to_string(), order_type: OrderType::Market, side: common::types::OrderSide::Buy, quantity: 100000.0, // Standard lot price: None, // Market order status: OrderStatus::Pending, created_at: chrono::Utc::now(), filled_quantity: 0.0, average_fill_price: None, }; // Submit order and measure latency let order_start = Instant::now(); let order_result = self.orchestrator.submit_order(market_order.clone()).await; let order_latency = order_start.elapsed(); // Validate order submission match order_result { Ok(order_id) => { test_results.add_success("Market order submission successful"); // Validate latency requirement (< 20μs) if order_latency.as_micros() <= self.orchestrator.config.performance_thresholds.max_order_latency_us as u128 { test_results.add_success(&format!( "Order latency within threshold: {}μs <= {}μs", order_latency.as_micros(), self.orchestrator.config.performance_thresholds.max_order_latency_us )); } else { test_results.add_failure(&format!( "Order latency exceeds threshold: {}μs > {}μs", order_latency.as_micros(), self.orchestrator.config.performance_thresholds.max_order_latency_us )); } // Wait for order execution and verify status tokio::time::sleep(Duration::from_millis(100)).await; let order_status = self.orchestrator.get_order_status(&order_id).await?; match order_status { OrderStatus::Filled => { test_results.add_success("Market order executed successfully"); }, OrderStatus::PartiallyFilled => { test_results.add_success("Market order partially filled (acceptable)"); }, _ => { test_results.add_failure(&format!("Unexpected order status: {:?}", order_status)); } } }, Err(e) => { test_results.add_failure(&format!("Market order submission failed: {}", e)); } } // Test Case 2: Limit Order Management let limit_order = Order { id: uuid::Uuid::new_v4(), symbol: "GBPUSD".to_string(), order_type: OrderType::Limit, side: common::types::OrderSide::Sell, quantity: 50000.0, price: Some(1.2650), // Limit price status: OrderStatus::Pending, created_at: chrono::Utc::now(), filled_quantity: 0.0, average_fill_price: None, }; let limit_order_result = self.orchestrator.submit_order(limit_order.clone()).await; match limit_order_result { Ok(order_id) => { test_results.add_success("Limit order submission successful"); // Test order cancellation tokio::time::sleep(Duration::from_millis(50)).await; let cancel_result = self.orchestrator.cancel_order(&order_id).await; match cancel_result { Ok(_) => { test_results.add_success("Order cancellation successful"); // Verify order status updated to cancelled let status = self.orchestrator.get_order_status(&order_id).await?; if status == OrderStatus::Cancelled { test_results.add_success("Order status correctly updated to cancelled"); } else { test_results.add_failure(&format!("Order status not cancelled: {:?}", status)); } }, Err(e) => { test_results.add_failure(&format!("Order cancellation failed: {}", e)); } } }, Err(e) => { test_results.add_failure(&format!("Limit order submission failed: {}", e)); } } // Test Case 3: Risk Validation Integration let oversized_order = Order { id: uuid::Uuid::new_v4(), symbol: "EURUSD".to_string(), order_type: OrderType::Market, side: common::types::OrderSide::Buy, quantity: 10_000_000.0, // Intentionally large to trigger risk checks price: None, status: OrderStatus::Pending, created_at: chrono::Utc::now(), filled_quantity: 0.0, average_fill_price: None, }; let risk_check_start = Instant::now(); let risk_result = self.orchestrator.submit_order(oversized_order.clone()).await; let risk_latency = risk_check_start.elapsed(); // Should be rejected by risk management match risk_result { Err(e) if e.to_string().contains("risk") || e.to_string().contains("limit") => { test_results.add_success("Risk validation correctly rejected oversized order"); // Validate risk check latency (< 10μs) if risk_latency.as_micros() <= self.orchestrator.config.performance_thresholds.max_risk_latency_us as u128 { test_results.add_success(&format!( "Risk validation latency within threshold: {}μs <= {}μs", risk_latency.as_micros(), self.orchestrator.config.performance_thresholds.max_risk_latency_us )); } else { test_results.add_failure(&format!( "Risk validation latency exceeds threshold: {}μs > {}μs", risk_latency.as_micros(), self.orchestrator.config.performance_thresholds.max_risk_latency_us )); } }, Ok(_) => { test_results.add_failure("Risk validation failed - oversized order should have been rejected"); }, Err(e) => { test_results.add_failure(&format!("Unexpected error in risk validation: {}", e)); } } test_results.duration = start_time.elapsed(); test_results.finalize(); Ok(test_results) } /// Test Suite 2: Position Management and Tracking /// /// Validates position lifecycle and management: /// - Position opening and tracking /// /// - PnL calculation accuracy /// - Position closing and settlement /// /// - Multi-symbol position management pub async fn test_position_management(&self) -> IntegrationTestResult { println!("📊 Testing Trading Service - Position Management"); let start_time = Instant::now(); let mut test_results = IntegrationTestResult::new("Trading Service Position Management"); // Test Case 1: Position Opening let buy_order = Order { id: uuid::Uuid::new_v4(), symbol: "EURUSD".to_string(), order_type: OrderType::Market, side: common::types::OrderSide::Buy, quantity: 100000.0, price: None, status: OrderStatus::Pending, created_at: chrono::Utc::now(), filled_quantity: 0.0, average_fill_price: None, }; let order_result = self.orchestrator.submit_order(buy_order.clone()).await?; tokio::time::sleep(Duration::from_millis(100)).await; // Verify position was created let positions = self.orchestrator.get_positions().await?; let eurusd_position = positions.iter().find(|p| p.symbol == "EURUSD"); match eurusd_position { Some(position) => { test_results.add_success("Position created successfully"); if position.quantity == 100000.0 { test_results.add_success("Position quantity matches order"); } else { test_results.add_failure(&format!( "Position quantity mismatch: expected 100000.0, got {}", position.quantity )); } if position.unrealized_pnl.is_some() { test_results.add_success("Position PnL calculation active"); } else { test_results.add_failure("Position PnL not calculated"); } }, None => { test_results.add_failure("Position not found after order execution"); } } // Test Case 2: Position Modification (Partial Close) let partial_close_order = Order { id: uuid::Uuid::new_v4(), symbol: "EURUSD".to_string(), order_type: OrderType::Market, side: common::types::OrderSide::Sell, quantity: 50000.0, // Close half price: None, status: OrderStatus::Pending, created_at: chrono::Utc::now(), filled_quantity: 0.0, average_fill_price: None, }; self.orchestrator.submit_order(partial_close_order).await?; tokio::time::sleep(Duration::from_millis(100)).await; // Verify position was reduced let updated_positions = self.orchestrator.get_positions().await?; let updated_eurusd_position = updated_positions.iter().find(|p| p.symbol == "EURUSD"); match updated_eurusd_position { Some(position) => { if position.quantity == 50000.0 { test_results.add_success("Position partially closed successfully"); } else { test_results.add_failure(&format!( "Position partial close incorrect: expected 50000.0, got {}", position.quantity )); } }, None => { test_results.add_failure("Position disappeared after partial close"); } } // Test Case 3: Multi-Symbol Position Management let gbpusd_order = Order { id: uuid::Uuid::new_v4(), symbol: "GBPUSD".to_string(), order_type: OrderType::Market, side: common::types::OrderSide::Buy, quantity: 75000.0, price: None, status: OrderStatus::Pending, created_at: chrono::Utc::now(), filled_quantity: 0.0, average_fill_price: None, }; self.orchestrator.submit_order(gbpusd_order).await?; tokio::time::sleep(Duration::from_millis(100)).await; // Verify multiple positions are tracked let all_positions = self.orchestrator.get_positions().await?; let eurusd_count = all_positions.iter().filter(|p| p.symbol == "EURUSD").count(); let gbpusd_count = all_positions.iter().filter(|p| p.symbol == "GBPUSD").count(); if eurusd_count == 1 && gbpusd_count == 1 { test_results.add_success("Multi-symbol position management working"); } else { test_results.add_failure(&format!( "Multi-symbol position issue: EURUSD count {}, GBPUSD count {}", eurusd_count, gbpusd_count )); } test_results.duration = start_time.elapsed(); test_results.finalize(); Ok(test_results) } /// Test Suite 3: Market Data Integration /// /// Validates market data processing and order book management: /// - Real-time tick processing /// /// - Price feed integration /// - Order book depth updates /// /// - Market data latency validation pub async fn test_market_data_integration(&self) -> IntegrationTestResult { println!("📈 Testing Trading Service - Market Data Integration"); let start_time = Instant::now(); let mut test_results = IntegrationTestResult::new("Trading Service Market Data Integration"); // Test Case 1: Market Data Subscription let symbols = vec!["EURUSD".to_string(), "GBPUSD".to_string(), "USDJPY".to_string()]; for symbol in &symbols { let subscription_result = self.orchestrator.subscribe_market_data(symbol).await; match subscription_result { Ok(_) => { test_results.add_success(&format!("Market data subscription successful for {}", symbol)); }, Err(e) => { test_results.add_failure(&format!("Market data subscription failed for {}: {}", symbol, e)); } } } // Test Case 2: Real-time Tick Processing tokio::time::sleep(Duration::from_millis(200)).await; // Allow ticks to flow for symbol in &symbols { let latest_tick = self.orchestrator.get_latest_tick(symbol).await; match latest_tick { Ok(Some(tick)) => { test_results.add_success(&format!("Received tick data for {}", symbol)); // Validate tick data completeness if tick.bid > 0.0 && tick.ask > 0.0 && tick.ask > tick.bid { test_results.add_success(&format!("Valid bid/ask spread for {}", symbol)); } else { test_results.add_failure(&format!("Invalid bid/ask data for {}: bid={}, ask={}", symbol, tick.bid, tick.ask)); } // Validate timestamp freshness (within last second) let tick_age = chrono::Utc::now().signed_duration_since(tick.timestamp); if tick_age.num_seconds() <= 1 { test_results.add_success(&format!("Fresh tick data for {} ({}s old)", symbol, tick_age.num_seconds())); } else { test_results.add_failure(&format!("Stale tick data for {} ({}s old)", symbol, tick_age.num_seconds())); } }, Ok(None) => { test_results.add_failure(&format!("No tick data received for {}", symbol)); }, Err(e) => { test_results.add_failure(&format!("Error retrieving tick data for {}: {}", symbol, e)); } } } // Test Case 3: Market Data Latency Validation let latency_test_start = Instant::now(); let tick_result = self.orchestrator.get_latest_tick("EURUSD").await; let market_data_latency = latency_test_start.elapsed(); match tick_result { Ok(_) => { if market_data_latency.as_micros() <= 1000 { // < 1ms acceptable test_results.add_success(&format!("Market data latency acceptable: {}μs", market_data_latency.as_micros())); } else { test_results.add_failure(&format!("Market data latency too high: {}μs", market_data_latency.as_micros())); } }, Err(e) => { test_results.add_failure(&format!("Market data latency test failed: {}", e)); } } test_results.duration = start_time.elapsed(); test_results.finalize(); Ok(test_results) } /// Test Suite 4: Emergency Shutdown Integration /// /// Validates kill switch functionality: /// - Emergency order cancellation /// /// - Position force-close capability /// - Service shutdown coordination /// /// - Recovery procedures pub async fn test_emergency_shutdown_integration(&self) -> IntegrationTestResult { println!("🚨 Testing Trading Service - Emergency Shutdown Integration"); let start_time = Instant::now(); let mut test_results = IntegrationTestResult::new("Trading Service Emergency Shutdown"); // Set up test scenario with active orders and positions let setup_order = Order { id: uuid::Uuid::new_v4(), symbol: "EURUSD".to_string(), order_type: OrderType::Limit, side: common::types::OrderSide::Buy, quantity: 100000.0, price: Some(1.0000), // Far from market to stay pending status: OrderStatus::Pending, created_at: chrono::Utc::now(), filled_quantity: 0.0, average_fill_price: None, }; let order_id = self.orchestrator.submit_order(setup_order).await?; tokio::time::sleep(Duration::from_millis(100)).await; // Test Case 1: Emergency Kill Switch Activation let kill_switch_start = Instant::now(); let kill_switch_result = self.orchestrator.activate_kill_switch("test_emergency").await; let kill_switch_latency = kill_switch_start.elapsed(); match kill_switch_result { Ok(_) => { test_results.add_success("Kill switch activation successful"); // Validate kill switch latency (should be < 1ms) if kill_switch_latency.as_millis() <= 1 { test_results.add_success(&format!("Kill switch latency acceptable: {}μs", kill_switch_latency.as_micros())); } else { test_results.add_failure(&format!("Kill switch latency too high: {}ms", kill_switch_latency.as_millis())); } }, Err(e) => { test_results.add_failure(&format!("Kill switch activation failed: {}", e)); } } // Test Case 2: Verify All Orders Cancelled tokio::time::sleep(Duration::from_millis(200)).await; let order_status = self.orchestrator.get_order_status(&order_id).await?; if order_status == OrderStatus::Cancelled { test_results.add_success("Pending orders cancelled by kill switch"); } else { test_results.add_failure(&format!("Order not cancelled by kill switch: status {:?}", order_status)); } // Test Case 3: Verify Service State After Kill Switch let service_health = self.orchestrator.check_service_health("trading").await; match service_health { Ok(health) if health.status == "emergency_shutdown" || health.status == "stopped" => { test_results.add_success("Trading service correctly in emergency shutdown state"); }, Ok(health) => { test_results.add_failure(&format!("Unexpected service state after kill switch: {}", health.status)); }, Err(e) => { test_results.add_failure(&format!("Could not check service health after kill switch: {}", e)); } } // Test Case 4: Recovery Procedure let recovery_result = self.orchestrator.recover_from_kill_switch().await; match recovery_result { Ok(_) => { test_results.add_success("Kill switch recovery initiated"); // Wait for service recovery tokio::time::sleep(Duration::from_millis(500)).await; let recovered_health = self.orchestrator.check_service_health("trading").await; match recovered_health { Ok(health) if health.status == "healthy" || health.status == "running" => { test_results.add_success("Trading service recovered successfully"); }, Ok(health) => { test_results.add_failure(&format!("Service not fully recovered: status {}", health.status)); }, Err(e) => { test_results.add_failure(&format!("Could not verify service recovery: {}", e)); } } }, Err(e) => { test_results.add_failure(&format!("Kill switch recovery failed: {}", e)); } } test_results.duration = start_time.elapsed(); test_results.finalize(); Ok(test_results) } /// Execute complete Trading Service test suite pub async fn run_all_tests(&self) -> Result, Box> { println!("🚀 Starting Trading Service Integration Test Suite"); let mut results = Vec::new(); // Test Suite 1: Order Lifecycle Management match timeout(Duration::from_secs(self.orchestrator.config.test_timeout_secs), self.test_order_lifecycle_management()).await { Ok(Ok(result)) => results.push(result), Ok(Err(e)) => { let mut failed_result = IntegrationTestResult::new("Trading Service Order Lifecycle"); failed_result.add_failure(&format!("Test suite failed: {}", e)); failed_result.finalize(); results.push(failed_result); }, Err(_) => { let mut timeout_result = IntegrationTestResult::new("Trading Service Order Lifecycle"); timeout_result.add_failure("Test suite timed out"); timeout_result.finalize(); results.push(timeout_result); } } // Test Suite 2: Position Management match timeout(Duration::from_secs(self.orchestrator.config.test_timeout_secs), self.test_position_management()).await { Ok(Ok(result)) => results.push(result), Ok(Err(e)) => { let mut failed_result = IntegrationTestResult::new("Trading Service Position Management"); failed_result.add_failure(&format!("Test suite failed: {}", e)); failed_result.finalize(); results.push(failed_result); }, Err(_) => { let mut timeout_result = IntegrationTestResult::new("Trading Service Position Management"); timeout_result.add_failure("Test suite timed out"); timeout_result.finalize(); results.push(timeout_result); } } // Test Suite 3: Market Data Integration match timeout(Duration::from_secs(self.orchestrator.config.test_timeout_secs), self.test_market_data_integration()).await { Ok(Ok(result)) => results.push(result), Ok(Err(e)) => { let mut failed_result = IntegrationTestResult::new("Trading Service Market Data Integration"); failed_result.add_failure(&format!("Test suite failed: {}", e)); failed_result.finalize(); results.push(failed_result); }, Err(_) => { let mut timeout_result = IntegrationTestResult::new("Trading Service Market Data Integration"); timeout_result.add_failure("Test suite timed out"); timeout_result.finalize(); results.push(timeout_result); } } // Test Suite 4: Emergency Shutdown Integration match timeout(Duration::from_secs(self.orchestrator.config.test_timeout_secs), self.test_emergency_shutdown_integration()).await { Ok(Ok(result)) => results.push(result), Ok(Err(e)) => { let mut failed_result = IntegrationTestResult::new("Trading Service Emergency Shutdown"); failed_result.add_failure(&format!("Test suite failed: {}", e)); failed_result.finalize(); results.push(failed_result); }, Err(_) => { let mut timeout_result = IntegrationTestResult::new("Trading Service Emergency Shutdown"); timeout_result.add_failure("Test suite timed out"); timeout_result.finalize(); results.push(timeout_result); } } // Print summary let total_tests = results.len(); let passed_tests = results.iter().filter(|r| r.passed).count(); let failed_tests = total_tests - passed_tests; println!("📊 Trading Service Integration Test Summary:"); println!(" Total Test Suites: {}", total_tests); println!(" Passed: {} ✅", passed_tests); println!(" Failed: {} ❌", failed_tests); if failed_tests == 0 { println!("🎉 All Trading Service integration tests passed!"); } else { println!("⚠️ {} Trading Service integration test suite(s) failed", failed_tests); } Ok(results) } } #[cfg(test)] mod tests { use super::*; use tokio; #[tokio::test] async fn integration_test_trading_service_complete() { let test_suite = TradingServiceTests::new().await .expect("Failed to initialize Trading Service test suite"); let results = test_suite.run_all_tests().await .expect("Failed to run Trading Service test suite"); // Ensure all tests passed for result in &results { assert!(result.passed, "Test suite '{}' failed: {:?}", result.test_name, result.failures); } // Validate performance requirements met for result in &results { assert!( result.duration.as_millis() <= 5000, "Test suite '{}' took too long: {}ms", result.test_name, result.duration.as_millis() ); } } }