Files
goclaw/internal/http/packages_rate_limiter_test.go
viettranx 860bfff38f chore: minor formatting and cleanup across config, audio, cache, http, and provider modules
- config: align struct tag formatting in TelemetryConfig for readability
- audio tests: modernize test patterns
- cache, http, hooks, providers: small style and cleanup passes
- No functional changes
2026-04-23 19:16:12 +07:00

77 lines
2.0 KiB
Go

package http
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestPerKeyRateLimiter_AllowThenBlock(t *testing.T) {
rl := newPerKeyRateLimiter(60, 2) // 1 rps, burst 2
// First two requests for key A succeed (burst).
for i := range 2 {
if !rl.Allow("A") {
t.Fatalf("request %d should be allowed", i)
}
}
// Third immediate request should be blocked.
if rl.Allow("A") {
t.Error("third immediate request should be rate-limited")
}
// Independent key is unaffected.
if !rl.Allow("B") {
t.Error("different key should not be rate-limited")
}
}
func TestPerKeyRateLimiter_Disabled(t *testing.T) {
rl := newPerKeyRateLimiter(0, 5)
for i := range 100 {
if !rl.Allow("x") {
t.Fatalf("disabled limiter should always allow (i=%d)", i)
}
}
}
func TestRateLimitKeyFromRequest(t *testing.T) {
// Authenticated user: prefix with uid:.
r := httptest.NewRequest(http.MethodGet, "/x", nil)
r.Header.Set("X-GoClaw-User-Id", "alice")
if got := rateLimitKeyFromRequest(r); got != "uid:alice" {
t.Errorf("want uid:alice, got %s", got)
}
// Anonymous: fall back to IP.
r2 := httptest.NewRequest(http.MethodGet, "/x", nil)
r2.RemoteAddr = "203.0.113.5:54321"
if got := rateLimitKeyFromRequest(r2); got != "ip:203.0.113.5" {
t.Errorf("want ip:203.0.113.5, got %s", got)
}
}
func TestEnforceGitHubReleasesLimit_Writes429(t *testing.T) {
// Swap the package-level limiter for a tight one, restore after.
prev := githubReleasesLimiter
githubReleasesLimiter = newPerKeyRateLimiter(60, 1) // burst 1
defer func() { githubReleasesLimiter = prev }()
r := httptest.NewRequest(http.MethodGet, "/x", nil)
r.Header.Set("X-GoClaw-User-Id", "bob")
w1 := httptest.NewRecorder()
if !enforceGitHubReleasesLimit(w1, r) {
t.Fatal("first call should pass")
}
w2 := httptest.NewRecorder()
if enforceGitHubReleasesLimit(w2, r) {
t.Fatal("second call should be throttled")
}
if w2.Code != http.StatusTooManyRequests {
t.Errorf("want 429, got %d", w2.Code)
}
if w2.Header().Get("Retry-After") == "" {
t.Error("Retry-After header missing")
}
}