import { describe, it, expect } from "vitest"; import { classifyTaskError, translateTaskError, type TaskErrorCategory } from "./translateTaskError"; // 每条规则至少一个正例,按规则顺序排列(classifyTaskError 先匹配先返回)。 const RULE_CASES: Array<{ name: string; input: string; category: TaskErrorCategory }> = [ { name: "content policy", input: "content violated our policies", category: "content_policy" }, { name: "nsfw", input: "image flagged as nsfw", category: "content_policy" }, { name: "auth 401", input: "401 Unauthorized", category: "auth_failure" }, { name: "token expired", input: "token expired", category: "auth_failure" }, { name: "insufficient balance 402", input: "402 Payment Required", category: "insufficient_balance" }, { name: "余额不足", input: "余额不足", category: "insufficient_balance" }, { name: "concurrency pool full", input: "concurrency pool is full", category: "concurrency_busy" }, { name: "rate limit 429", input: "429 Too Many Requests", category: "concurrency_busy" }, { name: "unsupported model", input: "model not found", category: "unsupported_model" }, { name: "invalid asset", input: "invalid image format", category: "invalid_asset" }, { name: "network ECONNREFUSED", input: "fetch failed: ECONNREFUSED", category: "network_failure" }, { name: "timeout ETIMEDOUT", input: "ETIMEDOUT", category: "timeout" }, { name: "quota exceeded", input: "quota exceeded", category: "insufficient_balance" }, { name: "cancelled", input: "task was cancelled", category: "cancelled" }, { name: "已取消", input: "任务已取消", category: "cancelled" }, { name: "all providers failed", input: "all providers failed", category: "concurrency_busy" }, { name: "500 server error", input: "500 Internal Server Error", category: "network_failure" }, { name: "forbidden 403", input: "403 Forbidden", category: "auth_failure" }, { name: "aborted", input: "request aborted", category: "timeout" }, ]; describe("classifyTaskError rule coverage", () => { for (const { name, input, category } of RULE_CASES) { it(`classifies "${name}" as ${category}`, () => { const result = classifyTaskError(input); expect(result.category).toBe(category); expect(result.message).toBeTruthy(); expect(result.action).toBeTruthy(); }); } }); describe("classifyTaskError edge cases", () => { it("returns unknown for empty/null/undefined", () => { expect(classifyTaskError("").category).toBe("unknown"); expect(classifyTaskError(undefined).category).toBe("unknown"); expect(classifyTaskError(null).category).toBe("unknown"); }); it("returns the raw (truncated) message for unrecognized Chinese errors", () => { const result = classifyTaskError("这是一条未知的中文错误信息"); expect(result.category).toBe("unknown"); expect(result.message).toContain("未知"); expect(result.message).not.toContain("服务异常"); }); it("truncates long Chinese errors to 80 chars + ellipsis", () => { const long = "错误".repeat(50); const result = classifyTaskError(long); expect(result.message.endsWith("...")).toBe(true); expect(result.message.length).toBeLessThanOrEqual(83); }); it("returns generic service message for unrecognized English errors", () => { const result = classifyTaskError("something completely unexpected"); expect(result.category).toBe("unknown"); expect(result.message).toBe("服务异常,请稍后重试"); }); }); describe("classifyTaskError rule ordering (first match wins)", () => { it("content_policy beats auth_failure when both patterns present", () => { // "nsfw" appears before "401" in rule order const result = classifyTaskError("nsfw content with 401"); expect(result.category).toBe("content_policy"); }); }); describe("translateTaskError", () => { it("returns the message from classifyTaskError", () => { expect(translateTaskError("401")).toBe("登录已过期,请重新登录"); }); it("returns generic message for empty input", () => { expect(translateTaskError("")).toBe("任务失败,请重试"); }); });