Files
yellowjacket/backend/explore/ratelimiter_test.go
T
yonlu 8fc075c24a perf(S01/T01): Add token-bucket rate limiter (1 req/sec), SQLite respon…
- backend/explore/ratelimiter.go
- backend/explore/cache.go
- backend/database/sql/schemas/explore_cache.sql
- backend/database/database.go
2026-03-23 07:55:25 -04:00

63 lines
1.2 KiB
Go

package explore
import (
"context"
"errors"
"testing"
"time"
)
func TestRateLimiterBurst(t *testing.T) {
rl := NewRateLimiter()
ctx := context.Background()
const n = 5
start := time.Now()
for i := range n {
if err := rl.Wait(ctx); err != nil {
t.Fatalf("Wait %d: %v", i, err)
}
}
elapsed := time.Since(start)
// First request is immediate; 4 more at 1/sec = ≥4s total.
if elapsed < 4*time.Second {
t.Errorf(
"elapsed %v, want ≥ 4s (rate limiter too fast)", elapsed,
)
}
// Generous upper bound to avoid CI flakes.
if elapsed > 7*time.Second {
t.Errorf(
"elapsed %v, want ≤ 7s (rate limiter too slow)", elapsed,
)
}
}
func TestRateLimiterContextCancel(t *testing.T) {
t.Parallel()
rl := NewRateLimiter()
// Drain the initial token so the next Wait must block.
if err := rl.Wait(context.Background()); err != nil {
t.Fatalf("drain token: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
cancel() // cancel immediately
err := rl.Wait(ctx)
if err == nil {
t.Fatal("expected error from cancelled context, got nil")
}
if !errors.Is(err, context.Canceled) {
t.Errorf("error = %v, want context.Canceled", err)
}
}