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.

115 lines
2.9 KiB

import { describe, expect, it, vi, beforeEach } from "vitest";
const prismaMock = vi.hoisted(() => ({
quoteCacheMeta: {
findUnique: vi.fn(),
create: vi.fn(),
update: vi.fn(),
},
alertLog: {
create: vi.fn(),
},
}));
vi.mock("@/lib/prisma", () => ({ prisma: prismaMock }));
import { detectDeviation } from "@/modules/alert/deviation-detector";
import type { RawQuoteTier } from "@/modules/pricing/engine";
const fourTiers = (): RawQuoteTier[] => [
{
service_level: "standard",
rate_option: "lowest",
carrier: "A",
transit_days: "3",
transit_description: "3d",
raw_freight: 320,
surcharges: 0,
raw_total: 320,
},
{
service_level: "standard",
rate_option: "fastest",
carrier: "A",
transit_days: "2",
transit_description: "2d",
raw_freight: 380,
surcharges: 0,
raw_total: 380,
},
{
service_level: "guaranteed",
rate_option: "lowest",
carrier: "B",
transit_days: "3",
transit_description: "3d",
raw_freight: 420,
surcharges: 0,
raw_total: 420,
},
{
service_level: "guaranteed",
rate_option: "fastest",
carrier: "B",
transit_days: "2",
transit_description: "2d",
raw_freight: 480,
surcharges: 0,
raw_total: 480,
},
];
describe("detectDeviation", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("TC-212 首次询价不触发偏差预警", async () => {
prismaMock.quoteCacheMeta.findUnique.mockResolvedValue(null);
prismaMock.quoteCacheMeta.create.mockResolvedValue({});
await detectDeviation("hash1", fourTiers(), "QTE001");
expect(prismaMock.alertLog.create).not.toHaveBeenCalled();
expect(prismaMock.quoteCacheMeta.create).toHaveBeenCalled();
});
it("TC-210 偏差≥5% 写 PRICE_DEVIATION", async () => {
prismaMock.quoteCacheMeta.findUnique.mockResolvedValue({
cargoHash: "hash1",
rawTotalStandard: 352,
});
prismaMock.quoteCacheMeta.update.mockResolvedValue({});
prismaMock.alertLog.create.mockResolvedValue({});
const quotes = fourTiers();
quotes[0].raw_total = 370;
quotes[0].raw_freight = 370;
await detectDeviation("hash1", quotes, "QTE002");
expect(prismaMock.alertLog.create).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({ alertType: "PRICE_DEVIATION" }),
}),
);
});
it("TC-211 偏差4.9% 不触发", async () => {
prismaMock.quoteCacheMeta.findUnique.mockResolvedValue({
cargoHash: "hash1",
rawTotalStandard: 352,
});
prismaMock.quoteCacheMeta.update.mockResolvedValue({});
const quotes = fourTiers();
quotes[0].raw_total = 369.25;
quotes[0].raw_freight = 369.25;
await detectDeviation("hash1", quotes, "QTE003");
expect(prismaMock.alertLog.create).not.toHaveBeenCalled();
expect(prismaMock.quoteCacheMeta.update).toHaveBeenCalled();
});
});