//! Comprehensive Error Factory Method and Trait Tests //! //! This module provides comprehensive tests for: //! - CommonError factory methods (config, network, service, validation, internal, etc.) //! - ErrorCategory Display trait implementations //! - ErrorSeverity Display trait implementations //! - Error categorization and classification //! - Error source chains and downcasting //! - Error message formatting //! - Edge cases and boundary conditions use common::error::{CommonError, ErrorCategory, ErrorSeverity, RetryStrategy}; // ============================================================================= // COMMON ERROR FACTORY METHODS // ============================================================================= #[test] fn test_common_error_config_factory() { let err = CommonError::config("Missing database URL"); match &err { CommonError::Configuration(msg) => { assert_eq!(msg, "Missing database URL"); }, _ => panic!("Expected Configuration variant"), } // Test category assert_eq!(err.category(), ErrorCategory::Configuration); assert_eq!(err.severity(), ErrorSeverity::Critical); assert!(!err.is_retryable()); } #[test] fn test_common_error_config_factory_string_types() { // Test with &str let err1 = CommonError::config("test"); assert!(matches!(err1, CommonError::Configuration(_))); // Test with String let err2 = CommonError::config(String::from("test")); assert!(matches!(err2, CommonError::Configuration(_))); // Test with format! let err3 = CommonError::config(format!("port {}", 8080)); match err3 { CommonError::Configuration(msg) => assert_eq!(msg, "port 8080"), _ => panic!("Expected Configuration variant"), } } #[test] fn test_common_error_network_factory() { let err = CommonError::network("Connection refused"); match &err { CommonError::Network(msg) => { assert_eq!(msg, "Connection refused"); }, _ => panic!("Expected Network variant"), } // Test category assert_eq!(err.category(), ErrorCategory::Network); assert_eq!(err.severity(), ErrorSeverity::Error); assert!(err.is_retryable()); } #[test] fn test_common_error_network_factory_string_types() { // Test with &str let err1 = CommonError::network("timeout"); assert!(matches!(err1, CommonError::Network(_))); // Test with String let err2 = CommonError::network(String::from("reset")); assert!(matches!(err2, CommonError::Network(_))); } #[test] fn test_common_error_service_factory_all_categories() { // Test each ErrorCategory variant let categories = vec![ ErrorCategory::MarketData, ErrorCategory::Trading, ErrorCategory::Network, ErrorCategory::System, ErrorCategory::Configuration, ErrorCategory::Validation, ErrorCategory::Critical, ErrorCategory::Connection, ErrorCategory::Authentication, ErrorCategory::RateLimit, ErrorCategory::Parse, ErrorCategory::Subscription, ErrorCategory::FinancialSafety, ErrorCategory::RiskManagement, ErrorCategory::Database, ErrorCategory::Broker, ErrorCategory::MachineLearning, ErrorCategory::Security, ErrorCategory::BusinessLogic, ErrorCategory::Resource, ErrorCategory::Development, ErrorCategory::Risk, ErrorCategory::ML, ErrorCategory::Other, ]; for category in categories { let err = CommonError::service(category, "test message"); match &err { CommonError::Service { category: cat, message } => { assert_eq!(*cat, category); assert_eq!(message, "test message"); }, _ => panic!("Expected Service variant for {:?}", category), } // Verify category method returns correct value assert_eq!(err.category(), category); } } #[test] fn test_common_error_validation_factory() { let err = CommonError::validation("Price must be positive"); match &err { CommonError::Validation(msg) => { assert_eq!(msg, "Price must be positive"); }, _ => panic!("Expected Validation variant"), } // Test category assert_eq!(err.category(), ErrorCategory::Validation); assert_eq!(err.severity(), ErrorSeverity::Warn); assert!(!err.is_retryable()); } #[test] fn test_common_error_internal_factory() { let err = CommonError::internal("Unexpected state"); match &err { CommonError::Service { category, message } => { assert_eq!(*category, ErrorCategory::System); assert_eq!(message, "Internal error: Unexpected state"); }, _ => panic!("Expected Service variant"), } // Test category assert_eq!(err.category(), ErrorCategory::System); } #[test] fn test_common_error_timeout_factory() { let err = CommonError::timeout(5000, 3000); match err { CommonError::Timeout { actual_ms, max_ms } => { assert_eq!(actual_ms, 5000); assert_eq!(max_ms, 3000); }, _ => panic!("Expected Timeout variant"), } // Test category assert_eq!(err.category(), ErrorCategory::System); assert_eq!(err.severity(), ErrorSeverity::Error); assert!(err.is_retryable()); } #[test] fn test_common_error_ml_factory() { let err = CommonError::ml("MAMBA-2", "Model not found"); match &err { CommonError::Service { category, message } => { assert_eq!(*category, ErrorCategory::MachineLearning); assert_eq!(message, "MAMBA-2: Model not found"); }, _ => panic!("Expected Service variant"), } // Test category assert_eq!(err.category(), ErrorCategory::MachineLearning); } #[test] fn test_common_error_ml_factory_string_types() { // Test with &str let err1 = CommonError::ml("model1", "error1"); assert!(matches!(err1, CommonError::Service { .. })); // Test with String let err2 = CommonError::ml(String::from("model2"), String::from("error2")); match err2 { CommonError::Service { message, .. } => { assert_eq!(message, "model2: error2"); }, _ => panic!("Expected Service variant"), } } #[test] fn test_common_error_serialization_factory() { let err = CommonError::serialization("Failed to parse JSON"); match &err { CommonError::Service { category, message } => { assert_eq!(*category, ErrorCategory::Parse); assert_eq!(message, "Serialization error: Failed to parse JSON"); }, _ => panic!("Expected Service variant"), } // Test category assert_eq!(err.category(), ErrorCategory::Parse); } #[test] fn test_common_error_resource_exhausted_factory() { let err = CommonError::resource_exhausted("Memory pool"); match &err { CommonError::Service { category, message } => { assert_eq!(*category, ErrorCategory::Resource); assert_eq!(message, "Resource exhausted: Memory pool"); }, _ => panic!("Expected Service variant"), } // Test category assert_eq!(err.category(), ErrorCategory::Resource); } // ============================================================================= // ERROR CATEGORY DISPLAY TRAIT // ============================================================================= #[test] fn test_error_category_display_all_variants() { let test_cases = vec![ (ErrorCategory::MarketData, "MARKET_DATA"), (ErrorCategory::Trading, "TRADING"), (ErrorCategory::Network, "NETWORK"), (ErrorCategory::System, "SYSTEM"), (ErrorCategory::Configuration, "CONFIGURATION"), (ErrorCategory::Validation, "VALIDATION"), (ErrorCategory::Critical, "CRITICAL"), (ErrorCategory::Connection, "CONNECTION"), (ErrorCategory::Authentication, "AUTHENTICATION"), (ErrorCategory::RateLimit, "RATE_LIMIT"), (ErrorCategory::Parse, "PARSE"), (ErrorCategory::Subscription, "SUBSCRIPTION"), (ErrorCategory::FinancialSafety, "FINANCIAL_SAFETY"), (ErrorCategory::RiskManagement, "RISK_MANAGEMENT"), (ErrorCategory::Database, "DATABASE"), (ErrorCategory::Broker, "BROKER"), (ErrorCategory::MachineLearning, "MACHINE_LEARNING"), (ErrorCategory::Security, "SECURITY"), (ErrorCategory::BusinessLogic, "BUSINESS_LOGIC"), (ErrorCategory::Resource, "RESOURCE"), (ErrorCategory::Development, "DEVELOPMENT"), (ErrorCategory::Risk, "RISK"), (ErrorCategory::ML, "ML"), (ErrorCategory::Other, "OTHER"), ]; for (category, expected) in test_cases { assert_eq!(format!("{}", category), expected); assert_eq!(category.to_string(), expected); } } #[test] fn test_error_category_debug_format() { let category = ErrorCategory::Trading; let debug_str = format!("{:?}", category); assert_eq!(debug_str, "Trading"); } // ============================================================================= // ERROR SEVERITY DISPLAY TRAIT // ============================================================================= #[test] fn test_error_severity_display_all_variants() { let test_cases = vec![ (ErrorSeverity::Debug, "DEBUG"), (ErrorSeverity::Info, "INFO"), (ErrorSeverity::Warn, "WARN"), (ErrorSeverity::Error, "ERROR"), (ErrorSeverity::Critical, "CRITICAL"), ]; for (severity, expected) in test_cases { assert_eq!(format!("{}", severity), expected); assert_eq!(severity.to_string(), expected); } } #[test] fn test_error_severity_debug_format() { let severity = ErrorSeverity::Critical; let debug_str = format!("{:?}", severity); assert_eq!(debug_str, "Critical"); } // ============================================================================= // COMMON ERROR DISPLAY TRAIT // ============================================================================= #[test] fn test_common_error_display_configuration() { let err = CommonError::config("Missing API key"); let display = format!("{}", err); assert_eq!(display, "Configuration error: Missing API key"); } #[test] fn test_common_error_display_network() { let err = CommonError::network("Connection timeout"); let display = format!("{}", err); assert_eq!(display, "Network error: Connection timeout"); } #[test] fn test_common_error_display_service() { let err = CommonError::service(ErrorCategory::Trading, "Order rejected"); let display = format!("{}", err); assert_eq!(display, "Service error: TRADING - Order rejected"); } #[test] fn test_common_error_display_validation() { let err = CommonError::validation("Invalid symbol"); let display = format!("{}", err); assert_eq!(display, "Validation error: Invalid symbol"); } #[test] fn test_common_error_display_timeout() { let err = CommonError::timeout(5000, 3000); let display = format!("{}", err); assert_eq!(display, "Timeout error: operation took 5000ms, max allowed 3000ms"); } #[test] fn test_common_error_display_database() { use common::database::DatabaseError; let err = CommonError::Database(DatabaseError::PoolExhausted); let display = format!("{}", err); assert!(display.starts_with("Database error:")); } // ============================================================================= // ERROR CATEGORIZATION AND CLASSIFICATION // ============================================================================= #[test] fn test_common_error_category_method() { // Test that category() returns correct category for each variant assert_eq!( CommonError::config("test").category(), ErrorCategory::Configuration ); assert_eq!( CommonError::network("test").category(), ErrorCategory::Network ); assert_eq!( CommonError::validation("test").category(), ErrorCategory::Validation ); assert_eq!( CommonError::timeout(1000, 500).category(), ErrorCategory::System ); assert_eq!( CommonError::service(ErrorCategory::Trading, "test").category(), ErrorCategory::Trading ); } #[test] fn test_common_error_severity_classification() { // Critical severity assert_eq!( CommonError::config("test").severity(), ErrorSeverity::Critical ); // Error severity assert_eq!( CommonError::network("test").severity(), ErrorSeverity::Error ); assert_eq!( CommonError::timeout(1000, 500).severity(), ErrorSeverity::Error ); // Warn severity assert_eq!( CommonError::validation("test").severity(), ErrorSeverity::Warn ); } #[test] fn test_common_error_is_retryable() { // Non-retryable errors assert!(!CommonError::config("test").is_retryable()); assert!(!CommonError::validation("test").is_retryable()); assert!(!CommonError::service(ErrorCategory::Authentication, "test").is_retryable()); assert!(!CommonError::service(ErrorCategory::Configuration, "test").is_retryable()); assert!(!CommonError::service(ErrorCategory::Validation, "test").is_retryable()); // Retryable errors assert!(CommonError::network("test").is_retryable()); assert!(CommonError::timeout(1000, 500).is_retryable()); assert!(CommonError::service(ErrorCategory::Network, "test").is_retryable()); assert!(CommonError::service(ErrorCategory::Trading, "test").is_retryable()); } // ============================================================================= // RETRY STRATEGY MAX ATTEMPTS // ============================================================================= #[test] fn test_retry_strategy_max_attempts() { assert_eq!(RetryStrategy::NoRetry.max_attempts(), Some(0)); assert_eq!(RetryStrategy::Immediate.max_attempts(), Some(3)); assert_eq!( RetryStrategy::Linear { base_delay_ms: 100 }.max_attempts(), Some(5) ); assert_eq!( RetryStrategy::Exponential { base_delay_ms: 100, max_delay_ms: 10000 }.max_attempts(), Some(7) ); assert_eq!(RetryStrategy::CircuitBreaker.max_attempts(), Some(1)); } // ============================================================================= // ERROR MESSAGE FORMATTING EDGE CASES // ============================================================================= #[test] fn test_common_error_empty_message() { let err = CommonError::config(""); let display = format!("{}", err); assert_eq!(display, "Configuration error: "); } #[test] fn test_common_error_special_characters() { let err = CommonError::network("Connection\nreset\tby\rpeer"); match err { CommonError::Network(msg) => { assert!(msg.contains('\n')); assert!(msg.contains('\t')); assert!(msg.contains('\r')); }, _ => panic!("Expected Network variant"), } } #[test] fn test_common_error_unicode_message() { let err = CommonError::validation("Invalid symbol: ¥€£"); match err { CommonError::Validation(msg) => { assert!(msg.contains('¥')); assert!(msg.contains('€')); assert!(msg.contains('£')); }, _ => panic!("Expected Validation variant"), } } #[test] fn test_common_error_very_long_message() { let long_msg = "x".repeat(10000); let err = CommonError::internal(&long_msg); match err { CommonError::Service { message, .. } => { assert_eq!(message.len(), 10000 + "Internal error: ".len()); }, _ => panic!("Expected Service variant"), } } // ============================================================================= // ERROR CATEGORY SERDE SERIALIZATION/DESERIALIZATION // ============================================================================= #[test] fn test_error_category_serde_round_trip() { use serde_json; let categories = vec![ ErrorCategory::MarketData, ErrorCategory::Trading, ErrorCategory::Critical, ErrorCategory::MachineLearning, ]; for category in categories { // Serialize let json = serde_json::to_string(&category).expect("Failed to serialize"); // Deserialize let deserialized: ErrorCategory = serde_json::from_str(&json) .expect("Failed to deserialize"); assert_eq!(category, deserialized); } } #[test] fn test_error_severity_serde_round_trip() { use serde_json; let severities = vec![ ErrorSeverity::Debug, ErrorSeverity::Info, ErrorSeverity::Warn, ErrorSeverity::Error, ErrorSeverity::Critical, ]; for severity in severities { // Serialize let json = serde_json::to_string(&severity).expect("Failed to serialize"); // Deserialize let deserialized: ErrorSeverity = serde_json::from_str(&json) .expect("Failed to deserialize"); assert_eq!(severity, deserialized); } } // ============================================================================= // ERROR CATEGORY EQUALITY AND COMPARISON // ============================================================================= #[test] fn test_error_category_equality() { assert_eq!(ErrorCategory::Trading, ErrorCategory::Trading); assert_ne!(ErrorCategory::Trading, ErrorCategory::MarketData); // Test ML and MachineLearning are different variants assert_ne!(ErrorCategory::ML, ErrorCategory::MachineLearning); } #[test] fn test_error_category_clone() { let category = ErrorCategory::Trading; let cloned = category; assert_eq!(category, cloned); } #[test] fn test_error_category_copy() { let category = ErrorCategory::Trading; let copied = category; // Copy trait allows this assert_eq!(category, copied); } #[test] fn test_error_severity_equality() { assert_eq!(ErrorSeverity::Error, ErrorSeverity::Error); assert_ne!(ErrorSeverity::Error, ErrorSeverity::Critical); } // ============================================================================= // RETRY STRATEGY SERDE // ============================================================================= #[test] fn test_retry_strategy_serde_round_trip() { use serde_json; let strategies = vec![ RetryStrategy::NoRetry, RetryStrategy::Immediate, RetryStrategy::Linear { base_delay_ms: 100 }, RetryStrategy::Exponential { base_delay_ms: 100, max_delay_ms: 10000, }, RetryStrategy::CircuitBreaker, ]; for strategy in strategies { // Serialize let json = serde_json::to_string(&strategy).expect("Failed to serialize"); // Deserialize let deserialized: RetryStrategy = serde_json::from_str(&json) .expect("Failed to deserialize"); assert_eq!(strategy, deserialized); } } // ============================================================================= // ERROR FACTORY METHODS WITH EDGE CASES // ============================================================================= #[test] fn test_common_error_timeout_zero_values() { let err = CommonError::timeout(0, 0); match err { CommonError::Timeout { actual_ms, max_ms } => { assert_eq!(actual_ms, 0); assert_eq!(max_ms, 0); }, _ => panic!("Expected Timeout variant"), } } #[test] fn test_common_error_timeout_max_values() { let err = CommonError::timeout(u64::MAX, u64::MAX); match err { CommonError::Timeout { actual_ms, max_ms } => { assert_eq!(actual_ms, u64::MAX); assert_eq!(max_ms, u64::MAX); }, _ => panic!("Expected Timeout variant"), } } #[test] fn test_common_error_timeout_actual_less_than_max() { // Even though actual < max, the error should still be created let err = CommonError::timeout(100, 5000); match err { CommonError::Timeout { actual_ms, max_ms } => { assert_eq!(actual_ms, 100); assert_eq!(max_ms, 5000); }, _ => panic!("Expected Timeout variant"), } } // ============================================================================= // COMMON ERROR DEBUG TRAIT // ============================================================================= #[test] fn test_common_error_debug_format() { let err = CommonError::config("test"); let debug = format!("{:?}", err); assert!(debug.contains("Configuration")); assert!(debug.contains("test")); } #[test] fn test_common_error_service_debug_format() { let err = CommonError::service(ErrorCategory::Trading, "test"); let debug = format!("{:?}", err); assert!(debug.contains("Service")); assert!(debug.contains("Trading")); assert!(debug.contains("test")); } // ============================================================================= // ERROR DOWNCAST AND SOURCE CHAINS // ============================================================================= #[test] fn test_common_error_implements_error_trait() { use std::error::Error; let err: Box = Box::new(CommonError::config("test")); assert!(err.source().is_none()); // No underlying cause } #[test] fn test_common_error_database_has_source() { use std::error::Error; use common::database::DatabaseError; let db_err = DatabaseError::PoolExhausted; let err = CommonError::from(db_err); let err_ref: &dyn Error = &err; // DatabaseError should be the source assert!(err_ref.source().is_some()); } // ============================================================================= // RETRY STRATEGY CALCULATE DELAY EDGE CASES // ============================================================================= #[test] fn test_retry_strategy_linear_large_attempt() { let strategy = RetryStrategy::Linear { base_delay_ms: 1000 }; // Very large attempt number should not panic (saturating math) let delay = strategy.calculate_delay(u32::MAX).expect("should have delay"); assert!(delay.as_millis() > 0); } #[test] fn test_retry_strategy_exponential_overflow_protection() { let strategy = RetryStrategy::Exponential { base_delay_ms: u64::MAX, max_delay_ms: 1000, }; // Should cap at max_delay_ms and not panic let delay = strategy.calculate_delay(10).expect("should have delay"); assert!(delay.as_millis() <= 1000); } #[test] fn test_retry_strategy_exponential_attempt_capping() { let strategy = RetryStrategy::Exponential { base_delay_ms: 100, max_delay_ms: 100000, }; // Attempts > 10 should be capped to prevent overflow let delay_10 = strategy.calculate_delay(10).expect("should have delay"); let delay_20 = strategy.calculate_delay(20).expect("should have delay"); // Both should use min(10) in the calculation assert_eq!(delay_10.as_millis(), delay_20.as_millis()); } // ============================================================================= // COMBINED ERROR SCENARIOS // ============================================================================= #[test] fn test_combined_error_categorization_and_retry() { // Test that categorization and retry strategy are consistent // Non-retryable configuration error let err = CommonError::config("test"); assert_eq!(err.category(), ErrorCategory::Configuration); assert!(!err.is_retryable()); assert!(matches!(err.retry_strategy(), RetryStrategy::NoRetry)); // Retryable network error let err = CommonError::network("test"); assert_eq!(err.category(), ErrorCategory::Network); assert!(err.is_retryable()); assert!(matches!(err.retry_strategy(), RetryStrategy::Linear { .. })); } #[test] fn test_error_severity_matches_category() { // Critical categories should have Critical or Error severity let critical_err = CommonError::service(ErrorCategory::Critical, "test"); assert_eq!(critical_err.severity(), ErrorSeverity::Critical); let financial_err = CommonError::service(ErrorCategory::FinancialSafety, "test"); assert_eq!(financial_err.severity(), ErrorSeverity::Critical); let auth_err = CommonError::service(ErrorCategory::Authentication, "test"); assert_eq!(auth_err.severity(), ErrorSeverity::Critical); // Trading should be Error severity let trade_err = CommonError::service(ErrorCategory::Trading, "test"); assert_eq!(trade_err.severity(), ErrorSeverity::Error); } #[test] fn test_error_factory_consistency() { // Test that factory methods create consistent error structures // config() should create Configuration variant let err = CommonError::config("test"); assert!(matches!(err, CommonError::Configuration(_))); // network() should create Network variant let err = CommonError::network("test"); assert!(matches!(err, CommonError::Network(_))); // validation() should create Validation variant let err = CommonError::validation("test"); assert!(matches!(err, CommonError::Validation(_))); // internal() should create Service variant with System category let err = CommonError::internal("test"); match err { CommonError::Service { category, .. } => { assert_eq!(category, ErrorCategory::System); }, _ => panic!("Expected Service variant"), } }