import assert from 'node:assert/strict'; import test from 'node:test'; import type { Vehicle, VehicleRow } from '../../types.js'; import { SUMMARY_TYPE_FILTERS, VEHICLE_TYPE_FILTERS, classifyVehicleType, compareModelNames, deriveModelTag, deriveType, filterByLocation, mapInventoryRegion, mapMacroRegion, mapOwnership, mapRegion, mapStatus, resolveCity, transformRow, } from './model.js'; import { addDateDays, compareDepartmentNames, countByType, getMileageStats, getStats, isDateParam, listDateRange, normalizeDateRange, } from './utils.js'; function vehicle(overrides: Partial = {}): Vehicle { return { id: '1', plateNumber: '粤A00001', vin: 'VIN-1', type: '4.5T', model: '现代4.5T普货', color: '白色', location: '广东', region: '广东', province: '广东省', city: '广州市', status: 'Operating', operationStatus: '2', ownership: 'Self', rentCompany: '', contractNo: null, customerName: null, orgName: null, departmentName: null, subjectOrg: null, projectName: null, customerManager: null, managerId: null, brandLabel: null, ...overrides, }; } test('preserves display, inventory and macro region mappings', () => { assert.equal(mapRegion(null, null), '其他'); assert.equal(mapRegion('广东省', '杭州市'), '广东'); assert.equal(mapRegion('广东省', '浙江省'), '嘉兴'); assert.equal(mapRegion('广东省', '未知市'), '广东'); assert.equal(mapRegion(null, '乌鲁木齐市'), '新疆'); assert.equal(mapInventoryRegion('嘉兴'), '江浙沪'); assert.equal(mapInventoryRegion('北京'), '其它'); assert.equal(mapMacroRegion('山东省', '青岛市'), '华东'); assert.equal(mapMacroRegion(null, '成都市'), '西南'); assert.equal(mapMacroRegion(null, null), '其他'); assert.equal(resolveCity('[北京市]', null), '北京市'); assert.equal(resolveCity(null, '[上海市]'), '上海市'); }); test('keeps vehicle status precedence and ownership aliases', () => { assert.equal(mapStatus('2', '4'), 'Pending'); assert.equal(mapStatus('1', '14'), 'Abnormal'); assert.equal(mapStatus('租赁', null), 'Operating'); assert.equal(mapStatus('待运营', null), 'Inventory'); assert.equal(mapStatus(null, null), 'Inventory'); assert.equal(mapOwnership('0'), 'Self'); assert.equal(mapOwnership('1'), 'Hanging'); assert.equal(mapOwnership('外租'), 'Leased'); assert.equal(mapOwnership(null), 'Unknown'); }); test('preserves type precedence, aliases and model sorting', () => { assert.equal(deriveType('重型集装箱半挂车', '1'), '挂车'); assert.equal(deriveType('4.5吨货车', '3'), '4.5T'); assert.equal(deriveType('氢能叉车', null), '叉车'); assert.equal(deriveModelTag('现代', '4.5吨货车', '白色', '自有', '忽略的公司'), '现代4.5T普货'); assert.equal( deriveModelTag('现代', '4.5吨货车', '白色', '外租', '广州开发区交投氢能运营管理有限公司'), '现代4.5T普货(交投)', ); assert.equal(deriveModelTag(null, null, null, null, null), '未知车型'); const models = ['未知车型', '49T宇通', '现代4.5T普货(恒运)', '现代4.5T普货']; assert.deepEqual(models.sort(compareModelNames), [ '现代4.5T普货', '现代4.5T普货(恒运)', '49T宇通', '未知车型', ]); }); test('keeps summary and list meanings of other vehicle types distinct', () => { const trailer = vehicle({ type: '挂车', model: '挂车' }); const forklift = vehicle({ type: '叉车', model: '氢能叉车' }); const coldChain = vehicle({ type: '4.5T', model: '现代4.5T冷链(羚牛)' }); const summaryOther = SUMMARY_TYPE_FILTERS.find((item) => item.name === '其他'); assert.equal(classifyVehicleType(trailer), 'trailer'); assert.equal(classifyVehicleType(forklift), 'other'); assert.equal(classifyVehicleType(coldChain), 't4_5c'); assert.equal(summaryOther?.filter(trailer), true); assert.equal(VEHICLE_TYPE_FILTERS['其他'](trailer), false); assert.equal(VEHICLE_TYPE_FILTERS['其他'](forklift), true); }); test('keeps location filtering semantics for the ambiguous other region', () => { const assetOtherInGuangdong = vehicle({ id: 'asset-other', location: '其他', province: '广东省', city: '广州市' }); const macroOther = vehicle({ id: 'macro-other', location: '其他', province: null, city: null }); const jiaxing = vehicle({ id: 'jiaxing', location: '嘉兴', province: '浙江省', city: '嘉兴市' }); const vehicles = [assetOtherInGuangdong, macroOther, jiaxing]; assert.deepEqual(filterByLocation(vehicles, '其他', 'asset').map((item) => item.id), ['asset-other', 'macro-other']); assert.deepEqual(filterByLocation(vehicles, '其他').map((item) => item.id), ['macro-other']); assert.deepEqual(filterByLocation(vehicles, '江浙沪').map((item) => item.id), ['jiaxing']); }); test('transforms database rows without changing the route model contract', () => { const row: VehicleRow = { id: '88', 车牌号: '粤A00088', vin: 'VIN-88', 车辆品牌: 'hyundai', 车辆型号: 'raw-model', 车辆颜色: '白色', 租赁公司: '', 车辆归属状态Label: '自有', 车辆型号Label: '4.5吨货车', 车辆类型参数: '1', 库存区域: null, 车辆租赁状态: null, 车辆租赁状态Label: '自营', 是否营运: 1, 省: '广东省', 市: '广州市', 纬度: null, 经度: null, 车辆品牌Label: '现代', 合同ID: null, 合同编码: 'HT-1', 客户名称: '客户', 合同归属公司: '公司', 合同归属部门: '业务一部', 主体: '主体', 项目名称: '项目', 客户经理: '经理', 经理ID: '9', }; assert.deepEqual(transformRow(row), { id: '88', plateNumber: '粤A00088', vin: 'VIN-88', type: '4.5T', model: '现代4.5T普货', color: '白色', location: '广东', region: '广东', province: '广东省', city: '广州市', status: 'Operating', operationStatus: '自营', ownership: 'Self', rentCompany: '', contractNo: 'HT-1', customerName: '客户', orgName: '公司', departmentName: '业务一部', subjectOrg: '主体', projectName: '项目', customerManager: '经理', managerId: '9', brandLabel: '现代', }); }); test('preserves inventory, weekly-flow and mileage aggregation boundaries', () => { const vehicles = [ vehicle({ id: '1', plateNumber: 'A', status: 'Operating', type: '4.5T', model: '普货' }), vehicle({ id: '2', plateNumber: 'B', status: 'Inventory', location: '嘉兴', type: '4.5T', model: '冷链' }), vehicle({ id: '3', plateNumber: 'C', status: 'Abnormal', location: '北京', type: '挂车', model: '挂车' }), vehicle({ id: '4', plateNumber: 'D', status: 'Pending', type: '叉车', model: '叉车' }), ]; const weeklyIds = { pending: new Set(), delivered: new Set(['1', '9']), returned: new Set(['2']), replaced: new Set(['3']), }; assert.deepEqual(countByType(vehicles), { t4_5: 1, t4_5c: 1, t18: 0, t49: 0, trailer: 1, other: 1, total: 4, }); assert.deepEqual(getStats(vehicles, weeklyIds), { total: 4, inventory: 2, inventoryRegions: { 嘉兴: 1, 广东: 0, 北京: 1, 新疆: 0, 其他: 0 }, pending: 1, operating: 1, weeklyDelivered: 1, weeklyReturned: 1, weeklyReplaced: 1, }); assert.deepEqual(getMileageStats(vehicles, new Map([['A', 100.4], ['B', 0], ['C', -5], ['D', 200.4]])), { attendanceRate: 50, avgMileage: 150, }); }); test('preserves date validation, inclusive ranges, caps and department sorting', () => { assert.equal(isDateParam('2026-08-13'), true); assert.equal(isDateParam('2026-13-99'), true); assert.equal(isDateParam('2026/08/13'), false); assert.equal(addDateDays('2024-02-28', 1), '2024-02-29'); assert.deepEqual(listDateRange('2026-08-11', '2026-08-13'), ['2026-08-11', '2026-08-12', '2026-08-13']); assert.deepEqual(normalizeDateRange(undefined, undefined, new Date(2026, 7, 13, 12)), { start: '2026-07-15', end: '2026-08-13', }); assert.deepEqual(normalizeDateRange('2026-08-10', '2026-08-01'), { start: '2026-08-01', end: '2026-08-10', }); const capped = normalizeDateRange('2020-01-01', '2026-08-13'); assert.equal(listDateRange(capped.start, capped.end).length, 370); const departments = ['公务车', '未分配部门', '业务十部', '业务二部', '业务一部']; assert.deepEqual(departments.sort(compareDepartmentNames), ['业务一部', '业务二部', '业务十部', '未分配部门', '公务车']); });