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.

213 lines
6.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { describe, expect, it } from "vitest";
import {
validateFlockQuoteInput,
defaultFlockPickupDate,
} from "@/modules/flock/validation";
import { ValidationError } from "@/modules/quote/types";
import { kgToLb, toAxelWholePounds } from "@/modules/quote/unit-converter";
function baseBody(overrides: Record<string, unknown> = {}) {
return {
request_id: "550e8400-e29b-41d4-a716-446655440000",
customer_id: "CUST_001",
flock_input: {
pickup_date: defaultFlockPickupDate(),
pickup_zip: "90001",
delivery_zip: "75201",
pallet_count: 6,
total_weight: { value: 3500, unit: "lb" },
dimensions: { length: 48, width: 40, height: 48, unit: "in" },
...overrides,
},
};
}
describe("validateFlockQuoteInput", () => {
it("接受合法英制入参", () => {
const parsed = validateFlockQuoteInput(baseBody());
expect(parsed.palletCount).toBe(6);
expect(parsed.totalWeightLb).toBe(3500);
expect(parsed.lengthIn).toBe(48);
});
it("500kg 换算为 1103lb小数进一", () => {
expect(toAxelWholePounds(kgToLb(500))).toBe(1103);
const parsed = validateFlockQuoteInput(
baseBody({ total_weight: { value: 500, unit: "kg" } }),
);
expect(parsed.totalWeightLb).toBe(1103);
});
it("0.5t 换算为 1103lb小数进一", () => {
const parsed = validateFlockQuoteInput(
baseBody({ total_weight: { value: 0.5, unit: "t" } }),
);
expect(parsed.totalWeightLb).toBe(1103);
});
it("拒绝 ton/tons 歧义单位", () => {
expect(() =>
validateFlockQuoteInput(
baseBody({ total_weight: { value: 0.5, unit: "ton" } }),
),
).toThrow(/歧义/);
});
it("1.2m 尺寸换算并向上取整", () => {
const parsed = validateFlockQuoteInput(
baseBody({
dimensions: { length: 1.2, width: 1, height: 1, unit: "m" },
}),
);
expect(parsed.lengthIn).toBe(48);
expect(parsed.widthIn).toBe(40);
expect(parsed.heightIn).toBe(40);
});
it("托盘数越界返回中文提示", () => {
expect(() =>
validateFlockQuoteInput(baseBody({ pallet_count: 3 })),
).toThrow(ValidationError);
expect(() =>
validateFlockQuoteInput(baseBody({ pallet_count: 3 })),
).toThrow(/4 到 20/);
expect(() =>
validateFlockQuoteInput(baseBody({ pallet_count: 21 })),
).toThrow(/4 到 20/);
});
it("总重超 45000lb 拒绝", () => {
expect(() =>
validateFlockQuoteInput(
baseBody({ total_weight: { value: 45001, unit: "lb" } }),
),
).toThrow(/45000/);
});
it("尺寸超限拒绝", () => {
expect(() =>
validateFlockQuoteInput(
baseBody({
dimensions: { length: 637, width: 40, height: 48, unit: "in" },
}),
),
).toThrow(/636/);
expect(() =>
validateFlockQuoteInput(
baseBody({
dimensions: { length: 48, width: 103, height: 48, unit: "in" },
}),
),
).toThrow(/102/);
expect(() =>
validateFlockQuoteInput(
baseBody({
dimensions: { length: 48, width: 40, height: 109, unit: "in" },
}),
),
).toThrow(/108/);
});
it("线性英尺超拖车拒绝(对应官网 Linear feet 报错)", () => {
expect(() =>
validateFlockQuoteInput(
baseBody({
pallet_count: 18,
dimensions: { length: 403, width: 102, height: 108, unit: "in" },
}),
),
).toThrow(/占用车长|挂车长度/);
});
it("缺少 ZIP 提示中文", () => {
expect(() =>
validateFlockQuoteInput(baseBody({ pickup_zip: "" })),
).toThrow(/提货邮编/);
});
it("提货日期早于今天拒绝", () => {
const d = new Date();
d.setDate(d.getDate() - 1);
const mm = String(d.getMonth() + 1).padStart(2, "0");
const dd = String(d.getDate()).padStart(2, "0");
const yyyy = d.getFullYear();
expect(() =>
validateFlockQuoteInput(
baseBody({ pickup_date: `${mm}/${dd}/${yyyy}` }),
),
).toThrow(/不能早于今天/);
});
it("提货日期为周末拒绝", () => {
const d = new Date();
d.setHours(12, 0, 0, 0);
const day = d.getDay();
const daysUntilSat = day === 6 ? 7 : (6 - day + 7) % 7 || 7;
d.setDate(d.getDate() + daysUntilSat);
const mm = String(d.getMonth() + 1).padStart(2, "0");
const dd = String(d.getDate()).padStart(2, "0");
const yyyy = d.getFullYear();
expect(() =>
validateFlockQuoteInput(
baseBody({ pickup_date: `${mm}/${dd}/${yyyy}` }),
),
).toThrow(/周六或周日/);
});
it("登录态调度字段透传", () => {
const parsed = validateFlockQuoteInput(
baseBody({
form_mode: "logged_in_quick",
pallet_count: 2,
total_weight: { value: 6000, unit: "lb" },
pickup_service: "during_window",
pickup_window_start_time: "8:00 am",
pickup_window_end_time: "5:00 pm",
delivery_service: "must_arrive_by",
delivery_must_arrive_by_date: "2026-08-01",
call_for_delivery_appointment: true,
pickup_location_type: "residential",
delivery_location_type: "limited_farm",
}),
);
expect(parsed.formMode).toBe("logged_in_quick");
expect(parsed.pickupService).toBe("during_window");
expect(parsed.pickupWindowStartTime).toBe("8:00 am");
expect(parsed.deliveryService).toBe("must_arrive_by");
expect(parsed.deliveryMustArriveByDate).toBe("2026-08-01");
expect(parsed.callForDeliveryAppointment).toBe(true);
expect(parsed.pickupLocationType).toBe("residential");
});
it("接受可选 registration 块", () => {
const parsed = validateFlockQuoteInput(
baseBody({
registration: {
email: "test@qq.com",
phone: "7536407420",
first_name: "Li",
},
}),
);
expect(parsed.registration?.email).toBe("test@qq.com");
expect(parsed.registration?.phone).toBe("7536407420");
expect(parsed.registration?.firstName).toBe("Li");
});
it("无效邮箱拒绝", () => {
expect(() =>
validateFlockQuoteInput(
baseBody({ registration: { email: "not-an-email" } }),
),
).toThrow(/工作邮箱/);
});
it("无效电话拒绝", () => {
expect(() =>
validateFlockQuoteInput(
baseBody({ registration: { phone: "123" } }),
),
).toThrow(/10 位/);
});
});