mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-06-05 23:03:48 +00:00
3aa83d4480
* 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
144 lines
5.6 KiB
Go
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")
|
|
}
|
|
}
|