Files
gods-eye-view/src/data/infrastructure.test.mjs
T
f4fcf267c6 fix(osm): stop using public Overpass by default (#648) (#742)
Traffic roads come from OpenFreeMap vector tiles, or TomTom flow tiles with a
key (Hybrid by default). Military areas use OpenFreeMap polygons with a bundled
worldwide name index; wide views show named points. ALPR reads an hourly
OpenStreetMap extract. Cockpit place context and country, state and US county
outlines use bundled Natural Earth and US Census data. Overpass is used only
when an operator configures their own endpoint.

Also: camera arrivals no longer land below ground, keyless terrain retries on
429, bounded relation geometry for configured Overpass, one shared
OpenStreetMap credit, and separate tile and API transports for embedders.

Co-authored-by: theGaryLarson <[email protected]>
Co-authored-by: Milan Khanal <[email protected]>
2026-09-28 19:07:57 -05:00

215 lines
6.4 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import * as Cesium from 'cesium';
import { createInfrastructureLayers } from 'gods-eye-view/infrastructure';
import { createLocalGeoJsonLayer } from 'gods-eye-view/infrastructure/geojson';
function services() {
const records = new Map();
const credited = [];
let selection;
return {
records,
credited,
showOsmCredit: (viewer) => credited.push(viewer),
overlayHost: { setEntries() {}, setVisible() {}, clearSource() {} },
registerEntityContext(entity, metadata) {
records.set(metadata.id, { entity, ...metadata });
},
selectEntityContext(entity) {
selection = entity;
},
clearSelectedEntityContextForLayer() {
selection = undefined;
},
removeEntityContextsForLayer(id) {
for (const [key, record] of records)
if (record.layerId === id) records.delete(key);
},
governorRequestRender() {},
selection: () => selection,
};
}
test('package exports import without an application, DOM, fetch, or timers', () => {
const result = spawnSync(
process.execPath,
[
'--input-type=module',
'-e',
`
globalThis.fetch = () => { throw new Error('unexpected fetch'); };
globalThis.setTimeout = () => { throw new Error('unexpected timer'); };
for (const key of ['window', 'document']) {
delete globalThis[key];
}
await import('gods-eye-view/infrastructure');
await import('gods-eye-view/infrastructure/geojson');
await import('gods-eye-view/infrastructure/lod');
`,
],
{ cwd: new URL('../..', import.meta.url), encoding: 'utf8' },
);
assert.equal(result.status, 0, result.stderr);
});
test('infrastructure factory preserves identity and creates independent state without loading', (t) => {
t.mock.method(globalThis, 'fetch', () => {
throw new Error('factory must not fetch');
});
const first = createInfrastructureLayers(services());
const second = createInfrastructureLayers(services());
assert.deepEqual(
first.map(({ id, name, source }) => ({ id, name, source })),
[
{ id: 'local-datacenters', name: 'Datacenters', source: 'Local' },
{ id: 'local-dams', name: 'Dams', source: 'USACE' },
],
);
first.forEach((layer, index) => {
assert.notEqual(layer, second[index]);
layer.destroy();
assert.deepEqual(second[index].getStats(), {
count: 0,
lastUpdate: null,
error: null,
});
second[index].destroy();
});
});
test('dataset URLs still name the complete bundled sources', () => {
for (const [file, count] of [
['datacenters', 4351],
['dams', 704],
]) {
const lines = readFileSync(
new URL(`./local_data/${file}/${file}.geojsonl`, import.meta.url),
'utf8',
)
.split('\n')
.filter((line) => line.trim());
assert.equal(lines.length, count);
}
});
test('two viewers use their supplied contexts and dispose independently', async (t) => {
t.mock.method(globalThis, 'fetch', async () => ({
ok: true,
text: async () =>
JSON.stringify({
type: 'Feature',
id: 'same-id',
properties: { name: 'Dam' },
geometry: {
type: 'Polygon',
coordinates: [
[
[0, 0],
[0.01, 0],
[0, 0.01],
[0, 0],
],
],
},
}),
}));
const hosts = [services(), services()];
const instances = hosts.map((host) => {
const sources = new Cesium.DataSourceCollection();
let click;
const layer = createLocalGeoJsonLayer(
{
id: 'local-dams',
name: 'Dams',
osmDerived: true,
color: '#0088ff',
url: '/dams.geojsonl',
screenSpaceEventHandlerFactory: () => ({
setInputAction(fn) {
click = fn;
},
destroy() {},
}),
},
host,
);
const viewer = {
dataSources: sources,
scene: {
canvas: {},
preRender: new Cesium.Event(),
requestRender() {},
screenSpaceCameraController: { enableInputs: true },
pick: () => ({ id: sources.get(0).entities.values[0] }),
},
camera: { moveEnd: new Cesium.Event(), flyTo() {} },
};
return { layer, viewer, click: () => click({ position: {} }) };
});
t.after(() =>
instances.forEach(({ layer, viewer }) => layer.destroy(viewer)),
);
await Promise.all(instances.map(({ layer, viewer }) => layer.enable(viewer)));
assert.equal(hosts[0].records.size, 1);
assert.deepEqual(
hosts.map((host) => host.credited),
instances.map(({ viewer }) => [viewer]),
);
assert.equal(hosts[1].records.size, 1);
instances[0].click();
assert.ok(hosts[0].selection());
assert.equal(hosts[1].selection(), undefined);
instances[0].layer.destroy(instances[0].viewer);
assert.equal(hosts[0].records.size, 0);
assert.equal(hosts[1].records.size, 1);
assert.equal(instances[1].viewer.dataSources.length, 1);
});
test('consumer build includes only infrastructure code and resolves assets under a non-root base', async () => {
const { build } = await import('vite');
const { fileURLToPath } = await import('node:url');
const result = await build({
configFile: false,
logLevel: 'silent',
base: '/example/',
build: {
write: false,
assetsInlineLimit: 0,
rollupOptions: {
input: fileURLToPath(
import.meta.resolve('gods-eye-view/infrastructure'),
),
external: ['cesium'],
preserveEntrySignatures: 'strict',
},
},
});
const output = result.output;
const entry = output.find((item) => item.type === 'chunk' && item.isEntry);
const sources = Object.keys(entry.modules).filter((id) => id.endsWith('.js'));
assert.deepEqual(sources.map((id) => id.split('/').at(-1)).sort(), [
'infrastructure.js',
'infrastructureOverlayEntry.js',
'localGeojsonCore.js',
'localGeojsonLod.js',
]);
assert.deepEqual(
entry.imports,
['cesium'],
'the viewer supplies the same Cesium dependency',
);
for (const name of ['datacenters', 'dams']) {
const asset = output.find(
(item) => item.type === 'asset' && item.fileName.includes(name),
);
assert.ok(asset, `${name} must be emitted`);
assert.ok(
entry.code.includes(`/example/${asset.fileName}`),
`${name} must retain the consumer base path`,
);
}
});