package apierror import ( "testing" "time" ) func TestErrorCategory_String(t *testing.T) { tests := []struct { cat ErrorCategory want string }{ {ErrUnknown, "unknown"}, {ErrAborted, "aborted"}, {ErrTimeout, "api_timeout"}, {ErrRateLimit, "rate_limit"}, {ErrOverloaded, "server_overload"}, {ErrPromptTooLong, "prompt_too_long"}, {ErrMediaTooLarge, "media_too_large"}, {ErrRequestTooLarge, "request_too_large"}, {ErrInvalidRequest, "invalid_request"}, {ErrAuthentication, "auth_error"}, {ErrModelNotFound, "model_not_found"}, {ErrBilling, "billing_error"}, {ErrServerError, "server_error"}, {ErrConnection, "connection_error"}, {ErrSSL, "ssl_cert_error"}, {ErrToolMismatch, "tool_use_mismatch"}, {ErrUnexpectedTool, "unexpected_tool_result"}, {ErrDuplicateToolID, "duplicate_tool_use_id"}, {ErrInvalidModel, "invalid_model"}, {ErrContentPolicy, "content_policy"}, } for _, tt := range tests { if got := tt.cat.String(); got != tt.want { t.Errorf("ErrorCategory(%d).String() = %q, want %q", tt.cat, got, tt.want) } } } func TestErrorCategory_String_Unknown(t *testing.T) { // 超出枚举范围的值应返回 "unknown" cat := ErrorCategory(9999) if got := cat.String(); got != "unknown" { t.Errorf("out-of-range = %q, want 'unknown'", got) } } func TestIsRetryableByDefault(t *testing.T) { tests := []struct { cat ErrorCategory retryable bool }{ // 默认可重试 {ErrTimeout, true}, {ErrOverloaded, true}, {ErrServerError, true}, {ErrConnection, true}, // 默认不可重试 {ErrUnknown, false}, {ErrAborted, false}, {ErrRateLimit, false}, // 有 retry-after 时才重试,默认不 {ErrPromptTooLong, false}, {ErrInvalidRequest, false}, {ErrAuthentication, false}, {ErrModelNotFound, false}, {ErrBilling, false}, {ErrSSL, false}, {ErrToolMismatch, false}, {ErrContentPolicy, false}, } for _, tt := range tests { if got := tt.cat.IsRetryableByDefault(); got != tt.retryable { t.Errorf("%q.IsRetryableByDefault() = %v, want %v", tt.cat.String(), got, tt.retryable) } } } func TestRetryInfo_Zero(t *testing.T) { // 零值应是安全的:不 retryable,无 delay var ri RetryInfo if ri.Retryable { t.Error("零值 RetryInfo.Retryable 应为 false") } if ri.After != 0 { t.Errorf("零值 After = %v", ri.After) } if ri.MaxRetries != 0 { t.Errorf("零值 MaxRetries = %d", ri.MaxRetries) } if ri.ServerSaid != nil { t.Errorf("零值 ServerSaid 应为 nil") } } func TestRetryInfo_WithValues(t *testing.T) { trueVal := true ri := RetryInfo{ Retryable: true, After: 5 * time.Second, MaxRetries: 3, ServerSaid: &trueVal, } if !ri.Retryable { t.Error("Retryable 应为 true") } if ri.After != 5*time.Second { t.Errorf("After = %v, want 5s", ri.After) } if ri.MaxRetries != 3 { t.Errorf("MaxRetries = %d", ri.MaxRetries) } if ri.ServerSaid == nil || !*ri.ServerSaid { t.Error("ServerSaid 应指向 true") } } func TestRetryInfo_ServerSaidFalse(t *testing.T) { // ServerSaid 区分 nil(未表态)和 false(明确拒绝重试) falseVal := false ri := RetryInfo{ ServerSaid: &falseVal, } if ri.ServerSaid == nil { t.Error("ServerSaid 应非 nil") } if *ri.ServerSaid { t.Error("ServerSaid 应为 false") } } // 所有枚举都有独立的 String 输出(不重复) func TestErrorCategory_UniqueStrings(t *testing.T) { seen := make(map[string]ErrorCategory) for cat := ErrUnknown; cat <= ErrContentPolicy; cat++ { s := cat.String() if existing, ok := seen[s]; ok { t.Errorf("重复的 String %q: cat %d 和 cat %d", s, existing, cat) } seen[s] = cat } // 验证至少有 20 个不同的分类 if len(seen) < 20 { t.Errorf("只找到 %d 个分类,预期 ≥ 20", len(seen)) } }