package recon import ( "context" "testing" "time" "github.com/stretchr/testify/require" "golang.org/x/time/rate" ) func TestLimiterPerSourceIsolation(t *testing.T) { r := NewLimiterRegistry() a := r.For("a", 10, 1) b := r.For("b", 10, 1) require.NotNil(t, a) require.NotNil(t, b) require.NotSame(t, a, b, "different source names must yield distinct limiters") } func TestLimiterIdempotent(t *testing.T) { r := NewLimiterRegistry() a1 := r.For("a", 10, 1) a2 := r.For("a", 10, 1) require.Same(t, a1, a2, "repeat calls with same name must return same *rate.Limiter") } func TestWaitRespectsContext(t *testing.T) { r := NewLimiterRegistry() // Prime limiter with a very slow rate so Wait would block _ = r.For("slow", rate.Limit(0.001), 1) // Consume the single burst token so the next Wait actually blocks l := r.For("slow", rate.Limit(0.001), 1) require.True(t, l.Allow()) ctx, cancel := context.WithCancel(context.Background()) cancel() // pre-cancelled err := r.Wait(ctx, "slow", rate.Limit(0.001), 1, false) require.Error(t, err) } func TestJitterRange(t *testing.T) { r := NewLimiterRegistry() // High rate so only jitter meaningfully contributes start := time.Now() err := r.Wait(context.Background(), "fast", rate.Limit(1000), 100, true) elapsed := time.Since(start) require.NoError(t, err) require.GreaterOrEqual(t, elapsed, 90*time.Millisecond, "jitter should be >= 100ms (with slack)") require.LessOrEqual(t, elapsed, 1200*time.Millisecond, "jitter should be <= 1s (with slack)") } func TestJitterDisabled(t *testing.T) { r := NewLimiterRegistry() start := time.Now() err := r.Wait(context.Background(), "fast2", rate.Limit(1000), 100, false) elapsed := time.Since(start) require.NoError(t, err) require.Less(t, elapsed, 50*time.Millisecond, "no jitter when stealth=false") }