Files
gods-eye-view/src/sources/openFreeMap.test.mjs
T
daikaginza c6b43a365d fix(tiles): derive the tile origin from the configured TileJSON (#933)
`createVectorTileSource` took `tileJsonUrl` and `allowedOrigin` as independent
options, and `createOpenFreeMapSource` hardcoded both to the default host. A
caller that repointed only `tileJsonUrl` — the obvious single change — kept the
default `allowedOrigin`, so the TileJSON's absolute upstream `tiles[0]` passed
the origin check and every tile was fetched from the default host instead. No
error, no warning, and nothing reached the configured mirror.

`allowedOrigin` now defaults to the origin of `tileJsonUrl`, so repointing one
option is enough, and a mirror that serves the default host's TileJSON verbatim
fails with the existing `Invalid vector tile origin` instead of silently
bypassing itself. The origin check is unchanged: a TileJSON still cannot point
the client at a host the caller did not configure.

All three existing callers pass `allowedOrigin` explicitly
(`openFreeMap.js`, `alpr/source.js`, `traffic/flowSource.js`), and an explicit
value still overrides the derived one, so behaviour is unchanged for every
current call site.
2026-10-05 16:50:30 +07:00

361 lines
11 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import {
decodeOpenFreeMapTile,
openMapRoad,
polygonCentroid,
clipTileLine,
createOpenFreeMapSource,
} from './openFreeMap.js';
import { createVectorTileSource } from './vectorTiles.js';
const fixture = (name) =>
readFileSync(new URL(`../data/fixtures/${name}`, import.meta.url));
test('real z14 and z12 Austin tiles decode only drivable roads', () => {
for (const [z, x, y, name] of [
[14, 3743, 6745, 'ofm-austin-14-3743-6745.pbf'],
[12, 935, 1686, 'ofm-austin-12-935-1686.pbf'],
]) {
const { roads } = decodeOpenFreeMapTile(fixture(name), z, x, y);
assert.ok(roads.length > 0);
assert.ok(
roads.every((r) =>
[
'motorway',
'trunk',
'primary',
'secondary',
'tertiary',
'residential',
'unclassified',
].includes(r.type),
),
);
assert.ok(roads.some((r) => r.oneway === 1));
}
});
test('OpenMapTiles class mapping and reverse oneway preserve the animated-road contract', () => {
const coords = [
[-97, 30],
[-97.001, 30.001],
];
assert.deepEqual(
openMapRoad(coords, { class: 'minor', oneway: -1 }).coordinates,
coords.slice().reverse(),
);
assert.equal(openMapRoad(coords, { class: 'minor', oneway: -1 }).oneway, 1);
assert.equal(openMapRoad(coords, { class: 'minor' }).type, 'residential');
assert.equal(openMapRoad(coords, { class: 'service' }), null);
assert.equal(openMapRoad(coords, { class: 'primary', oneway: 0 }).oneway, 0);
for (const klass of [
'track',
'path',
'rail',
'pier',
'motorway_construction',
'bridge',
])
assert.equal(openMapRoad(coords, { class: klass }), null);
});
test('tile buffer clipping rejects slivers and does not join disconnected lines', () => {
const box = { west: 0, east: 1, south: 0, north: 1 };
assert.deepEqual(
clipTileLine(
[
[-1, 0.5],
[2, 0.5],
],
box,
),
[
[
[0, 0.5],
[1, 0.5],
],
],
);
assert.deepEqual(
clipTileLine(
[
[0, 0],
[0.00001, 0],
],
box,
),
[],
);
});
test('Camp Mabry tile emits an unnamed polygon and its centroid marker', () => {
const { military } = decodeOpenFreeMapTile(
fixture('ofm-camp-mabry-12-935-1685.pbf'),
12,
935,
1685,
);
assert.ok(military.length > 0);
const camp = military.find(
(record) =>
Math.abs(record.latitude - 30.314) < 0.03 &&
Math.abs(record.longitude + 97.763) < 0.03,
);
assert.ok(camp);
assert.equal(camp.name, 'Military area');
assert.equal(camp.kind, 'installation');
assert.equal(camp.class, 'military_land');
assert.ok(camp.footprint.length > 3);
assert.deepEqual(
polygonCentroid([
[0, 0],
[4, 0],
[0, 4],
[0, 0],
]),
[4 / 3, 4 / 3],
);
});
test('TileJSON is resolved once and decoded tile LRU prevents per-pan refetch', async () => {
const calls = [];
const source = createOpenFreeMapSource({
fetchImpl: async (url) => {
calls.push(url);
return url.endsWith('/planet')
? Response.json({
tiles: [
'https://tiles.openfreemap.org/planet/version/{z}/{x}/{y}.pbf',
],
})
: new Response(fixture('ofm-austin-14-3743-6745.pbf'));
},
});
const box = { south: 30.267, north: 30.268, west: -97.744, east: -97.743 };
await source.fetchBounds(box, { zoom: 14 });
await source.fetchBounds({ ...box, north: 30.269 }, { zoom: 14 });
assert.equal(calls.length, 2);
assert.equal(source.getStats().cacheEntries, 1);
source.clear();
assert.equal(source.getStats().cacheBytes, 0);
});
test('tile reads cap view size, concurrency and retained bytes, and reject oversized bodies', async () => {
let concurrent = 0,
peak = 0;
const source = createVectorTileSource({
template: 'https://tiles.example/{z}/{x}/{y}',
allowedOrigin: 'https://tiles.example',
maxTiles: 8,
concurrency: 2,
maxEntries: 2,
maxCacheBytes: 100,
decode: () => [1, 2, 3],
fetchImpl: async () => {
peak = Math.max(peak, ++concurrent);
await new Promise((r) => setImmediate(r));
concurrent--;
return new Response('bytes');
},
});
await assert.rejects(
source.fetchBounds(
{ south: 20, north: 40, west: -100, east: -80 },
{ zoom: 14 },
),
{ code: 'TILE_VIEW_TOO_WIDE' },
);
await source.fetchBounds(
{ south: 30.26, north: 30.3, west: -97.8, east: -97.7 },
{ zoom: 12 },
);
assert.ok(peak <= 2);
assert.ok(source.getStats().cacheEntries <= 2);
assert.ok(source.getStats().cacheBytes <= 100);
const huge = createVectorTileSource({
template: 'https://tiles.example/{z}/{x}/{y}',
allowedOrigin: 'https://tiles.example',
maxResponseBytes: 4,
decode: () => assert.fail('oversize decoded'),
fetchImpl: async () => new Response('too many bytes'),
});
await assert.rejects(
huge.fetchBounds(
{ south: 30.267, north: 30.268, west: -97.744, east: -97.743 },
{ zoom: 12 },
),
{ code: 'RESPONSE_TOO_LARGE' },
);
});
test('shared TileJSON survives one camera cancellation and aborts when its last owner leaves', async () => {
let finish,
calls = 0,
upstreamSignal;
const source = createOpenFreeMapSource({
fetchImpl: (url, { signal }) => {
calls++;
upstreamSignal = signal;
return new Promise((resolve, reject) => {
finish = () =>
resolve(
Response.json({
tiles: ['https://tiles.openfreemap.org/planet/v/{z}/{x}/{y}.pbf'],
}),
);
signal.addEventListener('abort', () => reject(signal.reason), {
once: true,
});
});
},
});
const a = new AbortController(),
b = new AbortController();
const first = source.getMetadata(a.signal),
second = source.getMetadata(b.signal);
a.abort();
await assert.rejects(first, { name: 'AbortError' });
assert.equal(upstreamSignal.aborted, false);
finish();
await second;
assert.equal(calls, 1);
const cancelled = createOpenFreeMapSource({
fetchImpl: (url, { signal }) => {
upstreamSignal = signal;
return new Promise((resolve, reject) =>
signal.addEventListener('abort', () => reject(signal.reason), {
once: true,
}),
);
},
});
const c = new AbortController();
const waiting = cancelled.getMetadata(c.signal);
c.abort();
await assert.rejects(waiting, { name: 'AbortError' });
assert.equal(upstreamSignal.aborted, true);
});
test('destroying a tile source aborts in-flight bodies and prevents stale cache refill', async () => {
let started;
const ready = new Promise((resolve) => {
started = resolve;
});
const source = createVectorTileSource({
template: 'https://tiles.example/{z}/{x}/{y}',
allowedOrigin: 'https://tiles.example',
decode: () => [],
fetchImpl: (url, { signal }) => {
started();
return new Promise((resolve, reject) =>
signal.addEventListener('abort', () => reject(signal.reason), {
once: true,
}),
);
},
});
const work = source.fetchBounds(
{ south: 30.267, north: 30.268, west: -97.744, east: -97.743 },
{ zoom: 12 },
);
await ready;
source.clear();
await assert.rejects(work, { name: 'AbortError' });
assert.equal(source.getStats().cacheEntries, 0);
});
// Representative provider properties from the Capitol, Camp Mabry greenbelt
// and commercial car parks. Tiny property fixtures retain the rejection cause.
test('public road admission rejects paths, private access, service areas and tunnels', () => {
const coordinates = [
[-97.7404, 30.2747],
[-97.7404, 30.2757],
];
for (const properties of [
{ class: 'minor', access: 'no' },
{ class: 'minor', access: 'private' },
{ class: 'path', subclass: 'footway' },
{ class: 'path', subclass: 'cycleway' },
{ class: 'track' },
{ class: 'minor', subclass: 'pedestrian' },
{ class: 'minor', subclass: 'steps', ramp: 1 },
{ class: 'minor', subclass: 'platform' },
{ class: 'service' },
{ class: 'service', service: 'parking_aisle' },
{ class: 'service', service: 'driveway' },
{ class: 'service', service: 'drive-through' },
{ class: 'service', service: 'alley' },
{ class: 'primary', brunnel: 'tunnel' },
])
assert.equal(
openMapRoad(coordinates, properties),
null,
JSON.stringify(properties),
);
for (const properties of [
{ class: 'minor' },
{ class: 'primary', ramp: 1 },
{ class: 'tertiary', brunnel: 'bridge' },
{ class: 'minor', brunnel: 'ford' },
]) {
const road = openMapRoad(coordinates, properties);
assert.equal(road.drivable, true);
assert.deepEqual(road.roadProperties, properties);
}
});
test('Capitol, greenbelt and commercial property fixtures exclude non-public driving space', () => {
const records = JSON.parse(fixture('traffic-road-access.json'));
for (const record of records) {
const road = openMapRoad(record.coordinates, record.properties);
assert.equal(
Boolean(road),
record.drivable,
`${record.area}: ${JSON.stringify(record.properties)}`,
);
}
for (const area of ['capitol', 'greenbelt', 'commercial']) {
assert.ok(records.some((r) => r.area === area && r.drivable));
assert.ok(records.some((r) => r.area === area && !r.drivable));
}
});
test('repointing tileJsonUrl at a mirror never falls back to the default tile host', async () => {
const urls = [];
const mirror = 'http://127.0.0.1:12348';
const source = createOpenFreeMapSource({
fetchImpl: async (url) => {
urls.push(url);
return url.endsWith('/planet')
? Response.json({ tiles: [`${mirror}/planet/v/{z}/{x}/{y}.pbf`] })
: new Response(fixture('ofm-austin-14-3743-6745.pbf'));
},
tileJsonUrl: `${mirror}/planet`,
});
await source.fetchBounds(
{ south: 30.264, north: 30.271, west: -97.75, east: -97.74 },
{ zoom: 14 },
);
assert.ok(urls.length > 1, 'tiles were fetched');
assert.deepEqual(
urls.filter((url) => !url.startsWith(mirror)),
[],
'a repointed source must not fetch from tiles.openfreemap.org',
);
});
test('a mirror that serves the default host TileJSON verbatim fails loudly', async () => {
// A transparent caching proxy returns upstream's TileJSON unchanged, whose
// tiles[0] is an absolute upstream URL. That must be an error, not 100% of
// tile traffic quietly leaving the configured mirror.
const source = createOpenFreeMapSource({
fetchImpl: async () =>
Response.json({
tiles: ['https://tiles.openfreemap.org/planet/v/{z}/{x}/{y}.pbf'],
}),
tileJsonUrl: 'http://127.0.0.1:12348/planet',
});
await assert.rejects(source.getMetadata(), { retryable: false });
});