250 lines
8.6 KiB
TypeScript
250 lines
8.6 KiB
TypeScript
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> = {}): 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<string>(),
|
|
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), ['业务一部', '业务二部', '业务十部', '未分配部门', '公务车']);
|
|
});
|