143 lines
4.0 KiB
JavaScript
143 lines
4.0 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const test = require('node:test');
|
|
|
|
const { PassengerTelemetryAggregator } = require('../src/services/passengerTelemetryAggregator');
|
|
|
|
const NOW = new Date('2026-05-20T12:30:00Z');
|
|
const routeRows = [{
|
|
id: 1,
|
|
route_number: '12',
|
|
name: 'Route 12',
|
|
type: 'bus',
|
|
color: '#0066CC',
|
|
stops: [
|
|
{ stop_id: 1, sequence: 1, latitude: 55.485, longitude: 28.768 },
|
|
{ stop_id: 2, sequence: 2, latitude: 55.485, longitude: 28.778 }
|
|
]
|
|
}];
|
|
|
|
function createPool(rows = routeRows) {
|
|
return {
|
|
async query(sql, params = []) {
|
|
if (params.length > 0) {
|
|
return { rows: rows.filter((row) => row.id === params[0]) };
|
|
}
|
|
|
|
return { rows };
|
|
}
|
|
};
|
|
}
|
|
|
|
function createAggregator(options = {}) {
|
|
return new PassengerTelemetryAggregator({
|
|
pool: createPool(options.rows),
|
|
nowProvider: () => NOW,
|
|
...options
|
|
});
|
|
}
|
|
|
|
async function ingestCluster(aggregator, accuracyM = 18) {
|
|
await aggregator.ingest({
|
|
session_id: 'session-a',
|
|
latitude: 55.48502,
|
|
longitude: 28.7700,
|
|
accuracy_m: accuracyM,
|
|
speed_kmh: 31,
|
|
heading: 90,
|
|
observed_at: '2026-05-20T12:29:55Z',
|
|
route_id: 1
|
|
});
|
|
|
|
await aggregator.ingest({
|
|
session_id: 'session-b',
|
|
latitude: 55.48498,
|
|
longitude: 28.77015,
|
|
accuracy_m: accuracyM,
|
|
speed_kmh: 33,
|
|
heading: 92,
|
|
observed_at: '2026-05-20T12:29:58Z',
|
|
route_id: 1
|
|
});
|
|
}
|
|
|
|
test('one passenger point does not create a virtual vehicle', async () => {
|
|
const aggregator = createAggregator();
|
|
const result = await aggregator.ingest({
|
|
session_id: 'session-a',
|
|
latitude: 55.485,
|
|
longitude: 28.770,
|
|
accuracy_m: 18,
|
|
speed_kmh: 32,
|
|
heading: 90,
|
|
observed_at: '2026-05-20T12:29:55Z',
|
|
route_id: 1
|
|
});
|
|
|
|
assert.equal(result.accepted, true);
|
|
assert.equal(result.matched, true);
|
|
assert.deepEqual(result.virtualVehicles, []);
|
|
assert.deepEqual(await aggregator.getVirtualVehiclesForRoute(1), []);
|
|
});
|
|
|
|
test('nearby points from distinct sessions create a virtual crowdsourced vehicle', async () => {
|
|
const aggregator = createAggregator();
|
|
await ingestCluster(aggregator);
|
|
const vehicles = await aggregator.getVirtualVehiclesForRoute(1);
|
|
|
|
assert.equal(vehicles.length, 1);
|
|
assert.match(vehicles[0].vehicle_id, /^crowd:1:/);
|
|
assert.equal(vehicles[0].source, 'crowdsourced');
|
|
assert.equal(vehicles[0].source_ref, null);
|
|
assert.equal(vehicles[0].is_estimated, true);
|
|
assert.equal('session_id' in vehicles[0], false);
|
|
assert.equal('session_key' in vehicles[0], false);
|
|
});
|
|
|
|
test('poor accuracy lowers crowdsourced confidence', async () => {
|
|
const goodAggregator = createAggregator();
|
|
await ingestCluster(goodAggregator, 15);
|
|
const [goodVehicle] = await goodAggregator.getVirtualVehiclesForRoute(1);
|
|
|
|
const poorAggregator = createAggregator();
|
|
await ingestCluster(poorAggregator, 150);
|
|
const [poorVehicle] = await poorAggregator.getVirtualVehiclesForRoute(1);
|
|
|
|
assert.ok(goodVehicle.confidence > poorVehicle.confidence);
|
|
assert.equal(poorVehicle.source, 'crowdsourced');
|
|
});
|
|
|
|
test('points outside route geometry are accepted but ignored', async () => {
|
|
const aggregator = createAggregator();
|
|
const result = await aggregator.ingest({
|
|
session_id: 'session-a',
|
|
latitude: 56.0,
|
|
longitude: 29.5,
|
|
accuracy_m: 20,
|
|
observed_at: '2026-05-20T12:29:55Z',
|
|
route_id: 1
|
|
});
|
|
|
|
assert.equal(result.accepted, true);
|
|
assert.equal(result.matched, false);
|
|
assert.deepEqual(await aggregator.getVirtualVehiclesForRoute(1), []);
|
|
});
|
|
|
|
test('official telemetry suppresses nearby crowdsourced virtual vehicle', async () => {
|
|
const aggregator = createAggregator();
|
|
await ingestCluster(aggregator);
|
|
const [vehicle] = await aggregator.getVirtualVehiclesForRoute(1);
|
|
|
|
assert.ok(vehicle);
|
|
|
|
const suppressed = await aggregator.getVirtualVehiclesForRoute(1, {
|
|
officialVehicles: [{
|
|
vehicle_id: 99,
|
|
latitude: vehicle.latitude,
|
|
longitude: vehicle.longitude,
|
|
is_online: true
|
|
}]
|
|
});
|
|
|
|
assert.deepEqual(suppressed, []);
|
|
});
|