|
|
import { describe, expect, it } from "vitest";
|
|
|
import {
|
|
|
MOTHERSHIP_LIMITS,
|
|
|
validateQuoteInput,
|
|
|
rejectQuantityField,
|
|
|
} from "@/modules/quote/validation";
|
|
|
import { ValidationError } from "@/modules/quote/types";
|
|
|
import {
|
|
|
MOTHERSHIP_DELIVERY_CONFIRMED,
|
|
|
MOTHERSHIP_PICKUP_CONFIRMED,
|
|
|
} from "@/__tests__/fixtures/mothership-address";
|
|
|
|
|
|
const VALID_BODY = {
|
|
|
request_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
|
customer_id: "CUST_001",
|
|
|
pickup_address: {
|
|
|
street: "1234 Warehouse Blvd",
|
|
|
city: "Los Angeles",
|
|
|
state: "CA",
|
|
|
zip: "90001",
|
|
|
place_id: "ChIJ_pickup",
|
|
|
formatted_address: "1234 Warehouse Blvd, Los Angeles, CA 90001, USA",
|
|
|
selected_from_suggestions: true,
|
|
|
...MOTHERSHIP_PICKUP_CONFIRMED,
|
|
|
},
|
|
|
delivery_address: {
|
|
|
street: "5678 Distribution Dr",
|
|
|
city: "Dallas",
|
|
|
state: "TX",
|
|
|
zip: "75201",
|
|
|
place_id: "ChIJ_delivery",
|
|
|
formatted_address: "5678 Distribution Dr, Dallas, TX 75201, USA",
|
|
|
selected_from_suggestions: true,
|
|
|
...MOTHERSHIP_DELIVERY_CONFIRMED,
|
|
|
},
|
|
|
weight: { value: 500, unit: "lb" },
|
|
|
dimensions: { length: 48, width: 40, height: 48, unit: "in" },
|
|
|
pallet_count: 2,
|
|
|
cargo_type: "general_freight",
|
|
|
};
|
|
|
|
|
|
describe("validateQuoteInput", () => {
|
|
|
it("正常入参通过", () => {
|
|
|
const result = validateQuoteInput(VALID_BODY);
|
|
|
expect(result.cargoHash).toHaveLength(32);
|
|
|
expect(result.weightLb).toBe(500);
|
|
|
});
|
|
|
|
|
|
it("TC-201:weight 缺失 → VALIDATION_FAILED", () => {
|
|
|
const { weight: _w, ...rest } = VALID_BODY;
|
|
|
expect(() => validateQuoteInput(rest)).toThrow(ValidationError);
|
|
|
});
|
|
|
|
|
|
it("TC-202:weight=0 → 超范围", () => {
|
|
|
expect(() =>
|
|
|
validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
weight: { value: 0, unit: "lb" },
|
|
|
}),
|
|
|
).toThrow(/超出 Mothership 允许范围/);
|
|
|
});
|
|
|
|
|
|
it("空邮编通过(MotherShip 不要求填写邮编)", () => {
|
|
|
const result = validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
pickup_address: { ...VALID_BODY.pickup_address, zip: "" },
|
|
|
delivery_address: { ...VALID_BODY.delivery_address, zip: "" },
|
|
|
});
|
|
|
expect(result.cargoHash).toHaveLength(32);
|
|
|
});
|
|
|
|
|
|
it("TC-203:zip 4 位 → 无效", () => {
|
|
|
expect(() =>
|
|
|
validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
pickup_address: {
|
|
|
...VALID_BODY.pickup_address,
|
|
|
zip: "9001",
|
|
|
},
|
|
|
}),
|
|
|
).toThrow(/邮编/);
|
|
|
});
|
|
|
|
|
|
it("TC-204:request_id 非 UUID → 无效", () => {
|
|
|
expect(() =>
|
|
|
validateQuoteInput({ ...VALID_BODY, request_id: "not-uuid" }),
|
|
|
).toThrow("请求标识无效");
|
|
|
});
|
|
|
|
|
|
it("E4.3:selected_from_suggestions=false", () => {
|
|
|
expect(() =>
|
|
|
validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
pickup_address: {
|
|
|
...VALID_BODY.pickup_address,
|
|
|
selected_from_suggestions: false,
|
|
|
},
|
|
|
}),
|
|
|
).toThrow(ValidationError);
|
|
|
});
|
|
|
|
|
|
it("未确认 MotherShip 精确地址 → 拒绝询价", () => {
|
|
|
const { mothership_option_id: _a, mothership_display_label: _b, selected_from_mothership: _c, ...pickup } =
|
|
|
VALID_BODY.pickup_address;
|
|
|
expect(() =>
|
|
|
validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
pickup_address: pickup,
|
|
|
}),
|
|
|
).toThrow(ValidationError);
|
|
|
});
|
|
|
|
|
|
it("E4.7:quantity 字段拒绝", () => {
|
|
|
expect(() =>
|
|
|
rejectQuantityField({ ...VALID_BODY, quantity: 5 }),
|
|
|
).toThrow("请使用托盘数 pallet_count");
|
|
|
});
|
|
|
|
|
|
it("E4.6:pallet_count 超 25", () => {
|
|
|
expect(() =>
|
|
|
validateQuoteInput({ ...VALID_BODY, pallet_count: 26 }),
|
|
|
).toThrow(/托盘数超出/);
|
|
|
});
|
|
|
|
|
|
it("E4.6:单托重量超 9999", () => {
|
|
|
expect(() =>
|
|
|
validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
weight: { value: 10_000, unit: "lb" },
|
|
|
}),
|
|
|
).toThrow(/重量超出/);
|
|
|
});
|
|
|
|
|
|
it("E4.6:长度超 999 in", () => {
|
|
|
expect(() =>
|
|
|
validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
dimensions: { length: 1000, width: 40, height: 48, unit: "in" },
|
|
|
}),
|
|
|
).toThrow(/尺寸超出/);
|
|
|
});
|
|
|
|
|
|
it("边界:25 托、9999 lb、999×99×99 in 通过", () => {
|
|
|
const result = validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
weight: { value: 9_999, unit: "lb" },
|
|
|
dimensions: { length: 999, width: 99, height: 99, unit: "in" },
|
|
|
pallet_count: 25,
|
|
|
});
|
|
|
expect(result.palletCount).toBe(25);
|
|
|
});
|
|
|
|
|
|
it("SQL 注入拦截", () => {
|
|
|
expect(() =>
|
|
|
validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
pickup_address: {
|
|
|
...VALID_BODY.pickup_address,
|
|
|
street: "'; DROP TABLE users; --",
|
|
|
},
|
|
|
}),
|
|
|
).toThrow(/无效,请检查/);
|
|
|
});
|
|
|
|
|
|
it("kg/cm 换算", () => {
|
|
|
const result = validateQuoteInput({
|
|
|
...VALID_BODY,
|
|
|
weight: { value: 227, unit: "kg" },
|
|
|
dimensions: { length: 120, width: 100, height: 100, unit: "cm" },
|
|
|
});
|
|
|
expect(result.weightLb).toBe(500.45);
|
|
|
expect(result.dimLIn).toBe(47.24);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
describe("MOTHERSHIP_LIMITS", () => {
|
|
|
it("托盘 1-25", () => {
|
|
|
expect(MOTHERSHIP_LIMITS.palletCount.max).toBe(25);
|
|
|
});
|
|
|
});
|