//! Unit tests for the `retry` module. use std::time::Duration; use ontoref_compute::retry::{ oom_retry_allowed, BackoffPolicy, RetryDecision, RetryPolicy, MAX_OOM_RETRIES, }; use ontoref_compute::sizing::SizeTier; use ontoref_compute::{ComputeError, InstanceHandle, ProviderKind}; #[test] fn max_oom_retries_is_three() { assert_eq!(MAX_OOM_RETRIES, 3); } #[test] fn oom_retry_allowed_boundary() { assert!(oom_retry_allowed(0)); assert!(oom_retry_allowed(1)); assert!(oom_retry_allowed(2)); assert!(!oom_retry_allowed(3)); assert!(!oom_retry_allowed(100)); } #[test] fn backoff_fixed_zero_on_first_attempt() { let b = BackoffPolicy::Fixed { ms: 2_000 }; assert_eq!(b.delay_for(0), Duration::ZERO); assert_eq!(b.delay_for(1), Duration::from_millis(2_000)); assert_eq!(b.delay_for(5), Duration::from_millis(2_000)); } #[test] fn backoff_exponential_caps_at_max() { let b = BackoffPolicy::Exponential { base_ms: 100, max_ms: 3_000 }; assert_eq!(b.delay_for(0), Duration::ZERO); for i in 1..=20u32 { let d = b.delay_for(i); assert!(d <= Duration::from_millis(3_000), "attempt {i}: {d:?} exceeds cap"); assert!(d >= Duration::from_millis(100), "attempt {i}: {d:?} below base"); } } #[test] fn classify_oom_upsize_on_cloud_provider() { let policy = RetryPolicy::default(); let handle = InstanceHandle::new("test-handle"); let err = ComputeError::OutOfMemory { handle, size_mb: 8_192 }; let decision = ontoref_compute::retry::classify( &err, ProviderKind::Cloud, Some(SizeTier::S), &policy, 0, // no retries used yet ); assert_eq!(decision, RetryDecision::RetryUpsize(SizeTier::M)); } #[test] fn classify_oom_gives_up_at_top_tier() { let policy = RetryPolicy::default(); let handle = InstanceHandle::new("top-tier-handle"); let err = ComputeError::OutOfMemory { handle, size_mb: 32_768 }; let decision = ontoref_compute::retry::classify( &err, ProviderKind::Cloud, Some(SizeTier::L), // already at largest &policy, 0, ); assert_eq!(decision, RetryDecision::GiveUp); } #[test] fn classify_oom_gives_up_on_local_provider() { let policy = RetryPolicy::default(); let handle = InstanceHandle::new("docker-abc"); let err = ComputeError::OutOfMemory { handle, size_mb: 4_096 }; let decision = ontoref_compute::retry::classify( &err, ProviderKind::Local, // Docker does not support upsize Some(SizeTier::S), &policy, 0, ); assert_eq!(decision, RetryDecision::GiveUp); } #[test] fn classify_provider_unavailable_retries_same() { let policy = RetryPolicy::default(); let err = ComputeError::ProviderUnavailable("connection refused".into()); let decision = ontoref_compute::retry::classify( &err, ProviderKind::Cloud, Some(SizeTier::M), &policy, 0, ); assert_eq!(decision, RetryDecision::RetrySame); } #[test] fn classify_auth_error_gives_up() { let policy = RetryPolicy::default(); let err = ComputeError::Auth("invalid token".into()); let decision = ontoref_compute::retry::classify( &err, ProviderKind::Cloud, Some(SizeTier::M), &policy, 0, ); assert_eq!(decision, RetryDecision::GiveUp); }