diff --git a/crates/knot-proxy/src/breaker.rs b/crates/knot-proxy/src/breaker.rs index 881f569..4b6f6b6 100644 --- a/crates/knot-proxy/src/breaker.rs +++ b/crates/knot-proxy/src/breaker.rs @@ -144,6 +144,14 @@ impl Drop for BreakerPermit { } } +#[cfg(test)] +impl BreakerPermit { + fn record_failure_at(mut self, now: Instant) { + self.resolved = true; + self.breaker.record_failure_at(now); + } +} + #[cfg(test)] mod tests { use super::*; @@ -187,12 +195,13 @@ mod tests { #[test] fn cooldown_admits_one_trial_only() { let b = breaker(1, 50); - b.record_failure(); - assert!(b.try_acquire().is_err()); - std::thread::sleep(Duration::from_millis(60)); - let trial = b.try_acquire().expect("trial admitted after cooldown"); + let t0 = Instant::now(); + let after = t0 + Duration::from_millis(60); + b.record_failure_at(t0); + assert!(b.try_acquire_at(t0).is_err()); + let trial = b.try_acquire_at(after).expect("trial admitted after cooldown"); assert!( - b.try_acquire().is_err(), + b.try_acquire_at(after).is_err(), "second concurrent half-open call must be rejected", ); trial.record_success(); @@ -201,11 +210,14 @@ mod tests { #[test] fn half_open_failure_reopens() { let b = breaker(1, 50); - b.record_failure(); - std::thread::sleep(Duration::from_millis(60)); - b.try_acquire().expect("trial admitted").record_failure(); + let t0 = Instant::now(); + let after = t0 + Duration::from_millis(60); + b.record_failure_at(t0); + b.try_acquire_at(after) + .expect("trial admitted") + .record_failure_at(after); assert!( - b.try_acquire().is_err(), + b.try_acquire_at(after).is_err(), "half-open failure must reopen breaker", ); } @@ -213,11 +225,12 @@ mod tests { #[test] fn half_open_success_closes() { let b = breaker(1, 50); - b.record_failure(); - std::thread::sleep(Duration::from_millis(60)); - b.try_acquire().expect("trial admitted").record_success(); - b.try_acquire().expect("closed").record_success(); - b.try_acquire().expect("closed").record_success(); + let t0 = Instant::now(); + let after = t0 + Duration::from_millis(60); + b.record_failure_at(t0); + b.try_acquire_at(after).expect("trial admitted").record_success(); + b.try_acquire_at(after).expect("closed").record_success(); + b.try_acquire_at(after).expect("closed").record_success(); } #[test] @@ -244,10 +257,11 @@ mod tests { #[test] fn dropped_half_open_permit_closes_breaker() { let b = breaker(1, 50); - b.record_failure(); - std::thread::sleep(Duration::from_millis(60)); - drop(b.try_acquire().expect("trial admitted")); - b.try_acquire() + let t0 = Instant::now(); + let after = t0 + Duration::from_millis(60); + b.record_failure_at(t0); + drop(b.try_acquire_at(after).expect("trial admitted")); + b.try_acquire_at(after) .expect("dropped half-open permit must close breaker") .record_success(); }