import { describe, expect, it } from "vitest"; import { matchSenderCustomer } from "@/services/customer/resolve-sender"; import { isRecentlyCreated, TIMEOUT_VERIFY_RECENT_MS, } from "@/services/import/compensation"; import { isPastRetention } from "@/services/retention/cleanup"; import { assertTransition, canTransition, } from "@/services/state-machine"; import { classifyMail } from "@/services/parse/classify"; import { mapChannel } from "@/services/parse/channel-map"; import { isValidContainerNo } from "@/utils/iso6346"; /** * Resilience A-* 可测采样(docx/异常场景与系统韧性设计 §12) * 依赖 DB/HTTP 的链路见 force-import UI + worker compensation/retention。 */ describe("resilience A-*", () => { describe("A-1 input", () => { it("A-1.1 empty subject continues classify", () => { const r = classifyMail({ subject: "", body: "转仓", filenames: [] }); // 无主题不阻断;正文「转仓」现行口径为 TRANSFER expect(["TRANSFER", "WORK_ORDER"]).toContain(r.mail_type); }); it("A-1.6 invalid container format", () => { expect(isValidContainerNo("BAD")).toBe(false); }); it("A-1.9 unmapped channel still returns transporter", () => { const m = mapChannel("神秘渠道XYZ"); expect(m.unmapped).toBe(true); expect(m.transporter).toBeTruthy(); }); }); describe("A-3 timeout verify window", () => { it("A-3.x recent createDate within 10min is recently created", () => { const now = Date.now(); expect( isRecentlyCreated(new Date(now - 60_000).toISOString(), now), ).toBe(true); expect( isRecentlyCreated( new Date(now - TIMEOUT_VERIFY_RECENT_MS - 1).toISOString(), now, ), ).toBe(false); }); }); describe("A-5 / A-6 state machine", () => { it("A-5.8 reparse allowed from PARSE_FAILED", () => { expect(canTransition("PARSE_FAILED", "PARSING")).toBe(true); expect(canTransition("SUCCESS", "PARSING")).toBe(false); }); it("A-6.6 illegal transition rejected", () => { expect(canTransition("SUCCESS", "PENDING_CONFIRM")).toBe(false); expect(() => assertTransition("IMPORTING", "PARSED")).toThrow(); }); }); describe("retention + sender_customer_map", () => { it("DATA_RETENTION cutoff marks old mail as past", () => { const now = new Date("2026-07-22T00:00:00Z"); const old = new Date("2026-06-01T00:00:00Z"); expect(isPastRetention(old, old, 30, now)).toBe(true); expect(isPastRetention(now, now, 30, now)).toBe(false); }); it("sender map EMAIL beats DOMAIN", () => { const hit = matchSenderCustomer( { fromAddr: "demo-customer@example.com" }, [ { matchType: "DOMAIN", matchValue: "example.com", customerCode: "DOM", }, { matchType: "EMAIL", matchValue: "demo-customer@example.com", customerCode: "MAIL", }, ], ); expect(hit?.customerCode).toBe("MAIL"); }); it("sender map DOMAIN match", () => { const hit = matchSenderCustomer( { fromAddr: "ops@partner.example.com" }, [ { matchType: "DOMAIN", matchValue: "partner.example.com", customerCode: "PARTNER01", }, ], ); expect(hit?.customerCode).toBe("PARTNER01"); }); }); });