#![allow( clippy::tests_outside_test_module, clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::str_to_string, clippy::string_to_string, clippy::assertions_on_result_states, clippy::assertions_on_constants, clippy::absurd_extreme_comparisons, clippy::decimal_literal_representation, clippy::let_underscore_must_use, clippy::use_debug, clippy::doc_markdown, clippy::shadow_unrelated, clippy::shadow_reuse, clippy::similar_names, clippy::clone_on_copy, clippy::get_unwrap, clippy::modulo_arithmetic, clippy::integer_division, clippy::non_ascii_literal, clippy::useless_vec, clippy::useless_format, clippy::wildcard_enum_match_arm, clippy::manual_range_contains, clippy::const_is_empty, clippy::needless_range_loop, clippy::field_reassign_with_default, clippy::items_after_test_module, clippy::missing_const_for_fn, unused_imports, unused_variables, unused_mut, unused_assignments, unused_comparisons, unused_must_use, dead_code, )] //! Comprehensive trading engine tests targeting 95% coverage //! Tests for trading/engine.rs module covering all 12 public functions use common::{OrderId, OrderSide, OrderType}; use rust_decimal::Decimal; use std::str::FromStr; use std::sync::Arc; use trading_engine::trading::data_interface::{DataProvider, Subscription}; use trading_engine::trading::engine::TradingEngine; // ============================================================================ // Mock Data Provider for Testing // ============================================================================ #[derive(Debug, Clone)] struct MockDataProvider { market_data_tx: Arc>, order_update_tx: Arc>, } impl MockDataProvider { fn new() -> Self { let (market_data_tx, _) = tokio::sync::broadcast::channel(100); let (order_update_tx, _) = tokio::sync::broadcast::channel(100); Self { market_data_tx: Arc::new(market_data_tx), order_update_tx: Arc::new(order_update_tx), } } } #[async_trait::async_trait] impl DataProvider for MockDataProvider { async fn subscribe_market_data(&self, _subscription: Subscription) -> Result<(), String> { Ok(()) } fn subscribe_market_data_events( &self, ) -> tokio::sync::broadcast::Receiver { self.market_data_tx.subscribe() } fn subscribe_order_update_events( &self, ) -> tokio::sync::broadcast::Receiver { self.order_update_tx.subscribe() } } // ============================================================================ // Helper Functions // ============================================================================ fn create_test_engine() -> TradingEngine { let data_provider = Arc::new(MockDataProvider::new()); TradingEngine::new(data_provider) } // ============================================================================ // TradingEngine::new() Tests // ============================================================================ #[cfg(test)] mod trading_engine_creation_tests { use super::*; #[test] fn test_trading_engine_new_creates_valid_instance() { let engine = create_test_engine(); // Verify the engine was created successfully by calling a method let stats = tokio::runtime::Runtime::new() .unwrap() .block_on(engine.get_trading_stats()); assert_eq!(stats.total_orders, 0); assert_eq!(stats.filled_orders, 0); assert_eq!(stats.rejected_orders, 0); } #[test] fn test_trading_engine_new_with_different_providers() { let provider1 = Arc::new(MockDataProvider::new()); let engine1 = TradingEngine::new(provider1); let provider2 = Arc::new(MockDataProvider::new()); let engine2 = TradingEngine::new(provider2); // Both engines should be independently functional let rt = tokio::runtime::Runtime::new().unwrap(); let stats1 = rt.block_on(engine1.get_trading_stats()); let stats2 = rt.block_on(engine2.get_trading_stats()); assert_eq!(stats1.total_orders, 0); assert_eq!(stats2.total_orders, 0); } } // ============================================================================ // TradingEngine::submit_order() Tests // ============================================================================ #[cfg(test)] mod submit_order_tests { use super::*; #[tokio::test] async fn test_submit_order_market_buy_success() { let engine = create_test_engine(); let result = engine .submit_order( "AAPL".to_owned(), OrderSide::Buy, OrderType::Market, Decimal::from_str("100").unwrap(), None, None, ) .await; assert!(result.is_ok()); let order_id = result.unwrap(); assert!(!order_id.is_empty()); assert!(order_id.starts_with("ORD_")); } #[tokio::test] async fn test_submit_order_market_sell_success() { let engine = create_test_engine(); let result = engine .submit_order( "MSFT".to_owned(), OrderSide::Sell, OrderType::Market, Decimal::from_str("50").unwrap(), None, None, ) .await; result.unwrap(); } #[tokio::test] async fn test_submit_order_limit_buy_with_price() { let engine = create_test_engine(); let result = engine .submit_order( "GOOGL".to_owned(), OrderSide::Buy, OrderType::Limit, Decimal::from_str("10").unwrap(), Some(Decimal::from_str("2800.50").unwrap()), None, ) .await; result.unwrap(); } #[tokio::test] async fn test_submit_order_limit_sell_with_price() { let engine = create_test_engine(); let result = engine .submit_order( "TSLA".to_owned(), OrderSide::Sell, OrderType::Limit, Decimal::from_str("25").unwrap(), Some(Decimal::from_str("750.00").unwrap()), None, ) .await; result.unwrap(); } #[tokio::test] async fn test_submit_order_stop_loss_with_stop_price() { let engine = create_test_engine(); let result = engine .submit_order( "AMZN".to_owned(), OrderSide::Sell, OrderType::Stop, Decimal::from_str("20").unwrap(), None, Some(Decimal::from_str("3200.00").unwrap()), ) .await; result.unwrap(); } #[tokio::test] async fn test_submit_order_zero_quantity_validation() { let engine = create_test_engine(); let result = engine .submit_order( "AAPL".to_owned(), OrderSide::Buy, OrderType::Market, Decimal::ZERO, None, None, ) .await; // Order should still be submitted (validation happens at broker level) result.unwrap(); } #[tokio::test] async fn test_submit_order_fractional_shares() { let engine = create_test_engine(); let result = engine .submit_order( "AAPL".to_owned(), OrderSide::Buy, OrderType::Market, Decimal::from_str("0.5").unwrap(), None, None, ) .await; result.unwrap(); } #[tokio::test] async fn test_submit_order_large_quantity() { let engine = create_test_engine(); let result = engine .submit_order( "SPY".to_owned(), OrderSide::Buy, OrderType::Market, Decimal::from_str("100000").unwrap(), None, None, ) .await; result.unwrap(); } #[tokio::test] async fn test_submit_order_empty_symbol_handling() { let engine = create_test_engine(); let result = engine .submit_order( "".to_owned(), OrderSide::Buy, OrderType::Market, Decimal::from_str("100").unwrap(), None, None, ) .await; // Should accept empty symbol (validation at broker level) result.unwrap(); } #[tokio::test] async fn test_submit_order_concurrent_submissions() { let engine = Arc::new(create_test_engine()); let mut handles = vec![]; for i in 0..10 { let engine_clone = Arc::clone(&engine); let handle = tokio::spawn(async move { engine_clone .submit_order( format!("SYM{}", i), OrderSide::Buy, OrderType::Market, Decimal::from_str("10").unwrap(), None, None, ) .await }); handles.push(handle); } let results: Vec<_> = futures::future::join_all(handles).await; // All orders should succeed for result in results { assert!(result.is_ok()); result.unwrap().unwrap(); } } } // ============================================================================ // TradingEngine::cancel_order() Tests // ============================================================================ #[cfg(test)] mod cancel_order_tests { use super::*; #[tokio::test] async fn test_cancel_order_success() { let engine = create_test_engine(); // First submit an order let order_result = engine .submit_order( "AAPL".to_owned(), OrderSide::Buy, OrderType::Limit, Decimal::from_str("100").unwrap(), Some(Decimal::from_str("150.00").unwrap()), None, ) .await; order_result.unwrap(); // Then cancel it let order_id = OrderId::new(); let cancel_result = engine.cancel_order(order_id).await; cancel_result.unwrap(); } #[tokio::test] async fn test_cancel_order_nonexistent() { let engine = create_test_engine(); let order_id = OrderId::new(); let result = engine.cancel_order(order_id).await; // Should handle non-existent order gracefully assert!(result.is_ok() || result.is_err()); } #[tokio::test] async fn test_cancel_order_multiple_times() { let engine = create_test_engine(); let order_id = OrderId::new(); // Cancel same order multiple times let result1 = engine.cancel_order(order_id).await; let result2 = engine.cancel_order(order_id).await; // First should succeed or fail, second should handle gracefully assert!(result1.is_ok() || result1.is_err()); assert!(result2.is_ok() || result2.is_err()); } #[tokio::test] async fn test_cancel_order_concurrent_cancellations() { let engine = Arc::new(create_test_engine()); let order_id = OrderId::new(); let mut handles = vec![]; for _ in 0..5 { let engine_clone = Arc::clone(&engine); let handle = tokio::spawn(async move { engine_clone.cancel_order(order_id).await }); handles.push(handle); } let results: Vec<_> = futures::future::join_all(handles).await; // All cancellations should complete (may succeed or fail gracefully) for result in results { result.unwrap(); } } } // ============================================================================ // TradingEngine::get_order_status() Tests // ============================================================================ #[cfg(test)] mod get_order_status_tests { use super::*; #[tokio::test] async fn test_get_order_status_nonexistent() { let engine = create_test_engine(); let order_id = OrderId::new(); let result = engine.get_order_status(order_id).await; // Should return error for non-existent order result.unwrap_err(); } #[tokio::test] async fn test_get_order_status_multiple_queries() { let engine = create_test_engine(); let order_id = OrderId::new(); // Query same order multiple times let _ = engine.get_order_status(order_id).await; let _ = engine.get_order_status(order_id).await; let result = engine.get_order_status(order_id).await; // Should consistently return error for non-existent order result.unwrap_err(); } } // ============================================================================ // TradingEngine::get_account_info() Tests // ============================================================================ #[cfg(test)] mod get_account_info_tests { use super::*; #[tokio::test] async fn test_get_account_info_default_account() { let engine = create_test_engine(); let result = engine.get_account_info("default".to_owned()).await; result.unwrap(); } #[tokio::test] async fn test_get_account_info_custom_account() { let engine = create_test_engine(); let result = engine.get_account_info("account-123".to_owned()).await; result.unwrap(); } #[tokio::test] async fn test_get_account_info_empty_account_id() { let engine = create_test_engine(); let result = engine.get_account_info("".to_owned()).await; result.unwrap(); } #[tokio::test] async fn test_get_account_info_concurrent_queries() { let engine = Arc::new(create_test_engine()); let mut handles = vec![]; for i in 0..10 { let engine_clone = Arc::clone(&engine); let handle = tokio::spawn(async move { engine_clone .get_account_info(format!("account-{}", i)) .await }); handles.push(handle); } let results: Vec<_> = futures::future::join_all(handles).await; // All queries should succeed for result in results { assert!(result.is_ok()); result.unwrap().unwrap(); } } } // ============================================================================ // TradingEngine::get_positions() Tests // ============================================================================ #[cfg(test)] mod get_positions_tests { use super::*; #[tokio::test] async fn test_get_positions_default_account() { let engine = create_test_engine(); let result = engine.get_positions(Some("default".to_owned())).await; assert!(result.is_ok()); let positions = result.unwrap(); assert_eq!(positions.len(), 0); // No positions initially } #[tokio::test] async fn test_get_positions_custom_account() { let engine = create_test_engine(); let result = engine.get_positions(Some("account-456".to_owned())).await; result.unwrap(); } #[tokio::test] async fn test_get_positions_empty_account_id() { let engine = create_test_engine(); let result = engine.get_positions(Some("".to_owned())).await; result.unwrap(); } #[tokio::test] async fn test_get_positions_concurrent_queries() { let engine = Arc::new(create_test_engine()); let mut handles = vec![]; for i in 0..5 { let engine_clone = Arc::clone(&engine); let handle = tokio::spawn(async move { engine_clone .get_positions(Some(format!("account-{}", i))) .await }); handles.push(handle); } let results: Vec<_> = futures::future::join_all(handles).await; for result in results { assert!(result.is_ok()); result.unwrap().unwrap(); } } } // ============================================================================ // TradingEngine::subscribe_market_data() Tests // ============================================================================ #[cfg(test)] mod subscribe_market_data_tests { use super::*; #[tokio::test] async fn test_subscribe_market_data_single_symbol() { let engine = create_test_engine(); let result = engine.subscribe_market_data(vec!["AAPL".to_owned()]).await; result.unwrap(); } #[tokio::test] async fn test_subscribe_market_data_multiple_symbols() { let engine = create_test_engine(); let result1 = engine.subscribe_market_data(vec!["AAPL".to_owned()]).await; let result2 = engine.subscribe_market_data(vec!["MSFT".to_owned()]).await; let result3 = engine.subscribe_market_data(vec!["GOOGL".to_owned()]).await; result1.unwrap(); result2.unwrap(); result3.unwrap(); } #[tokio::test] async fn test_subscribe_market_data_empty_symbol() { let engine = create_test_engine(); let result = engine.subscribe_market_data(vec!["".to_owned()]).await; result.unwrap(); } #[tokio::test] async fn test_subscribe_market_data_duplicate_subscription() { let engine = create_test_engine(); let result1 = engine.subscribe_market_data(vec!["AAPL".to_owned()]).await; let result2 = engine.subscribe_market_data(vec!["AAPL".to_owned()]).await; result1.unwrap(); result2.unwrap(); } #[tokio::test] async fn test_subscribe_market_data_concurrent_subscriptions() { let engine = Arc::new(create_test_engine()); let mut handles = vec![]; for i in 0..10 { let engine_clone = Arc::clone(&engine); let handle = tokio::spawn(async move { engine_clone .subscribe_market_data(vec![format!("SYM{}", i)]) .await }); handles.push(handle); } let results: Vec<_> = futures::future::join_all(handles).await; for result in results { assert!(result.is_ok()); result.unwrap().unwrap(); } } } // ============================================================================ // TradingEngine::subscribe_order_updates() Tests // ============================================================================ #[cfg(test)] mod subscribe_order_updates_tests { use super::*; #[tokio::test] async fn test_subscribe_order_updates_creates_receiver() { let engine = create_test_engine(); let receiver = engine.subscribe_order_updates(None).await; // Receiver should be created successfully receiver.unwrap(); } #[tokio::test] async fn test_subscribe_order_updates_multiple_subscribers() { let engine = create_test_engine(); let receiver1 = engine.subscribe_order_updates(None).await; let receiver2 = engine.subscribe_order_updates(None).await; let receiver3 = engine.subscribe_order_updates(None).await; receiver1.unwrap(); receiver2.unwrap(); receiver3.unwrap(); } #[tokio::test] async fn test_subscribe_order_updates_concurrent_subscriptions() { let engine = Arc::new(create_test_engine()); let mut handles = vec![]; for _ in 0..5 { let engine_clone = Arc::clone(&engine); let handle = tokio::spawn(async move { engine_clone.subscribe_order_updates(None).await }); handles.push(handle); } let results: Vec<_> = futures::future::join_all(handles).await; for result in results { assert!(result.is_ok()); result.unwrap().unwrap(); } } } // ============================================================================ // TradingEngine::get_trading_stats() Tests // ============================================================================ #[cfg(test)] mod get_trading_stats_tests { use super::*; #[tokio::test] async fn test_get_trading_stats_initial_state() { let engine = create_test_engine(); let stats = engine.get_trading_stats().await; assert_eq!(stats.total_orders, 0); assert_eq!(stats.filled_orders, 0); assert_eq!(stats.rejected_orders, 0); } #[tokio::test] async fn test_get_trading_stats_after_orders() { let engine = create_test_engine(); // Submit some orders let _ = engine .submit_order( "AAPL".to_owned(), OrderSide::Buy, OrderType::Market, Decimal::from_str("100").unwrap(), None, None, ) .await; let _ = engine .submit_order( "MSFT".to_owned(), OrderSide::Sell, OrderType::Limit, Decimal::from_str("50").unwrap(), Some(Decimal::from_str("300.00").unwrap()), None, ) .await; let stats = engine.get_trading_stats().await; // Stats should be retrievable (actual values depend on implementation) assert!(stats.total_orders >= 0); } #[tokio::test] async fn test_get_trading_stats_concurrent_queries() { let engine = Arc::new(create_test_engine()); let mut handles = vec![]; for _ in 0..10 { let engine_clone = Arc::clone(&engine); let handle = tokio::spawn(async move { engine_clone.get_trading_stats().await }); handles.push(handle); } let results: Vec<_> = futures::future::join_all(handles).await; // All queries should succeed for result in results { assert!(result.is_ok()); let stats = result.unwrap(); assert!(stats.total_orders >= 0); } } } // ============================================================================ // Edge Case and Error Condition Tests // ============================================================================ #[cfg(test)] mod edge_case_tests { use super::*; #[tokio::test] async fn test_engine_under_high_load() { let engine = Arc::new(create_test_engine()); let mut handles = vec![]; // Simulate high load with mixed operations for i in 0..50 { let engine_clone = Arc::clone(&engine); let handle = tokio::spawn(async move { match i % 4 { 0 => { engine_clone .submit_order( format!("SYM{}", i), OrderSide::Buy, OrderType::Market, Decimal::from_str("10").unwrap(), None, None, ) .await .ok(); }, 1 => { engine_clone.get_trading_stats().await; }, 2 => { engine_clone .get_positions(Some(format!("account-{}", i))) .await .ok(); }, _ => { engine_clone .subscribe_market_data(vec![format!("SYM{}", i)]) .await .ok(); }, } }); handles.push(handle); } let results: Vec<_> = futures::future::join_all(handles).await; // All operations should complete without panicking for result in results { result.unwrap(); } } #[tokio::test] async fn test_engine_operations_after_errors() { let engine = create_test_engine(); // Attempt operations that might fail let _ = engine.cancel_order(OrderId::new()).await; let _ = engine.get_order_status(OrderId::new()).await; // Engine should still be functional let result = engine .submit_order( "AAPL".to_owned(), OrderSide::Buy, OrderType::Market, Decimal::from_str("100").unwrap(), None, None, ) .await; result.unwrap(); } #[tokio::test] async fn test_engine_with_extreme_decimal_values() { let engine = create_test_engine(); // Very large quantity let result1 = engine .submit_order( "SPY".to_owned(), OrderSide::Buy, OrderType::Market, Decimal::from_str("999999999").unwrap(), None, None, ) .await; // Very small quantity let result2 = engine .submit_order( "BTC".to_owned(), OrderSide::Buy, OrderType::Market, Decimal::from_str("0.00000001").unwrap(), None, None, ) .await; // Very high price let result3 = engine .submit_order( "BRK.A".to_owned(), OrderSide::Buy, OrderType::Limit, Decimal::from_str("1").unwrap(), Some(Decimal::from_str("500000.00").unwrap()), None, ) .await; result1.unwrap(); result2.unwrap(); result3.unwrap(); } #[tokio::test] async fn test_engine_with_special_symbols() { let engine = create_test_engine(); // Test various symbol formats let symbols = vec!["AAPL", "BRK.B", "^VIX", "EUR/USD", "BTC-USD", "ES_F"]; for symbol in symbols { let result = engine .submit_order( symbol.to_owned(), OrderSide::Buy, OrderType::Market, Decimal::from_str("10").unwrap(), None, None, ) .await; assert!(result.is_ok(), "Failed for symbol: {}", symbol); } } }