Files
gods-eye-view/src/overpassOffload.test.mjs
T

476 lines
16 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { Readable } from 'node:stream';
import { randomUUID } from 'node:crypto';
import { overpassProxy } from '../server/providers/overpass.js';
import { militaryInstallationsProxy } from '../server/providers/military-installations.js';
import { regionalBriefProxy } from '../server/providers/regional/briefing.js';
import {
parseOverpassUpstreams,
resolveOverpassUpstreams,
} from '../server/providers/overpass/constants.js';
import { fetchOverpassPayload } from '../server/providers/overpass/transport.js';
import { _overpassCache } from '../server/providers/overpass/cache.js';
import { _militaryInstallationCache } from '../server/providers/military-installations/cache.js';
import {
militaryInstallationCacheKey,
quantizeMilitaryInstallationBox,
} from '../server/providers/military-installations/query.js';
import { createApplicationRequestServices } from './services/requests.js';
import { createInstallationSource } from './layers/installations/source.js';
import {
resolveOutlineWithRetry,
createAnnotationEngine,
} from './annotations/annotationEngine.js';
import { buildOverpassQuery } from './layers/alpr/records.js';
function env(t, value) {
const prior = process.env.OVERPASS_UPSTREAMS;
if (value === undefined) delete process.env.OVERPASS_UPSTREAMS;
else process.env.OVERPASS_UPSTREAMS = value;
t.after(() => {
if (prior === undefined) delete process.env.OVERPASS_UPSTREAMS;
else process.env.OVERPASS_UPSTREAMS = prior;
});
}
function routes(...plugins) {
const found = new Map();
for (const plugin of plugins)
plugin.configureServer({
middlewares: { use: (path, handler) => found.set(path, handler) },
});
return found;
}
async function call(handlers, url, init = {}) {
const parsed = new URL(url, 'http://localhost');
// Connect-style mounting: the handler sees the path below its mount point.
const mount = [...handlers.keys()]
.filter(
(path) =>
parsed.pathname === path || parsed.pathname.startsWith(`${path}/`),
)
.sort((a, b) => b.length - a.length)[0];
const req = Readable.from(init.body ? [Buffer.from(init.body)] : []);
Object.assign(req, {
method: init.method || 'GET',
url: parsed.pathname.slice(mount.length) + parsed.search || '/',
headers: init.headers || {},
socket: { remoteAddress: '127.0.0.1' },
});
let status, body;
let headers = {};
await handlers.get(mount)(req, {
// The cross-site gate answers through statusCode/setHeader, not writeHead.
set statusCode(s) {
status = s;
},
setHeader(name, value) {
headers[name] = value;
},
writeHead(s, h) {
status = s;
headers = { ...headers, ...h };
},
end(b) {
body = b;
},
});
return new Response(body, { status, headers });
}
test('lazy configuration validates HTTP(S), allows private hosts, replaces defaults and never exposes bad values', (t) => {
env(t);
assert.deepEqual(resolveOverpassUpstreams(), []);
process.env.OVERPASS_UPSTREAMS =
'http://localhost:1234/api, https://paid.example/query?key=secret, http://localhost:1234/api';
assert.deepEqual(resolveOverpassUpstreams(), [
'http://localhost:1234/api',
'https://paid.example/query?key=secret',
]);
assert.deepEqual(
parseOverpassUpstreams(
'ftp://bad, nonsense, javascript:bad, http://10.0.0.1/query',
),
['http://10.0.0.1/query'],
);
});
test('the shared transport refuses an unset chain without calling fetch', async (t) => {
env(t);
const payload = await fetchOverpassPayload('data=x', 1024, {
fetchImpl: () => assert.fail('default transport performed I/O'),
});
assert.equal(payload.status, 503);
assert.deepEqual(JSON.parse(payload.body), {
error: 'Detailed OpenStreetMap queries are not configured',
code: 'OVERPASS_NOT_CONFIGURED',
retryable: false,
});
});
test('zero egress: every Overpass consumer reaches real default handlers, and regional context never calls Nominatim', async (t) => {
env(t);
const seen = [];
t.mock.method(globalThis, 'fetch', async (url) => {
seen.push(String(url));
throw new Error('Network unavailable');
});
const handlers = routes(
overpassProxy(),
militaryInstallationsProxy(),
regionalBriefProxy(),
);
const queries = [
'[out:json];way["highway"="primary"](30.2,-97.8,30.3,-97.7);out geom;',
buildOverpassQuery(30.2, -97.8, 30.3, -97.7),
];
for (const q of queries) {
const response = await call(handlers, '/api/overpass', {
method: 'POST',
body: `data=${encodeURIComponent(q)}`,
});
assert.equal(response.status, 503);
assert.equal((await response.json()).code, 'OVERPASS_NOT_CONFIGURED');
}
// A fresh source for each operation proves every query crosses the server guard.
const operations = [
'getAdministrativeAreas',
'getAreaGeometry',
'getNeighborhoodAreas',
'getStreetAreas',
'getStreetLines',
'getFootprints',
'getEnclosingAreas',
'getMonuments',
'getFocusFootprints',
];
for (const operation of operations) {
const services = createApplicationRequestServices({
fetchImpl: (url, options) => call(handlers, url, options),
});
const result = await services.features[operation](
operation === 'getAreaGeometry'
? 3600012345
: { lat: 30.2672, lon: -97.7431 },
);
assert.equal(result.code, 'OVERPASS_NOT_CONFIGURED', operation);
assert.equal(result.retryable, false, operation);
}
const installations = await call(
handlers,
'/api/military-installations?south=30.2&west=-97.8&north=30.3&east=-97.7',
);
assert.equal(installations.status, 200);
assert.equal((await installations.json()).code, 'OVERPASS_NOT_CONFIGURED');
const regional = await call(
handlers,
'/api/regional-brief?latitude=30.2672&longitude=-97.7431',
);
assert.equal(regional.status, 200);
assert.equal((await regional.json()).place.source, 'Natural Earth');
assert.ok(
seen.every((url) => !/overpass|nominatim/i.test(new URL(url).hostname)),
JSON.stringify(seen),
);
});
test('the installations proxy refuses cross-site and forwarded requests before any Overpass work', async (t) => {
env(t);
t.mock.method(globalThis, 'fetch', () =>
assert.fail('unexpected upstream fetch'),
);
const handlers = routes(militaryInstallationsProxy());
const url =
'/api/military-installations?south=48.8&west=2.2&north=48.9&east=2.4';
for (const headers of [
{ 'sec-fetch-site': 'cross-site' },
{ 'sec-fetch-site': 'same-site' },
{ origin: 'https://foreign.example' },
{ origin: 'null' },
{ 'sec-fetch-site': 'same-origin', 'x-forwarded-for': '203.0.113.9' },
]) {
const response = await call(handlers, url, { headers });
assert.equal(response.status, 403, JSON.stringify(headers));
assert.equal(response.headers.get('cache-control'), 'no-store');
}
for (const headers of [
{},
{ origin: 'http://localhost:4173', 'sec-fetch-site': 'same-origin' },
]) {
const response = await call(handlers, url, {
headers: { host: 'localhost:4173', ...headers },
});
assert.notEqual(response.status, 403, JSON.stringify(headers));
}
});
test('an unconfigured server is probed once and never sent a feature query', async (t) => {
env(t);
t.mock.method(globalThis, 'fetch', () =>
assert.fail('unconfigured Overpass made a network request'),
);
const handlers = routes(overpassProxy());
const seen = [];
const services = createApplicationRequestServices({
fetchImpl: (url, options = {}) => {
seen.push(`${options.method || 'GET'} ${url}`);
return call(handlers, url, options);
},
});
for (const operation of [
'getNeighborhoodAreas',
'getFocusFootprints',
'getEnclosingAreas',
]) {
const result = await services.features[operation]({
lat: 37.7989,
lon: -122.4662,
});
assert.equal(result.code, 'OVERPASS_NOT_CONFIGURED', operation);
assert.equal(result.retryable, false, operation);
}
assert.deepEqual(seen, ['GET /api/overpass/status']);
env(t, 'http://localhost:9/api/interpreter');
const status = await call(handlers, '/api/overpass/status');
assert.equal(status.status, 200);
assert.equal(status.headers.get('cache-control'), 'no-store');
assert.deepEqual(await status.json(), { configured: true });
});
test('a server without the status probe still answers through the query itself', async () => {
const seen = [];
const services = createApplicationRequestServices({
fetchImpl: async (url, options = {}) => {
seen.push(`${options.method || 'GET'} ${url}`);
return String(url).endsWith('/status')
? Response.json({ error: 'Method Not Allowed' }, { status: 405 })
: Response.json({ elements: [] });
},
});
assert.deepEqual(await services.boundaries.query('fixture'), []);
assert.deepEqual(await services.boundaries.query('fixture'), []);
assert.deepEqual(seen, [
'GET /api/overpass/status',
'POST /api/overpass',
'POST /api/overpass',
]);
});
test('default handlers serve expired caches with original dates and no upstream header or refresh', async (t) => {
env(t);
t.mock.method(globalThis, 'fetch', () =>
assert.fail('cached default made network request'),
);
const handlers = routes(overpassProxy(), militaryInstallationsProxy());
const q = `[out:json];node(around:10,30,-97)["name"="${randomUUID()}"];out;`;
const body = `data=${encodeURIComponent(q)}`;
const cachedAt = Date.now() - 60 * 86400000;
_overpassCache.set(body, {
status: 200,
body: '{"elements":[]}',
cachedAt,
endpoint: 'https://secret.example/private?token=secret',
});
t.after(() => _overpassCache.delete(body));
const response = await call(handlers, '/api/overpass', {
method: 'POST',
body,
});
assert.equal(response.status, 200);
assert.equal(response.headers.get('x-overpass-cache'), 'STALE');
assert.equal(
response.headers.get('x-overpass-cached-at'),
new Date(cachedAt).toISOString(),
);
assert.equal(response.headers.get('x-overpass-upstream'), null);
assert.doesNotMatch(JSON.stringify([...response.headers]), /secret/);
const box = { south: 31.2, west: -97.8, north: 31.3, east: -97.7 };
const key = militaryInstallationCacheKey(
quantizeMilitaryInstallationBox(box),
);
_militaryInstallationCache.set(key, {
cachedAt,
payload: { elements: [], retrievedAt: new Date(cachedAt).toISOString() },
});
t.after(() => _militaryInstallationCache.delete(key));
const sites = await (
await call(
handlers,
'/api/military-installations?' + new URLSearchParams(box),
)
).json();
assert.equal(sites.status, 'stale');
assert.equal(sites.retrievedAt, new Date(cachedAt).toISOString());
});
test('configured refusal response has a safe JSON body, Retry-After and no URL header', async (t) => {
env(t, 'https://configured-redact.example/private?token=secret');
t.mock.method(
globalThis,
'fetch',
async (url) =>
new Response(url, { status: 406, headers: { 'Retry-After': '45' } }),
);
const handler = routes(overpassProxy());
const response = await call(handler, '/api/overpass', {
method: 'POST',
body:
'data=' +
encodeURIComponent(
`[out:json];node(around:10,33,-97)["name"="${randomUUID()}"];out;`,
),
});
assert.equal(response.status, 406);
assert.equal(response.headers.get('retry-after'), '45');
assert.equal(response.headers.get('x-overpass-upstream'), null);
assert.doesNotMatch(await response.text(), /secret|https:/);
});
test('feature capability misses stop source calls and deferred outline retry waits', async () => {
let requests = 0,
waits = 0;
const services = createApplicationRequestServices({
fetchImpl: async (url, options = {}) => {
if (options.method === 'POST') requests++;
return Response.json(
{ code: 'OVERPASS_NOT_CONFIGURED', retryable: false },
{ status: 503 },
);
},
});
const point = { lat: 30, lon: -97 };
const result = await resolveOutlineWithRetry(
() => services.features.getFootprints(point),
{
waitFn: async () => {
waits++;
},
},
);
assert.equal(result.unavailable, true);
assert.equal(
(await services.features.getEnclosingAreas(point)).unavailable,
true,
);
assert.equal(
(await services.features.getFocusFootprints(point)).unavailable,
true,
);
assert.equal(requests, 1);
assert.equal(waits, 0);
});
test('installation capability miss switches to tiles once, including later viewport loads', async () => {
let queries = 0,
tiles = 0;
const source = createInstallationSource({
fetchImpl: async () => {
queries++;
return Response.json(
{ code: 'OVERPASS_NOT_CONFIGURED', retryable: false },
{ status: 503 },
);
},
mapTiles: {
async fetchBounds() {
tiles++;
return {
tiles: [
{
military: [{ id: 'area', sources: [{ name: 'OpenStreetMap' }] }],
},
],
partial: false,
};
},
clear() {},
},
});
const box = { south: 30.2, north: 30.3, west: -97.8, east: -97.7 };
const first = await source.getMappedSites(box);
await source.getMappedSites(box);
assert.equal(queries, 1);
assert.equal(tiles, 2);
assert.ok(first.records[0].retrievedAt);
assert.equal(first.tileSource, true);
});
test('annotation capability miss keeps its pin and exposes the unavailable label without a synthetic ring', async (t) => {
const prior = globalThis.requestAnimationFrame;
const priorCancel = globalThis.cancelAnimationFrame;
globalThis.requestAnimationFrame = () => 1;
globalThis.cancelAnimationFrame = () => {};
const capability = {
unavailable: true,
code: 'OVERPASS_NOT_CONFIGURED',
retryable: false,
};
let updated,
event,
attempts = 0;
const engine = createAnnotationEngine({
viewer: {},
renderer: {
add() {},
update(mark) {
updated = mark;
},
remove() {},
sync() {},
destroy() {},
},
resolveTarget: async () => ({
lon: -97.74,
lat: 30.27,
height: 0,
label: 'Local building',
ring: null,
resolveOutline: async () => {
attempts++;
return capability;
},
}),
});
t.after(() => engine.destroy());
t.after(() => {
globalThis.requestAnimationFrame = prior;
globalThis.cancelAnimationFrame = priorCancel;
});
engine.onOutlineEvent((value) => {
event = value;
});
await engine.annotate([
{ type: 'area', target: 'Local building', footprint: true },
]);
for (let i = 0; i < 30; i++) await Promise.resolve();
assert.equal(engine.count(), 1);
assert.equal(attempts, 1);
assert.equal(updated.outlineUnavailable, true);
assert.ok(!updated.ring && !updated.synthesized);
assert.equal(engine.list()[0].outlineUnavailable, true);
assert.equal(event.message, 'Detailed outline unavailable');
});
test('configured URL credentials stay server-side and never appear in returned metadata', async () => {
const payload = await fetchOverpassPayload('data=x', 1024, {
endpoints: [
'https://operator:private%[email protected]/api?token=secret',
],
fetchImpl: async (url, options) => {
assert.equal(url, 'https://paid-credentials.example/api?token=secret');
assert.equal(
options.headers.Authorization,
'Basic ' + Buffer.from('operator:private key').toString('base64'),
);
assert.equal(options.redirect, 'error');
return Response.json({ elements: [] });
},
});
assert.equal(payload.status, 200);
assert.doesNotMatch(
JSON.stringify(payload),
/private|secret|operator|https:/,
);
});