diff options
Diffstat (limited to 'internal/executor/ratelimit_test.go')
| -rw-r--r-- | internal/executor/ratelimit_test.go | 200 |
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) - } -} |
