Something went wrong. Try again.
sloproxy
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308package helps
import ( "net/http" "strconv" "testing" "time")
func TestParseClaudeRateLimitReset_AllCases(t *testing.T) { now := time.Now()
t.Run("nil headers returns nil", func(t *testing.T) { if got := ParseClaudeRateLimitReset(nil, now); got != nil { t.Fatalf("expected nil, got %v", got) } })
t.Run("empty headers returns nil", func(t *testing.T) { h := make(http.Header) if got := ParseClaudeRateLimitReset(h, now); got != nil { t.Fatalf("expected nil, got %v", got) } })
t.Run("retry-after only seconds", func(t *testing.T) { h := make(http.Header) h.Set("Retry-After", "60") got := parseClaudeRateLimitResetWithFuzz(h, now, 0, 0) if got == nil { t.Fatal("expected non-nil RetryAfter") } if *got != 60*time.Second { t.Fatalf("expected 60s, got %v", *got) } })
t.Run("retry-after HTTP date", func(t *testing.T) { h := make(http.Header) futureTime := now.Add(90 * time.Second).UTC().Truncate(time.Second) h.Set("Retry-After", futureTime.Format(http.TimeFormat)) got := parseClaudeRateLimitResetWithFuzz(h, now, 0, 0) if got == nil { t.Fatal("expected non-nil RetryAfter") } if *got < 89*time.Second || *got > 91*time.Second { t.Fatalf("expected ~90s, got %v", *got) } })
t.Run("5h rejected and 7d allowed with unified reset", func(t *testing.T) { h := make(http.Header) // Missing Anthropic-Ratelimit-Unified-Status, 5h is rejected, 7d is allowed h.Set("Anthropic-Ratelimit-Unified-5h-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-5h-Reset", strconv.FormatInt(now.Add(5*time.Hour).Unix(), 10)) h.Set("Anthropic-Ratelimit-Unified-7d-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-7d-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10)) h.Set("Anthropic-Ratelimit-Unified-Reset", strconv.FormatInt(now.Add(5*time.Hour).Unix(), 10))
got := parseClaudeRateLimitResetWithFuzz(h, now, 0, 0) if got == nil { t.Fatal("expected non-nil RetryAfter") } if *got < 5*time.Hour-5*time.Second || *got > 5*time.Hour+5*time.Second { t.Fatalf("expected ~5h, got %v", *got) } })
t.Run("7d rejected and 5h allowed", func(t *testing.T) { h := make(http.Header) h.Set("Anthropic-Ratelimit-Unified-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-5h-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-5h-Reset", strconv.FormatInt(now.Add(5*time.Hour).Unix(), 10)) h.Set("Anthropic-Ratelimit-Unified-7d-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-7d-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10))
got := parseClaudeRateLimitResetWithFuzz(h, now, 0, 0) if got == nil { t.Fatal("expected non-nil RetryAfter") } if *got < 7*24*time.Hour-5*time.Second || *got > 7*24*time.Hour+5*time.Second { t.Fatalf("expected ~7d, got %v", *got) } })
t.Run("both 5h and 7d rejected chooses longest", func(t *testing.T) { h := make(http.Header) h.Set("Anthropic-Ratelimit-Unified-5h-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-5h-Reset", strconv.FormatInt(now.Add(5*time.Hour).Unix(), 10)) h.Set("Anthropic-Ratelimit-Unified-7d-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-7d-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10))
got := parseClaudeRateLimitResetWithFuzz(h, now, 0, 0) if got == nil { t.Fatal("expected non-nil RetryAfter") } if *got < 7*24*time.Hour-5*time.Second || *got > 7*24*time.Hour+5*time.Second { t.Fatalf("expected ~7d, got %v", *got) } })
t.Run("all allowed returns nil", func(t *testing.T) { h := make(http.Header) h.Set("Anthropic-Ratelimit-Unified-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-5h-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-5h-Reset", strconv.FormatInt(now.Add(5*time.Hour).Unix(), 10)) h.Set("Anthropic-Ratelimit-Unified-7d-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-7d-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10))
got := ParseClaudeRateLimitReset(h, now) if got != nil { t.Fatalf("expected nil for allowed status, got %v", got) } })
t.Run("fable-only rejection with 7d_oi reset and retry-after uses retry-after only", func(t *testing.T) { h := make(http.Header) h.Set("Anthropic-Ratelimit-Unified-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-5h-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-7d-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-7d_oi-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-7d_oi-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10)) h.Set("Anthropic-Ratelimit-Unified-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10)) h.Set("Retry-After", "60")
got := parseClaudeRateLimitResetWithFuzz(h, now, 0, 0) if got == nil { t.Fatal("expected non-nil RetryAfter") } if *got != 60*time.Second { t.Fatalf("expected 60s from Retry-After, got %v", *got) } })
t.Run("fable-only rejection with 7d_oi reset only returns nil for exponential backoff", func(t *testing.T) { h := make(http.Header) h.Set("Anthropic-Ratelimit-Unified-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-5h-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-7d-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-7d_oi-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-7d_oi-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10)) h.Set("Anthropic-Ratelimit-Unified-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10))
got := ParseClaudeRateLimitReset(h, now) if got != nil { t.Fatalf("expected nil for fable-only rejection without retry-after, got %v", *got) } })
t.Run("fable-only rejection with allowed_warning on shared window uses retry-after only", func(t *testing.T) { h := make(http.Header) h.Set("Anthropic-Ratelimit-Unified-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-5h-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-7d-Status", "allowed_warning") h.Set("Anthropic-Ratelimit-Unified-7d_oi-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-7d_oi-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10)) h.Set("Anthropic-Ratelimit-Unified-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10)) h.Set("Retry-After", "60")
got := parseClaudeRateLimitResetWithFuzz(h, now, 0, 0) if got == nil { t.Fatal("expected non-nil RetryAfter") } if *got != 60*time.Second { t.Fatalf("expected 60s from Retry-After, got %v", *got) } })
t.Run("missing unified status with allowed_warning shared windows ignores unified reset and uses retry-after", func(t *testing.T) { h := make(http.Header) // Missing Anthropic-Ratelimit-Unified-Status, shared windows are allowed_warning h.Set("Anthropic-Ratelimit-Unified-5h-Status", "allowed_warning") h.Set("Anthropic-Ratelimit-Unified-7d-Status", "allowed_warning") h.Set("Anthropic-Ratelimit-Unified-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10)) h.Set("Retry-After", "60")
got := parseClaudeRateLimitResetWithFuzz(h, now, 0, 0) if got == nil { t.Fatal("expected non-nil RetryAfter") } if *got != 60*time.Second { t.Fatalf("expected 60s from Retry-After, got %v", *got) } })
t.Run("non-fable combined rejection with 7d_oi reset keeps longer duration", func(t *testing.T) { h := make(http.Header) h.Set("Anthropic-Ratelimit-Unified-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-5h-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-5h-Reset", strconv.FormatInt(now.Add(5*time.Hour).Unix(), 10)) h.Set("Anthropic-Ratelimit-Unified-7d-Status", "allowed") h.Set("Anthropic-Ratelimit-Unified-7d_oi-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-7d_oi-Reset", strconv.FormatInt(now.Add(7*24*time.Hour).Unix(), 10))
got := parseClaudeRateLimitResetWithFuzz(h, now, 0, 0) if got == nil { t.Fatal("expected non-nil RetryAfter") } if *got < 7*24*time.Hour-5*time.Second || *got > 7*24*time.Hour+5*time.Second { t.Fatalf("expected ~7d, got %v", *got) } })
t.Run("past timestamp returns nil", func(t *testing.T) { h := make(http.Header) h.Set("Anthropic-Ratelimit-Unified-5h-Status", "rejected") h.Set("Anthropic-Ratelimit-Unified-5h-Reset", strconv.FormatInt(now.Add(-5*time.Hour).Unix(), 10))
got := ParseClaudeRateLimitReset(h, now) if got != nil { t.Fatalf("expected nil for past reset, got %v", got) } })
t.Run("fuzz is bounded and non-negative", func(t *testing.T) { h := make(http.Header) h.Set("Retry-After", "100") for i := 0; i < 50; i++ { got := ParseClaudeRateLimitReset(h, now) if got == nil { t.Fatal("expected non-nil") } diff := *got - 100*time.Second if diff < 1*time.Second || diff > 30*time.Second { t.Fatalf("fuzz %v out of bounds [1s, 30s]", diff) } } })}
func TestClaudeHeadersIndicateUnifiedRateLimitRejection_AllowedWarning(t *testing.T) { tests := []struct { name string headers http.Header expected bool }{ { name: "both shared windows allowed, 7d_oi rejected is fable-only", headers: http.Header{ "Anthropic-Ratelimit-Unified-Status": []string{"rejected"}, "Anthropic-Ratelimit-Unified-5h-Status": []string{"allowed"}, "Anthropic-Ratelimit-Unified-7d-Status": []string{"allowed"}, "Anthropic-Ratelimit-Unified-7d_oi-Status": []string{"rejected"}, }, expected: false, }, { name: "7d allowed_warning and 5h allowed with 7d_oi rejected is fable-only", headers: http.Header{ "Anthropic-Ratelimit-Unified-Status": []string{"rejected"}, "Anthropic-Ratelimit-Unified-5h-Status": []string{"allowed"}, "Anthropic-Ratelimit-Unified-7d-Status": []string{"allowed_warning"}, "Anthropic-Ratelimit-Unified-7d_oi-Status": []string{"rejected"}, }, expected: false, }, { name: "5h allowed_warning and 7d allowed with 7d_oi rejected is fable-only", headers: http.Header{ "Anthropic-Ratelimit-Unified-Status": []string{"rejected"}, "Anthropic-Ratelimit-Unified-5h-Status": []string{"allowed_warning"}, "Anthropic-Ratelimit-Unified-7d-Status": []string{"allowed"}, "Anthropic-Ratelimit-Unified-7d_oi-Status": []string{"rejected"}, }, expected: false, }, { name: "both shared windows allowed_warning with 7d_oi rejected is fable-only", headers: http.Header{ "Anthropic-Ratelimit-Unified-Status": []string{"rejected"}, "Anthropic-Ratelimit-Unified-5h-Status": []string{"allowed_warning"}, "Anthropic-Ratelimit-Unified-7d-Status": []string{"allowed_warning"}, "Anthropic-Ratelimit-Unified-7d_oi-Status": []string{"rejected"}, }, expected: false, }, { name: "5h rejected even if 7d allowed_warning is unified rejection", headers: http.Header{ "Anthropic-Ratelimit-Unified-Status": []string{"rejected"}, "Anthropic-Ratelimit-Unified-5h-Status": []string{"rejected"}, "Anthropic-Ratelimit-Unified-7d-Status": []string{"allowed_warning"}, "Anthropic-Ratelimit-Unified-7d_oi-Status": []string{"rejected"}, }, expected: true, }, { name: "7d rejected even if 5h allowed_warning is unified rejection", headers: http.Header{ "Anthropic-Ratelimit-Unified-Status": []string{"rejected"}, "Anthropic-Ratelimit-Unified-5h-Status": []string{"allowed_warning"}, "Anthropic-Ratelimit-Unified-7d-Status": []string{"rejected"}, "Anthropic-Ratelimit-Unified-7d_oi-Status": []string{"rejected"}, }, expected: true, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ClaudeHeadersIndicateUnifiedRateLimitRejection(tt.headers) if got != tt.expected { t.Fatalf("ClaudeHeadersIndicateUnifiedRateLimitRejection() = %v, want %v", got, tt.expected) } }) }}