use qubit_config::Config;
use qubit_retry::{RetryBuilder, RetryError};
use std::sync::{Arc, Mutex};
use std::time::Duration;
#[test]
fn test_sync_operation_timeout_post_check_mechanism() {
let executor = RetryBuilder::<String>::new()
.set_max_attempts(3)
.set_operation_timeout(Some(Duration::from_millis(100)))
.set_fixed_delay_strategy(Duration::from_millis(50))
.build();
let attempt_count = Arc::new(Mutex::new(0));
let attempt_count_clone = attempt_count.clone();
let result = executor.run(|| {
let mut count = attempt_count_clone.lock().unwrap();
*count += 1;
std::thread::sleep(Duration::from_millis(150));
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
});
assert!(result.is_err());
match result {
Err(RetryError::MaxAttemptsExceeded { attempts, .. }) => {
assert_eq!(attempts, 3);
}
_ => panic!("Expected MaxAttemptsExceeded error"),
}
assert_eq!(*attempt_count.lock().unwrap(), 3);
}
#[test]
fn test_sync_operation_no_timeout() {
let executor = RetryBuilder::<String>::new()
.set_max_attempts(3)
.set_unlimited_operation_timeout()
.build();
let result = executor.run(|| {
std::thread::sleep(Duration::from_millis(200));
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
});
assert!(result.is_ok());
assert_eq!(result.unwrap(), "SUCCESS");
}
#[test]
fn test_sync_operation_within_timeout() {
let executor = RetryBuilder::<String>::new()
.set_max_attempts(3)
.set_operation_timeout(Some(Duration::from_millis(200)))
.build();
let result = executor.run(|| {
std::thread::sleep(Duration::from_millis(50));
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
});
assert!(result.is_ok());
assert_eq!(result.unwrap(), "SUCCESS");
}
#[test]
fn test_sync_operation_timeout_with_retry() {
let executor = RetryBuilder::<String>::new()
.set_max_attempts(3)
.set_operation_timeout(Some(Duration::from_millis(100)))
.set_fixed_delay_strategy(Duration::from_millis(50))
.build();
let attempt_count = Arc::new(Mutex::new(0));
let attempt_count_clone = attempt_count.clone();
let result = executor.run(|| {
let mut count = attempt_count_clone.lock().unwrap();
*count += 1;
if *count < 3 {
std::thread::sleep(Duration::from_millis(150));
} else {
std::thread::sleep(Duration::from_millis(50));
}
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
});
assert!(result.is_ok());
assert_eq!(result.unwrap(), "SUCCESS");
assert_eq!(*attempt_count.lock().unwrap(), 3);
}
#[tokio::test]
async fn test_async_operation_timeout_true_interruption() {
let executor = RetryBuilder::<String>::new()
.set_max_attempts(3)
.set_operation_timeout(Some(Duration::from_millis(100)))
.set_fixed_delay_strategy(Duration::from_millis(50))
.build();
let attempt_count = Arc::new(Mutex::new(0));
let attempt_count_clone = attempt_count.clone();
let result = executor
.run_async(|| {
let attempt_count = attempt_count_clone.clone();
async move {
{
let mut count = attempt_count.lock().unwrap();
*count += 1;
}
tokio::time::sleep(Duration::from_millis(500)).await;
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
}
})
.await;
assert!(result.is_err());
match result {
Err(RetryError::MaxAttemptsExceeded { attempts, .. }) => {
assert_eq!(attempts, 3);
}
_ => panic!("Expected MaxAttemptsExceeded error"),
}
assert_eq!(*attempt_count.lock().unwrap(), 3);
}
#[tokio::test]
async fn test_async_operation_no_timeout() {
let executor = RetryBuilder::<String>::new()
.set_max_attempts(3)
.set_unlimited_operation_timeout()
.build();
let result = executor
.run_async(|| async {
tokio::time::sleep(Duration::from_millis(200)).await;
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
})
.await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "SUCCESS");
}
#[tokio::test]
async fn test_async_operation_within_timeout() {
let executor = RetryBuilder::<String>::new()
.set_max_attempts(3)
.set_operation_timeout(Some(Duration::from_millis(200)))
.build();
let result = executor
.run_async(|| async {
tokio::time::sleep(Duration::from_millis(50)).await;
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
})
.await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "SUCCESS");
}
#[tokio::test]
async fn test_async_operation_timeout_with_retry() {
let executor = RetryBuilder::<String>::new()
.set_max_attempts(3)
.set_operation_timeout(Some(Duration::from_millis(100)))
.set_fixed_delay_strategy(Duration::from_millis(50))
.build();
let attempt_count = Arc::new(Mutex::new(0));
let attempt_count_clone = attempt_count.clone();
let result = executor
.run_async(|| {
let attempt_count = attempt_count_clone.clone();
async move {
let current_attempt = {
let mut count = attempt_count.lock().unwrap();
*count += 1;
*count
};
if current_attempt < 3 {
tokio::time::sleep(Duration::from_millis(200)).await;
} else {
tokio::time::sleep(Duration::from_millis(50)).await;
}
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
}
})
.await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "SUCCESS");
assert_eq!(*attempt_count.lock().unwrap(), 3);
}
#[tokio::test]
async fn test_async_max_duration_vs_operation_timeout() {
let executor = RetryBuilder::<String>::new()
.set_max_attempts(10)
.set_operation_timeout(Some(Duration::from_millis(100))) .set_max_duration(Some(Duration::from_millis(500))) .set_fixed_delay_strategy(Duration::from_millis(100))
.build();
let result = executor
.run_async(|| async {
tokio::time::sleep(Duration::from_millis(200)).await;
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
})
.await;
assert!(result.is_err());
}
#[test]
fn test_operation_timeout_config_from_file() {
use qubit_retry::DefaultRetryConfig;
let mut config = Config::new();
config.set("retry.max_attempts", 3u32).unwrap();
config
.set("retry.operation_timeout_millis", 100u64)
.unwrap();
let retry_config = DefaultRetryConfig::with_config(config);
let executor = RetryBuilder::<String>::with_config(retry_config).build();
assert_eq!(executor.config().max_attempts(), 3);
assert_eq!(
executor.config().operation_timeout(),
Some(Duration::from_millis(100))
);
}
#[test]
fn test_operation_timeout_event_listening() {
let retry_count = Arc::new(Mutex::new(0));
let retry_count_clone = retry_count.clone();
let executor = RetryBuilder::<String>::new()
.set_max_attempts(3)
.set_operation_timeout(Some(Duration::from_millis(100)))
.set_fixed_delay_strategy(Duration::from_millis(50))
.on_retry(move |_event: &qubit_retry::RetryEvent<String>| {
let mut count = retry_count_clone.lock().unwrap();
*count += 1;
})
.build();
let _ = executor.run(|| {
std::thread::sleep(Duration::from_millis(150));
Ok::<String, Box<dyn std::error::Error + Send + Sync>>("SUCCESS".to_string())
});
assert_eq!(*retry_count.lock().unwrap(), 2);
}