import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("@/lib/api/auth-context", () => ({ parseServiceAuth: vi.fn(), assertCustomerMatch: vi.fn(), })); vi.mock("@/lib/api/rate-limit", () => ({ enforceRateLimits: vi.fn().mockResolvedValue(null), })); vi.mock("@/modules/flock/hold-service", () => ({ declineFlockQuoteHold: vi.fn(), continueFlockQuoteHold: vi.fn(), })); import { parseServiceAuth, assertCustomerMatch } from "@/lib/api/auth-context"; import { POST as declinePost } from "@/app/api/quotes/flock-hold/decline/route"; import { POST as continuePost } from "@/app/api/quotes/flock-hold/continue/route"; import { continueFlockQuoteHold, declineFlockQuoteHold, } from "@/modules/flock/hold-service"; import { FLOCK_HOLD_MSG } from "@/lib/constants/flock-quote-hold"; const SESSION = "flock_q1"; function authOk() { vi.mocked(parseServiceAuth).mockResolvedValue({ authType: "service", customerId: "CUST_001", permissions: [], } as never); vi.mocked(assertCustomerMatch).mockImplementation(() => undefined); } function jsonReq(url: string, body: unknown) { return new Request(url, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body), }); } describe("flock-hold continue / decline API", () => { beforeEach(() => { vi.clearAllMocks(); authOk(); }); it("decline:缺 session → 400 中文", async () => { const res = await declinePost( jsonReq("http://localhost/api/quotes/flock-hold/decline", { customer_id: "CUST_001", quote_id: "q1", }), ); expect(res.status).toBe(400); const body = await res.json(); expect(body.code).not.toBe(0); expect(String(body.message)).toMatch(/询价会话|会话标识|Required/); }); it("decline:非法 JSON → 400", async () => { const res = await declinePost( new Request("http://localhost/api/quotes/flock-hold/decline", { method: "POST", headers: { "content-type": "application/json" }, body: "{", }), ); expect(res.status).toBe(400); }); it("decline:成功 → released", async () => { vi.mocked(declineFlockQuoteHold).mockResolvedValue({ released: true }); const res = await declinePost( jsonReq("http://localhost/api/quotes/flock-hold/decline", { customer_id: "CUST_001", quote_id: "q1", quote_session_id: SESSION, }), ); const body = await res.json(); expect(body.code).toBe(0); expect(body.data.released).toBe(true); }); it("continue:成功 → filling", async () => { vi.mocked(continueFlockQuoteHold).mockResolvedValue({ status: "filling" }); const res = await continuePost( jsonReq("http://localhost/api/quotes/flock-hold/continue", { customer_id: "CUST_001", quote_id: "q1", quote_session_id: SESSION, }), ); const body = await res.json(); expect(body.code).toBe(0); expect(body.data.status).toBe("filling"); }); it("continue:60s 超时业务错误 → 400 中文", async () => { vi.mocked(continueFlockQuoteHold).mockRejectedValue( new Error(FLOCK_HOLD_MSG.decisionTimeoutApi), ); const res = await continuePost( jsonReq("http://localhost/api/quotes/flock-hold/continue", { customer_id: "CUST_001", quote_id: "q1", quote_session_id: SESSION, }), ); expect(res.status).toBe(400); const body = await res.json(); expect(body.message).toBe(FLOCK_HOLD_MSG.decisionTimeoutApi); }); it("continue:5min 超时 → 400 中文含 5 分钟", async () => { vi.mocked(continueFlockQuoteHold).mockRejectedValue( new Error(FLOCK_HOLD_MSG.totalTimeoutApi), ); const res = await continuePost( jsonReq("http://localhost/api/quotes/flock-hold/continue", { customer_id: "CUST_001", quote_id: "q1", quote_session_id: SESSION, }), ); const body = await res.json(); expect(body.message).toMatch(/5 分钟/); }); });