error-rules-default-thinking-tooluse.test.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { describe, expect, test, vi } from "vitest";
  2. // 该测试通过 mock 仓储层验证默认规则内容,不需要真实 DB/Redis。
  3. // 禁用 tests/setup.ts 中基于 DSN/Redis 的默认同步与清理协调,避免无关依赖引入。
  4. process.env.DSN = "";
  5. process.env.AUTO_CLEANUP_TEST_DATA = "false";
  6. const capturedInsertedRules: any[] = [];
  7. vi.mock("drizzle-orm", async (importOriginal) => {
  8. const actual = await importOriginal<typeof import("drizzle-orm")>();
  9. return {
  10. ...actual,
  11. // 仅用于构造查询条件参数,单测不关心其实现细节
  12. desc: vi.fn((...args: unknown[]) => ({ args, op: "desc" })),
  13. eq: vi.fn((...args: unknown[]) => ({ args, op: "eq" })),
  14. inArray: vi.fn((...args: unknown[]) => ({ args, op: "inArray" })),
  15. };
  16. });
  17. vi.mock("@/drizzle/schema", () => ({
  18. // 仅需提供被 syncDefaultErrorRules 用到的字段占位符
  19. errorRules: {
  20. id: "error_rules.id",
  21. pattern: "error_rules.pattern",
  22. isDefault: "error_rules.is_default",
  23. },
  24. }));
  25. vi.mock("@/drizzle/db", () => ({
  26. db: {
  27. transaction: vi.fn(async (fn: (tx: any) => Promise<void>) => {
  28. const tx = {
  29. query: {
  30. errorRules: {
  31. findMany: vi.fn(async () => []),
  32. },
  33. },
  34. delete: vi.fn(() => ({
  35. where: vi.fn(async () => []),
  36. })),
  37. insert: vi.fn(() => ({
  38. values: (rule: any) => {
  39. capturedInsertedRules.push(rule);
  40. return {
  41. onConflictDoNothing: () => ({
  42. returning: vi.fn(async () => [{ id: 1 }]),
  43. }),
  44. };
  45. },
  46. })),
  47. update: vi.fn(() => ({
  48. set: vi.fn(() => ({
  49. where: vi.fn(async () => []),
  50. })),
  51. })),
  52. };
  53. await fn(tx);
  54. }),
  55. },
  56. }));
  57. vi.mock("@/lib/emit-event", () => ({
  58. emitErrorRulesUpdated: vi.fn(async () => {}),
  59. }));
  60. vi.mock("@/lib/logger", () => ({
  61. logger: {
  62. info: vi.fn(),
  63. warn: vi.fn(),
  64. error: vi.fn(),
  65. trace: vi.fn(),
  66. },
  67. }));
  68. describe("syncDefaultErrorRules - 默认 thinking/tool_use 兜底规则", () => {
  69. test("应包含 must start with a thinking block 的默认规则,并提供可操作提示", async () => {
  70. capturedInsertedRules.length = 0;
  71. vi.resetModules();
  72. const { syncDefaultErrorRules } = await import("@/repository/error-rules");
  73. await syncDefaultErrorRules();
  74. const rule = capturedInsertedRules.find(
  75. (r) => r.pattern === "must start with a thinking block"
  76. );
  77. expect(rule).toBeTruthy();
  78. expect(rule.matchType).toBe("contains");
  79. expect(rule.category).toBe("thinking_error");
  80. // 覆写响应需为 Claude 错误格式,且包含清晰的自助修复建议
  81. expect(rule.overrideResponse?.type).toBe("error");
  82. expect(rule.overrideResponse?.error?.type).toBe("thinking_error");
  83. expect(rule.overrideResponse?.error?.message).toContain("tool_result");
  84. expect(rule.overrideResponse?.error?.message).toContain("signature");
  85. expect(rule.overrideResponse?.error?.message).toContain("关闭");
  86. });
  87. test("应包含 Expected thinking/redacted_thinking but found tool_use 的默认规则,并提供可操作提示", async () => {
  88. capturedInsertedRules.length = 0;
  89. vi.resetModules();
  90. const { syncDefaultErrorRules } = await import("@/repository/error-rules");
  91. await syncDefaultErrorRules();
  92. const rule = capturedInsertedRules.find(
  93. (r) =>
  94. typeof r?.pattern === "string" &&
  95. r.pattern.includes("redacted_thinking") &&
  96. r.pattern.includes("tool_use") &&
  97. r.pattern.toLowerCase().includes("expected")
  98. );
  99. expect(rule).toBeTruthy();
  100. expect(rule.matchType).toBe("regex");
  101. expect(rule.category).toBe("thinking_error");
  102. expect(rule.priority).toBe(68);
  103. // 覆写响应需为 Claude 错误格式,且包含清晰的自助修复建议
  104. expect(rule.overrideResponse?.type).toBe("error");
  105. expect(rule.overrideResponse?.error?.type).toBe("thinking_error");
  106. expect(rule.overrideResponse?.error?.message).toContain("tool_result");
  107. expect(rule.overrideResponse?.error?.message).toContain("signature");
  108. expect(rule.overrideResponse?.error?.message).toContain("关闭");
  109. });
  110. });