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