You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

109 lines
2.9 KiB

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();
});
});