|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
import type { FlockRunQuoteResult } from "@/workers/rpa/flock/types";
|
|
|
|
|
|
const directMock = vi.fn<() => Promise<FlockRunQuoteResult>>();
|
|
|
const visualMock = vi.fn<() => Promise<FlockRunQuoteResult>>();
|
|
|
const customerLoginMock = vi.fn();
|
|
|
const hasCredMock = vi.fn();
|
|
|
|
|
|
vi.mock("@/lib/flock/direct-quote", () => ({
|
|
|
fetchFlockQuotesViaDirect: () => directMock(),
|
|
|
}));
|
|
|
|
|
|
vi.mock("@/workers/rpa/flock/visual-chain", () => ({
|
|
|
runFlockVisualChain: () => visualMock(),
|
|
|
}));
|
|
|
|
|
|
vi.mock("@/workers/rpa/flock/worker-session", () => ({
|
|
|
runFlockQuoteWithWorkerSession: () => visualMock(),
|
|
|
shouldRotateFlockSession: () => false,
|
|
|
}));
|
|
|
|
|
|
vi.mock("@/lib/flock/exclusive-lock", () => ({
|
|
|
withFlockExclusiveLock: async (
|
|
|
_owner: string,
|
|
|
fn: () => Promise<FlockRunQuoteResult>,
|
|
|
) => fn(),
|
|
|
}));
|
|
|
|
|
|
vi.mock("@/modules/customer/provider-credentials", () => ({
|
|
|
getCustomerProviderLogin: (...args: unknown[]) => customerLoginMock(...args),
|
|
|
hasCustomerProviderCredential: (...args: unknown[]) => hasCredMock(...args),
|
|
|
}));
|
|
|
|
|
|
describe("runFlockQuoteRpa customer login forces DOM", () => {
|
|
|
beforeEach(() => {
|
|
|
directMock.mockReset();
|
|
|
visualMock.mockReset();
|
|
|
customerLoginMock.mockReset();
|
|
|
hasCredMock.mockReset();
|
|
|
hasCredMock.mockResolvedValue(false);
|
|
|
process.env.RPA_MOCK_MODE = "false";
|
|
|
process.env.FLOCK_QUOTE_MODE = "direct_then_dom";
|
|
|
delete process.env.FLOCK_LOGIN_EMAIL;
|
|
|
delete process.env.FLOCK_LOGIN_PASSWORD;
|
|
|
process.env.FLOCK_WORKER_REUSE_SESSION = "false";
|
|
|
});
|
|
|
|
|
|
const input = {
|
|
|
pickupZip: "94102",
|
|
|
deliveryZip: "90212",
|
|
|
pickupDate: "07/20/2026",
|
|
|
palletCount: 4,
|
|
|
totalWeightLb: 2800,
|
|
|
lengthIn: 48,
|
|
|
widthIn: 40,
|
|
|
heightIn: 48,
|
|
|
};
|
|
|
|
|
|
it("有客户账密时跳过 Direct,走 DOM", async () => {
|
|
|
customerLoginMock.mockResolvedValue({
|
|
|
email: "cust@example.com",
|
|
|
password: "secret",
|
|
|
});
|
|
|
visualMock.mockResolvedValue({
|
|
|
ok: true,
|
|
|
quotes: [{ tier: "standard", totalUsd: 100 } as FlockRunQuoteResult["quotes"][0]],
|
|
|
});
|
|
|
|
|
|
const { runFlockQuoteRpa } = await import("@/workers/rpa/flock/run-quote");
|
|
|
const result = await runFlockQuoteRpa(input, { customerId: "CUST_001" });
|
|
|
|
|
|
expect(result.ok).toBe(true);
|
|
|
expect(directMock).not.toHaveBeenCalled();
|
|
|
expect(visualMock).toHaveBeenCalled();
|
|
|
expect(customerLoginMock).toHaveBeenCalledWith("CUST_001", "flock");
|
|
|
});
|
|
|
|
|
|
it("库中已配账密但解密失败时不回落 Direct", async () => {
|
|
|
customerLoginMock.mockResolvedValue(null);
|
|
|
hasCredMock.mockResolvedValue(true);
|
|
|
|
|
|
const { runFlockQuoteRpa } = await import("@/workers/rpa/flock/run-quote");
|
|
|
const result = await runFlockQuoteRpa(input, { customerId: "CUST_001" });
|
|
|
|
|
|
expect(result.ok).toBe(false);
|
|
|
expect(result.errorMessage).toMatch(/解密失败|禁止改走 Direct/i);
|
|
|
expect(directMock).not.toHaveBeenCalled();
|
|
|
expect(visualMock).not.toHaveBeenCalled();
|
|
|
});
|
|
|
|
|
|
it("无客户账密时走 Direct", async () => {
|
|
|
customerLoginMock.mockResolvedValue(null);
|
|
|
hasCredMock.mockResolvedValue(false);
|
|
|
directMock.mockResolvedValue({
|
|
|
ok: true,
|
|
|
quotes: [{ tier: "standard", totalUsd: 200 } as FlockRunQuoteResult["quotes"][0]],
|
|
|
});
|
|
|
|
|
|
const { runFlockQuoteRpa } = await import("@/workers/rpa/flock/run-quote");
|
|
|
const result = await runFlockQuoteRpa(input, { customerId: "CUST_001" });
|
|
|
|
|
|
expect(result.ok).toBe(true);
|
|
|
expect(directMock).toHaveBeenCalled();
|
|
|
});
|
|
|
});
|