mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
818 lines
33 KiB
JavaScript
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');
|
|
});
|