use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use std::time::Duration; use tokio::time::sleep; use network_monitor::retry::{RetryConfig, retry_with_config}; /// Test simulating network failure recovery #[tokio::test] async fn test_network_failure_recovery() { // Simulate an operation that fails a few times then succeeds let attempt_count = Arc::new(AtomicU32::new(0)); let max_failures = 3; let config = RetryConfig { max_attempts: 10, initial_delay: Duration::from_millis(10), max_delay: Duration::from_millis(100), backoff_multiplier: 1.5, jitter: false, // Disable jitter for more predictable testing }; let attempt_count_clone = attempt_count.clone(); let result = retry_with_config(config, move || { let attempt_count = attempt_count_clone.clone(); async move { let current_attempt = attempt_count.fetch_add(1, Ordering::SeqCst) + 1; if current_attempt <= max_failures { // Simulate network error Err(format!("Network error on attempt {}", current_attempt)) } else { // Simulate successful recovery Ok(format!("Success on attempt {}", current_attempt)) } } }).await; assert!(result.is_ok()); assert_eq!(result.unwrap(), "Success on attempt 4"); assert_eq!(attempt_count.load(Ordering::SeqCst), 4); } /// Test connection timeout scenario #[tokio::test] async fn test_connection_timeout_scenario() { let config = RetryConfig { max_attempts: 3, initial_delay: Duration::from_millis(5), max_delay: Duration::from_millis(20), backoff_multiplier: 2.0, jitter: false, }; let attempt_count = Arc::new(AtomicU32::new(0)); let attempt_count_clone = attempt_count.clone(); let result: Result = retry_with_config(config, move || { let attempt_count = attempt_count_clone.clone(); async move { attempt_count.fetch_add(1, Ordering::SeqCst); // Simulate connection timeout sleep(Duration::from_millis(1)).await; Err("Connection timeout") } }).await; assert!(result.is_err()); assert_eq!(attempt_count.load(Ordering::SeqCst), 3); // Should have attempted 3 times } /// Test fast recovery scenario #[tokio::test] async fn test_fast_recovery() { let config = RetryConfig::fast(); let attempt_count = Arc::new(AtomicU32::new(0)); let attempt_count_clone = attempt_count.clone(); let result = retry_with_config(config, move || { let attempt_count = attempt_count_clone.clone(); async move { let current_attempt = attempt_count.fetch_add(1, Ordering::SeqCst) + 1; if current_attempt == 1 { Err("Temporary failure") } else { Ok("Quick recovery") } } }).await; assert!(result.is_ok()); assert_eq!(result.unwrap(), "Quick recovery"); assert_eq!(attempt_count.load(Ordering::SeqCst), 2); } /// Test slow retry scenario #[tokio::test] async fn test_slow_retry_scenario() { let config = RetryConfig::slow(); let attempt_count = Arc::new(AtomicU32::new(0)); let attempt_count_clone = attempt_count.clone(); let result = retry_with_config(config, move || { let attempt_count = attempt_count_clone.clone(); async move { let current_attempt = attempt_count.fetch_add(1, Ordering::SeqCst) + 1; if current_attempt <= 2 { Err("Service unavailable") } else { Ok("Service restored") } } }).await; assert!(result.is_ok()); assert_eq!(result.unwrap(), "Service restored"); assert_eq!(attempt_count.load(Ordering::SeqCst), 3); } /// Test maximum retry limit #[tokio::test] async fn test_max_retry_limit() { let config = RetryConfig { max_attempts: 2, initial_delay: Duration::from_millis(1), max_delay: Duration::from_millis(5), backoff_multiplier: 2.0, jitter: false, }; let attempt_count = Arc::new(AtomicU32::new(0)); let attempt_count_clone = attempt_count.clone(); let result = retry_with_config(config, move || { let attempt_count = attempt_count_clone.clone(); async move { attempt_count.fetch_add(1, Ordering::SeqCst); Err::("Persistent failure") } }).await; assert!(result.is_err()); assert_eq!(attempt_count.load(Ordering::SeqCst), 2); }