use selfware::supervision::circuit_breaker::{
CircuitBreaker, CircuitBreakerConfig, CircuitBreakerError, CircuitState,
};
use std::time::Duration;
#[tokio::test]
async fn test_circuit_breaker_transitions() {
let config = CircuitBreakerConfig {
failure_threshold: 3,
success_threshold: 2,
reset_timeout: Duration::from_millis(100),
half_open_max_requests: 2,
};
let cb = CircuitBreaker::new(config);
assert_eq!(cb.current_state(), CircuitState::Closed);
for _ in 0..3 {
let result: Result<(), CircuitBreakerError<String>> =
cb.call(|| async { Err("Failed".to_string()) }).await;
assert!(matches!(
result,
Err(CircuitBreakerError::OperationFailed(_))
));
}
assert_eq!(cb.current_state(), CircuitState::Open);
let result: Result<(), CircuitBreakerError<String>> = cb.call(|| async { Ok(()) }).await;
assert!(matches!(result, Err(CircuitBreakerError::CircuitOpen)));
tokio::time::sleep(Duration::from_millis(150)).await;
let result: Result<(), CircuitBreakerError<String>> = cb.call(|| async { Ok(()) }).await;
assert!(result.is_ok());
assert_eq!(cb.current_state(), CircuitState::HalfOpen);
let result: Result<(), CircuitBreakerError<String>> = cb.call(|| async { Ok(()) }).await;
assert!(result.is_ok());
assert_eq!(cb.current_state(), CircuitState::Closed);
}
#[tokio::test]
async fn test_circuit_breaker_half_open_failure() {
let config = CircuitBreakerConfig {
failure_threshold: 1,
success_threshold: 2,
reset_timeout: Duration::from_millis(100),
half_open_max_requests: 2,
};
let cb = CircuitBreaker::new(config);
let _: Result<(), CircuitBreakerError<String>> =
cb.call(|| async { Err("Failed".to_string()) }).await;
assert_eq!(cb.current_state(), CircuitState::Open);
tokio::time::sleep(Duration::from_millis(150)).await;
let _: Result<(), CircuitBreakerError<String>> =
cb.call(|| async { Err("Failed again".to_string()) }).await;
assert_eq!(cb.current_state(), CircuitState::Open);
}