//! Comprehensive test suite for asset classification system //! //! Tests cover pattern matching, database integration, trading parameters, //! volatility profiling, and ConfigManager integration. #![allow(unused_crate_dependencies)] use chrono::Utc; use config::asset_classification_integration::MarketCapTier; use config::{ create_default_configurations, manager::ConfigManagerBuilder, AssetClass, AssetClassificationManager, AssetConfig, CryptoType, DetailedVolatilityProfile, EquitySector, ExecutionConfig, ForexPairType, GeographicRegion, JumpRiskProfile, OrderType, PositionLimits, RiskThresholds, ServiceConfig, SettlementConfig, TimeInForce, TradingParameters, }; use rust_decimal::Decimal; use std::str::FromStr; use uuid::Uuid; #[tokio::test] async fn test_asset_classification_manager_creation() { let manager = AssetClassificationManager::new(); // Test initial state assert_eq!(manager.get_active_configurations().len(), 0); // Initially no configurations loaded, so classification may be unknown } #[tokio::test] async fn test_default_configurations_loading() { let mut manager = AssetClassificationManager::new(); let configs = create_default_configurations(); assert!( !configs.is_empty(), "Default configurations should not be empty" ); manager.load_configurations(configs).await.unwrap(); // Test that configurations were loaded assert!(!manager.get_active_configurations().is_empty()); } #[tokio::test] async fn test_symbol_classification() { let mut manager = AssetClassificationManager::new(); let configs = create_default_configurations(); manager.load_configurations(configs).await.unwrap(); // Test blue chip equity classification let aapl_class = manager.classify_symbol("AAPL"); match aapl_class { AssetClass::Equity { sector: EquitySector::Technology, .. } => {} _ => panic!("AAPL should be classified as Technology equity"), } // Test crypto classification let btc_class = manager.classify_symbol("BTCUSD"); match btc_class { AssetClass::Crypto { crypto_type: CryptoType::Bitcoin, .. } => {} _ => panic!("BTCUSD should be classified as Bitcoin crypto"), } // Test forex classification let eur_class = manager.classify_symbol("EURUSD"); match eur_class { AssetClass::Forex { pair_type: ForexPairType::Major, .. } => {} _ => panic!("EURUSD should be classified as Major forex pair"), } // Test unknown symbol assert_eq!( manager.classify_symbol("UNKNOWN_SYMBOL"), AssetClass::Unknown ); } #[tokio::test] async fn test_volatility_profiles() { let mut manager = AssetClassificationManager::new(); let configs = create_default_configurations(); manager.load_configurations(configs).await.unwrap(); // Test AAPL volatility let aapl_vol = manager.get_daily_volatility("AAPL"); assert!(aapl_vol > 0.0, "AAPL should have positive volatility"); assert!(aapl_vol < 0.1, "AAPL daily volatility should be reasonable"); let aapl_profile = manager.get_volatility_profile("AAPL"); assert!( aapl_profile.is_some(), "AAPL should have volatility profile" ); if let Some(profile) = aapl_profile { assert!(profile.base_annual_volatility > 0.0); assert!(profile.stress_volatility_multiplier >= 1.0); assert!(profile.jump_risk.jump_probability >= 0.0); assert!(profile.jump_risk.jump_probability <= 1.0); } // Test crypto has higher volatility than equity let btc_vol = manager.get_daily_volatility("BTCUSD"); assert!( btc_vol > aapl_vol, "Crypto should have higher volatility than equity" ); } #[tokio::test] async fn test_trading_parameters() { let mut manager = AssetClassificationManager::new(); let configs = create_default_configurations(); manager.load_configurations(configs).await.unwrap(); // Test AAPL trading parameters let aapl_params = manager.get_trading_parameters("AAPL"); assert!(aapl_params.is_some(), "AAPL should have trading parameters"); if let Some(params) = aapl_params { assert!(params.position_limits.max_position_fraction > 0.0); assert!(params.position_limits.max_position_fraction <= 1.0); assert!(params.position_limits.max_leverage >= 1.0); assert!(params.risk_thresholds.daily_loss_limit > 0.0); assert!(!params.execution_config.preferred_order_types.is_empty()); } } #[tokio::test] async fn test_position_size_recommendations() { let mut manager = AssetClassificationManager::new(); let configs = create_default_configurations(); manager.load_configurations(configs).await.unwrap(); let portfolio_nav = Decimal::from_str("1000000.00").unwrap(); // $1M // Test AAPL position sizing let aapl_size = manager.get_position_size_recommendation("AAPL", portfolio_nav); assert!( aapl_size.is_some(), "Should get position size recommendation for AAPL" ); if let Some(size) = aapl_size { assert!(size > Decimal::ZERO, "Position size should be positive"); assert!( size <= portfolio_nav, "Position size should not exceed portfolio NAV" ); } // Test that crypto has smaller recommended position than equity let btc_size = manager.get_position_size_recommendation("BTCUSD", portfolio_nav); if let (Some(aapl), Some(btc)) = (aapl_size, btc_size) { assert!( btc < aapl, "Crypto position should be smaller than equity due to higher risk" ); } } #[tokio::test] async fn test_trading_hours() { let mut manager = AssetClassificationManager::new(); let configs = create_default_configurations(); manager.load_configurations(configs).await.unwrap(); let timestamp = Utc::now(); // Test equity trading hours (should have restrictions) let _aapl_active = manager.is_trading_active("AAPL", timestamp); // Test crypto trading hours (should be 24/7) let btc_active = manager.is_trading_active("BTCUSD", timestamp); assert!(btc_active, "Crypto should trade 24/7"); // Note: AAPL result depends on current time, but should not panic // This tests the mechanism works } #[tokio::test] async fn test_custom_asset_configuration() { let mut manager = AssetClassificationManager::new(); // Create custom configuration let custom_config = create_test_configuration(); let configs = vec![custom_config]; manager.load_configurations(configs).await.unwrap(); // Test that custom configuration works let test_class = manager.classify_symbol("TESTSTOCK"); match test_class { AssetClass::Equity { sector: EquitySector::Technology, market_cap: MarketCapTier::SmallCap, .. } => {} _ => panic!("TESTSTOCK should match custom configuration"), } // Test parameters let params = manager.get_trading_parameters("TESTSTOCK"); assert!( params.is_some(), "Custom configuration should have trading parameters" ); } #[tokio::test] async fn test_pattern_priority() { let mut manager = AssetClassificationManager::new(); // Create configurations with different priorities let high_priority = create_priority_test_config("^TEST.*$", 100); let low_priority = create_priority_test_config("^TEST.*$", 50); let configs = vec![low_priority, high_priority]; // Load in reverse priority order manager.load_configurations(configs).await.unwrap(); // Should match high priority configuration let test_class = manager.classify_symbol("TESTPATTERN"); match test_class { AssetClass::Equity { market_cap: MarketCapTier::LargeCap, .. } => {} _ => panic!("Should match high priority configuration"), } } #[tokio::test] async fn test_config_manager_integration() { let mut asset_manager = AssetClassificationManager::new(); let configs = create_default_configurations(); asset_manager.load_configurations(configs).await.unwrap(); let service_config = ServiceConfig { name: "test_service".to_string(), environment: "test".to_string(), version: "1.0.0".to_string(), settings: serde_json::json!({}), }; let config_manager = ConfigManagerBuilder::new(service_config) .with_asset_classification(asset_manager) .build(); // Test integration methods let asset_class = config_manager.classify_symbol("AAPL"); assert_ne!(asset_class, AssetClass::Unknown); let daily_vol = config_manager.get_daily_volatility("AAPL"); assert!(daily_vol > 0.0); let params = config_manager.get_trading_parameters("AAPL"); assert!(params.is_some()); let portfolio_nav = Decimal::from_str("100000.00").unwrap(); let position_size = config_manager.get_position_size_recommendation("AAPL", portfolio_nav); assert!(position_size.is_some()); } #[tokio::test] async fn test_cache_functionality() { let service_config = ServiceConfig { name: "test_service".to_string(), environment: "test".to_string(), version: "1.0.0".to_string(), settings: serde_json::json!({}), }; let config_manager = ConfigManagerBuilder::new(service_config) .with_cache_timeout(std::time::Duration::from_secs(1)) .build(); // Test cache set/get let test_value = serde_json::json!({"test": "value"}); config_manager.set_cached_config("test_key".to_string(), test_value.clone()); let cached = config_manager.get_cached_config("test_key"); assert_eq!(cached, Some(test_value)); // Test cache expiration tokio::time::sleep(std::time::Duration::from_secs(2)).await; let expired = config_manager.get_cached_config("test_key"); assert_eq!(expired, None); } #[tokio::test] async fn test_configuration_validation() { let manager = AssetClassificationManager::new(); // Test with invalid regex pattern let invalid_config = AssetConfig { id: Uuid::new_v4(), name: "Invalid Pattern".to_string(), symbol_pattern: "[invalid_regex".to_string(), // Invalid regex compiled_pattern: None, asset_class: AssetClass::Unknown, volatility_profile: create_default_volatility_profile(), trading_parameters: create_default_trading_parameters(), priority: 100, is_active: true, created_at: Utc::now(), updated_at: Utc::now(), trading_hours: None, settlement_config: SettlementConfig { settlement_days: 2, settlement_currency: "USD".to_string(), physical_settlement: false, }, }; // Should handle invalid configuration gracefully let mut test_manager = manager; let result = test_manager.load_configurations(vec![invalid_config]).await; assert!( result.is_ok(), "Should handle invalid configurations gracefully" ); } // Helper functions for test configurations fn create_test_configuration() -> AssetConfig { let now = Utc::now(); AssetConfig { id: Uuid::new_v4(), name: "Test Configuration".to_string(), symbol_pattern: "^TESTSTOCK$".to_string(), compiled_pattern: None, asset_class: AssetClass::Equity { sector: EquitySector::Technology, market_cap: MarketCapTier::SmallCap, region: GeographicRegion::NorthAmerica, }, volatility_profile: create_default_volatility_profile(), trading_parameters: create_default_trading_parameters(), priority: 100, is_active: true, created_at: now, updated_at: now, trading_hours: None, settlement_config: SettlementConfig { settlement_days: 2, settlement_currency: "USD".to_string(), physical_settlement: false, }, } } fn create_priority_test_config(pattern: &str, priority: u32) -> AssetConfig { let now = Utc::now(); let market_cap = if priority > 75 { MarketCapTier::LargeCap } else { MarketCapTier::SmallCap }; AssetConfig { id: Uuid::new_v4(), name: format!("Priority {} Configuration", priority), symbol_pattern: pattern.to_string(), compiled_pattern: None, asset_class: AssetClass::Equity { sector: EquitySector::Technology, market_cap, region: GeographicRegion::NorthAmerica, }, volatility_profile: create_default_volatility_profile(), trading_parameters: create_default_trading_parameters(), priority, is_active: true, created_at: now, updated_at: now, trading_hours: None, settlement_config: SettlementConfig { settlement_days: 2, settlement_currency: "USD".to_string(), physical_settlement: false, }, } } fn create_default_volatility_profile() -> DetailedVolatilityProfile { DetailedVolatilityProfile { base_annual_volatility: 0.25, stress_volatility_multiplier: 2.0, intraday_pattern: vec![1.0; 24], volatility_persistence: 0.85, jump_risk: JumpRiskProfile { jump_probability: 0.02, jump_magnitude: 0.05, max_jump_size: 0.15, }, } } fn create_default_trading_parameters() -> TradingParameters { TradingParameters { position_limits: PositionLimits { max_position_fraction: 0.10, max_leverage: 2.0, concentration_limit: 0.20, min_position_size: Decimal::from(100), }, risk_thresholds: RiskThresholds { var_limit: 0.05, daily_loss_limit: 0.03, stop_loss_threshold: 0.10, volatility_circuit_breaker: 0.05, max_drawdown_threshold: 0.15, }, execution_config: ExecutionConfig { preferred_order_types: vec![OrderType::Limit, OrderType::Market], tick_size: Decimal::from_str("0.01").unwrap(), min_order_size: Decimal::from(1), max_order_size: Decimal::from(10000), time_in_force_default: TimeInForce::Day, slippage_tolerance: 0.001, }, market_making: None, } } #[tokio::test] async fn test_comprehensive_workflow() { // This test demonstrates a complete workflow println!("🧪 Running comprehensive asset classification workflow test"); // 1. Initialize manager let mut manager = AssetClassificationManager::new(); // 2. Load configurations let configs = create_default_configurations(); manager.load_configurations(configs).await.unwrap(); // 3. Test multiple symbols let symbols = vec!["AAPL", "MSFT", "BTCUSD", "EURUSD", "UNKNOWN"]; for symbol in symbols { let asset_class = manager.classify_symbol(symbol); let daily_vol = manager.get_daily_volatility(symbol); let trading_params = manager.get_trading_parameters(symbol); println!( "Symbol: {} | Class: {:?} | Daily Vol: {:.2}% | Has Params: {}", symbol, asset_class, daily_vol * 100.0, trading_params.is_some() ); } // 4. Test position sizing let portfolio_nav = Decimal::from_str("500000.00").unwrap(); for symbol in ["AAPL", "BTCUSD"] { if let Some(size) = manager.get_position_size_recommendation(symbol, portfolio_nav) { let percentage = (size / portfolio_nav) * Decimal::from(100); println!( "Recommended position for {}: ${} ({:.1}%)", symbol, size, percentage ); } } println!("✅ Comprehensive workflow test completed successfully"); }