ax(mining): rename circuit_breaker_test.go to TestFilename_Function_{Good,Bad,Ugly} convention
All test functions in circuit_breaker_test.go now follow the AX-required naming pattern. Added missing _Bad and _Ugly variants and usage-example comments on each test function. Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
ee3fb3d6b7
commit
99f37ed1bc
1 changed files with 70 additions and 43 deletions
|
|
@ -7,7 +7,10 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func TestCircuitBreakerDefaultConfig(t *testing.T) {
|
||||
// TestCircuitBreaker_DefaultConfig_Good verifies that DefaultCircuitBreakerConfig() returns sane defaults.
|
||||
// configuration := DefaultCircuitBreakerConfig()
|
||||
// configuration.FailureThreshold == 3
|
||||
func TestCircuitBreaker_DefaultConfig_Good(t *testing.T) {
|
||||
configuration := DefaultCircuitBreakerConfig()
|
||||
|
||||
if configuration.FailureThreshold != 3 {
|
||||
|
|
@ -21,7 +24,9 @@ func TestCircuitBreakerDefaultConfig(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerStateString(t *testing.T) {
|
||||
// TestCircuitBreaker_StateString_Good verifies all known CircuitState values stringify correctly.
|
||||
// CircuitClosed.String() == "closed"
|
||||
func TestCircuitBreaker_StateString_Good(t *testing.T) {
|
||||
tests := []struct {
|
||||
state CircuitState
|
||||
expected string
|
||||
|
|
@ -29,7 +34,6 @@ func TestCircuitBreakerStateString(t *testing.T) {
|
|||
{CircuitClosed, "closed"},
|
||||
{CircuitOpen, "open"},
|
||||
{CircuitHalfOpen, "half-open"},
|
||||
{CircuitState(99), "unknown"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -39,14 +43,24 @@ func TestCircuitBreakerStateString(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerClosed(t *testing.T) {
|
||||
// TestCircuitBreaker_StateString_Ugly verifies unknown CircuitState values stringify to "unknown".
|
||||
// CircuitState(99).String() == "unknown"
|
||||
func TestCircuitBreaker_StateString_Ugly(t *testing.T) {
|
||||
if got := CircuitState(99).String(); got != "unknown" {
|
||||
t.Errorf("expected 'unknown' for unknown state, got %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCircuitBreaker_Execute_Good verifies a closed circuit allows execution and returns results.
|
||||
// cb := NewCircuitBreaker("test", DefaultCircuitBreakerConfig())
|
||||
// result, err := cb.Execute(func() (interface{}, error) { return "success", nil })
|
||||
func TestCircuitBreaker_Execute_Good(t *testing.T) {
|
||||
cb := NewCircuitBreaker("test", DefaultCircuitBreakerConfig())
|
||||
|
||||
if cb.State() != CircuitClosed {
|
||||
t.Error("expected initial state to be closed")
|
||||
}
|
||||
|
||||
// Successful execution
|
||||
result, err := cb.Execute(func() (interface{}, error) {
|
||||
return "success", nil
|
||||
})
|
||||
|
|
@ -62,7 +76,9 @@ func TestCircuitBreakerClosed(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerOpensAfterFailures(t *testing.T) {
|
||||
// TestCircuitBreaker_Execute_Bad verifies that exceeding FailureThreshold opens the circuit.
|
||||
// cb.Execute(failingFn) × FailureThreshold → cb.State() == CircuitOpen
|
||||
func TestCircuitBreaker_Execute_Bad(t *testing.T) {
|
||||
configuration := CircuitBreakerConfig{
|
||||
FailureThreshold: 2,
|
||||
ResetTimeout: time.Minute,
|
||||
|
|
@ -72,7 +88,6 @@ func TestCircuitBreakerOpensAfterFailures(t *testing.T) {
|
|||
|
||||
testErr := errors.New("test error")
|
||||
|
||||
// First failure
|
||||
_, err := cb.Execute(func() (interface{}, error) {
|
||||
return nil, testErr
|
||||
})
|
||||
|
|
@ -83,7 +98,6 @@ func TestCircuitBreakerOpensAfterFailures(t *testing.T) {
|
|||
t.Error("should still be closed after 1 failure")
|
||||
}
|
||||
|
||||
// Second failure - should open circuit
|
||||
_, err = cb.Execute(func() (interface{}, error) {
|
||||
return nil, testErr
|
||||
})
|
||||
|
|
@ -95,16 +109,17 @@ func TestCircuitBreakerOpensAfterFailures(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerRejectsWhenOpen(t *testing.T) {
|
||||
// TestCircuitBreaker_Execute_Ugly verifies that an open circuit rejects calls with ErrCircuitOpen.
|
||||
// cb (open) → cb.Execute(fn) returns ErrCircuitOpen without calling fn
|
||||
func TestCircuitBreaker_Execute_Ugly(t *testing.T) {
|
||||
configuration := CircuitBreakerConfig{
|
||||
FailureThreshold: 1,
|
||||
ResetTimeout: time.Hour, // Long timeout to keep circuit open
|
||||
ResetTimeout: time.Hour,
|
||||
SuccessThreshold: 1,
|
||||
}
|
||||
cb := NewCircuitBreaker("test", configuration)
|
||||
|
||||
// Open the circuit
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
|
||||
|
|
@ -112,7 +127,6 @@ func TestCircuitBreakerRejectsWhenOpen(t *testing.T) {
|
|||
t.Fatal("circuit should be open")
|
||||
}
|
||||
|
||||
// Next request should be rejected
|
||||
called := false
|
||||
_, err := cb.Execute(func() (interface{}, error) {
|
||||
called = true
|
||||
|
|
@ -127,7 +141,10 @@ func TestCircuitBreakerRejectsWhenOpen(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerTransitionsToHalfOpen(t *testing.T) {
|
||||
// TestCircuitBreaker_HalfOpen_Good verifies that the circuit transitions from open to half-open after ResetTimeout,
|
||||
// and closes again on a successful probe.
|
||||
// cb (open) → sleep(ResetTimeout) → cb.Execute(successFn) → cb.State() == CircuitClosed
|
||||
func TestCircuitBreaker_HalfOpen_Good(t *testing.T) {
|
||||
configuration := CircuitBreakerConfig{
|
||||
FailureThreshold: 1,
|
||||
ResetTimeout: 50 * time.Millisecond,
|
||||
|
|
@ -135,8 +152,7 @@ func TestCircuitBreakerTransitionsToHalfOpen(t *testing.T) {
|
|||
}
|
||||
cb := NewCircuitBreaker("test", configuration)
|
||||
|
||||
// Open the circuit
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
|
||||
|
|
@ -144,10 +160,8 @@ func TestCircuitBreakerTransitionsToHalfOpen(t *testing.T) {
|
|||
t.Fatal("circuit should be open")
|
||||
}
|
||||
|
||||
// Wait for reset timeout
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Next request should transition to half-open and execute
|
||||
result, err := cb.Execute(func() (interface{}, error) {
|
||||
return "probe success", nil
|
||||
})
|
||||
|
|
@ -163,7 +177,9 @@ func TestCircuitBreakerTransitionsToHalfOpen(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerHalfOpenFailureReopens(t *testing.T) {
|
||||
// TestCircuitBreaker_HalfOpen_Bad verifies that a failed probe in half-open state reopens the circuit.
|
||||
// cb (half-open) → cb.Execute(failFn) → cb.State() == CircuitOpen
|
||||
func TestCircuitBreaker_HalfOpen_Bad(t *testing.T) {
|
||||
configuration := CircuitBreakerConfig{
|
||||
FailureThreshold: 1,
|
||||
ResetTimeout: 50 * time.Millisecond,
|
||||
|
|
@ -171,16 +187,13 @@ func TestCircuitBreakerHalfOpenFailureReopens(t *testing.T) {
|
|||
}
|
||||
cb := NewCircuitBreaker("test", configuration)
|
||||
|
||||
// Open the circuit
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
|
||||
// Wait for reset timeout
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Probe fails
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
return nil, errors.New("probe failed")
|
||||
})
|
||||
|
||||
|
|
@ -189,7 +202,9 @@ func TestCircuitBreakerHalfOpenFailureReopens(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerCaching(t *testing.T) {
|
||||
// TestCircuitBreaker_Caching_Good verifies that a successful result is cached and returned when the circuit is open.
|
||||
// cb.Execute(successFn) → opens circuit → cb.Execute(anyFn) returns cached value
|
||||
func TestCircuitBreaker_Caching_Good(t *testing.T) {
|
||||
configuration := CircuitBreakerConfig{
|
||||
FailureThreshold: 1,
|
||||
ResetTimeout: time.Hour,
|
||||
|
|
@ -197,7 +212,6 @@ func TestCircuitBreakerCaching(t *testing.T) {
|
|||
}
|
||||
cb := NewCircuitBreaker("test", configuration)
|
||||
|
||||
// Successful call - caches result
|
||||
result, err := cb.Execute(func() (interface{}, error) {
|
||||
return "cached value", nil
|
||||
})
|
||||
|
|
@ -208,12 +222,10 @@ func TestCircuitBreakerCaching(t *testing.T) {
|
|||
t.Fatalf("expected 'cached value', got %v", result)
|
||||
}
|
||||
|
||||
// Open the circuit
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
|
||||
// Should return cached value when circuit is open
|
||||
result, err = cb.Execute(func() (interface{}, error) {
|
||||
return "should not run", nil
|
||||
})
|
||||
|
|
@ -226,17 +238,17 @@ func TestCircuitBreakerCaching(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerGetCached(t *testing.T) {
|
||||
// TestCircuitBreaker_GetCached_Good verifies that GetCached returns the last successful result.
|
||||
// cb.Execute(successFn) → result, ok := cb.GetCached() → ok == true
|
||||
func TestCircuitBreaker_GetCached_Good(t *testing.T) {
|
||||
cb := NewCircuitBreaker("test", DefaultCircuitBreakerConfig())
|
||||
|
||||
// No cache initially
|
||||
_, ok := cb.GetCached()
|
||||
if ok {
|
||||
t.Error("expected no cached value initially")
|
||||
}
|
||||
|
||||
// Cache a value
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
return "test value", nil
|
||||
})
|
||||
|
||||
|
|
@ -249,7 +261,20 @@ func TestCircuitBreakerGetCached(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerReset(t *testing.T) {
|
||||
// TestCircuitBreaker_GetCached_Bad verifies that GetCached returns false when no execution has occurred.
|
||||
// cb := NewCircuitBreaker(...) → _, ok := cb.GetCached() → ok == false
|
||||
func TestCircuitBreaker_GetCached_Bad(t *testing.T) {
|
||||
cb := NewCircuitBreaker("test", DefaultCircuitBreakerConfig())
|
||||
|
||||
_, ok := cb.GetCached()
|
||||
if ok {
|
||||
t.Error("expected no cached value on fresh circuit breaker")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCircuitBreaker_Reset_Good verifies that Reset forces the circuit back to closed state.
|
||||
// cb (open) → cb.Reset() → cb.State() == CircuitClosed
|
||||
func TestCircuitBreaker_Reset_Good(t *testing.T) {
|
||||
configuration := CircuitBreakerConfig{
|
||||
FailureThreshold: 1,
|
||||
ResetTimeout: time.Hour,
|
||||
|
|
@ -257,8 +282,7 @@ func TestCircuitBreakerReset(t *testing.T) {
|
|||
}
|
||||
cb := NewCircuitBreaker("test", configuration)
|
||||
|
||||
// Open the circuit
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
|
||||
|
|
@ -266,7 +290,6 @@ func TestCircuitBreakerReset(t *testing.T) {
|
|||
t.Fatal("circuit should be open")
|
||||
}
|
||||
|
||||
// Manual reset
|
||||
cb.Reset()
|
||||
|
||||
if cb.State() != CircuitClosed {
|
||||
|
|
@ -274,7 +297,9 @@ func TestCircuitBreakerReset(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCircuitBreakerConcurrency(t *testing.T) {
|
||||
// TestCircuitBreaker_Concurrency_Ugly verifies no panics occur under concurrent execution with mixed success/failure.
|
||||
// 100 goroutines concurrently call cb.Execute — no race condition or panic should occur.
|
||||
func TestCircuitBreaker_Concurrency_Ugly(t *testing.T) {
|
||||
cb := NewCircuitBreaker("test", DefaultCircuitBreakerConfig())
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
|
@ -282,7 +307,7 @@ func TestCircuitBreakerConcurrency(t *testing.T) {
|
|||
wg.Add(1)
|
||||
go func(n int) {
|
||||
defer wg.Done()
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
if n%3 == 0 {
|
||||
return nil, errors.New("fail")
|
||||
}
|
||||
|
|
@ -292,11 +317,12 @@ func TestCircuitBreakerConcurrency(t *testing.T) {
|
|||
}
|
||||
wg.Wait()
|
||||
|
||||
// Just verify no panics occurred
|
||||
_ = cb.State()
|
||||
}
|
||||
|
||||
func TestGetGitHubCircuitBreaker(t *testing.T) {
|
||||
// TestCircuitBreaker_GitHubSingleton_Good verifies that getGitHubCircuitBreaker returns the same instance each call.
|
||||
// cb1 := getGitHubCircuitBreaker(); cb2 := getGitHubCircuitBreaker(); cb1 == cb2
|
||||
func TestCircuitBreaker_GitHubSingleton_Good(t *testing.T) {
|
||||
cb1 := getGitHubCircuitBreaker()
|
||||
cb2 := getGitHubCircuitBreaker()
|
||||
|
||||
|
|
@ -310,12 +336,13 @@ func TestGetGitHubCircuitBreaker(t *testing.T) {
|
|||
}
|
||||
|
||||
// Benchmark tests
|
||||
|
||||
func BenchmarkCircuitBreakerExecute(b *testing.B) {
|
||||
cb := NewCircuitBreaker("bench", DefaultCircuitBreakerConfig())
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
return "result", nil
|
||||
})
|
||||
}
|
||||
|
|
@ -326,7 +353,7 @@ func BenchmarkCircuitBreakerConcurrent(b *testing.B) {
|
|||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
cb.Execute(func() (interface{}, error) {
|
||||
cb.Execute(func() (interface{}, error) { //nolint:errcheck
|
||||
return "result", nil
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue