Files
graphql-monitoring-proxy/circuit_breaker_state_test.go
T
lukaszraczylo 3aa83d4480 chore(security,refactor): extract sanitization and improve code quality (#41)
* chore(security,refactor): extract sanitization and improve code quality

- [x] Extract sanitization functions to dedicated sanitization.go module
- [x] Add comprehensive golangci-lint v2 configuration with security rules
- [x] Replace interface{} with any type throughout codebase
- [x] Add admin API authentication security warning
- [x] Extract WebSocket and stats streaming constants
- [x] Add best-effort error handling comments for resource cleanup
- [x] Expand sensitive field patterns for improved PII redaction
- [x] Simplify safety checks and remove redundant nil validations
- [x] Improve test coverage for password field redaction patterns

* refactor: replace interface{} with any type alias

- [x] Replace all `map[string]interface{}` with `map[string]any`
- [x] Replace all `interface{}` with `any` in function signatures and type definitions
- [x] Update sync.Pool New function returns from `interface{}` to `any`
- [x] Add package documentation comments to 8 package files
- [x] Update type assertions and casts to work with `any` type
2026-01-17 00:04:12 +00:00

144 lines
5.6 KiB
Go

package main
import (
"errors"
"time"
"github.com/sony/gobreaker"
"github.com/stretchr/testify/assert"
)
// TestCircuitBreakerStateTransitions tests the circuit breaker state transitions:
// Closed -> Open -> Half-Open -> Closed/Open
func (suite *CircuitBreakerTestSuite) TestCircuitBreakerStateTransitions() {
// Reset the buffer before the test
suite.outputBuffer.Reset()
// Initialize circuit breaker with a shorter timeout for testing
cfg.CircuitBreaker.Timeout = 1 // 1 second timeout to half-open state
cfg.CircuitBreaker.MaxFailures = 3
initCircuitBreaker(cfg)
// 1. Initially the circuit should be closed
assert.Equal(suite.T(), gobreaker.StateClosed.String(), cb.State().String(), "Circuit should start in closed state")
// 2. Generate failures to trip the circuit
testErr := errors.New("test error")
for i := 0; i < cfg.CircuitBreaker.MaxFailures; i++ {
_, err := cb.Execute(func() (any, error) {
return nil, testErr
})
assert.Error(suite.T(), err, "Execute should return error")
}
// 3. Circuit should now be open
assert.Equal(suite.T(), gobreaker.StateOpen.String(), cb.State().String(), "Circuit should transition to open state after failures")
// Verify that requests are rejected during open state
_, err := cb.Execute(func() (any, error) {
return "success", nil
})
assert.Equal(suite.T(), gobreaker.ErrOpenState.Error(), err.Error(), "Should return ErrOpenState when circuit is open")
// Verify that the state change was logged
assert.True(suite.T(), suite.logContains("Circuit breaker state changed"),
"State change should be logged")
assert.True(suite.T(), suite.logContains(`"from":"closed"`),
"Log should mention transition from closed state")
assert.True(suite.T(), suite.logContains(`"to":"open"`),
"Log should mention transition to open state")
// 4. Wait for timeout to allow transition to half-open
time.Sleep(time.Duration(cfg.CircuitBreaker.Timeout+1) * time.Second)
// The next request should transition the circuit to half-open
// (Sony's gobreaker transitions to half-open on the next request after timeout)
tmpState := cb.State()
// Execute a successful request to check state
_, _ = cb.Execute(func() (any, error) {
return "success", nil
})
// 5. Verify half-open state was reached
suite.T().Logf("Current circuit state: %s", cb.State())
if tmpState.String() != gobreaker.StateHalfOpen.String() {
suite.T().Skip("Circuit didn't transition to half-open as expected, likely due to timing issues in test environment")
}
// Verify the state change was logged
assert.True(suite.T(), suite.logContains(`"from":"open"`),
"Log should mention transition from open state")
assert.True(suite.T(), suite.logContains(`"to":"half-open"`),
"Log should mention transition to half-open state")
// 6. Execute successful requests in half-open state to transition back to closed
for i := 0; i < cfg.CircuitBreaker.MaxRequestsInHalfOpen; i++ {
_, err = cb.Execute(func() (any, error) {
return "success", nil
})
assert.NoError(suite.T(), err, "Execute should not return error")
}
// 7. Circuit should now be closed again
assert.Equal(suite.T(), gobreaker.StateClosed.String(), cb.State().String(), "Circuit should transition to closed state after successes")
// Verify the final state change was logged
assert.True(suite.T(), suite.logContains(`"from":"half-open"`),
"Log should mention transition from half-open state")
assert.True(suite.T(), suite.logContains(`"to":"closed"`),
"Log should mention transition to closed state")
}
// TestCircuitBreakerHalfOpenToOpen tests that the circuit transitions from half-open to open
// when failures occur during half-open state
func (suite *CircuitBreakerTestSuite) TestCircuitBreakerHalfOpenToOpen() {
// Reset the buffer before the test
suite.outputBuffer.Reset()
// Initialize circuit breaker with a shorter timeout for testing
cfg.CircuitBreaker.Timeout = 1 // 1 second timeout to half-open state
cfg.CircuitBreaker.MaxFailures = 3
cfg.CircuitBreaker.MaxRequestsInHalfOpen = 2
initCircuitBreaker(cfg)
// 1. Generate failures to trip the circuit
testErr := errors.New("test error")
for i := 0; i < cfg.CircuitBreaker.MaxFailures; i++ {
_, err := cb.Execute(func() (any, error) {
return nil, testErr
})
assert.Error(suite.T(), err, "Execute should return error")
}
// 2. Circuit should now be open
assert.Equal(suite.T(), gobreaker.StateOpen.String(), cb.State().String(), "Circuit should be open after failures")
// 3. Wait for timeout to allow transition to half-open
time.Sleep(time.Duration(cfg.CircuitBreaker.Timeout+1) * time.Second)
// The next request should transition the circuit to half-open
tmpState := cb.State()
// Try a request that will fail
_, _ = cb.Execute(func() (any, error) {
return nil, testErr
})
// 4. If we successfully reached half-open state, verify it transitions back to open after failure
if tmpState.String() == gobreaker.StateHalfOpen.String() {
assert.Equal(suite.T(), gobreaker.StateOpen.String(), cb.State().String(),
"Circuit should transition back to open state after failure in half-open")
// Verify the state changes were logged
assert.True(suite.T(), suite.logContains(`"from":"open"`),
"Log should mention transition from open state")
assert.True(suite.T(), suite.logContains(`"to":"half-open"`),
"Log should mention transition to half-open state")
assert.True(suite.T(), suite.logContains(`"from":"half-open"`),
"Log should mention transition from half-open state")
assert.True(suite.T(), suite.logContains(`"to":"open"`),
"Log should mention transition back to open state")
} else {
suite.T().Skip("Circuit didn't transition to half-open as expected, likely due to timing issues in test environment")
}
}