Files
traefikoidc/internal/cache/resilience/review_r152_test.go
T
lukaszraczylo 4d23d618f5 fix(cache): harden cache backends, resilience, recovery, and transports
- RESP -ERR no longer closes healthy conn / aborts batch; global cap replacement-aware
- global health-check pass serialized (latent data race); MemoryMonitor via ResourceManager
- token-exchange retry, open-circuit rejections counted as failures
- TLS-posture matching transport reuse (no silent InsecureSkipVerify handoff)
2026-08-14 16:16:51 +01:00

52 lines
1.5 KiB
Go

package resilience
import (
"context"
"errors"
"testing"
"time"
)
// TestCircuitBreaker_HalfOpenGateNotExceeded guards the R152 fix to
// AllowRequest: the open→half-open transition returned true without
// incrementing halfOpenRequests, so the very probe that opens the
// half-open window was free — allowing exactly one more request than
// the configured gate (on a serial refusal loop: gate+1 pass). The
// probe must be counted so cumulative half-open admissions stay
// within HalfOpenMaxRequests.
func TestCircuitBreaker_HalfOpenGateNotExceeded(t *testing.T) {
config := &CircuitBreakerConfig{
MaxFailures: 2,
Timeout: 50 * time.Millisecond,
HalfOpenMaxRequests: 2,
}
cb := NewCircuitBreaker(config)
ctx := context.Background()
// Open the circuit.
for i := 0; i < 2; i++ {
cb.Execute(ctx, func() error { return errors.New("boom") })
}
if cb.GetState() != StateOpen {
t.Fatal("expected open after failures")
}
time.Sleep(100 * time.Millisecond) // past timeout → cooldown expired
// Serially offer gate+overflow admission slots. The transition probe
// must count toward the gate; un-fixed code allows one extra.
allowed := 0
for i := 0; i < config.HalfOpenMaxRequests+5; i++ {
if !cb.AllowRequest() {
break
}
allowed++
}
if allowed > config.HalfOpenMaxRequests {
t.Fatalf("half-open gate exceeded: %d admitted, max %d", allowed, config.HalfOpenMaxRequests)
}
if allowed == 0 {
t.Fatal("at least one probe must be allowed once the cooldown expires")
}
}