import { describe, expect, it, vi, beforeEach } from "vitest"; import { noteImportContention, emitResilience } from "@/lib/resilience-metrics"; import { extractMailMeta } from "@/services/imap/snapshot"; import { computeRawHash, sha256 } from "@/utils/hash"; vi.mock("@/lib/logger", () => ({ logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), }, })); import { logger } from "@/lib/logger"; describe("resilience metrics helpers", () => { beforeEach(() => { vi.clearAllMocks(); }); it("emitResilience writes stable metric name", () => { emitResilience("idempotency.hash_collision", "error", { a: 1 }); expect(logger.error).toHaveBeenCalledWith( expect.objectContaining({ metric: "idempotency.hash_collision", a: 1 }), "idempotency.hash_collision", ); }); it("noteImportContention fires on 4th hit", () => { const id = `mail-${Date.now()}`; for (let i = 0; i < 3; i++) noteImportContention(id); expect(logger.warn).not.toHaveBeenCalled(); noteImportContention(id); expect(logger.warn).toHaveBeenCalledWith( expect.objectContaining({ metric: "import.contention", mailId: id, count: 4, }), "import.contention", ); }); it("subject missing marked when empty subject", () => { const meta = extractMailMeta({ subject: " " } as never); expect(meta.subject).toBe("(无主题)"); expect(meta.subjectMissing).toBe(true); }); it("hash collision salt differs from base raw_hash", () => { const base = computeRawHash({ date: "2026-01-01T00:00:00.000Z", from: "a@b.com", subject: "x", filenames: ["a.xlsx"], }); const forked = sha256(`${base}|acc|INBOX|99`); expect(forked).not.toEqual(base); }); });