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(501); expect(result.dimLIn).toBe(48); expect(result.dimWIn).toBe(40); expect(result.dimHIn).toBe(40); }); it("500 kg 换算为 1103 lb", () => { const result = validateQuoteInput({ ...VALID_BODY, weight: { value: 500, unit: "kg" }, }); expect(result.weightLb).toBe(1103); }); it("0.5 t 换算为 1103 lb", () => { const result = validateQuoteInput({ ...VALID_BODY, weight: { value: 0.5, unit: "t" }, }); expect(result.weightLb).toBe(1103); }); it("1.2 m 尺寸换算并向上取整", () => { const result = validateQuoteInput({ ...VALID_BODY, dimensions: { length: 1.2, width: 1, height: 1, unit: "m" }, }); expect(result.dimLIn).toBe(48); expect(result.dimWIn).toBe(40); expect(result.dimHIn).toBe(40); }); it("拒绝 ton/tons 歧义单位", () => { expect(() => validateQuoteInput({ ...VALID_BODY, weight: { value: 0.5, unit: "ton" }, }), ).toThrow(/歧义/); }); it("拒绝 Infinity 重量", () => { expect(() => validateQuoteInput({ ...VALID_BODY, weight: { value: Number.POSITIVE_INFINITY, unit: "lb" }, }), ).toThrow(ValidationError); }); }); describe("MOTHERSHIP_LIMITS", () => { it("托盘 1-25", () => { expect(MOTHERSHIP_LIMITS.palletCount.max).toBe(25); }); });