Files
gods-eye-view/src/data/militaryInstallations.test.mjs
T

818 lines
33 KiB
JavaScript

import { readLayerSource } from '../testSupport/readLayerSource.mjs';
import test from 'node:test';
import assert from 'node:assert/strict';
import {
approximateSurfaceDistanceM,
classifyGoogleMilitaryPlace,
installationSourceLabel,
installationResponseSaturated,
installationSurfaceHeightM,
installationWithinViewport,
} from './militaryInstallations.js';
import militaryInstallationsLayer from './militaryInstallations.js';
import {
_clearMeshFloorCellsForTest,
cachedGroundFloor,
FLOOR_RESOLVE_DEADLINE_MS,
reportMeshFloorCell,
setMeshFloorPreferred,
} from './groundFloor.js';
import { _resetFireAnchorsForTest } from './fireAnchors.js';
import {
_resetRenderGovernorForTest,
getRenderGovernorDiagnostics,
installRenderGovernor,
} from '../renderGovernor.js';
import * as Cesium from 'cesium';
import { registerEntityContext, selectEntityContext, getSelectedEntityContext } from './contextStore.js';
test('clicking a selected installation again or empty map clears it through refresh', async () => {
const run = await runInstallationLoad({ elements: [{ type: 'node', id: 42,
lat: 30.2, lon: -97.7, tags: { military: 'base', name: 'Site' } }] });
try {
for (const nextPick of ['osm:node:42', null]) {
run.click('osm:node:42');
assert.equal(getSelectedEntityContext()?.id, 'osm:node:42');
run.click(nextPick);
assert.equal(getSelectedEntityContext(), null);
await militaryInstallationsLayer.update();
assert.equal(getSelectedEntityContext(), null, 'refresh must not resurrect selection');
assert.equal(run.entities()[0].point.pixelSize.getValue(), 9);
}
} finally { run.restore(); }
});
test('clearing a stale installation highlight does not clear or reclaim another layer selection', async () => {
const run = await runInstallationLoad({ elements: [{ type: 'node', id: 42,
lat: 30.2, lon: -97.7, tags: { military: 'base', name: 'Site' } }] });
try {
run.click('osm:node:42');
const aircraft = { id: 'aircraft:test' };
registerEntityContext(aircraft, { id: aircraft.id, layerId: 'military', label: 'Aircraft' });
selectEntityContext(aircraft);
await militaryInstallationsLayer.update();
assert.equal(getSelectedEntityContext()?.id, aircraft.id, 'non-canvas selection survives a repaint');
run.click('osm:node:42');
selectEntityContext(aircraft);
run.click(aircraft);
assert.equal(getSelectedEntityContext()?.id, aircraft.id);
await militaryInstallationsLayer.update();
assert.equal(getSelectedEntityContext()?.id, aircraft.id);
} finally { run.restore(); }
});
test('switching sites keeps the new selection through refresh and disable clears it', async () => {
const run = await runInstallationLoad({ elements: [42, 43].map(id => ({ type: 'node', id,
lat: 30.2, lon: -97.7, tags: { military: 'base', name: `Site ${id}` } })) });
try {
run.click('osm:node:42');
run.click('osm:node:43');
assert.equal(getSelectedEntityContext()?.id, 'osm:node:43');
await militaryInstallationsLayer.update();
assert.equal(getSelectedEntityContext()?.id, 'osm:node:43');
assert.equal(run.entities().find(e => e.id === 'osm:node:42').point.pixelSize.getValue(), 9);
assert.equal(run.entities().find(e => e.id === 'osm:node:43').point.pixelSize.getValue(), 13);
militaryInstallationsLayer.disable();
run.click('osm:node:42');
assert.equal(getSelectedEntityContext(), null, 'disabled layer ignores clicks');
militaryInstallationsLayer.enable();
await militaryInstallationsLayer.update();
assert.equal(getSelectedEntityContext(), null);
} finally { run.restore(); }
});
test('cheap installation distance prefilter is local and antimeridian-safe', () => {
const oneDegree = approximateSurfaceDistanceM(0, 0, 0, 1);
assert.ok(oneDegree > 111000 && oneDegree < 111300);
const acrossDateline = approximateSurfaceDistanceM(
Cesium.Math.toRadians(10),
Cesium.Math.toRadians(179.9),
10,
-179.9,
);
assert.ok(acrossDateline > 21000 && acrossDateline < 23000);
});
test('keeps generic Places hits distinct from explicitly typed military facilities', () => {
assert.equal(classifyGoogleMilitaryPlace({ primaryType: 'military_base' }), 'military_land');
assert.equal(classifyGoogleMilitaryPlace({ types: ['point_of_interest', 'military_base'] }), 'military_land');
assert.equal(classifyGoogleMilitaryPlace({ name: 'Army Recruiting Office', types: ['government_office'] }), 'places_candidate');
assert.equal(classifyGoogleMilitaryPlace({ name: 'Military Museum', types: ['museum'] }), 'places_candidate');
});
test('reports the record source instead of attributing Places records to OpenStreetMap', () => {
assert.equal(installationSourceLabel({ sources: [{ name: 'Google Maps Places' }] }), 'Google Maps Places');
assert.equal(installationSourceLabel({ sources: [{ name: 'OpenStreetMap' }, { name: 'OpenStreetMap' }] }), 'OpenStreetMap');
});
test('places installation anchors on the shared cached rendered floor', () => {
setMeshFloorPreferred(true);
_clearMeshFloorCellsForTest();
reportMeshFloorCell(30.2, -97.7, 182.25);
assert.equal(installationSurfaceHeightM({ latitude: 30.2, longitude: -97.7 }), 183.75);
_clearMeshFloorCellsForTest();
});
test('real enabled installation entities carry no native label graphics', async () => {
const originalDocument = globalThis.document;
const originalWindow = globalThis.window;
const originalFetch = globalThis.fetch;
const canvas = {
addEventListener() {},
removeEventListener() {},
};
globalThis.document = {
addEventListener() {},
removeEventListener() {},
};
globalThis.window = { dispatchEvent() {} };
globalThis.fetch = async (url) => {
if (String(url).includes('/api/terrain/heights')) {
return { ok: true, status: 200, json: async () => ({ results: [{ ellipsoid: 100 }] }) };
}
return {
ok: true,
status: 200,
json: async () => ({
status: 'fresh',
retrievedAt: '2026-08-02T00:00:00.000Z',
elements: [{
type: 'node',
id: 42,
lat: 30.2,
lon: -97.7,
tags: { military: 'base', name: 'Runtime Installation' },
}],
}),
};
};
const moveEndListeners = new Set();
const dataSources = [];
const viewer = {
camera: {
moveEnd: {
addEventListener(listener) {
moveEndListeners.add(listener);
return () => moveEndListeners.delete(listener);
},
},
computeViewRectangle() {
return {
south: Cesium.Math.toRadians(30),
west: Cesium.Math.toRadians(-98),
north: Cesium.Math.toRadians(31),
east: Cesium.Math.toRadians(-97),
};
},
},
scene: {
canvas,
globe: { ellipsoid: Cesium.Ellipsoid.WGS84 },
pick() { return null; },
},
dataSources: {
add(dataSource) { dataSources.push(dataSource); return dataSource; },
remove(dataSource) {
const index = dataSources.indexOf(dataSource);
if (index >= 0) dataSources.splice(index, 1);
return index >= 0;
},
},
};
try {
militaryInstallationsLayer.init(viewer);
militaryInstallationsLayer.enable();
await militaryInstallationsLayer.update();
const entities = dataSources[0].entities.values;
assert.ok(entities.length > 0, 'runtime guard requires rendered installation records');
assert.ok(entities.every((entity) => entity.label === undefined));
} finally {
militaryInstallationsLayer.destroy(viewer);
globalThis.fetch = originalFetch;
if (originalDocument === undefined) delete globalThis.document;
else globalThis.document = originalDocument;
if (originalWindow === undefined) delete globalThis.window;
else globalThis.window = originalWindow;
}
});
const VIEWPORT = { south: 30, west: -98, north: 31, east: -97 };
test('Context focus past the render cap selects for real instead of flying blind', async () => {
// Context navigation walks the FULL nearby cohort; only the first 700 records
// get entities. Focusing item 701+ used to fly the camera, find no entity,
// silently drop the selection, and report success — so the Context subject
// stayed stale and NEXT offered the same installation forever.
const elements = Array.from({ length: 760 }, (_, index) => ({
type: 'node',
id: 1000 + index,
lat: 30.1 + (index % 40) * 0.002,
lon: -97.9 + Math.floor(index / 40) * 0.002,
tags: { military: 'base', name: `Installation ${index}` },
}));
const run = await runInstallationLoad({ elements });
const flights = [];
try {
const renderedIds = new Set(run.entities().map((entity) => entity.id));
assert.equal(renderedIds.size, 700, 'the ambient paint stays capped');
const cohort = militaryInstallationsLayer.getNearby(
Cesium.Cartesian3.fromDegrees(-97.8, 30.15, 0),
Number.POSITIVE_INFINITY,
5000,
);
assert.ok(cohort.length > 700, 'the cohort reaches past the render cap');
const beyondCap = cohort.find((record) => !renderedIds.has(record.id));
assert.ok(beyondCap, 'a cohort item exists outside the rendered window');
const focused = militaryInstallationsLayer.focusById(beyondCap.id);
assert.equal(focused, true, 'focus succeeds');
// The proof: a real entity now backs the selection, so the Context subject
// actually changes rather than the camera moving over a stale subject.
const nowRendered = run.entities().find((entity) => entity.id === beyondCap.id);
assert.ok(nowRendered, 'the focused record was rendered on demand');
assert.equal(
run.contextLabels().at(-1),
beyondCap.name,
'the selection reached the context store',
);
} finally {
void flights;
run.restore();
}
});
/**
* Drive one real `update()` of the layer against a stubbed proxy, and expose
* what actually reached the map and the context store.
*/
async function runInstallationLoad({
elements = [],
saturated = false,
exactElements = null,
exactSaturated = false,
legacyPayload = false,
failWith = null,
}) {
const originalDocument = globalThis.document;
const originalWindow = globalThis.window;
const originalFetch = globalThis.fetch;
const requests = [];
const contextEvents = [];
_resetRenderGovernorForTest();
globalThis.document = { addEventListener() {}, removeEventListener() {} };
globalThis.window = {
dispatchEvent(event) {
if (event?.detail?.label) contextEvents.push(event.detail.label);
},
CustomEvent: class { constructor(type, init) { this.type = type; Object.assign(this, init); } },
};
globalThis.fetch = async (url) => {
const href = String(url);
if (href.includes('/api/terrain/heights')) {
return { ok: true, status: 200, json: async () => ({ results: [] }) };
}
requests.push(href);
if (failWith) {
return { ok: false, status: 503, json: async () => ({ error: failWith }) };
}
const exact = href.includes('exact=1');
const payload = {
status: 'fresh',
retrievedAt: '2026-08-18T00:00:00.000Z',
elements: exact && exactElements ? exactElements : elements,
elementCap: 700,
};
// A pre-fix cached entry carries no `saturated` field at all.
if (!legacyPayload) payload.saturated = exact ? exactSaturated : saturated;
return { ok: true, status: 200, json: async () => payload };
};
const dataSources = [];
const cameraFlights = [];
let picked = null;
let clickAction;
const viewer = {
camera: {
moveEnd: { addEventListener() { return () => {}; } },
flyToBoundingSphere(sphere, options) { cameraFlights.push({ sphere, options }); },
computeViewRectangle() {
return {
south: Cesium.Math.toRadians(VIEWPORT.south),
west: Cesium.Math.toRadians(VIEWPORT.west),
north: Cesium.Math.toRadians(VIEWPORT.north),
east: Cesium.Math.toRadians(VIEWPORT.east),
};
},
},
scene: {
canvas: { addEventListener() {}, removeEventListener() {} },
globe: { ellipsoid: Cesium.Ellipsoid.WGS84 },
pick() { return picked; },
// Enough surface for the real render governor to drive this viewer, so
// one-shot render requests are observable.
requestRenderMode: false,
maximumRenderTimeChange: 0,
requestRender() {},
},
dataSources: {
add(dataSource) { dataSources.push(dataSource); return dataSource; },
remove(dataSource) {
const index = dataSources.indexOf(dataSource);
if (index >= 0) dataSources.splice(index, 1);
return index >= 0;
},
},
};
const originalSetInputAction = Cesium.ScreenSpaceEventHandler.prototype.setInputAction;
Cesium.ScreenSpaceEventHandler.prototype.setInputAction = function (action, type, modifier) {
if (type === Cesium.ScreenSpaceEventType.LEFT_CLICK) clickAction = action;
return originalSetInputAction.call(this, action, type, modifier);
};
try { militaryInstallationsLayer.init(viewer); }
finally { Cesium.ScreenSpaceEventHandler.prototype.setInputAction = originalSetInputAction; }
installRenderGovernor(viewer);
militaryInstallationsLayer.enable();
await militaryInstallationsLayer.update();
return {
requests,
cameraFlights,
entities: () => dataSources[0]?.entities?.values || [],
contextLabels: () => contextEvents,
stats: () => militaryInstallationsLayer.getStats(),
click(target) {
const entity = typeof target === 'string' ? dataSources[0].entities.getById(target) : target;
picked = entity ? { id: entity } : undefined;
clickAction({ position: { x: 0, y: 0 } });
},
renderRequests: () => getRenderGovernorDiagnostics().recentRequests.map((item) => item.reason),
restore() {
militaryInstallationsLayer.destroy(viewer);
_resetRenderGovernorForTest();
globalThis.fetch = originalFetch;
if (originalDocument === undefined) delete globalThis.document;
else globalThis.document = originalDocument;
if (originalWindow === undefined) delete globalThis.window;
else globalThis.window = originalWindow;
},
};
}
test('viewport membership keeps intersecting footprints and drops the snap ring', () => {
const inside = { osmType: 'node', latitude: 30.5, longitude: -97.5, footprint: null };
const outside = { osmType: 'node', latitude: 30.5, longitude: -96.5, footprint: null };
assert.equal(installationWithinViewport(inside, VIEWPORT), true);
assert.equal(installationWithinViewport(outside, VIEWPORT), false);
// A large base whose CENTRE sits just outside but whose footprint overlaps
// was always returned by the bbox query and must keep rendering.
const straddling = {
osmType: 'way',
latitude: 30.5,
longitude: -96.95,
footprint: [[-97.05, 30.4], [-96.9, 30.4], [-96.9, 30.6], [-97.05, 30.6]],
};
assert.equal(installationWithinViewport(straddling, VIEWPORT), true);
const farWithFootprint = {
osmType: 'way',
latitude: 30.5,
longitude: -96.5,
footprint: [[-96.6, 30.4], [-96.4, 30.4], [-96.4, 30.6], [-96.6, 30.6]],
};
assert.equal(installationWithinViewport(farWithFootprint, VIEWPORT), false);
assert.equal(installationWithinViewport(null, VIEWPORT), false);
assert.equal(installationWithinViewport(inside, null), false);
});
test('extended features with unknown extent are kept, not centre-tested', () => {
// Relations carry geometry on their members and ways over MAX_FOOTPRINT_POINTS
// are normalized without one, so their true extent is unknown here. Overpass
// already proved they intersect the queried bbox; centre-testing them would
// erase exactly the biggest installations.
const relation = { osmType: 'relation', latitude: 30.5, longitude: -96.99, footprint: null };
const hugeWay = { osmType: 'way', latitude: 29.5, longitude: -97.5, footprint: null };
assert.equal(installationWithinViewport(relation, VIEWPORT), true);
assert.equal(installationWithinViewport(hugeWay, VIEWPORT), true);
// A node IS its geometry, so excluding it on centre loses nothing.
assert.equal(
installationWithinViewport({ osmType: 'node', latitude: 30.5, longitude: -96.99, footprint: null }, VIEWPORT),
false,
);
// Unknown provenance is treated inclusively, same as an extended feature.
assert.equal(installationWithinViewport({ latitude: 30.5, longitude: -96.99, footprint: null }, VIEWPORT), true);
});
test('a footprint-less relation just outside the viewport still renders', async () => {
const harness = await runInstallationLoad({
elements: [
// A relation whose CENTER sits outside the viewport and whose geometry
// lives on members Overpass did not inline. It was returned because it
// intersects the queried bbox, so it must survive the viewport filter.
{ type: 'relation', id: 91, center: { lat: 30.5, lon: -96.995 }, tags: { military: 'range', name: 'Straddling Range' } },
// A NODE at the same off-view spot has no extent and must still be cut.
{ type: 'node', id: 92, lat: 30.5, lon: -96.995, tags: { military: 'range', name: 'Off View Node' } },
],
});
try {
assert.deepEqual(
harness.entities().map((entity) => entity.gevLabelModel?.title),
['Straddling Range'],
);
} finally {
harness.restore();
}
});
test('a legacy cached response with no saturation flag still triggers the exact retry', () => {
const atCap = { elements: new Array(700).fill({ type: 'node' }), elementCap: 700 };
assert.equal(installationResponseSaturated(atCap), true, 'derived from the reported cap');
assert.equal(
installationResponseSaturated({ elements: new Array(699).fill({ type: 'node' }), elementCap: 700 }),
false,
);
// An explicit flag always wins over the derivation.
assert.equal(installationResponseSaturated({ ...atCap, saturated: false }), false);
assert.equal(installationResponseSaturated({ elements: [], saturated: true }), true);
// Nothing to derive from: do not invent saturation.
assert.equal(installationResponseSaturated({ elements: new Array(700).fill({}) }), false);
assert.equal(installationResponseSaturated(null), false);
});
test('a legacy-shaped payload at the cap fires the exact-viewport retry end to end', async () => {
const elements = [];
for (let index = 0; index < 700; index += 1) {
elements.push({ type: 'node', id: 3000 + index, lat: 30.5, lon: -96.2, tags: { military: 'range' } });
}
const harness = await runInstallationLoad({
elements,
// Pre-fix cache shape: no `saturated` field at all, but at the cap.
legacyPayload: true,
exactElements: [
{ type: 'node', id: 8, lat: 30.5, lon: -97.5, tags: { military: 'range', name: 'Rescued From Legacy' } },
],
});
try {
assert.equal(harness.requests.length, 2, 'a legacy entry must not skip the retry');
assert.equal(harness.requests[1].includes('exact=1'), true);
assert.deepEqual(
harness.entities().map((entity) => entity.gevLabelModel?.title),
['Rescued From Legacy'],
);
} finally {
harness.restore();
}
});
test('a failed load buys the frame its status change needs', async () => {
const harness = await runInstallationLoad({ failWith: 'Installation feed HTTP 503' });
try {
assert.equal(harness.stats().status, 'unavailable');
assert.ok(
harness.renderRequests().some((reason) => reason === 'installations-status'),
'an idle governor would otherwise leave the last healthy readout on screen',
);
} finally {
harness.restore();
}
});
test('off-viewport records from the snapped superset never render or enter context', async () => {
const harness = await runInstallationLoad({
// The snapped bbox reaches ~5.5 km beyond the viewport; this node sits a
// full degree outside it.
elements: [
{ type: 'node', id: 1, lat: 30.5, lon: -97.5, tags: { military: 'range', name: 'In View' } },
{ type: 'node', id: 2, lat: 30.5, lon: -96.2, tags: { military: 'range', name: 'Off View' } },
],
});
try {
const titles = harness.entities().map((entity) => entity.gevLabelModel?.title);
assert.deepEqual(titles, ['In View'], 'only the in-viewport site renders');
assert.equal(harness.contextLabels().includes('Off View'), false, 'and none enters context');
assert.equal(harness.stats().count, 1);
} finally {
harness.restore();
}
});
test('a saturated snapped tile refetches the exact viewport before rendering', async () => {
const elements = [];
for (let index = 0; index < 700; index += 1) {
// A saturated snapped response full of OFF-viewport sites: the in-view ones
// were crowded out upstream.
elements.push({ type: 'node', id: 1000 + index, lat: 30.5, lon: -96.2, tags: { military: 'range' } });
}
const harness = await runInstallationLoad({
elements,
saturated: true,
exactElements: [
{ type: 'node', id: 7, lat: 30.5, lon: -97.5, tags: { military: 'range', name: 'Rescued' } },
],
});
try {
assert.equal(harness.requests.length, 2, 'saturation triggers exactly one retry');
assert.equal(harness.requests[0].includes('exact=1'), false, 'first ask uses the shared snapped tile');
assert.equal(harness.requests[1].includes('exact=1'), true, 'retry opts out of the snap');
assert.deepEqual(
harness.entities().map((entity) => entity.gevLabelModel?.title),
['Rescued'],
'the in-viewport site is no longer starved by off-view ones',
);
} finally {
harness.restore();
}
});
test('an unsaturated response never pays for a second upstream ask', async () => {
const harness = await runInstallationLoad({
elements: [{ type: 'node', id: 3, lat: 30.5, lon: -97.5, tags: { military: 'range' } }],
});
try {
assert.equal(harness.requests.length, 1);
assert.equal(harness.stats().saturated, false);
} finally {
harness.restore();
}
});
test('a still-saturated exact viewport is reported honestly instead of implied complete', async () => {
const elements = [];
for (let index = 0; index < 700; index += 1) {
elements.push({ type: 'node', id: 2000 + index, lat: 30.5, lon: -97.5, tags: { military: 'range' } });
}
const harness = await runInstallationLoad({ elements, saturated: true, exactSaturated: true });
try {
assert.equal(harness.stats().saturated, true);
assert.match(harness.stats().error, /Too many mapped sites/);
} finally {
harness.restore();
}
});
test('a floor that lands after the render deadline lifts the dots off the ellipsoid', async () => {
const originalDocument = globalThis.document;
const originalWindow = globalThis.window;
const originalFetch = globalThis.fetch;
// A cell no other test in this file warms, so the assertion is about THIS
// load, not a cache another test left behind.
const lat = 44.123;
const lon = -110.456;
let terrainCalls = 0;
setMeshFloorPreferred(false);
_clearMeshFloorCellsForTest();
_resetFireAnchorsForTest();
globalThis.document = { addEventListener() {}, removeEventListener() {} };
globalThis.window = { dispatchEvent() {} };
globalThis.fetch = async (url) => {
if (String(url).includes('/api/terrain/heights')) {
terrainCalls += 1;
// The production failure: the bounded pre-render resolve gives up before
// Re:Earth answers, so the first paint has no floor to stand on.
if (terrainCalls === 1) {
await new Promise((resolve) => setTimeout(resolve, FLOOR_RESOLVE_DEADLINE_MS + 200));
}
return { ok: true, status: 200, json: async () => ({ results: [{ ellipsoid: 2400 }] }) };
}
return {
ok: true,
status: 200,
json: async () => ({
status: 'fresh',
retrievedAt: '2026-08-18T00:00:00.000Z',
elements: [{ type: 'node', id: 77, lat, lon, tags: { military: 'range' } }],
}),
};
};
const dataSources = [];
const viewer = {
camera: {
moveEnd: { addEventListener() { return () => {}; } },
computeViewRectangle() {
return {
south: Cesium.Math.toRadians(44),
west: Cesium.Math.toRadians(-111),
north: Cesium.Math.toRadians(45),
east: Cesium.Math.toRadians(-110),
};
},
},
scene: {
canvas: { addEventListener() {}, removeEventListener() {} },
globe: { ellipsoid: Cesium.Ellipsoid.WGS84 },
pick() { return null; },
},
dataSources: {
add(dataSource) { dataSources.push(dataSource); return dataSource; },
remove(dataSource) {
const index = dataSources.indexOf(dataSource);
if (index >= 0) dataSources.splice(index, 1);
return index >= 0;
},
},
};
const heightOf = (entity) => Cesium.Cartographic.fromCartesian(
entity.position.getValue(Cesium.JulianDate.now()),
).height;
try {
militaryInstallationsLayer.init(viewer);
militaryInstallationsLayer.enable();
await militaryInstallationsLayer.update();
const buried = dataSources[0].entities.values[0];
assert.ok(buried, 'the cold-floor pass still renders the record');
assert.ok(Math.abs(heightOf(buried)) < 1, 'a cold floor anchors at the ellipsoid, as before');
// The warm chain resolves out of band; wait for the floor to land.
for (let attempt = 0; attempt < 50 && cachedGroundFloor(lat, lon) == null; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 10));
}
assert.equal(cachedGroundFloor(lat, lon), 2400, 'the late floor landed in the shared cache');
const lifted = dataSources[0].entities.values[0];
assert.ok(
Math.abs(heightOf(lifted) - 2401.5) < 0.5,
`re-render must lift the dot onto the resolved floor, got ${heightOf(lifted)}`,
);
} finally {
militaryInstallationsLayer.destroy(viewer);
_resetFireAnchorsForTest();
setMeshFloorPreferred(true);
globalThis.fetch = originalFetch;
if (originalDocument === undefined) delete globalThis.document;
else globalThis.document = originalDocument;
if (originalWindow === undefined) delete globalThis.window;
else globalThis.window = originalWindow;
}
});
test('reports bounded installation requests as loading and clears on settlement', async () => {
const originalDocument = globalThis.document;
const originalWindow = globalThis.window;
const originalFetch = globalThis.fetch;
let resolveInstallations;
const installationsResponse = new Promise((resolve) => { resolveInstallations = resolve; });
globalThis.document = { addEventListener() {}, removeEventListener() {} };
globalThis.window = { dispatchEvent() {} };
globalThis.fetch = async (url) => {
if (String(url).includes('/api/terrain/heights')) {
return { ok: true, status: 200, json: async () => ({ results: [] }) };
}
return installationsResponse;
};
const viewer = {
camera: {
moveEnd: { addEventListener() { return () => {}; } },
computeViewRectangle() {
return {
south: Cesium.Math.toRadians(30),
west: Cesium.Math.toRadians(-98),
north: Cesium.Math.toRadians(31),
east: Cesium.Math.toRadians(-97),
};
},
},
scene: {
canvas: { addEventListener() {}, removeEventListener() {} },
globe: { ellipsoid: Cesium.Ellipsoid.WGS84 },
pick() { return null; },
},
dataSources: { add(value) { return value; }, remove() { return true; } },
};
try {
militaryInstallationsLayer.init(viewer);
militaryInstallationsLayer.enable();
const update = militaryInstallationsLayer.update();
assert.equal(militaryInstallationsLayer.getStats().loading, true);
assert.equal(
militaryInstallationsLayer.getStats().loadingLabel,
'loading mapped installation context',
);
resolveInstallations({
ok: true,
status: 200,
json: async () => ({ status: 'fresh', elements: [] }),
});
await update;
assert.equal(militaryInstallationsLayer.getStats().loading, false);
} finally {
militaryInstallationsLayer.destroy(viewer);
globalThis.fetch = originalFetch;
if (originalDocument === undefined) delete globalThis.document;
else globalThis.document = originalDocument;
if (originalWindow === undefined) delete globalThis.window;
else globalThis.window = originalWindow;
}
});
test('zoom-out aborts an active installation request and returns non-loading guidance', async () => {
const originalDocument = globalThis.document;
const originalWindow = globalThis.window;
const originalFetch = globalThis.fetch;
let globalView = false;
let observedSignal;
globalThis.document = { addEventListener() {}, removeEventListener() {} };
globalThis.window = { dispatchEvent() {} };
globalThis.fetch = async (_url, options = {}) => {
observedSignal = options.signal;
return new Promise((_resolve, reject) => {
options.signal?.addEventListener('abort', () => {
const error = new Error('aborted');
error.name = 'AbortError';
reject(error);
}, { once: true });
});
};
const viewer = {
camera: {
moveEnd: { addEventListener() { return () => {}; } },
computeViewRectangle() {
return globalView ? null : {
south: Cesium.Math.toRadians(30),
west: Cesium.Math.toRadians(-98),
north: Cesium.Math.toRadians(31),
east: Cesium.Math.toRadians(-97),
};
},
},
scene: {
canvas: { addEventListener() {}, removeEventListener() {} },
globe: { ellipsoid: Cesium.Ellipsoid.WGS84 },
pick() { return null; },
},
dataSources: { add(value) { return value; }, remove() { return true; } },
};
try {
militaryInstallationsLayer.init(viewer);
militaryInstallationsLayer.enable();
const pending = militaryInstallationsLayer.update();
assert.equal(militaryInstallationsLayer.getStats().loading, true);
globalView = true;
await militaryInstallationsLayer.update();
await pending;
assert.equal(observedSignal.aborted, true);
assert.equal(militaryInstallationsLayer.getStats().loading, false);
assert.equal(militaryInstallationsLayer.getStats().status, 'zoom-in');
assert.equal(militaryInstallationsLayer.getStats().error, null, 'guidance is not a fault');
assert.match(militaryInstallationsLayer.getStats().statusMessage, /zoom in/i);
} finally {
militaryInstallationsLayer.destroy(viewer);
globalThis.fetch = originalFetch;
if (originalDocument === undefined) delete globalThis.document;
else globalThis.document = originalDocument;
if (originalWindow === undefined) delete globalThis.window;
else globalThis.window = originalWindow;
}
});
// The unavailable-state retry: 'temporarily unavailable' must mean temporarily.
// Fetches otherwise fire only on enable and on camera moveEnd, so a parked
// camera whose first request failed stayed unavailable forever while the proxy
// sat healthy — observed in the field as a layer stuck reporting unavailable
// while its own endpoint served hundreds of features. The backoff progression
// is a pure exported helper so it pins without booting the layer (the full
// layer needs a Cesium viewer and interaction handlers); the wiring is pinned
// by source probes against the shipped file, the same technique the HUD datum
// tests use where a full boot is impractical.
import { installationRetryDelayMs } from './militaryInstallations.js';
import fs from 'node:fs';
const installationsSource = readLayerSource(
new URL('./militaryInstallations.js', import.meta.url), 'utf8');
test('the unavailable retry backs off 30s to a 240s ceiling and restarts clean', () => {
assert.equal(installationRetryDelayMs(0), 30000, 'first failure retries in 30s');
assert.equal(installationRetryDelayMs(undefined), 30000, 'no prior delay means the minimum');
assert.equal(installationRetryDelayMs(30000), 60000, 'each failure doubles');
assert.equal(installationRetryDelayMs(60000), 120000);
assert.equal(installationRetryDelayMs(120000), 240000, 'the ceiling is four minutes');
assert.equal(installationRetryDelayMs(240000), 240000, 'and it stays there');
assert.equal(installationRetryDelayMs(-5), 30000, 'garbage restarts at the minimum');
});
test('the retry is wired to every lifecycle edge, not just declared', () => {
assert.match(installationsSource,
/setInstallationStatus\(\s*'unavailable',[^]*?\);\n\s*parts\.viewport\.scheduleUnavailableRetry\(\);/,
'a failed load schedules the retry immediately after reporting unavailable');
assert.match(installationsSource,
/clearUnavailableRetry\(\);\n\s*setInstallationStatus\(\n?\s*layerState\.records\.length/,
'a successful load clears the pending retry and resets the backoff');
assert.match(installationsSource,
/clearUnavailableRetry\(\);\n\s*setInstallationStatus\('zoom-in'/,
'zooming out of range cancels the retry — moveEnd owns re-entry there');
assert.match(installationsSource, /disable\(\) \{[^]*?clearUnavailableRetry\(\);/,
'disabling the layer cancels the retry');
assert.match(installationsSource,
/function scheduleLoad\(\) \{[^]*?clearUnavailableRetry\(\{ resetBackoff: false \}\)/,
'a user-driven load supersedes the retry without resetting the backoff step');
assert.match(installationsSource,
/layerState\.enabled && !layerState\.loading\)\s*parts\.ingestion\.loadInstallations\(\)/,
'the fired retry re-checks enablement and never races an in-flight load');
});