summaryrefslogtreecommitdiff
path: root/internal/executor/ratelimit_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/executor/ratelimit_test.go')
-rw-r--r--internal/executor/ratelimit_test.go200
1 files changed, 0 insertions, 200 deletions
diff --git a/internal/executor/ratelimit_test.go b/internal/executor/ratelimit_test.go
deleted file mode 100644
index 1434810..0000000
--- a/internal/executor/ratelimit_test.go
+++ /dev/null
@@ -1,200 +0,0 @@
-package executor
-
-import (
- "context"
- "errors"
- "fmt"
- "testing"
- "time"
-)
-
-// --- isRateLimitError tests ---
-
-func TestIsRateLimitError_RateLimitMessage(t *testing.T) {
- err := errors.New("claude exited with error: rate limit exceeded")
- if !isRateLimitError(err) {
- t.Error("want true for 'rate limit exceeded', got false")
- }
-}
-
-func TestIsRateLimitError_TooManyRequests(t *testing.T) {
- err := errors.New("too many requests to the API")
- if !isRateLimitError(err) {
- t.Error("want true for 'too many requests', got false")
- }
-}
-
-func TestIsRateLimitError_HTTP429(t *testing.T) {
- err := errors.New("API returned status 429")
- if !isRateLimitError(err) {
- t.Error("want true for '429', got false")
- }
-}
-
-func TestIsRateLimitError_Overloaded(t *testing.T) {
- err := errors.New("API overloaded, please retry later")
- if !isRateLimitError(err) {
- t.Error("want true for 'overloaded', got false")
- }
-}
-
-func TestIsRateLimitError_NonRateLimitError(t *testing.T) {
- err := errors.New("claude exited with error: exit status 1")
- if isRateLimitError(err) {
- t.Error("want false for non-rate-limit error, got true")
- }
-}
-
-func TestIsRateLimitError_NilError(t *testing.T) {
- if isRateLimitError(nil) {
- t.Error("want false for nil error, got true")
- }
-}
-
-// --- parseRetryAfter tests ---
-
-func TestParseRetryAfter_RetryAfterSeconds(t *testing.T) {
- msg := "rate limit exceeded, retry after 30 seconds"
- d := parseRetryAfter(msg)
- if d != 30*time.Second {
- t.Errorf("want 30s, got %v", d)
- }
-}
-
-func TestParseRetryAfter_RetryAfterHeader(t *testing.T) {
- msg := "rate_limit_error: retry-after: 60"
- d := parseRetryAfter(msg)
- if d != 60*time.Second {
- t.Errorf("want 60s, got %v", d)
- }
-}
-
-func TestParseRetryAfter_NoRetryInfo(t *testing.T) {
- msg := "rate limit exceeded"
- d := parseRetryAfter(msg)
- if d != 0 {
- t.Errorf("want 0, got %v", d)
- }
-}
-
-// --- isQuotaExhausted tests ---
-
-func TestIsQuotaExhausted_GeminiDailyQuota(t *testing.T) {
- err := errors.New("container execution failed: exit status 1\nTerminalQuotaError: You have exhausted your daily quota on this model.")
- if !isQuotaExhausted(err) {
- t.Error("want true for Gemini TerminalQuotaError, got false")
- }
-}
-
-func TestIsQuotaExhausted_GeminiExhaustedMessage(t *testing.T) {
- err := errors.New("container execution failed: exit status 1\nyou have exhausted your daily quota")
- if !isQuotaExhausted(err) {
- t.Error("want true for 'exhausted your daily quota', got false")
- }
-}
-
-func TestIsQuotaExhausted_GeminiQuotaExceeded(t *testing.T) {
- err := errors.New("container execution failed: exit status 1\nQuota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests")
- if !isQuotaExhausted(err) {
- t.Error("want true for Gemini free tier quota exceeded, got false")
- }
-}
-
-func TestIsQuotaExhausted_NotQuota(t *testing.T) {
- err := errors.New("container execution failed: exit status 1")
- if isQuotaExhausted(err) {
- t.Error("want false for generic exit status 1, got true")
- }
-}
-
-// --- runWithBackoff tests ---
-
-func TestRunWithBackoff_SuccessOnFirstTry(t *testing.T) {
- calls := 0
- fn := func() error {
- calls++
- return nil
- }
- err := runWithBackoff(context.Background(), 3, time.Millisecond, fn)
- if err != nil {
- t.Errorf("want nil error, got %v", err)
- }
- if calls != 1 {
- t.Errorf("want 1 call, got %d", calls)
- }
-}
-
-func TestRunWithBackoff_RetriesOnRateLimit(t *testing.T) {
- calls := 0
- fn := func() error {
- calls++
- if calls < 3 {
- return fmt.Errorf("rate limit exceeded")
- }
- return nil
- }
- err := runWithBackoff(context.Background(), 3, time.Millisecond, fn)
- if err != nil {
- t.Errorf("want nil error, got %v", err)
- }
- if calls != 3 {
- t.Errorf("want 3 calls, got %d", calls)
- }
-}
-
-func TestRunWithBackoff_GivesUpAfterMaxRetries(t *testing.T) {
- calls := 0
- rateLimitErr := fmt.Errorf("rate limit exceeded")
- fn := func() error {
- calls++
- return rateLimitErr
- }
- err := runWithBackoff(context.Background(), 3, time.Millisecond, fn)
- if err == nil {
- t.Fatal("want error after max retries, got nil")
- }
- // maxRetries=3: 1 initial call + 3 retries = 4 total calls
- if calls != 4 {
- t.Errorf("want 4 calls (1 initial + 3 retries), got %d", calls)
- }
-}
-
-func TestRunWithBackoff_DoesNotRetryNonRateLimitError(t *testing.T) {
- calls := 0
- fn := func() error {
- calls++
- return fmt.Errorf("permission denied")
- }
- err := runWithBackoff(context.Background(), 3, time.Millisecond, fn)
- if err == nil {
- t.Fatal("want error, got nil")
- }
- if calls != 1 {
- t.Errorf("want 1 call (no retry for non-rate-limit), got %d", calls)
- }
-}
-
-func TestRunWithBackoff_ContextCancellation(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- calls := 0
-
- fn := func() error {
- calls++
- cancel() // cancel immediately after first call
- return fmt.Errorf("rate limit exceeded")
- }
-
- start := time.Now()
- err := runWithBackoff(ctx, 3, time.Second, fn) // large delay confirms ctx preempts wait
- elapsed := time.Since(start)
-
- if err == nil {
- t.Fatal("want error on context cancellation, got nil")
- }
- if elapsed > 500*time.Millisecond {
- t.Errorf("context cancellation too slow: %v (want < 500ms)", elapsed)
- }
- if calls != 1 {
- t.Errorf("want 1 call before cancellation, got %d", calls)
- }
-}