import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import test from "node:test"; import { gridShouldBeVisible, resolveGridMode, selectGridLod, snapGridCenter, } from "../apps/catalog/src/mapGridPolicy.mjs"; const settings = { gridVisible: true, gridLodEnabled: true, gridLod1MaxHeightKm: 10, gridLod1StepKm: 1, gridLod1Mode: "3d", gridLod2MaxHeightKm: 50, gridLod2StepKm: 5, gridLod2Mode: "3d", gridLod3MaxHeightKm: 180, gridLod3StepKm: 25, gridLod3Mode: "3d", gridLod4MaxHeightKm: 700, gridLod4StepKm: 100, gridLod4Mode: "graticule", gridLod5StepKm: 500, gridLod5Mode: "graticule", grid3dEnabled: true, gridGraticuleEnabled: true, gridMax3dViewAngleDegrees: 30, gridAutoDisableHeightKm: 10_000, gridCenterMode: "camera", gridTileSizeKm: 10, }; test("five grid LODs preserve the close 3D and distant graticule contract", () => { assert.deepEqual( [5, 40, 120, 500, 2_000].map((height) => { const band = selectGridLod(settings, height); return [band.id, band.stepKm, band.mode]; }), [ ["lod-1", 1, "3d"], ["lod-2", 5, "3d"], ["lod-3", 25, "3d"], ["lod-4", 100, "graticule"], ["lod-5", 500, "graticule"], ], ); }); test("LOD hysteresis holds the previous band around a threshold", () => { assert.equal(selectGridLod(settings, 10.5, 0).id, "lod-1"); assert.equal(selectGridLod(settings, 10.9, 0).id, "lod-2"); assert.equal(selectGridLod(settings, 9.5, 1).id, "lod-2"); assert.equal(selectGridLod(settings, 9.1, 1).id, "lod-1"); }); test("oblique close view falls back to the graticule without dropping the grid", () => { assert.equal(resolveGridMode(settings, "3d", 12), "3d"); assert.equal(resolveGridMode(settings, "3d", 48), "graticule"); assert.equal(resolveGridMode({ ...settings, gridGraticuleEnabled: false }, "3d", 48), "hidden"); }); test("grid auto-disable and stable camera tile center are deterministic", () => { assert.equal(gridShouldBeVisible(settings, 9_999), true); assert.equal(gridShouldBeVisible(settings, 10_001), false); const first = snapGridCenter({ latitude: 55.7558, longitude: 37.6173 }, settings); const second = snapGridCenter({ latitude: 55.76, longitude: 37.62 }, settings); assert.deepEqual(first, second); }); test("renderer swaps double-buffered data sources and never clears the live grid first", async () => { const source = await readFile(new URL("../apps/catalog/src/CesiumMapRenderer.tsx", import.meta.url), "utf8"); assert.match(source, /class GridLayerController/); assert.match(source, /dataSourceDisplay\.ready/); assert.doesNotMatch(source, /function rebuildElevatedGrid[\s\S]*?entities\.removeAll\(\)/); });