package ratelimit import ( "context" "sync" "testing" "time" ) func TestLimiterDisabled(t *testing.T) { for _, rpm := range []int{-1, 0} { l := New(rpm, 6) for i := 0; i < 10; i++ { if err := l.Wait(context.Background()); err != nil { t.Fatalf("New(%d).Wait() error = %v", rpm, err) } } } } func TestLimiterContextCancel(t *testing.T) { // burst 1 means strict spacing, so the second request must block. l := New(1, 1) if err := l.Wait(context.Background()); err != nil { t.Fatalf("first Wait() error = %v", err) } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond) defer cancel() if err := l.Wait(ctx); err == nil { t.Fatalf("expected context error") } } // TestLimiterAllowsBurst is the regression test for album rips appearing // serial: with the old strict-spacing limiter, max_connections workers could // not get their API calls through concurrently, so only one track downloaded // at a time. func TestLimiterAllowsBurst(t *testing.T) { const burst = 6 l := New(60, burst) // the shipped defaults start := time.Now() for i := 0; i < burst; i++ { if err := l.Wait(context.Background()); err != nil { t.Fatalf("Wait() %d error = %v", i, err) } } elapsed := time.Since(start) // 60 rpm is one token per second; the burst must not have waited. if elapsed > 200*time.Millisecond { t.Fatalf("%d burst requests took %v, want them to proceed immediately", burst, elapsed) } } // TestLimiterEnforcesSustainedRate checks the burst does not become a way to // exceed the configured rate: once the bucket is drained, requests are spaced // by the interval again. func TestLimiterEnforcesSustainedRate(t *testing.T) { // 6000 rpm = 10ms per token, so this stays fast. const burst = 3 l := New(6000, burst) ctx := context.Background() for i := 0; i < burst; i++ { if err := l.Wait(ctx); err != nil { t.Fatal(err) } } // The bucket is drained; the next two must each wait about one interval. start := time.Now() for i := 0; i < 2; i++ { if err := l.Wait(ctx); err != nil { t.Fatal(err) } } elapsed := time.Since(start) if elapsed < 15*time.Millisecond { t.Fatalf("post-burst requests took %v, want them throttled to ~10ms each", elapsed) } } // TestLimiterBurstCappedByRate stops a large max_connections from letting a // deliberately tiny rate through in one go. func TestLimiterBurstCappedByRate(t *testing.T) { l := New(2, 64) if l.burst != 2 { t.Fatalf("burst = %d, want it clamped to the rate (2)", l.burst) } } func TestLimiterConcurrentCallersShareTheRate(t *testing.T) { const burst = 8 l := New(6000, burst) var wg sync.WaitGroup start := time.Now() for i := 0; i < burst; i++ { wg.Add(1) go func() { defer wg.Done() if err := l.Wait(context.Background()); err != nil { t.Errorf("Wait() error = %v", err) } }() } wg.Wait() // All within the burst, so none should have blocked meaningfully. if elapsed := time.Since(start); elapsed > 200*time.Millisecond { t.Fatalf("%d concurrent burst requests took %v", burst, elapsed) } }