Files
ln-bi/src/server/routes/vehicle-heatmap-model.test.ts
T
shishengliang 6ea1b3d5dd
ci/woodpecker/push/woodpecker Pipeline failed
revert: restore v1.1.14 (37a9f303)
2026-09-16 09:29:30 +08:00

148 lines
4.6 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
buildVehicleGrid,
buildVehicleRanking,
daysInclusive,
filterVehicleRecordsByRadius,
haversineKm,
parseNearbyQuery,
parseVehicleHeatmapQuery,
summarizeVehicleRecords,
vehicleGridPrecision,
type VehicleHeatmapRecord,
} from './vehicle-heatmap-model.js';
function record(overrides: Partial<VehicleHeatmapRecord> = {}): VehicleHeatmapRecord {
return {
date: '2026-07-01',
vin: 'VIN-A',
plate: '粤A00001',
time: '2026-07-01 08:00:00',
longitude: 120,
latitude: 30,
source: 'test',
sourceRecordId: '1',
...overrides,
};
}
test('车辆热力图查询继续使用固定默认日期和原有字符串处理规则', () => {
assert.deepEqual(parseVehicleHeatmapQuery({}), {
startDate: '2026-01-01',
endDate: '2026-07-13',
query: '',
batchModel: '',
metric: 'locations',
});
assert.deepEqual(parseVehicleHeatmapQuery({
startDate: '2026-06-01',
endDate: 'invalid',
query: ' 粤A ',
batchModel: ' 190台冷链 ',
metric: 'vehicles',
}), {
startDate: '2026-06-01',
endDate: '2026-07-13',
query: ' 粤A ',
batchModel: '190台冷链',
metric: 'vehicles',
});
});
test('附近查询继续沿用经纬度 Number 语义和 5 至 200 公里半径限制', () => {
assert.deepEqual(parseNearbyQuery({ lng: '', lat: '0', radiusKm: '0' }), {
center: { lng: 0, lat: 0 },
radiusKm: 50,
});
assert.deepEqual(parseNearbyQuery({ lng: '120', lat: '30', radiusKm: '-1' }), {
center: { lng: 120, lat: 30 },
radiusKm: 5,
});
assert.equal(parseNearbyQuery({ lng: '120', lat: '30', radiusKm: '500' }).radiusKm, 200);
assert.equal(parseNearbyQuery({ lng: 'bad', lat: '30' }).center, null);
});
test('网格精度继续按含首尾天数和筛选条件决定', () => {
assert.equal(daysInclusive('2026-07-01', '2026-07-14'), 14);
assert.equal(daysInclusive('2026-07-14', '2026-07-01'), 0);
assert.equal(vehicleGridPrecision('2026-07-01', '2026-07-14', '', ''), 3);
assert.equal(vehicleGridPrecision('2026-07-01', '2026-07-15', '', ''), 2);
assert.equal(vehicleGridPrecision('2026-07-01', '2026-07-15', ' ', ''), 3);
});
test('车辆网格继续区分位置次数和去重车辆数并保留插入顺序', () => {
const records = [
record({ vin: 'VIN-A', longitude: 120.1231, latitude: 30.9878 }),
record({ vin: 'VIN-A', longitude: 120.1232, latitude: 30.9879 }),
record({ vin: 'VIN-B', longitude: 120.1233, latitude: 30.9877 }),
record({ vin: 'VIN-C', longitude: 121, latitude: 31 }),
];
assert.deepEqual(buildVehicleGrid(records, 'locations', 3), {
points: [
{ lng: 120.123, lat: 30.988, count: 3 },
{ lng: 121, lat: 31, count: 1 },
],
max: 3,
});
assert.deepEqual(buildVehicleGrid(records, 'vehicles', 3), {
points: [
{ lng: 120.123, lat: 30.988, count: 2 },
{ lng: 121, lat: 31, count: 1 },
],
max: 2,
});
assert.deepEqual(buildVehicleGrid([], 'locations', 2), { points: [], max: 1 });
});
test('车辆排名继续按记录数、车牌排序并保留首末出现时间', () => {
const records = [
record({ vin: 'VIN-B', plate: '', time: '2026-07-02 10:00:00' }),
record({ vin: 'VIN-A', plate: '粤A00001', time: '2026-07-02 09:00:00' }),
record({ vin: 'VIN-B', plate: '粤B00001', time: '2026-07-01 08:00:00' }),
record({ vin: 'VIN-A', plate: '粤A00001', time: '2026-07-03 11:00:00' }),
];
assert.deepEqual(buildVehicleRanking(records), [
{
vin: 'VIN-A',
plateNumber: '粤A00001',
locationCount: 2,
firstSeen: '2026-07-02 09:00:00',
lastSeen: '2026-07-03 11:00:00',
},
{
vin: 'VIN-B',
plateNumber: '粤B00001',
locationCount: 2,
firstSeen: '2026-07-01 08:00:00',
lastSeen: '2026-07-02 10:00:00',
},
]);
assert.equal(buildVehicleRanking(records, 1).length, 1);
});
test('车辆统计和附近筛选继续使用 VIN、自然日去重及 Haversine 距离', () => {
const records = [
record({ vin: 'VIN-A', date: '2026-07-01' }),
record({ vin: 'VIN-A', date: '2026-07-02', longitude: 120.01 }),
record({ vin: 'VIN-B', date: '2026-07-02', longitude: 121 }),
];
assert.deepEqual(summarizeVehicleRecords(records), {
locationCount: 3,
vehicleCount: 2,
dayCount: 2,
});
assert.equal(haversineKm(120, 30, 120, 30), 0);
assert.ok(Math.abs(haversineKm(0, 0, 0, 1) - 111.195) < 0.01);
assert.deepEqual(
filterVehicleRecordsByRadius(records, { lng: 120, lat: 30 }, 5).map(({ vin, date }) => ({ vin, date })),
[
{ vin: 'VIN-A', date: '2026-07-01' },
{ vin: 'VIN-A', date: '2026-07-02' },
],
);
});