Cybersecurity-Projects/PROJECTS/advanced/monitor-the-situation-dashb.../backend/internal/ratelimit/limiter_test.go

66 lines
1.6 KiB
Go

// ©AngelaMos | 2026
// limiter_test.go
package ratelimit_test
import (
"context"
"errors"
"testing"
"time"
"github.com/sony/gobreaker/v2"
"github.com/stretchr/testify/require"
"golang.org/x/time/rate"
"github.com/carterperez-dev/monitor-the-situation/backend/internal/ratelimit"
)
func TestLimiter_AllowsAtNominalRate(t *testing.T) {
lim := ratelimit.New[string](ratelimit.Config{
Name: "test-allow",
Rate: rate.Every(20 * time.Millisecond),
Burst: 4,
ConsecutiveFailureBudget: 5,
BreakerTimeout: 100 * time.Millisecond,
})
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
calls := 0
for i := 0; i < 4; i++ {
_, err := lim.Do(ctx, func(_ context.Context) (string, error) {
calls++
return "ok", nil
})
require.NoError(t, err)
}
require.Equal(t, 4, calls)
}
func TestLimiter_OpensBreakerOnRepeatedFailures(t *testing.T) {
lim := ratelimit.New[string](ratelimit.Config{
Name: "test-fail",
Rate: rate.Every(2 * time.Millisecond),
Burst: 10,
ConsecutiveFailureBudget: 3,
BreakerTimeout: 50 * time.Millisecond,
})
ctx := context.Background()
failure := errors.New("upstream broken")
for i := 0; i < 3; i++ {
_, _ = lim.Do(ctx, func(_ context.Context) (string, error) { return "", failure })
}
called := false
_, err := lim.Do(ctx, func(_ context.Context) (string, error) {
called = true
return "", nil
})
require.False(t, called, "should not have called function — breaker should be open")
require.ErrorIs(t, err, gobreaker.ErrOpenState)
}