Merge remote-tracking branch 'origin/main' into fix/realtime-debug-log-hardening

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Pedro Lobato committed 2026-09-15 11:49:03 -05:00
commit 4fd168ff3c
332 files changed
+56273 -6359

No files matched your search

+90
View File
@@ -1,4 +1,5 @@
import test from 'node:test';
import { execFileSync } from 'node:child_process';
import assert from 'node:assert/strict';
import {
mkdtemp,
@@ -93,3 +94,92 @@ test(
);
},
);
test('runtime discovery includes new nested modules and respects Git and formatter exclusions', async (t) => {
const root = await fixture(t);
execFileSync('git', ['init', '-q'], { cwd: root });
await mkdir(path.join(root, 'src/nested'), { recursive: true });
await mkdir(path.join(root, 'vendor'), { recursive: true });
await writeFile(
path.join(root, 'scripts/format-runtime.json'),
JSON.stringify({ roots: ['src'] }),
);
await writeFile(path.join(root, '.gitignore'), 'src/ignored.js\n');
await writeFile(path.join(root, '.prettierignore'), 'src/generated.js\n');
const source = 'export const value="yes"';
for (const name of [
'src/nested/new.js',
'src/module.mjs',
'src/check.test.mjs',
'src/ignored.js',
'src/generated.js',
'vendor/other.js',
])
await writeFile(path.join(root, name), source);
const expected = ['adopted.js', 'src/module.mjs', 'src/nested/new.js'];
assert.deepEqual(
(await formatAdoptedFiles(root, '--check')).changed,
expected,
);
await formatAdoptedFiles(root, '--write');
assert.deepEqual((await formatAdoptedFiles(root, '--check')).changed, []);
for (const name of [
'src/check.test.mjs',
'src/ignored.js',
'src/generated.js',
'vendor/other.js',
])
assert.equal(await readFile(path.join(root, name), 'utf8'), source);
});
test('runtime scope rejects unsafe roots before formatting existing entries', async (t) => {
for (const roots of [
[],
['src', 'src'],
['../outside'],
['/absolute'],
['.'],
['src/*'],
]) {
const root = await fixture(t);
await writeFile(
path.join(root, 'scripts/format-runtime.json'),
JSON.stringify({ roots }),
);
await assert.rejects(
formatAdoptedFiles(root, '--write'),
/Runtime formatting roots/,
);
assert.equal(
await readFile(path.join(root, 'adopted.js'), 'utf8'),
'const value="yes"\r\n',
);
}
});
test(
'discovered runtime symlinks cannot escape the repository',
{ skip: process.platform === 'win32' },
async (t) => {
const root = await fixture(t);
const other = await fixture(t);
execFileSync('git', ['init', '-q'], { cwd: root });
await mkdir(path.join(root, 'src'));
await writeFile(
path.join(root, 'scripts/format-runtime.json'),
JSON.stringify({ roots: ['src'] }),
);
await symlink(
path.join(other, 'adopted.js'),
path.join(root, 'src/outside.js'),
);
await assert.rejects(
formatAdoptedFiles(root, '--write'),
/repository files/,
);
assert.equal(
await readFile(path.join(root, 'adopted.js'), 'utf8'),
'const value="yes"\r\n',
);
},
);
+184
View File
@@ -0,0 +1,184 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { execFileSync } from 'node:child_process';
import {
mkdtempSync,
mkdirSync,
writeFileSync,
rmSync,
symlinkSync,
} from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { analyzeModule } from '../../scripts/module-analysis.mjs';
import { checkImportDirections } from '../../scripts/check-import-directions.mjs';
function fixture(t, exports = {}) {
const root = mkdtempSync(path.join(os.tmpdir(), 'gev-directions-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
execFileSync('git', ['init', '--quiet'], { cwd: root });
const write = (name, source = '') => {
mkdirSync(path.dirname(path.join(root, name)), { recursive: true });
writeFileSync(path.join(root, name), source);
};
write(
'package.json',
JSON.stringify({ name: '@gods-eye-view/core', exports }),
);
write('src/data/feedState.js');
return { root, write };
}
test('module analysis parses dependencies and platform references without reading comments or property labels', () => {
assert.deepEqual(
analyzeModule(
"// import('node:fs'); document\nimport x from 'one'; export { x } from 'two'; export * from 'three'; import('four'); const data = { document: 1 }; data.window;",
),
{ imports: ['one', 'two', 'three', 'four'], browser: [] },
);
assert.deepEqual(
analyzeModule(
"const data = { element: document }; globalThis['navigator'];",
).browser,
['document', 'navigator'],
);
assert.throws(() => analyzeModule('import(name)'), /Computed/);
assert.throws(() => analyzeModule("require('node:fs')"), /ES imports/);
});
test('portable sources can share records, and standalone can assemble rendering', (t) => {
const { root, write } = fixture(t, {
'./layers/demo/source': './src/layers/demo/source.js',
});
write('src/layers/demo/source.js', "export { read } from './records.js';");
write('src/layers/demo/records.js', 'export const read = (value) => value;');
write('src/standalone/catalog.js', "import '../layers/demo/index.js';");
write('src/layers/demo/index.js', "import * as Cesium from 'cesium';");
assert.equal(checkImportDirections(root).modules, 5);
});
for (const [name, files, pattern] of [
[
'unreachable source imports rendering',
{
'src/layers/demo/source.js': "import './rendering.js';",
'src/layers/demo/rendering.js': '',
},
/Source imports rendering/,
],
[
'source reaches a platform through a record helper',
{
'src/layers/demo/source.js': "import './records.js';",
'src/layers/demo/records.js': "import 'cesium';",
},
/reaches platform\/rendering/,
],
[
'source reaches a DOM helper',
{
'src/sources/demo.js': "import '../data/helper.js';",
'src/data/helper.js': 'export const view = () => globalThis.document;',
},
/reaches browser globals/,
],
[
'browser imports Node without prefix',
{ 'src/unused.mjs': "import 'fs/promises';" },
/Node builtin/,
],
[
'browser hides Node outside src',
{
'src/unused.js': "import '../shared/helper.js';",
'shared/helper.js': "export * from 'node:fs';",
},
/Browser graph reaches Node/,
],
[
'browser imports server',
{
'src/data/demo.js': "import '../../server/helper.js';",
'server/helper.js': '',
},
/Browser imports server/,
],
[
'reusable module selects standalone',
{
'src/app/demo.js': "import '../standalone/catalog.js';",
'src/standalone/catalog.js': '',
},
/Reusable module imports standalone/,
],
[
'provider selects application setup',
{
'server/providers/demo.js': "import '../standalone/key-setup.js';",
'server/standalone/key-setup.js': '',
},
/Provider imports application/,
],
[
'voice controls reach protocol indirectly',
{
'src/voice/sessionCommands.js': "import './helper.js';",
'src/voice/helper.js': "import './realtimeSession.js';",
'src/voice/realtimeSession.js': '',
},
/Common voice controls import protocol/,
],
[
'dynamic import cannot evade direction',
{ 'src/app/demo.js': "const target = './module.js'; import(target);" },
/Computed/,
],
[
'browser imports test helper',
{
'src/app/demo.js': "import '../testSupport/helper.js';",
'src/testSupport/helper.js': '',
},
/non-runtime owner/,
],
]) {
test(`rejects ${name}`, (t) => {
const { root, write } = fixture(t);
for (const [file, body] of Object.entries(files)) write(file, body);
assert.throws(() => checkImportDirections(root), pattern);
});
}
test('self package imports cannot evade portable ownership', (t) => {
const { root, write } = fixture(t, { './view': './src/ui/view.js' });
write('src/sources/demo.js', "import '@gods-eye-view/core/view';");
write('src/ui/view.js');
assert.throws(() => checkImportDirections(root), /Source imports rendering/);
});
test('compatibility exceptions are limited to the two existing composition entries', (t) => {
const { root, write } = fixture(t);
write('src/ui.js', "import './standalone/catalog.js';");
write('src/standalone/catalog.js');
write('server/providers/local.js', "import '../standalone/key-setup.js';");
write('server/standalone/key-setup.js');
assert.doesNotThrow(() => checkImportDirections(root));
write('src/ui/new.js', "import '../standalone/catalog.js';");
assert.throws(
() => checkImportDirections(root),
/Reusable module imports standalone/,
);
});
test('symlink aliases do not hide renderer ownership', (t) => {
const { root, write } = fixture(t);
write('src/sources/demo.js', "import './helper/view.js';");
write('src/ui/view.js');
// Directory junctions exercise the same resolved edge without Windows symlink privileges.
symlinkSync(
path.join(root, 'src/ui'),
path.join(root, 'src/sources/helper'),
'junction',
);
assert.throws(() => checkImportDirections(root), /rendering|symlinks/);
});
+368
View File
@@ -0,0 +1,368 @@
// THE LAST-RESORT PLACE SEARCH ROUTE — what it is allowed to send upstream.
//
// The public instance permits one request per second. One request per second
// and an unbounded queue are incompatible: a burst of searches would keep the
// upstream busy long after everyone who asked has given up. These cases pin the
// queue bound, the shared answer for identical searches, the cancellation path,
// and the chain order that only reaches here when nothing else answered.
//
// Run with: npm test
import test from 'node:test';
import assert from 'node:assert/strict';
import {
fetchNominatimSearch,
geocodeProxy,
NOMINATIM_MAX_PENDING,
} from '../../server/providers/regional/place.js';
import { fetchRegionalJson } from '../../server/providers/regional/http.js';
import { localProviderPlugins } from '../../server/providers/local.js';
import { createPlaceSearch, createGoogleGeocoder } from '../search/index.js';
import { createPhotonGeocoder } from '../keylessGeocoder.js';
const HIT = [
{
lat: '43.1731',
lon: '-79.0384',
display_name: 'Niagara Falls, Ontario, Canada',
name: 'Niagara Falls',
addresstype: 'city',
boundingbox: ['43.05', '43.15', '-79.15', '-79.0'],
},
];
/** Mount the geocode route the way the dev server does. */
let clientCounter = 0;
function mountGeocode(
install = (server) => geocodeProxy().configureServer(server),
) {
// A fresh client address per mount: the per-client rate limiter is module
// state shared by every case in this file, and one case's burst must not
// decide another case's outcome.
clientCounter += 1;
const remoteAddress = `10.0.0.${clientCounter % 250}`;
const routes = new Map();
install({
middlewares: {
use(route, handler) {
routes.set(route, handler);
},
},
});
return async function request(path) {
const requested = new URL(path, 'http://localhost');
const handler = routes.get('/api/geocode');
if (!handler) throw new Error('no /api/geocode route');
// connect strips the mount prefix before the handler sees the request.
const rest =
requested.pathname.slice('/api/geocode'.length) + requested.search;
const req = {
method: 'GET',
url: rest[0] === '/' ? rest : `/${rest}`,
headers: {},
socket: { remoteAddress },
on() {},
};
return await new Promise((resolve, reject) => {
let status = 200;
let headers = {};
const res = {
writableEnded: false,
on() {},
writeHead(code, sent) {
status = code;
headers = sent || {};
return res;
},
end(body = '') {
res.writableEnded = true;
let parsed = null;
try {
parsed = body ? JSON.parse(String(body)) : null;
} catch {
parsed = String(body);
}
resolve({ status, headers, body: parsed });
},
};
Promise.resolve(handler(req, res)).catch(reject);
});
};
}
/** Answer the upstream from a stub, counting and optionally stalling calls. */
async function withUpstream(respond, run) {
const previous = globalThis.fetch;
const calls = [];
globalThis.fetch = async (url, options) => {
calls.push({ url: String(url), options });
return await respond(String(url), options, calls.length);
};
try {
return await run(calls);
} finally {
globalThis.fetch = previous;
}
}
const jsonResponse = (value) =>
new Response(JSON.stringify(value), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
test('the route is part of the application on both dev and preview', () => {
const plugin = localProviderPlugins().find(
(entry) => entry.name === 'geocode-proxy',
);
assert.ok(plugin, 'the application must compose the place-search route');
assert.equal(typeof plugin.configureServer, 'function');
assert.equal(typeof plugin.configurePreviewServer, 'function');
});
test('a search reaches the upstream identified, and the answer is cached', async () => {
const request = mountGeocode();
await withUpstream(
async () => jsonResponse(HIT),
async (calls) => {
const query = `niagara-${Date.now()}`;
const first = await request(`/api/geocode?q=${query}`);
assert.equal(first.status, 200);
assert.equal(first.body.status, 'OK');
assert.deepEqual(first.body.results[0].geometry.location, {
lat: 43.1731,
lng: -79.0384,
});
assert.equal(calls.length, 1);
const headers = calls[0].options.headers;
assert.match(
String(headers['User-Agent']),
/gods-eye-view\/\d/,
'the policy asks for a User-Agent identifying the application',
);
assert.ok(headers.Referer, 'a Referer is sent alongside it');
assert.equal(
calls[0].options.redirect,
'error',
'a fixed API endpoint may not be redirected elsewhere',
);
// The policy asks that results be cached; a repeat costs no request.
const repeat = await request(`/api/geocode?q=${query}`);
assert.equal(repeat.status, 200);
assert.equal(
calls.length,
1,
'the second identical search is served from cache',
);
},
);
});
test('identical searches in flight share one upstream call', async () => {
const request = mountGeocode();
const query = `coalesce-${Date.now()}`;
await withUpstream(
async () => {
await new Promise((resolve) => setTimeout(resolve, 40));
return jsonResponse(HIT);
},
async (calls) => {
const answers = await Promise.all([
request(`/api/geocode?q=${query}`),
request(`/api/geocode?q=${query}`),
request(`/api/geocode?q=${query}`),
]);
for (const answer of answers) assert.equal(answer.status, 200);
assert.equal(calls.length, 1, 'one upstream call serves all three');
},
);
});
test('a burst past the queue bound is refused rather than queued for minutes', async () => {
const request = mountGeocode();
const stamp = Date.now();
await withUpstream(
async () => jsonResponse([]),
async (calls) => {
const answers = await Promise.all(
Array.from({ length: 20 }, (_unused, index) =>
request(`/api/geocode?q=burst-${stamp}-${index}`),
),
);
const refused = answers.filter((answer) => answer.status === 429);
assert.ok(
refused.length > 0,
'a burst this size must be refused somewhere',
);
for (const answer of refused)
assert.equal(answer.headers['Retry-After'], '5');
assert.ok(
calls.length <= NOMINATIM_MAX_PENDING + 1,
`the upstream saw ${calls.length} of 20 searches; the queue bound is ${NOMINATIM_MAX_PENDING}`,
);
assert.equal(
answers.length,
20,
'every caller is answered, none is left hanging',
);
},
);
});
test('a query that is missing or too long never reaches the upstream', async () => {
const request = mountGeocode();
await withUpstream(
async () => jsonResponse(HIT),
async (calls) => {
for (const path of [
'/api/geocode',
'/api/geocode?q=',
'/api/geocode?q=%20%20',
`/api/geocode?q=${'x'.repeat(201)}`,
]) {
const answer = await request(path);
assert.equal(answer.status, 400, path);
}
assert.deepEqual(calls, []);
},
);
});
test('an upstream failure is a service answer, not a crash', async () => {
const request = mountGeocode();
await withUpstream(
async () => new Response('upstream down', { status: 502 }),
async () => {
const answer = await request(`/api/geocode?q=down-${Date.now()}`);
assert.equal(answer.status, 503);
assert.equal(answer.headers['Cache-Control'], 'no-store');
assert.match(String(answer.body.error), /temporarily unavailable/);
},
);
});
test('a search with no hit is a clean empty answer', async () => {
const request = mountGeocode();
await withUpstream(
async () => jsonResponse([]),
async () => {
const answer = await request(`/api/geocode?q=nowhere-${Date.now()}`);
assert.equal(answer.status, 200);
assert.equal(answer.body.status, 'ZERO_RESULTS');
assert.deepEqual(answer.body.results, []);
},
);
});
test('the chain reaches this route only when Google and Photon do not answer', async () => {
const asked = [];
const request = mountGeocode();
await withUpstream(
async (url) => {
if (url.includes('photon.komoot.io')) {
asked.push('photon');
return new Response('photon is down', { status: 503 });
}
asked.push('nominatim');
return jsonResponse(HIT);
},
async () => {
const search = createPlaceSearch({
providers: [
createGoogleGeocoder({
request() {
asked.push('google');
// An unconfigured provider contributes no verdict.
return null;
},
}),
createPhotonGeocoder({
fetchImpl: (...args) => globalThis.fetch(...args),
}),
createGoogleGeocoder({
request(query) {
return request(
`/api/geocode?q=${encodeURIComponent(query)}`,
).then(
(answer) =>
new Response(JSON.stringify(answer.body), {
status: answer.status,
}),
);
},
}),
],
});
const outcome = await search.geocode(`chain-${Date.now()}`);
assert.deepEqual(
asked,
['google', 'photon', 'nominatim'],
`Google first, then Photon, then this route (saw ${asked.join(' → ')})`,
);
assert.equal(outcome.place.lat, 43.1731);
assert.equal(outcome.fallbackUsed, true);
},
);
});
test('a search whose caller gave up is dropped before it costs a slot', async () => {
const abandoned = new AbortController();
abandoned.abort();
await withUpstream(
async () => jsonResponse(HIT),
async (calls) => {
await assert.rejects(
fetchNominatimSearch(`cancelled-${Date.now()}`, null, {
signal: abandoned.signal,
}),
(error) => error.code === 'NOMINATIM_ABANDONED',
);
assert.deepEqual(calls, [], 'a search nobody is waiting for is not sent');
},
);
});
test('the upstream deadline covers the body, not just the headers', async () => {
// A response whose headers arrive and whose body then never does. Before the
// deadline covered the body, the abort timer was already cleared by the time
// the first byte was due, so this read had no deadline at all.
const stalled = () => {
let cancelled = false;
const stream = new ReadableStream({
pull() {
return new Promise(() => {});
},
cancel() {
cancelled = true;
},
});
return {
response: new Response(stream, { status: 200 }),
wasCancelled: () => cancelled,
};
};
const previous = globalThis.fetch;
const { response, wasCancelled } = stalled();
globalThis.fetch = async (_url, options) => {
// The same signal must reach the body read, or aborting stops nothing.
options.signal.addEventListener('abort', () => {
void response.body?.cancel().catch(() => {});
});
return response;
};
try {
const started = Date.now();
await assert.rejects(
fetchRegionalJson('https://example.test/stalls', { timeoutMs: 300 }),
'a stalled body must fail rather than hang',
);
const elapsed = Date.now() - started;
assert.ok(elapsed < 5000, `gave up after ${elapsed} ms`);
assert.ok(wasCancelled(), 'the abort reached the body read');
} finally {
globalThis.fetch = previous;
}
});
+431
View File
@@ -0,0 +1,431 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { PbfWriter } from 'pbf';
import { transitProxy } from 'gods-eye-view/server/providers/transit';
import {
TRANSIT_BACKOFF_LADDER_MS,
TRANSIT_PROXY_TTL_MS,
} from 'gods-eye-view/sources/transit';
/** Mount the plugin and return a caller for its single route. */
function install(plugin, mode = 'configureServer') {
const routes = new Map();
plugin[mode]({
middlewares: {
use(route, handler) {
routes.set(route, handler);
},
},
});
assert.equal(routes.size, 1);
return async (url = '/', method = 'GET') => {
const res = {
headersSent: false,
writeHead(status, headers) {
Object.assign(this, {
status,
headers: new Headers(headers),
headersSent: true,
});
},
end(body) {
this.body = body;
},
};
await [...routes.values()][0]({ url, method }, res);
return res;
};
}
/** One GTFS-RT FeedMessage with the given header and vehicles. */
function feedBytes({
incrementality = 0,
timestamp = 1_700_000_000,
vehicles = [],
}) {
const writer = new PbfWriter();
const header = new PbfWriter();
header.writeStringField(1, '2.0');
header.writeVarintField(2, incrementality);
header.writeVarintField(3, timestamp);
writer.writeBytesField(1, header.finish());
for (const item of vehicles) {
const entity = new PbfWriter();
entity.writeStringField(1, item.id);
const vehicle = new PbfWriter();
const position = new PbfWriter();
position.writeFloatField(1, item.lat);
position.writeFloatField(2, item.lon);
vehicle.writeBytesField(2, position.finish());
vehicle.writeVarintField(5, item.timestamp ?? timestamp);
const descriptor = new PbfWriter();
descriptor.writeStringField(1, item.id);
vehicle.writeBytesField(8, descriptor.finish());
entity.writeBytesField(4, vehicle.finish());
writer.writeBytesField(2, entity.finish());
}
return writer.finish();
}
function upstreamResponse({ status = 200, headers = {}, bytes = null }) {
const map = new Map(
Object.entries(headers).map(([name, value]) => [name.toLowerCase(), value]),
);
return {
status,
ok: status >= 200 && status < 300,
headers: { get: (name) => map.get(String(name).toLowerCase()) ?? null },
body: null,
async arrayBuffer() {
const view = bytes || new Uint8Array(0);
return view.buffer.slice(
view.byteOffset,
view.byteOffset + view.byteLength,
);
},
};
}
/** A scripted upstream that records every request it is actually given. */
function scriptedUpstream(steps) {
const calls = [];
let index = 0;
const fetchImpl = async (url, init) => {
calls.push({ url, headers: init?.headers || {}, redirect: init?.redirect });
const step = steps[Math.min(index, steps.length - 1)];
index += 1;
if (typeof step === 'function') return step(url, init);
return step;
};
return { fetchImpl, calls };
}
const FULL = feedBytes({
vehicles: [{ id: 'bus-1', lat: 42.36, lon: -71.06 }],
});
test('a 304 revalidation is a success, not a redirect with a missing Location', async (t) => {
t.mock.timers.enable({ apis: ['Date'], now: 1_700_000_000_000 });
const { fetchImpl, calls } = scriptedUpstream([
upstreamResponse({ headers: { etag: 'W/"v1"' }, bytes: FULL }),
upstreamResponse({ status: 304, headers: { etag: 'W/"v1"' } }),
upstreamResponse({ status: 304, headers: { etag: 'W/"v1"' } }),
]);
const call = install(transitProxy({ fetchImpl }));
const first = await call('/vehicles/mbta');
assert.equal(first.status, 200);
assert.equal(first.headers.get('X-GEV-Cache'), 'MISS');
assert.equal(JSON.parse(first.body).count, 1);
// Past the freshness window the proxy asks again, conditionally.
t.mock.timers.setTime(Date.now() + TRANSIT_PROXY_TTL_MS + 1_000);
const second = await call('/vehicles/mbta');
assert.equal(calls[1].headers['If-None-Match'], 'W/"v1"');
assert.equal(second.status, 200, 'an unchanged feed is not a failure');
// MISS, not STALE-ERROR: the refresh SUCCEEDED. Treating 304 as a redirect
// made this a failure that happened to be masked by the serve-stale path.
assert.equal(second.headers.get('X-GEV-Cache'), 'MISS');
assert.equal(JSON.parse(second.body).count, 1);
// And the third one too: a 304 must not have started a backoff ladder.
t.mock.timers.setTime(Date.now() + TRANSIT_PROXY_TTL_MS + 1_000);
const third = await call('/vehicles/mbta');
assert.equal(third.status, 200);
assert.equal(third.headers.get('X-GEV-Cache'), 'MISS');
assert.equal(calls.length, 3, 'every poll past the TTL reached the operator');
assert.equal(third.headers.get('Retry-After'), null);
assert.equal(
third.headers.get('X-Transit-Backoff'),
null,
'no ladder was started',
);
});
test('a differential feed condemns the snapshot that preceded it', async (t) => {
t.mock.timers.enable({ apis: ['Date'], now: 1_700_000_000_000 });
const differential = feedBytes({
incrementality: 1,
vehicles: [{ id: 'bus-1', lat: 42.36, lon: -71.06 }],
});
const { fetchImpl } = scriptedUpstream([
upstreamResponse({ bytes: FULL }),
upstreamResponse({ bytes: differential }),
upstreamResponse({ bytes: differential }),
]);
const call = install(transitProxy({ fetchImpl }));
assert.equal((await call('/vehicles/mbta')).status, 200);
t.mock.timers.setTime(Date.now() + TRANSIT_PROXY_TTL_MS + 1_000);
const refused = await call('/vehicles/mbta');
assert.equal(refused.status, 502);
assert.match(JSON.parse(refused.body).error, /differential/);
// The next caller arrives INSIDE the cooldown the failure just started, which
// is precisely the path that used to hand back the condemned snapshot as a
// 200 because it was still inside the serve-stale window.
t.mock.timers.setTime(Date.now() + 2_000);
const after = await call('/vehicles/mbta');
assert.notEqual(
after.status,
200,
`served ${after.status} with ${after.body}`,
);
assert.equal(after.headers.get('X-GEV-Cache'), 'NONE');
assert.equal(after.headers.get('X-Transit-Backoff'), 'cooldown');
});
test('a failing operator is put on a cooldown instead of being re-asked every poll', async (t) => {
t.mock.timers.enable({ apis: ['Date'], now: 1_700_000_000_000 });
const { fetchImpl, calls } = scriptedUpstream([
upstreamResponse({ status: 500 }),
upstreamResponse({ status: 500 }),
]);
const call = install(transitProxy({ fetchImpl }));
const failed = await call('/vehicles/mbta');
assert.equal(failed.status, 502);
assert.equal(
failed.headers.get('Retry-After'),
String(TRANSIT_BACKOFF_LADDER_MS[0] / 1000),
);
assert.equal(calls.length, 1);
// Inside the cooldown the operator is not contacted at all.
t.mock.timers.setTime(Date.now() + 1_000);
const held = await call('/vehicles/mbta');
assert.equal(held.status, 503);
assert.equal(held.headers.get('X-Transit-Backoff'), 'cooldown');
assert.equal(calls.length, 1, 'the operator was left alone');
// Past the first rung it is tried again, and a second failure waits longer.
t.mock.timers.setTime(Date.now() + TRANSIT_BACKOFF_LADDER_MS[0]);
const retried = await call('/vehicles/mbta');
assert.equal(calls.length, 2);
assert.equal(
retried.headers.get('Retry-After'),
String(TRANSIT_BACKOFF_LADDER_MS[1] / 1000),
);
});
test('a redirect is followed only within the feed origin, and never contacted first', async (t) => {
t.mock.timers.enable({ apis: ['Date'], now: 1_700_000_000_000 });
const sameOrigin = scriptedUpstream([
upstreamResponse({
status: 302,
headers: { location: '/files/current.pb' },
}),
upstreamResponse({ bytes: FULL }),
]);
const call = install(transitProxy({ fetchImpl: sameOrigin.fetchImpl }));
const ok = await call('/vehicles/capmetro-austin');
assert.equal(ok.status, 200);
assert.equal(sameOrigin.calls.length, 2);
assert.equal(
sameOrigin.calls[1].url,
'https://data.texas.gov/files/current.pb',
);
assert.equal(sameOrigin.calls[0].redirect, 'manual');
const offOrigin = scriptedUpstream([
upstreamResponse({
status: 302,
headers: { location: 'https://cdn.elsewhere.test/current.pb' },
}),
upstreamResponse({ bytes: FULL }),
]);
const refuse = install(transitProxy({ fetchImpl: offOrigin.fetchImpl }));
const blocked = await refuse('/vehicles/capmetro-austin');
assert.equal(blocked.status, 504);
assert.equal(
offOrigin.calls.length,
1,
'the disallowed host was never contacted, only its Location read',
);
});
test('only registered feed ids reach an operator', async (t) => {
const { fetchImpl, calls } = scriptedUpstream([
upstreamResponse({ bytes: FULL }),
]);
const call = install(transitProxy({ fetchImpl }));
for (const path of [
'/vehicles/not-a-feed',
'/vehicles/..%2F..%2Fetc',
'/vehicles/',
'/elsewhere',
]) {
const missing = await call(path);
assert.equal(missing.status, 404, `${path} is not a door to an upstream`);
}
assert.equal(calls.length, 0, 'no operator was contacted for any of them');
const catalog = JSON.parse((await call('/feeds')).body);
assert.ok(catalog.feeds.length > 0);
for (const feed of catalog.feeds) {
assert.equal('url' in feed, false);
assert.equal('headers' in feed, false);
}
});
test('a 304 reports that the operator answered, not that the body is new', async (t) => {
// The body a revalidated feed keeps serving carries the fetch time of the
// payload it holds, which stops advancing the moment the operator's file
// stops changing. That is the right answer for "how old are these positions"
// and the wrong one for "when did we last hear from them" — read as the
// latter it aged a feed whose every request succeeded into silence.
t.mock.timers.enable({ apis: ['Date'], now: 1_700_000_000_000 });
const firstAt = Date.now();
const { fetchImpl } = scriptedUpstream([
upstreamResponse({ headers: { etag: 'W/"v1"' }, bytes: FULL }),
upstreamResponse({ status: 304, headers: { etag: 'W/"v1"' } }),
upstreamResponse({ status: 304, headers: { etag: 'W/"v1"' } }),
]);
const call = install(transitProxy({ fetchImpl }));
const first = await call('/vehicles/mbta');
assert.equal(Number(first.headers.get('X-Transit-Contact')), firstAt);
assert.equal(JSON.parse(first.body).fetchedAt, firstAt);
t.mock.timers.setTime(Date.now() + TRANSIT_PROXY_TTL_MS + 1_000);
const revalidatedAt = Date.now();
const second = await call('/vehicles/mbta');
assert.equal(
JSON.parse(second.body).fetchedAt,
firstAt,
'the positions are as old as they are',
);
assert.equal(
Number(second.headers.get('X-Transit-Contact')),
revalidatedAt,
'but the operator answered just now',
);
// Served from the fresh cache, the contact time is the last real contact —
// not the moment this request happened to arrive.
const third = await call('/vehicles/mbta');
assert.equal(third.headers.get('X-GEV-Cache'), 'HIT');
assert.equal(Number(third.headers.get('X-Transit-Contact')), revalidatedAt);
});
test('a feed served from cache during an outage does not claim fresh contact', async (t) => {
// The other half: STALE-ERROR must carry the LAST SUCCESSFUL contact, so the
// browser can tell a revalidated feed from a dead one.
t.mock.timers.enable({ apis: ['Date'], now: 1_700_000_000_000 });
const contactedAt = Date.now();
const { fetchImpl } = scriptedUpstream([
upstreamResponse({ bytes: FULL }),
upstreamResponse({ status: 500 }),
upstreamResponse({ status: 500 }),
]);
const call = install(transitProxy({ fetchImpl }));
await call('/vehicles/mbta');
t.mock.timers.setTime(Date.now() + TRANSIT_PROXY_TTL_MS + 1_000);
const during = await call('/vehicles/mbta');
assert.equal(during.headers.get('X-GEV-Cache'), 'STALE-ERROR');
assert.equal(
Number(during.headers.get('X-Transit-Contact')),
contactedAt,
'silence does not count as an answer',
);
});
// Include the provider-local storage contract in the repository's src test discovery.
import '../../server/providers/transitHistory.test.mjs';
test('history reads never fetch upstream, and 304/cache/failure replays never append', async (t) => {
t.mock.timers.enable({ apis: ['Date'], now: 1700000000000 });
const { calls, fetchImpl } = scriptedUpstream([
upstreamResponse({ bytes: FULL }),
upstreamResponse({ status: 304 }),
upstreamResponse({ status: 503 }),
]);
const plugin = transitProxy({ fetchImpl });
t.after(() => plugin.closeBundle());
const call = install(plugin);
const read = () => call('/trail/mbta/bus-1');
assert.equal(JSON.parse((await read()).body).fixes.length, 0);
assert.equal(calls.length, 0);
await call('/vehicles/mbta');
const initial = JSON.parse((await read()).body);
assert.equal(initial.fixes.length, 1);
await call('/vehicles/mbta');
assert.equal(calls.length, 1);
t.mock.timers.setTime(Date.now() + TRANSIT_PROXY_TTL_MS + 1);
await call('/vehicles/mbta');
assert.deepEqual(JSON.parse((await read()).body), initial);
t.mock.timers.setTime(Date.now() + TRANSIT_PROXY_TTL_MS + 1);
await call('/vehicles/mbta');
assert.deepEqual(JSON.parse((await read()).body), initial);
assert.equal(calls.length, 3);
for (const path of [
'/trail/mbta/%2Fescape',
'/trail/mbta/abc%00bad',
'/trail/mbta/%E0%A4%A',
`/trail/mbta/${'a'.repeat(257)}`,
'/trail/capmetro-austin/bus-1',
])
assert.equal((await call(path)).status, 404);
assert.equal(
calls.length,
3,
'malformed and non-retained reads cannot reach an operator',
);
});
test('history read admission is separate from upstream admission and closes with the server', async (t) => {
t.mock.timers.enable({ apis: ['Date'], now: 1700000000000 });
const { calls, fetchImpl } = scriptedUpstream([
upstreamResponse({ bytes: FULL }),
]);
const plugin = transitProxy({ fetchImpl });
t.after(() => plugin.closeBundle());
const call = install(plugin);
for (let i = 0; i < 30; i++)
assert.equal((await call('/trail/mbta/bus-1')).status, 200);
assert.equal((await call('/trail/mbta/bus-1')).status, 429);
assert.equal(calls.length, 0);
assert.equal((await call('/vehicles/mbta')).status, 200);
assert.equal(calls.length, 1);
});
test('development and preview mount the same transit service and close it on shutdown', async () => {
for (const mode of ['configureServer', 'configurePreviewServer']) {
let close;
let handler;
const plugin = transitProxy({
fetchImpl: async () => {
throw new Error('unexpected upstream');
},
});
plugin[mode]({
middlewares: {
use(path, fn) {
assert.equal(path, '/api/transit');
handler = fn;
},
},
httpServer: {
once(event, fn) {
assert.equal(event, 'close');
close = fn;
},
},
});
async function call(method) {
const res = {
writeHead(status) {
this.status = status;
},
end() {},
};
await handler({ url: '/feeds', method }, res);
return res.status;
}
assert.equal(await call('GET'), 200);
assert.equal(await call('TRACE'), 405);
close();
assert.equal(await call('GET'), 503);
plugin.closeBundle();
}
});
+631
View File
@@ -0,0 +1,631 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import {
chooseLiveCities,
selectStyle,
detectOn,
} from '../../scripts/qa-transit-controls.mjs';
import { createStyleParameters } from '../ui/styleParameters.js';
import { bindDisplayControls } from '../ui/displayControls.js';
import { thermalShader } from '../styles/thermal.js';
import {
cycleMode,
setMode,
getMode,
setDetectionTuning,
} from '../data/detection.js';
const pause = async () => {};
class Element extends EventTarget {
constructor() {
super();
this.children = [];
this.attributes = new Map();
this.classList = { contains: () => false };
}
setAttribute(name, value) {
this.attributes.set(name, value);
}
getAttribute(name) {
return this.attributes.get(name);
}
append(...children) {
this.children.push(...children);
}
appendChild(child) {
this.children.push(child);
}
replaceChildren() {
this.children = [];
}
scrollIntoView() {}
getBoundingClientRect() {
return { width: 100 };
}
}
function pageFixture(t) {
const prior = { document: globalThis.document, window: globalThis.window };
const elements = new Map();
const document = {
documentElement: { dataset: { gevStyle: 'normal' } },
createElement: () => new Element(),
getElementById: (id) => elements.get(id),
querySelector: () => null,
};
globalThis.document = document;
globalThis.window = {};
t.after(() => {
for (const [name, value] of Object.entries(prior)) {
if (value === undefined) delete globalThis[name];
else globalThis[name] = value;
}
});
const find = (selector) => {
if (selector.startsWith('#param-sliders ')) {
const label = selector.match(/aria-label="([^"]+)"/)[1];
return elements
.get('param-sliders')
.children.flatMap((row) => row.children)
.find((el) => el.getAttribute('aria-label') === label);
}
return elements.get(selector.slice(1));
};
const page = {
evaluate: async (fn, ...args) => fn(...args),
waitForFunction: async (fn, options, ...args) =>
assert.ok(fn(...args), 'UI condition must hold'),
waitForSelector: async (selector) =>
assert.ok(find(selector), `missing ${selector}`),
$eval: async (selector, fn, ...args) => {
const el = find(selector);
assert.ok(el, `missing ${selector}`);
return fn(el, ...args);
},
click: async (selector) =>
find(selector)?.dispatchEvent(new Event('click')),
};
return { page, elements, document };
}
test('live visual cities follow local operating hours in summer and winter', () => {
for (const date of ['2026-09-15T06:30:00Z', '2026-01-15T06:30:00Z']) {
assert.equal(chooseLiveCities(new Date(date))[0].id, 'helsinki');
}
assert.deepEqual(
chooseLiveCities(new Date('2026-09-15T17:00:00Z')).map((c) => c.id),
['boston', 'austin'],
);
for (let hour = 0; hour < 24; hour++) {
const now = new Date(`2026-09-15T${String(hour).padStart(2, '0')}:00:00Z`);
const cities = chooseLiveCities(now);
assert.ok(cities.length >= 1);
for (const city of cities) {
const local = Number(
new Intl.DateTimeFormat('en', {
timeZone: city.zone,
hour: 'numeric',
hourCycle: 'h23',
}).format(now),
);
assert.ok(local >= 6 && local < 22);
}
}
});
test('thermal controls wait for activation and use the real generated parameter rows', async (t) => {
const { page, document, elements } = pageFixture(t);
const container = new Element();
container.ownerDocument = document;
elements.set('param-sliders', container);
const parameters = createStyleParameters({ container });
t.after(() => parameters.destroy());
const values = Object.fromEntries(
Object.entries(thermalShader.uniforms).map(([key, meta]) => [
key,
meta.default,
]),
);
window.__godsEyeView = {
styleManager: {
setPanelCollapsed() {},
setStyle(name) {
document.documentElement.dataset.gevStyle = name;
parameters.render({
uniforms: thermalShader.uniforms,
readValue: (key) => values[key],
writeValue: (key, value) => {
values[key] = value;
},
onChange() {},
});
},
},
};
// A swallowed click resolves normally; the helper must notice no activation.
page.$eval = async (selector, fn, ...args) => {
if (selector.startsWith('.style-btn')) return fn(new Element(), ...args);
const label = selector.match(/aria-label="([^"]+)"/)[1];
const slider = container.children
.flatMap((row) => row.children)
.find((el) => el.getAttribute('aria-label') === label);
assert.ok(slider);
return fn(slider, ...args);
};
const how = await selectStyle(
page,
'thermal',
{ 'WHOT/BHOT': 1, Ironbow: 1 },
pause,
);
assert.match(how, /button did not activate/);
assert.equal(values.mode, 1);
assert.equal(values.palette, 1);
});
test('DETECT uses the real OFF/restore button and density input to reach DENSE', async (t) => {
const { page, elements } = pageFixture(t);
const button = new Element(),
slider = new Element();
elements.set('detection-toggle', button);
elements.set('detection-density-slider', slider);
button.getAttribute = (name) =>
name === 'aria-pressed'
? String(getMode() !== 'OFF')
: `Detection overlay: ${getMode().toLowerCase()}`;
setMode('BALANCED');
setMode('OFF');
const modes = [];
const controls = bindDisplayControls({
elements: { detectionButton: button, densitySlider: slider },
actions: {
cycleDetection() {
cycleMode();
modes.push(getMode());
},
setDensity(value) {
setDetectionTuning({ densityPct: Number(value) });
},
},
});
t.after(() => {
controls.destroy();
setMode('OFF');
});
assert.equal(await detectOn(page, pause), 'DENSE');
assert.deepEqual(modes, ['BALANCED', 'OFF', 'DENSE']);
const template = readFileSync(
new URL('../ui/templates/display-controls.html', import.meta.url),
'utf8',
);
assert.match(template, /id="detection-density-slider"/);
assert.match(template, /id="detection-toggle"/);
});
test('city motion checks load deterministic playback before reading rendered samples', () => {
const source = readFileSync(
new URL('../../scripts/qa-transit.mjs', import.meta.url),
'utf8',
);
const fixture = source.indexOf('_loadTransitFleetForTest(16');
assert.ok(fixture >= 0);
assert.ok(
source.indexOf('const motionFirst = await sampleVisible(page)', fixture) >
fixture,
);
assert.ok(
source.indexOf('const glideA = await sampleVisible(page)', fixture) >
fixture,
);
assert.ok(
source.indexOf('const headingA = await sampleScreen(page)', fixture) >
fixture,
);
assert.match(source, /scene.renderError.addEventListener/);
});
const { boundPageEvaluations, restoreRetainedTransit, sampleTransitPixels } =
await import('../../scripts/qa-transit-browser.mjs');
test('page promise deadline rejects a stuck enable or teardown and preserves ordinary results', async () => {
const page = { evaluate: async (fn, ...args) => fn(...args) };
boundPageEvaluations(page, 15);
assert.equal(await page.evaluate((a, b) => a + b, 3, 4), 7);
await assert.rejects(
page.evaluate(() => new Promise(() => {})),
/Transit QA page wait timed out after 15 ms/,
);
});
test('reload picks a surviving primed vehicle and reports unavailable snapshots as unexercised', async (t) => {
const previous = globalThis.window;
t.after(() => {
globalThis.window = previous;
});
let selected;
const entry = { key: 'mbta:survivor', record: { lon: -71.06, lat: 42.36 } };
const vehicles = new Map([[entry.key, entry]]);
globalThis.window = {
__godsEyeView: {
viewer: {
camera: {
positionCartographic: {
constructor: { fromDegrees: (...v) => v, toCartesian: (v) => v },
},
setView() {},
},
},
dataManager: {
setEnabled: async () => {},
layers: new Map([
[
'transit',
{
module: {
update: async () => {},
_transitStateForTest: () => ({ _vehicles: vehicles }),
_transitPartsForTest: () => ({
selection: {
selectVehicle: (key) => {
selected = key;
},
},
}),
},
},
],
]),
},
},
};
const primed = [
{ vehicleId: 'departed', oldestT: 10 },
{ vehicleId: 'survivor', oldestT: 20 },
];
assert.deepEqual(await restoreRetainedTransit(primed), {
key: entry.key,
oldest: 20,
});
assert.equal(selected, entry.key);
vehicles.clear();
const manager = window.__godsEyeView.dataManager;
const layer = manager.layers.get('transit').module;
layer._transitStateForTest = () => ({
_vehicles: vehicles,
_activeFeeds: new Map([['mbta', {}]]),
});
manager.setEnabled = async () => {
setTimeout(() => vehicles.set(entry.key, entry), 5);
};
assert.equal(
(await restoreRetainedTransit(primed)).key,
entry.key,
'wait for discovery instead of declaring every vehicle departed',
);
manager.setEnabled = async () => {};
assert.equal(
(
await restoreRetainedTransit([
{ ...primed[1], feedId: 'mbta', primedAt: Date.now() - 60000 },
])
).unexercised,
true,
'expired priming cannot pass',
);
layer._transitStateForTest = () => ({ _vehicles: vehicles });
vehicles.clear();
assert.equal((await restoreRetainedTransit(primed)).unexercised, true);
assert.equal((await restoreRetainedTransit([])).unexercised, true);
});
test('sensor sampler waits for postRender and reads framebuffer pixels; stopped rendering rejects and removes listener', async (t) => {
const previousWindow = globalThis.window,
previousDocument = globalThis.document,
previousImage = globalThis.Image;
t.after(() => {
globalThis.window = previousWindow;
globalThis.document = previousDocument;
globalThis.Image = previousImage;
});
const identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
let listener,
reads = 0,
render = true;
const gl = {
RGBA: 1,
UNSIGNED_BYTE: 2,
readPixels(x, y, w, h, format, type, buffer) {
reads++;
buffer.set([255, 255, 255, 255]);
},
};
const scene = {
camera: { viewMatrix: identity, frustum: { projectionMatrix: identity } },
canvas: {
width: 400,
height: 400,
clientWidth: 400,
clientHeight: 400,
getContext: () => gl,
},
postRender: {
addEventListener(fn) {
listener = fn;
return () => {
listener = null;
};
},
},
requestRender() {
if (render) queueMicrotask(() => listener?.());
},
};
const raster = new Uint8ClampedArray(16 * 16 * 4).fill(255);
globalThis.Image = class {
width = 16;
height = 16;
async decode() {}
};
globalThis.document = {
getElementById: () => null,
createElement: () => ({
getContext: () => ({
drawImage() {},
getImageData: () => ({ data: raster }),
}),
}),
};
globalThis.window = {
__godsEyeView: {
viewer: { scene },
dataManager: {
layers: new Map([
[
'transit',
{
module: {
_transitStateForTest: () => ({
_vehicles: new Map([
[
'bus',
{
key: 'bus',
mode: 'bus',
marker: {
show: true,
image: 'raster',
position: { x: 0, y: 0, z: 0 },
},
},
],
]),
}),
},
},
],
]),
},
},
};
const page = { evaluate: async (fn, ...args) => fn(...args) };
const pixels = await sampleTransitPixels(page, {});
assert.ok(
reads >= 13,
'centre, rings and background come from WebGL readPixels',
);
assert.ok(Math.abs(pixels[0].centre - 1) < 1e-6);
assert.equal(listener, null);
scene.pick = () => ({ id: 'overlapping-label' });
assert.equal(
(await sampleTransitPixels(page, {})).length,
0,
'a foreign pick across the sampled core disqualifies an obscured sprite',
);
scene.pick = ({ x }) => ({ id: x < 200 ? 'overlapping-sprite' : 'bus' });
assert.equal(
(await sampleTransitPixels(page, {})).length,
0,
'a clear centre cannot qualify a partially obscured core',
);
delete scene.pick;
const module = window.__godsEyeView.dataManager.layers.get('transit').module;
const getState = module._transitStateForTest;
module._transitStateForTest = () => {
const state = getState();
state._stylePreset = 'noir';
state._vehicles.get('bus').marker.position.x = 0.65;
return state;
};
assert.equal(
(await sampleTransitPixels(page, {})).length,
0,
'the intentional noir edge vignette is outside unobscured-core acceptance',
);
module._transitStateForTest = getState;
// The fixture returns fresh wrapper objects, so exercise raster and DOM
// eligibility independently of framebuffer brightness.
raster.fill(0);
assert.equal((await sampleTransitPixels(page, {})).length, 0);
raster.fill(255);
document.elementFromPoint = () => ({ tagName: 'DIV', closest: () => null });
assert.equal((await sampleTransitPixels(page, {})).length, 0);
delete document.elementFromPoint;
document.getElementById = () => ({
width: 400,
clientWidth: 400,
getContext: () => ({
getImageData: () => ({ data: new Uint8ClampedArray(144).fill(255) }),
}),
});
assert.equal((await sampleTransitPixels(page, {})).length, 0);
document.getElementById = () => null;
window.__godsEyeView.styleManager = {
getDetectionDiagnostics: () => ({
calloutRects: [{ x: 205, y: 195, w: 10, h: 10, alpha: 1 }],
}),
};
assert.equal(
(await sampleTransitPixels(page, {})).length,
0,
'plate over the edge excludes the whole sprite even with a clear centre',
);
delete window.__godsEyeView.styleManager;
document.getElementById = () => ({
width: 400,
clientWidth: 400,
getContext: () => ({
getImageData: (x, y, w = 1, h = 1) => ({
data:
w > 1
? new Uint8ClampedArray(w * h * 4)
: new Uint8ClampedArray(
x === 191 && [190, 193, 196].includes(y)
? [20, 30, 40, 255]
: x === 191 && y === 207
? [94, 240, 138, 255]
: [0, 0, 0, 0],
),
}),
}),
});
assert.deepEqual(
(await sampleTransitPixels(page, {}))[0].bracket,
[94, 240, 138],
'stroke centre is sampled away from the top label plate',
);
document.getElementById = () => null;
render = false;
t.mock.timers.enable({ apis: ['setTimeout'] });
const pending = sampleTransitPixels(page, {});
const rejected = assert.rejects(pending, /no postRender within 5000 ms/);
await new Promise((resolve) => setImmediate(resolve));
t.mock.timers.tick(5000);
await rejected;
assert.equal(listener, null);
});
test('preset baseline clears a previous city selection before comparing fleet sizes', async (t) => {
const { readTransitFleetStyle } =
await import('../../scripts/qa-transit-browser.mjs');
const prior = globalThis.window;
t.after(() => {
globalThis.window = prior;
});
const entry = {
key: 'mbta:tram',
marker: {
show: true,
width: 33.75,
image: 'raster-selected',
color: { toCssColorString: () => 'rgb(255,194,74)' },
},
};
const state = {
_selectedKey: entry.key,
_vehicles: new Map([[entry.key, entry]]),
};
globalThis.window = {
__godsEyeView: {
dataManager: {
layers: new Map([
[
'transit',
{
module: {
_transitStateForTest: () => state,
_transitPartsForTest: () => ({
selection: {
clearSelection() {
state._selectedKey = null;
entry.marker.width = 22.5;
entry.marker.image = 'raster-fleet';
},
},
}),
},
},
],
]),
},
},
};
const baseline = readTransitFleetStyle({ resetSelection: true });
assert.equal(baseline.width, 22.5);
assert.equal(baseline.selected, false);
assert.deepEqual(readTransitFleetStyle({ key: entry.key }), baseline);
});
test('allocation accounting includes Cesium render aliases and excludes QA helper subtrees', async () => {
const { allocationTotals } =
await import('../../scripts/qa-transit-scenes.mjs');
const node = (functionName, url, selfSize, children = []) => ({
callFrame: { functionName, url },
selfSize,
children,
});
const profile = {
head: node('(root)', '', 0, [
node('render2', 'cesium.js', 0, [
node('Scene4.render', 'cesium.js', 10, [
node('_drawOverlay', '/src/data/detection.js', 100),
node('', '/src/layers/transit/testing.js', 200, [
node('hypot', '', 300),
node('onPreRender', '/src/layers/transit/rendering.js', 20, [
node('sampleAt', '/src/data/contactPlayback.js', 30),
node('style', '/src/data/transitPresetStyle.js', 10),
]),
]),
]),
]),
]),
};
const { topAllocators, ...totals } = allocationTotals(profile);
assert.equal(topAllocators[0].bytes, 100);
assert.deepEqual(totals, {
frameBytes: 170,
layerFrameBytes: 60,
sharedOverlayBytes: 100,
qaBytes: 500,
});
});
test('public transit documentation uses courtesy attribution and the approved ground sentence', () => {
const data = readFileSync(
new URL('../../DATA_SOURCES.md', import.meta.url),
'utf8',
);
const rows = data
.split('\n')
.filter((line) =>
/^\| \*\*(MBTA|CapMetro|Metro Transit|OVapi|Entur|TransLink|HSL)/.test(
line,
),
);
assert.equal(rows.length, 7);
for (const row of rows) {
assert.doesNotMatch(
row,
/licen[cs]e|unlicensed|unstated|grants|permits|public.domain|CC BY|NLOD|terms|trademarks/i,
);
assert.match(row, /open realtime vehicle data/);
assert.match(row, /published for developer use and not against their use/);
}
const readme = readFileSync(
new URL('../../README.md', import.meta.url),
'utf8',
);
assert.ok(
readme.includes(
'Fifteen layers and map sources. **Thirteen have a keyless path.**',
),
);
assert.ok(
readme.includes(
'**Sits on the real ground.** Entity heights are aligned to work with Google 3D tiles, so aircraft park on aprons and cameras stand on street corners instead of floating.',
),
);
assert.doesNotMatch(
readme,
/geoid-aware|sampled against the _rendered_ terrain mesh/,
);
});
+141
View File
@@ -0,0 +1,141 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { createTransitService } from 'gods-eye-view/sources/transit-service';
import { createTransitSource } from 'gods-eye-view/layers/transit/source';
const request = (path, method = 'GET') => ({
url: `https://example.test${path}`,
method,
});
test('portable service confines requests to registered feeds and GET', async (t) => {
let calls = 0;
const service = createTransitService({
fetchImpl: async () => {
calls++;
throw new Error('unexpected');
},
});
t.after(service.close);
for (const path of [
'/other',
'/api/transit/vehicles/unknown',
'/api/transit/vehicles/https%3A%2F%2Fexample.test',
]) {
assert.equal((await service.handle(request(path))).status, 404);
}
for (const method of ['POST', 'HEAD', 'TRACE']) {
assert.equal(
(await service.handle(request('/api/transit/feeds', method))).status,
405,
);
}
const response = await service.handle(request('/api/transit/feeds'));
assert.ok(response instanceof Response);
assert.equal(response.status, 200);
assert.ok((await response.json()).feeds.some((feed) => feed.id === 'mbta'));
assert.equal(calls, 0);
service.close();
assert.equal(
(await service.handle(request('/api/transit/feeds'))).status,
503,
);
});
test('source sends snapshots and bounded history through the supplied transport', async () => {
const calls = [];
const controller = new AbortController();
const payload = {
version: 1,
feedId: 'mbta',
vehicleId: 'bus 1',
fixes: [],
epochs: [],
};
const source = createTransitSource({
fetchImpl: async (url, init) => {
calls.push({ url, init });
return Response.json(url.includes('/trail/') ? payload : { count: 0 });
},
});
assert.deepEqual(
await (
await source.requestSnapshot('mbta', { signal: controller.signal })
).json(),
{ count: 0 },
);
assert.deepEqual(
await source.getHistory('mbta', 'bus 1', { signal: controller.signal }),
payload,
);
assert.deepEqual(
calls.map(({ url }) => url),
['/api/transit/vehicles/mbta', '/api/transit/trail/mbta/bus%201'],
);
assert.ok(calls.every(({ init }) => init.signal === controller.signal));
controller.abort();
assert.throws(
() => source.requestSnapshot('mbta', { signal: controller.signal }),
{ name: 'AbortError' },
);
assert.throws(
() => source.getHistory('mbta', 'bus 1', { signal: controller.signal }),
{ name: 'AbortError' },
);
assert.equal(calls.length, 2);
});
test('source retains history validation and never retries through another transport', async () => {
let calls = 0;
const source = createTransitSource({
fetchImpl: async () => {
calls++;
return Response.json({
version: 1,
feedId: 'wrong',
vehicleId: 'bus',
fixes: [],
epochs: [],
});
},
});
await assert.rejects(
source.getHistory('mbta', 'bus'),
/Invalid transit history response/,
);
assert.equal(calls, 1);
await assert.rejects(
source.getHistory('mbta', '../bus'),
/Invalid transit vehicle identifier/,
);
assert.equal(calls, 1);
});
test('snapshot cancellation covers headers and body parsing', async () => {
for (const phase of ['headers', 'body']) {
const controller = new AbortController();
const source = createTransitSource({
fetchImpl: async () => {
if (phase === 'headers') controller.abort();
return {
ok: true,
status: 200,
headers: new Headers(),
json: async () => {
controller.abort();
return { vehicles: [] };
},
};
},
});
await assert.rejects(
async () => {
const response = await source.requestSnapshot('mbta', {
signal: controller.signal,
});
await response.json();
},
{ name: 'AbortError' },
);
}
});