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.

363 lines
11 KiB

import { describe, expect, it } from "vitest";
import {
buildAddressQuery,
buildDashboardCargo,
buildDashboardLocation,
buildPickupDateIso,
decodeMsCarrierLabel,
isMsDashboardDirectQuoteEnabled,
mapDashboardQuoteBodyToItems,
mapMsDashboardCargoType,
parseMsReadyTimeToHm,
zonedWallTimeToUtcIso,
} from "@/lib/mothership/dashboard-direct-quote";
import type { QuoteRequest } from "@/modules/providers/quote-provider";
describe("dashboard-direct-quote", () => {
it("decodeMsCarrierLabel 解码 base64 并附加 Direct", () => {
const label = decodeMsCarrierLabel({
encodedCarrierName: Buffer.from("ABF Freight", "utf8").toString("base64"),
serviceLaneType: "direct",
carrierScac: "abfs",
});
expect(label).toBe("ABF Freight Direct");
});
it("mapDashboardQuoteBodyToItems 从 availableRates 产出多承运商", () => {
const items = mapDashboardQuoteBodyToItems({
rates: {
standard: {
bestValue: {
id: "rate_a",
finalPrice: 100,
days: 3,
serviceLevel: "standard",
serviceType: "bestValue",
carrierScac: "xpol",
encodedCarrierName: Buffer.from("XPO", "utf8").toString("base64"),
serviceLaneType: "direct",
},
},
},
availableRates: {
rate_a: {
id: "rate_a",
finalPrice: 100,
days: 3,
serviceLevel: "standard",
serviceType: "bestValue",
carrierScac: "xpol",
encodedCarrierName: Buffer.from("XPO", "utf8").toString("base64"),
serviceLaneType: "direct",
},
rate_b: {
id: "rate_b",
finalPrice: 200,
days: 2,
serviceLevel: "standard",
serviceType: "customRate",
carrierScac: "abfs",
encodedCarrierName: Buffer.from("ABF", "utf8").toString("base64"),
serviceLaneType: "direct",
},
},
});
expect(items.length).toBeGreaterThanOrEqual(2);
expect(items.some((i) => /XPO/i.test(i.carrier))).toBe(true);
expect(items.some((i) => /ABF/i.test(i.carrier))).toBe(true);
expect(items.every((i) => i.rawTotal > 0)).toBe(true);
});
it("buildDashboardLocation 写入二级联系人与营业时间", () => {
const loc = buildDashboardLocation(
{
placeId: "p1",
city: "Minneapolis",
state: "MN",
zip: "55402",
street: "800 Nicollet Mall",
timezone: "America/Chicago",
zone: null,
coordinates: { latitude: 1, longitude: 2 },
neighborhood: "",
},
["liftgate"],
{
company_name: "Acme Co.",
suite: "Ste 301",
contact_first: "Ops",
contact_last: "Lead",
contact_email: "ops@example.com",
contact_phone: "5550101",
opens_at: "8:00 AM",
closes_at: "5:00 PM",
},
);
expect(loc.accessorials).toEqual(["liftgate"]);
expect(loc.name).toBe("Acme Co.");
expect(loc.subStreet).toBe("Ste 301");
expect(loc.serviceStartTime).toBe("8:00 AM");
expect(loc.serviceEndTime).toBe("5:00 PM");
expect(loc.email).toBe("ops@example.com");
});
it("buildAddressQuery 优先结构化地址", () => {
const q = buildAddressQuery({
street: "1000 S Alameda St",
city: "Los Angeles",
state: "CA",
zip: "90021",
placeId: "x",
formattedAddress: "ignored",
selectedFromSuggestions: true,
mothershipOptionId: "x",
mothershipDisplayLabel: "ignored",
selectedFromMothership: true,
});
expect(q).toContain("1000 S Alameda St");
expect(q).toContain("Los Angeles");
});
it("buildDashboardCargo 支持多行并向上取整", () => {
const req = {
cargoHash: "h",
pickup: {} as QuoteRequest["pickup"],
delivery: {} as QuoteRequest["delivery"],
weightLb: 10.2,
dimsIn: { l: 48.1, w: 40, h: 48 },
palletCount: 2,
cargoType: "general_freight",
cargoLines: [
{
cargoType: "pallet",
quantity: 2,
weightLb: 10.2,
lengthIn: 48.1,
widthIn: 40,
heightIn: 48,
},
],
} as QuoteRequest;
const cargo = buildDashboardCargo(req);
expect(cargo).toHaveLength(1);
expect(cargo[0]!.weight).toBe(11);
expect(cargo[0]!.length).toBe(49);
expect(cargo[0]!.quantity).toBe(2);
expect(cargo[0]!.type).toBe("Pallet");
});
it("mapMsDashboardCargoType 与官网货型对齐,禁止非托盘打成 Pallet", () => {
expect(mapMsDashboardCargoType("case")).toBe("Case");
expect(mapMsDashboardCargoType("drum")).toBe("Drum");
expect(mapMsDashboardCargoType("piece")).toBe("Pieces");
expect(mapMsDashboardCargoType("tote")).toBe("Tote");
expect(mapMsDashboardCargoType("roll")).toBe("Roll");
expect(mapMsDashboardCargoType("skid")).toBe("Skid");
expect(mapMsDashboardCargoType("box")).toBe("Box");
expect(mapMsDashboardCargoType("carton")).toBe("Carton");
expect(mapMsDashboardCargoType("crate")).toBe("Crate");
expect(mapMsDashboardCargoType("pallet")).toBe("Pallet");
expect(mapMsDashboardCargoType("general_freight")).toBe("Pallet");
});
it("buildDashboardCargo 多货型逐行保留官网 type", () => {
const req = {
cargoHash: "h",
pickup: {} as QuoteRequest["pickup"],
delivery: {} as QuoteRequest["delivery"],
weightLb: 500,
dimsIn: { l: 48, w: 40, h: 48 },
palletCount: 1,
cargoType: "general_freight",
cargoLines: [
{
cargoType: "pallet",
quantity: 2,
weightLb: 425,
lengthIn: 42,
widthIn: 40,
heightIn: 38,
},
{
cargoType: "drum",
quantity: 8,
weightLb: 329,
lengthIn: 26,
widthIn: 28,
heightIn: 19,
},
{
cargoType: "carton",
quantity: 3,
weightLb: 166,
lengthIn: 28,
widthIn: 19,
heightIn: 29,
},
],
} as QuoteRequest;
const cargo = buildDashboardCargo(req);
expect(cargo.map((c) => c.type)).toEqual(["Pallet", "Drum", "Carton"]);
});
it("buildDashboardCargo piece 行使用官网 API 值 Pieces", () => {
const req = {
cargoHash: "h",
pickup: {} as QuoteRequest["pickup"],
delivery: {} as QuoteRequest["delivery"],
weightLb: 255,
dimsIn: { l: 17, w: 26, h: 24 },
palletCount: 2,
cargoType: "general_freight",
cargoLines: [
{
cargoType: "pallet",
quantity: 2,
weightLb: 735,
lengthIn: 48,
widthIn: 40,
heightIn: 48,
},
{
cargoType: "piece",
quantity: 12,
weightLb: 255,
lengthIn: 17,
widthIn: 26,
heightIn: 24,
},
],
} as QuoteRequest;
const cargo = buildDashboardCargo(req);
expect(cargo.map((c) => c.type)).toEqual(["Pallet", "Pieces"]);
expect(Array.isArray(cargo[1]?.commodities)).toBe(true);
});
it("mapDashboardQuoteBodyToItems 保留同承运商不同价", () => {
const items = mapDashboardQuoteBodyToItems({
availableRates: {
a: {
id: "a",
finalPrice: 100,
days: 5,
serviceLevel: "standard",
serviceType: "customRate",
encodedCarrierName: Buffer.from("ABF Freight", "utf8").toString(
"base64",
),
serviceLaneType: "direct",
},
b: {
id: "b",
finalPrice: 130,
days: 3,
serviceLevel: "standard",
serviceType: "customRate",
encodedCarrierName: Buffer.from("ABF Freight", "utf8").toString(
"base64",
),
serviceLaneType: "direct",
},
c: {
id: "c",
finalPrice: 90,
days: 6,
serviceLevel: "standard",
serviceType: "customRate",
encodedCarrierName: Buffer.from("XPO", "utf8").toString("base64"),
serviceLaneType: "direct",
},
},
});
expect(items.length).toBe(3);
expect(items.filter((i) => /ABF/i.test(i.carrier))).toHaveLength(2);
expect(items.filter((i) => /XPO/i.test(i.carrier))).toHaveLength(1);
});
it("mapDashboardQuoteBodyToItems 优先 ratesV2 全量价卡", () => {
const items = mapDashboardQuoteBodyToItems({
ratesV2: [
{
id: "v2_a",
finalPrice: 90,
days: 4,
serviceLevel: "standard",
serviceType: "customRate",
encodedCarrierName: Buffer.from("TForce", "utf8").toString("base64"),
serviceLaneType: "direct",
},
{
id: "v2_b",
finalPrice: 110,
days: 3,
serviceLevel: "standard",
serviceType: "customRate",
encodedCarrierName: Buffer.from("XPO", "utf8").toString("base64"),
serviceLaneType: "direct",
},
],
availableRates: {
only_ar: {
id: "only_ar",
finalPrice: 999,
days: 9,
serviceLevel: "standard",
serviceType: "customRate",
encodedCarrierName: Buffer.from("Echo", "utf8").toString("base64"),
serviceLaneType: "direct",
},
},
});
expect(items).toHaveLength(2);
expect(items.some((i) => /Echo/i.test(i.carrier))).toBe(false);
expect(items.some((i) => /TForce/i.test(i.carrier))).toBe(true);
});
it("buildPickupDateIso 使用提货时区 + 就绪时刻", () => {
expect(parseMsReadyTimeToHm("8:00 AM")).toEqual({ h: 8, m: 0 });
expect(parseMsReadyTimeToHm("12:00 PM")).toEqual({ h: 12, m: 0 });
expect(parseMsReadyTimeToHm("4:00 PM")).toEqual({ h: 16, m: 0 });
const iso = zonedWallTimeToUtcIso(
"2026-08-12",
8,
0,
"America/Los_Angeles",
);
const asLa = new Intl.DateTimeFormat("en-US", {
timeZone: "America/Los_Angeles",
hour: "numeric",
hourCycle: "h23",
day: "2-digit",
}).formatToParts(new Date(iso));
expect(Number(asLa.find((p) => p.type === "hour")?.value)).toBe(8);
const req = {
cargoHash: "h",
pickup: {} as QuoteRequest["pickup"],
delivery: {} as QuoteRequest["delivery"],
weightLb: 500,
dimsIn: { l: 48, w: 40, h: 48 },
palletCount: 1,
cargoType: "pallet",
readyDate: "2026-08-12",
readyTime: "4:00 PM",
} as QuoteRequest;
const pickupIso = buildPickupDateIso(req, "America/Los_Angeles");
const hourLa = new Intl.DateTimeFormat("en-US", {
timeZone: "America/Los_Angeles",
hour: "2-digit",
hourCycle: "h23",
}).formatToParts(new Date(pickupIso));
expect(Number(hourLa.find((p) => p.type === "hour")?.value)).toBe(16);
});
it("isMsDashboardDirectQuoteEnabled 默认开启", () => {
const prev = process.env.MS_DASHBOARD_DIRECT_QUOTE;
delete process.env.MS_DASHBOARD_DIRECT_QUOTE;
expect(isMsDashboardDirectQuoteEnabled()).toBe(true);
process.env.MS_DASHBOARD_DIRECT_QUOTE = "false";
expect(isMsDashboardDirectQuoteEnabled()).toBe(false);
if (prev === undefined) delete process.env.MS_DASHBOARD_DIRECT_QUOTE;
else process.env.MS_DASHBOARD_DIRECT_QUOTE = prev;
});
});