Drive circuit breaker state transitions via internal/clock

The Open->HalfOpen promotion used time.Now/time.Since directly, forcing tests
to use real time.Sleep and diverging from the project's clock convention. Add
an unexported withClock option (default clock.System) and replace the real
sleeps in tests with mock-clock Advance, making the transitions deterministic
and the package faster.
This commit is contained in:
2026-05-23 13:47:26 +03:00
parent 43d3ecfba1
commit b07d487e63
3 changed files with 32 additions and 9 deletions

View File

@@ -8,6 +8,7 @@ import (
"testing"
"time"
"git.codelab.vc/pkg/httpx/internal/clock"
"git.codelab.vc/pkg/httpx/middleware"
)
@@ -80,9 +81,11 @@ func TestBreaker_OpenRejectsRequests(t *testing.T) {
func TestBreaker_TransitionsToHalfOpenAfterDuration(t *testing.T) {
const openDuration = 50 * time.Millisecond
clk := clock.Mock(time.Now())
b := NewBreaker(
WithFailureThreshold(1),
WithOpenDuration(openDuration),
withClock(clk),
)
// Trip the breaker.
@@ -96,8 +99,8 @@ func TestBreaker_TransitionsToHalfOpenAfterDuration(t *testing.T) {
t.Fatalf("state = %v, want %v", s, StateOpen)
}
// Wait for the open duration to elapse.
time.Sleep(openDuration + 10*time.Millisecond)
// Advance past the open duration.
clk.Advance(openDuration + time.Millisecond)
if s := b.State(); s != StateHalfOpen {
t.Fatalf("state = %v, want %v", s, StateHalfOpen)
@@ -106,9 +109,11 @@ func TestBreaker_TransitionsToHalfOpenAfterDuration(t *testing.T) {
func TestBreaker_HalfOpenToClosedOnSuccess(t *testing.T) {
const openDuration = 50 * time.Millisecond
clk := clock.Mock(time.Now())
b := NewBreaker(
WithFailureThreshold(1),
WithOpenDuration(openDuration),
withClock(clk),
)
// Trip the breaker.
@@ -118,8 +123,8 @@ func TestBreaker_HalfOpenToClosedOnSuccess(t *testing.T) {
}
done(false)
// Wait for half-open.
time.Sleep(openDuration + 10*time.Millisecond)
// Advance into half-open.
clk.Advance(openDuration + time.Millisecond)
// A successful request in half-open should close the breaker.
done, err = b.Allow()
@@ -135,9 +140,11 @@ func TestBreaker_HalfOpenToClosedOnSuccess(t *testing.T) {
func TestBreaker_HalfOpenToOpenOnFailure(t *testing.T) {
const openDuration = 50 * time.Millisecond
clk := clock.Mock(time.Now())
b := NewBreaker(
WithFailureThreshold(1),
WithOpenDuration(openDuration),
withClock(clk),
)
// Trip the breaker.
@@ -147,8 +154,8 @@ func TestBreaker_HalfOpenToOpenOnFailure(t *testing.T) {
}
done(false)
// Wait for half-open.
time.Sleep(openDuration + 10*time.Millisecond)
// Advance into half-open.
clk.Advance(openDuration + time.Millisecond)
// A failed request in half-open should re-open the breaker.
done, err = b.Allow()