package inference import ( "testing" "time" ) func TestBreakerTripsAndRecovers(t *testing.T) { b := newBreaker(3, 10*time.Second) now := time.Unix(1000, 0) b.now = func() time.Time { return now } // Three consecutive failures trip the breaker. for i := 0; i < 3; i++ { if !b.allow() { t.Fatalf("attempt %d should be allowed while closed", i) } b.record(false) } if b.allow() { t.Fatal("breaker should be open after the failure threshold, failing fast") } // Before the cooldown elapses it stays open. now = now.Add(9 * time.Second) if b.allow() { t.Fatal("breaker should still be open before the cooldown elapses") } // After the cooldown one probe is allowed (half open). now = now.Add(2 * time.Second) if !b.allow() { t.Fatal("breaker should allow a single probe after the cooldown") } // A second concurrent probe is refused. if b.allow() { t.Fatal("breaker must allow only one probe at a time while half open") } // A successful probe closes the breaker. b.record(true) if !b.allow() { t.Fatal("a successful probe should close the breaker") } } func TestBreakerProbeFailureReopens(t *testing.T) { b := newBreaker(1, 5*time.Second) now := time.Unix(2000, 0) b.now = func() time.Time { return now } b.allow() b.record(false) // trips immediately (threshold 1) now = now.Add(6 * time.Second) if !b.allow() { t.Fatal("probe should be allowed after cooldown") } b.record(false) // probe fails if b.allow() { t.Fatal("a failed probe must reopen the breaker, not close it") } }