82 lines
4.1 KiB
TypeScript
82 lines
4.1 KiB
TypeScript
|
|
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("任务失败,请重试");
|
||
|
|
});
|
||
|
|
});
|