mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
256 lines
8.4 KiB
JavaScript
256 lines
8.4 KiB
JavaScript
// src/data/traffic.test.mjs
|
|
// Feed-state honesty for the traffic layer (roadmap L7).
|
|
//
|
|
// A launch-day stranger runs a keyless build. The layer then simulates
|
|
// traffic, and every surface it drives — the toggle chip, the panel meta
|
|
// line, the traffic sync chip — has to say so. The two pure helpers below own
|
|
// that contract; the layer's getStats() is a thin caller.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import trafficLayer, {
|
|
deriveTrafficFlowError,
|
|
trafficFeedPresentation,
|
|
} from './traffic.js';
|
|
import { DataLayerManager, layerFeedState } from './manager.js';
|
|
|
|
/**
|
|
* The app's live markers. Case-SENSITIVE on purpose: uppercase LIVE/GPS is
|
|
* how this UI asserts a real feed ("LIVE · TomTom flow", the old "initiating
|
|
* global GPS sync"), while lowercase "add TomTom key for live" names the
|
|
* remedy without claiming one.
|
|
*/
|
|
const LIVE_CLAIM = /\bLIVE\b|\bGPS\b|\breal[- ]?time\b/;
|
|
|
|
test('a superseded flow fetch is not an outage', () => {
|
|
assert.equal(
|
|
deriveTrafficFlowError({ name: 'AbortError', message: 'aborted' }),
|
|
null,
|
|
);
|
|
assert.equal(deriveTrafficFlowError(null), null);
|
|
assert.equal(deriveTrafficFlowError(undefined), null);
|
|
});
|
|
|
|
test('flow failures map onto short, specific reasons', () => {
|
|
const reason = (message) => deriveTrafficFlowError(new Error(message));
|
|
assert.equal(reason('flow tile 12/1/1: HTTP 503'), 'TomTom key unavailable');
|
|
assert.equal(
|
|
reason('flow tile 12/1/1: HTTP 429'),
|
|
'TomTom daily budget reached',
|
|
);
|
|
assert.equal(
|
|
reason('flow tile 12/1/1: HTTP 502'),
|
|
'TomTom upstream unreachable',
|
|
);
|
|
assert.equal(
|
|
reason('flow tile 12/1/1: HTTP 504'),
|
|
'TomTom upstream unreachable',
|
|
);
|
|
assert.equal(
|
|
reason('flow tile 12/1/1: HTTP 418'),
|
|
'TomTom flow error (HTTP 418)',
|
|
);
|
|
assert.equal(reason('flow fetch failed'), 'TomTom flow unavailable');
|
|
});
|
|
|
|
test('keyless traffic names the mode and the remedy, loading or idle', () => {
|
|
const idle = trafficFeedPresentation({ liveMode: false, fetching: false });
|
|
const loading = trafficFeedPresentation({ liveMode: false, fetching: true });
|
|
assert.equal(idle.mode, 'sim');
|
|
assert.equal(loading.mode, 'sim');
|
|
// Keyless is a designed fallback, not a fault — no error, or every keyless
|
|
// build would boot with a red chip.
|
|
assert.equal(idle.error, null);
|
|
assert.equal(loading.error, null);
|
|
// One terse line in both states; the chip's progress text carries "working".
|
|
assert.equal(idle.loadingLabel, 'SIMULATED — add TomTom key for live');
|
|
assert.equal(loading.loadingLabel, 'SIMULATED — add TomTom key for live');
|
|
});
|
|
|
|
test('no keyless label ever implies a live feed', () => {
|
|
const labels = [
|
|
trafficFeedPresentation({ liveMode: false, fetching: false }),
|
|
trafficFeedPresentation({ liveMode: false, fetching: true }),
|
|
trafficFeedPresentation({ statusUnavailable: true }),
|
|
trafficFeedPresentation({
|
|
liveMode: true,
|
|
flowError: 'TomTom flow unavailable',
|
|
}),
|
|
trafficFeedPresentation({
|
|
liveMode: true,
|
|
fetching: true,
|
|
flowError: 'TomTom flow unavailable',
|
|
}),
|
|
].map((feed) => feed.loadingLabel);
|
|
for (const label of labels) {
|
|
assert.ok(!LIVE_CLAIM.test(label), `label implies live data: ${label}`);
|
|
assert.ok(
|
|
label.startsWith('SIMULATED'),
|
|
`fallback label must lead with the mode: ${label}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('simulating because the status probe failed reads differently from keyless by design', () => {
|
|
const probeDown = trafficFeedPresentation({ statusUnavailable: true });
|
|
assert.equal(probeDown.mode, 'sim');
|
|
assert.equal(
|
|
probeDown.loadingLabel,
|
|
'SIMULATED — traffic service unreachable',
|
|
);
|
|
});
|
|
|
|
test('a healthy keyed layer reports live flow with its real coverage', () => {
|
|
const idle = trafficFeedPresentation({ liveMode: true, coveragePct: 87 });
|
|
assert.deepEqual(idle, {
|
|
mode: 'live',
|
|
error: null,
|
|
loadingLabel:
|
|
'LIVE · Roads: OpenStreetMap · Flow: TomTom · 87% cov · Unmatched: simulated',
|
|
});
|
|
assert.equal(
|
|
trafficFeedPresentation({ liveMode: true, fetching: true }).loadingLabel,
|
|
'Syncing flow · Roads: OpenStreetMap · Flow: TomTom · Unmatched: simulated',
|
|
);
|
|
});
|
|
|
|
test('a mid-session flow outage degrades instead of reporting stale live coverage', () => {
|
|
const down = trafficFeedPresentation({
|
|
liveMode: true,
|
|
flowError: 'TomTom daily budget reached',
|
|
coveragePct: 87, // last-good number — must not be presented as current
|
|
});
|
|
// error and loadingLabel are ONE string: the manager's error branch renders
|
|
// `error` and drops `loadingLabel`, so the copy has to live in both.
|
|
assert.equal(down.error, 'SIMULATED — TomTom daily budget reached');
|
|
assert.equal(down.loadingLabel, down.error);
|
|
assert.ok(!down.loadingLabel.includes('87'));
|
|
const busy = trafficFeedPresentation({
|
|
liveMode: true,
|
|
fetching: true,
|
|
flowError: 'TomTom daily budget reached',
|
|
});
|
|
assert.deepEqual(
|
|
busy,
|
|
down,
|
|
'the degraded state reads the same whether or not a load is in flight',
|
|
);
|
|
});
|
|
|
|
test('the rendered steady-state meta line carries the SIMULATED copy', () => {
|
|
const mgr = new DataLayerManager({});
|
|
const stats = (feed) => ({ count: 544, lastUpdate: Date.now(), ...feed });
|
|
assert.equal(
|
|
mgr._buildMetaText({
|
|
source: 'OpenStreetMap',
|
|
stats: stats(trafficFeedPresentation({ liveMode: false })),
|
|
}),
|
|
'FALLBACK · OpenStreetMap · SIMULATED — add TomTom key for live',
|
|
);
|
|
assert.equal(
|
|
mgr._buildMetaText({
|
|
source: 'OpenStreetMap',
|
|
stats: stats(
|
|
trafficFeedPresentation({
|
|
liveMode: true,
|
|
flowError: 'TomTom daily budget reached',
|
|
}),
|
|
),
|
|
}),
|
|
'DEGRADED · OpenStreetMap · SIMULATED — TomTom daily budget reached',
|
|
);
|
|
});
|
|
|
|
test('the manager reads keyless as FALLBACK and an outage as DEGRADED', () => {
|
|
const settled = { count: 4200, lastUpdate: Date.now() };
|
|
assert.equal(
|
|
layerFeedState({
|
|
...settled,
|
|
...trafficFeedPresentation({ liveMode: false }),
|
|
}),
|
|
'fallback',
|
|
);
|
|
assert.equal(
|
|
layerFeedState({
|
|
...settled,
|
|
...trafficFeedPresentation({ liveMode: true }),
|
|
}),
|
|
'nominal',
|
|
);
|
|
assert.equal(
|
|
layerFeedState({
|
|
...settled,
|
|
...trafficFeedPresentation({
|
|
liveMode: true,
|
|
flowError: 'TomTom flow unavailable',
|
|
}),
|
|
}),
|
|
'degraded',
|
|
);
|
|
});
|
|
|
|
test('the shipped layer boots keyless-honest before any status check', () => {
|
|
const stats = trafficLayer.getStats();
|
|
assert.equal(stats.mode, 'sim');
|
|
assert.equal(stats.error, null);
|
|
assert.ok(
|
|
!LIVE_CLAIM.test(stats.loadingLabel),
|
|
`boot label implies live data: ${stats.loadingLabel}`,
|
|
);
|
|
assert.equal(layerFeedState(stats), 'fallback');
|
|
});
|
|
|
|
test('traffic can be destroyed before its first enable and destroyed repeatedly', async () => {
|
|
const { default: traffic } = await import('./traffic.js');
|
|
const viewer = { camera: { changed: { removeEventListener() {} } } };
|
|
assert.doesNotThrow(() => traffic.destroy(viewer));
|
|
assert.doesNotThrow(() => traffic.destroy(viewer));
|
|
assert.equal(traffic.getStats().count, 0);
|
|
});
|
|
|
|
test('zero matched dots never claims live coverage', () => {
|
|
const feed = trafficFeedPresentation({ liveMode: true, coveragePct: 0 });
|
|
assert.equal(feed.mode, 'live');
|
|
assert.match(feed.loadingLabel, /^SIMULATED/);
|
|
assert.match(feed.loadingLabel, /TomTom \(no matches\)/);
|
|
assert.ok(!LIVE_CLAIM.test(feed.loadingLabel));
|
|
});
|
|
|
|
test('the traffic layer source passes fetchFlowDetail through, so stale tiles reach the layer', async () => {
|
|
const { trafficSource, configureTrafficSource } =
|
|
await import('./traffic.js');
|
|
assert.equal(typeof trafficSource.fetchFlowDetail, 'function');
|
|
const base = {
|
|
requestRoads: async () => ({}),
|
|
getStatus: async () => ({}),
|
|
fetchFlowForBounds: async () => ['segment'],
|
|
};
|
|
let release = configureTrafficSource({
|
|
...base,
|
|
fetchFlowDetail: async () => ({
|
|
segments: ['segment'],
|
|
stale: true,
|
|
oldestTileAt: 1000,
|
|
}),
|
|
});
|
|
try {
|
|
assert.deepEqual(await trafficSource.fetchFlowDetail({}), {
|
|
segments: ['segment'],
|
|
stale: true,
|
|
oldestTileAt: 1000,
|
|
});
|
|
} finally {
|
|
release();
|
|
}
|
|
// A source without fetchFlowDetail answers with fresh segments.
|
|
release = configureTrafficSource(base);
|
|
try {
|
|
assert.deepEqual(await trafficSource.fetchFlowDetail({}), {
|
|
segments: ['segment'],
|
|
stale: false,
|
|
oldestTileAt: null,
|
|
});
|
|
} finally {
|
|
release();
|
|
}
|
|
});
|