#[cfg(test)]
mod tests {
use super::*;
use crate::config::Config;
use crate::traits::*;
#[tokio::test]
async fn test_resilience_components_creation() {
let config = Config::default();
let circuit_breaker = crate::resilience::create_circuit_breaker(&config);
assert!(circuit_breaker.is_ok(), "Failed to create circuit breaker");
let retry_strategy = crate::resilience::create_retry_strategy(&config);
assert!(retry_strategy.is_ok(), "Failed to create retry strategy");
let bulkhead = crate::resilience::create_bulkhead(&config);
assert!(bulkhead.is_ok(), "Failed to create bulkhead");
}
#[tokio::test]
async fn test_circuit_breaker_protection() {
let config = Config::default();
let circuit_breaker = crate::resilience::create_circuit_breaker(&config).unwrap();
let result = circuit_breaker
.call_json(
"test_operation",
serde_json::json!({"test": "data"}),
)
.await;
assert!(result.is_ok(), "Circuit breaker should allow calls when closed");
}
#[tokio::test]
async fn test_bulkhead_isolation() {
let config = Config::default();
let bulkhead = crate::resilience::create_bulkhead(&config).unwrap();
assert!(
bulkhead.has_capacity("test_bulkhead"),
"Bulkhead should have capacity initially"
);
let result = bulkhead
.execute_json(
"test_bulkhead",
serde_json::json!({"test": "data"}),
)
.await;
assert!(result.is_ok(), "Bulkhead should allow execution with capacity");
}
#[tokio::test]
async fn test_retry_strategy() {
let config = Config::default();
let retry_strategy = crate::resilience::create_retry_strategy(&config).unwrap();
let result = retry_strategy
.execute_json(
"test_operation",
serde_json::json!({"test": "data"}),
)
.await;
assert!(result.is_ok(), "Retry strategy should execute successfully");
}
use std::sync::atomic::{AtomicU32, Ordering};
use tokio::time::{sleep, Duration};
#[tokio::test]
async fn test_standard_circuit_breaker() {
let config = Config::default();
let circuit_breaker = create_circuit_breaker(&config).unwrap();
assert_eq!(circuit_breaker.state("test"), CircuitState::Closed);
let result = circuit_breaker.call("test", || async {
Ok::<_, anyhow::Error>("success")
}).await;
assert!(result.is_ok());
for _ in 0..5 {
let _ = circuit_breaker.call("test", || async {
Err::<String, _>(anyhow::anyhow!("failure"))
}).await;
}
let state = circuit_breaker.state("test");
assert!(matches!(state, CircuitState::Open | CircuitState::HalfOpen));
}
#[tokio::test]
async fn test_standard_health_check() {
let config = Config::default();
let health_checker = create_health_checker(&config).unwrap();
let status = health_checker.check().await.unwrap();
assert_eq!(status, HealthStatus::Healthy);
let report = health_checker.detailed_check().await.unwrap();
assert_eq!(report.status, HealthStatus::Healthy);
assert!(report.checks.is_empty()); }
#[tokio::test]
async fn test_standard_recovery() {
let config = Config::default();
let recovery = create_recovery_strategy(&config).unwrap();
let context = RecoveryContext {
failure_count: 1,
last_error: "test error".to_string(),
recovery_attempts: 0,
service_name: "test_service".to_string(),
};
let result = recovery.recover(&context, "test_operation").await;
assert!(result.is_err());
let error = anyhow::anyhow!("connection timeout");
assert!(recovery.can_recover(&error));
let error = anyhow::anyhow!("401 unauthorized");
assert!(!recovery.can_recover(&error));
}
#[tokio::test]
async fn test_health_check_with_dependencies() {
let config = Config::default();
let main_checker = create_health_checker(&config).unwrap();
let dep_checker = create_health_checker(&config).unwrap();
main_checker.register_dependency("database".to_string(), dep_checker.clone());
let status = main_checker.check().await.unwrap();
assert_eq!(status, HealthStatus::Healthy);
let report = main_checker.detailed_check().await.unwrap();
assert_eq!(report.checks.len(), 1);
assert_eq!(report.checks[0].name, "database");
assert_eq!(report.checks[0].status, HealthStatus::Healthy);
}
#[tokio::test]
async fn test_recovery_stats() {
let config = Config::default();
let recovery = create_recovery_strategy(&config).unwrap();
let stats = recovery.stats();
assert_eq!(stats.recoveries_attempted, 0);
assert_eq!(stats.recoveries_succeeded, 0);
assert_eq!(stats.fallbacks_used, 0);
assert_eq!(stats.current_state, RecoveryState::Normal);
let context = RecoveryContext {
failure_count: 1,
last_error: "test".to_string(),
recovery_attempts: 1,
service_name: "test".to_string(),
};
let _ = recovery.recover(&context, "op").await;
let stats = recovery.stats();
assert_eq!(stats.recoveries_attempted, 1);
assert_eq!(stats.current_state, RecoveryState::Failed);
}
}