package executor import ( "net/http" "strconv" "testing" "time") func TestParseCodexRetryAfter(t *testing.T) { now := time.Unix(1_700_000_000, 0) t.Run("resets_in_seconds", func(t *testing.T) { body := []byte(`{"error":{"type":"usage_limit_reached","resets_in_seconds":123}}`) retryAfter := parseCodexRetryAfter(http.StatusTooManyRequests, body, now) if retryAfter == nil { t.Fatalf("expected retryAfter, got nil") } if *retryAfter != 123*time.Second { t.Fatalf("retryAfter = %v, want %v", *retryAfter, 123*time.Second) } }) t.Run("prefers resets_at", func(t *testing.T) { resetAt := now.Add(5 * time.Minute).Unix() body := []byte(`{"error":{"type":"usage_limit_reached","resets_at":` + itoa(resetAt) + `,"resets_in_seconds":1}}`) retryAfter := parseCodexRetryAfter(http.StatusTooManyRequests, body, now) if retryAfter == nil { t.Fatalf("expected retryAfter, got nil") } if *retryAfter != 5*time.Minute { t.Fatalf("retryAfter = %v, want %v", *retryAfter, 5*time.Minute) } }) t.Run("fallback when resets_at is past", func(t *testing.T) { resetAt := now.Add(-1 * time.Minute).Unix() body := []byte(`{"error":{"type":"usage_limit_reached","resets_at":` + itoa(resetAt) + `,"resets_in_seconds":77}}`) retryAfter := parseCodexRetryAfter(http.StatusTooManyRequests, body, now) if retryAfter == nil { t.Fatalf("expected retryAfter, got nil") } if *retryAfter != 77*time.Second { t.Fatalf("retryAfter = %v, want %v", *retryAfter, 77*time.Second) } }) t.Run("non-429 status code", func(t *testing.T) { body := []byte(`{"error":{"type":"usage_limit_reached","resets_in_seconds":30}}`) if got := parseCodexRetryAfter(http.StatusBadRequest, body, now); got != nil { t.Fatalf("expected nil for non-429, got %v", *got) } }) t.Run("non usage_limit_reached error type", func(t *testing.T) { body := []byte(`{"error":{"type":"server_error","resets_in_seconds":30}}`) if got := parseCodexRetryAfter(http.StatusTooManyRequests, body, now); got != nil { t.Fatalf("expected nil for non-usage_limit_reached, got %v", *got) } })} func itoa(v int64) string { return strconv.FormatInt(v, 10)}
package executor
import (
"net/http"
"strconv"
"testing"
"time"
)
func TestParseCodexRetryAfter(t *testing.T) {
now := time.Unix(1_700_000_000, 0)
t.Run("resets_in_seconds", func(t *testing.T) {
body := []byte(`{"error":{"type":"usage_limit_reached","resets_in_seconds":123}}`)
retryAfter := parseCodexRetryAfter(http.StatusTooManyRequests, body, now)
if retryAfter == nil {
t.Fatalf("expected retryAfter, got nil")
}
if *retryAfter != 123*time.Second {
t.Fatalf("retryAfter = %v, want %v", *retryAfter, 123*time.Second)
}
})
t.Run("prefers resets_at", func(t *testing.T) {
resetAt := now.Add(5 * time.Minute).Unix()
body := []byte(`{"error":{"type":"usage_limit_reached","resets_at":` + itoa(resetAt) + `,"resets_in_seconds":1}}`)
retryAfter := parseCodexRetryAfter(http.StatusTooManyRequests, body, now)
if retryAfter == nil {
t.Fatalf("expected retryAfter, got nil")
}
if *retryAfter != 5*time.Minute {
t.Fatalf("retryAfter = %v, want %v", *retryAfter, 5*time.Minute)
}
})
t.Run("fallback when resets_at is past", func(t *testing.T) {
resetAt := now.Add(-1 * time.Minute).Unix()
body := []byte(`{"error":{"type":"usage_limit_reached","resets_at":` + itoa(resetAt) + `,"resets_in_seconds":77}}`)
retryAfter := parseCodexRetryAfter(http.StatusTooManyRequests, body, now)
if retryAfter == nil {
t.Fatalf("expected retryAfter, got nil")
}
if *retryAfter != 77*time.Second {
t.Fatalf("retryAfter = %v, want %v", *retryAfter, 77*time.Second)
}
})
t.Run("non-429 status code", func(t *testing.T) {
body := []byte(`{"error":{"type":"usage_limit_reached","resets_in_seconds":30}}`)
if got := parseCodexRetryAfter(http.StatusBadRequest, body, now); got != nil {
t.Fatalf("expected nil for non-429, got %v", *got)
}
})
t.Run("non usage_limit_reached error type", func(t *testing.T) {
body := []byte(`{"error":{"type":"server_error","resets_in_seconds":30}}`)
if got := parseCodexRetryAfter(http.StatusTooManyRequests, body, now); got != nil {
t.Fatalf("expected nil for non-usage_limit_reached, got %v", *got)
}
})
}
func itoa(v int64) string {
return strconv.FormatInt(v, 10)
}