import { beforeEach, describe, expect, it, vi } from "vitest"; import { checkIdempotency, saveIdempotency, } from "@/modules/quote/idempotency"; vi.mock("@/modules/cache/redis-cache", () => ({ getL1: vi.fn(), setL1: vi.fn(), })); vi.mock("@/lib/prisma", () => ({ prisma: { idempotencyRecord: { findUnique: vi.fn(), create: vi.fn(), }, quoteRecord: { findUnique: vi.fn(), }, }, })); import { getL1, setL1 } from "@/modules/cache/redis-cache"; import { prisma } from "@/lib/prisma"; const mockedGetL1 = vi.mocked(getL1); const mockedSetL1 = vi.mocked(setL1); const mockedFindUnique = vi.mocked(prisma.idempotencyRecord.findUnique); const mockedCreate = vi.mocked(prisma.idempotencyRecord.create); const mockedQuoteFind = vi.mocked(prisma.quoteRecord.findUnique); describe("checkIdempotency", () => { beforeEach(() => { vi.clearAllMocks(); }); it("L1 命中 → 返回 quote_id", async () => { mockedGetL1.mockResolvedValue({ quoteId: "QTE_20260616_0001", response: { quote_id: "QTE_20260616_0001", status: "done" }, }); const result = await checkIdempotency("550e8400-e29b-41d4-a716-446655440000"); expect(result.hit).toBe(true); if (result.hit) { expect(result.quoteId).toBe("QTE_20260616_0001"); } }); it("L1 未命中、DB 命中 → 回填 L1", async () => { mockedGetL1.mockResolvedValue(null); mockedFindUnique.mockResolvedValue({ quoteId: "QTE_20260616_0002", } as never); mockedQuoteFind.mockResolvedValue({ quoteId: "QTE_20260616_0002", status: "done", } as never); const result = await checkIdempotency("550e8400-e29b-41d4-a716-446655440000"); expect(result.hit).toBe(true); expect(mockedSetL1).toHaveBeenCalled(); }); it("均未命中 → hit=false", async () => { mockedGetL1.mockResolvedValue(null); mockedFindUnique.mockResolvedValue(null); const result = await checkIdempotency("550e8400-e29b-41d4-a716-446655440000"); expect(result.hit).toBe(false); }); }); describe("saveIdempotency", () => { beforeEach(() => { vi.clearAllMocks(); mockedCreate.mockResolvedValue({} as never); mockedSetL1.mockResolvedValue(true); }); it("写入 DB + Redis", async () => { await saveIdempotency( "550e8400-e29b-41d4-a716-446655440000", "CUST_001", "QTE_20260616_0001", { quote_id: "QTE_20260616_0001", status: "done" }, ); expect(mockedCreate).toHaveBeenCalled(); expect(mockedSetL1).toHaveBeenCalled(); }); it("P2002 冲突 → 静默返回", async () => { mockedCreate.mockRejectedValue({ code: "P2002" }); mockedFindUnique.mockResolvedValue({ quoteId: "QTE_20260616_0001", } as never); await expect( saveIdempotency( "550e8400-e29b-41d4-a716-446655440000", "CUST_001", "QTE_20260616_0001", {}, ), ).resolves.toBeUndefined(); }); });