add passenger telemetry
This commit is contained in:
142
backend/test/passengerTelemetryAggregator.test.js
Normal file
142
backend/test/passengerTelemetryAggregator.test.js
Normal file
@@ -0,0 +1,142 @@
|
||||
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, []);
|
||||
});
|
||||
Reference in New Issue
Block a user