#![allow(
clippy::arithmetic_side_effects,
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic
)]
use super::*;
#[test]
fn test_health_check_config_default() {
let config = HealthCheckConfig::default();
assert_eq!(config.check_interval, Duration::from_secs(30));
assert_eq!(config.recovery_check_interval, Duration::from_secs(5));
assert_eq!(config.check_timeout, Duration::from_secs(5));
}
#[test]
fn test_health_check_worker_creation() {
let upstream = Arc::new(
GenericRestAdapter::new("http://localhost:8080", Duration::from_secs(30)).unwrap(),
);
let tracker = Arc::new(HealthTracker::new());
let cancel = CancellationToken::new();
let worker = HealthCheckWorker::with_defaults(upstream, tracker.clone(), cancel.clone());
assert!(worker.is_running());
assert_eq!(worker.status(), UpstreamStatus::Online);
cancel.cancel();
assert!(!worker.is_running());
}
#[tokio::test]
async fn test_health_check_worker_cancellation() {
let upstream = Arc::new(
GenericRestAdapter::new("http://localhost:8080", Duration::from_secs(30)).unwrap(),
);
let tracker = Arc::new(HealthTracker::new());
let cancel = CancellationToken::new();
let worker = Arc::new(HealthCheckWorker::new(
upstream,
tracker,
HealthCheckConfig {
check_interval: Duration::from_millis(50),
recovery_check_interval: Duration::from_millis(10),
check_timeout: Duration::from_millis(100),
},
cancel.clone(),
));
let worker_clone = worker.clone();
let handle = tokio::spawn(async move {
worker_clone.run().await;
});
tokio::time::sleep(Duration::from_millis(100)).await;
cancel.cancel();
let result = tokio::time::timeout(Duration::from_millis(200), handle).await;
assert!(result.is_ok());
}
#[test]
fn test_failure_escalation_online_to_offline() {
let tracker = HealthTracker::new();
assert_eq!(tracker.status(), UpstreamStatus::Online);
tracker.record_failure();
assert_eq!(tracker.consecutive_failures(), 1);
assert_eq!(
tracker.status(),
UpstreamStatus::Online,
"1 fail still online"
);
tracker.record_failure();
assert_eq!(tracker.consecutive_failures(), 2);
assert_eq!(
tracker.status(),
UpstreamStatus::Online,
"2 fails still online"
);
tracker.record_failure();
assert_eq!(tracker.consecutive_failures(), 3);
assert_eq!(
tracker.status(),
UpstreamStatus::Offline,
"3 fails → offline"
);
}
#[test]
fn test_failure_intermittent_does_not_escalate() {
let tracker = HealthTracker::new();
tracker.record_failure();
tracker.record_failure();
assert_eq!(tracker.consecutive_failures(), 2);
tracker.record_success();
assert_eq!(
tracker.consecutive_failures(),
0,
"success resets failure count"
);
assert_eq!(tracker.status(), UpstreamStatus::Online);
}
#[test]
fn test_recovery_offline_to_online() {
let tracker = HealthTracker::new();
for _ in 0..3 {
tracker.record_failure();
}
assert_eq!(tracker.status(), UpstreamStatus::Offline);
tracker.record_success();
assert_eq!(
tracker.status(),
UpstreamStatus::Offline,
"1 success not enough to recover"
);
tracker.record_success();
assert_eq!(
tracker.status(),
UpstreamStatus::Online,
"2 successes → online"
);
}
#[test]
fn test_custom_thresholds() {
let tracker = HealthTracker::with_thresholds(2, 1, 0.5);
tracker.record_failure();
assert_eq!(tracker.status(), UpstreamStatus::Online);
tracker.record_failure();
assert_eq!(
tracker.status(),
UpstreamStatus::Offline,
"custom threshold=2 trips after 2 fails"
);
tracker.record_success();
assert_eq!(
tracker.status(),
UpstreamStatus::Online,
"1 success recovers with custom threshold=1"
);
}
#[test]
fn test_reset_window_clears_total_failed_counts() {
let tracker = HealthTracker::new();
for _ in 0..3 {
tracker.record_failure();
}
assert_eq!(tracker.status(), UpstreamStatus::Offline);
tracker.reset_window();
assert_eq!(tracker.consecutive_failures(), 3);
assert_eq!(
tracker.status(),
UpstreamStatus::Offline,
"reset_window does not clear offline state"
);
}
#[test]
fn test_total_and_failed_request_counts() {
let tracker = HealthTracker::new();
tracker.record_success();
tracker.record_success();
tracker.record_failure();
assert_eq!(
tracker.consecutive_successes(),
0,
"failure resets consecutive successes"
);
assert_eq!(
tracker.consecutive_failures(),
1,
"last event was a failure"
);
}
#[test]
fn test_time_since_last_success() {
let tracker = HealthTracker::new();
let since = tracker.time_since_last_success();
assert!(since.is_none(), "no success recorded → None");
tracker.record_success();
let since = tracker.time_since_last_success();
assert!(since.is_some(), "after success → Some");
let dur = since.unwrap();
assert!(
dur < Duration::from_secs(1),
"time since should be < 1s, got: {dur:?}"
);
}