Files
NODEDC_DESIGN_GUIDELINE/server/map-grid-persistence.test.mjs
T

99 lines
3.7 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import vm from "node:vm";
import { DEFAULT_GRID_LOD_PROFILES } from "../apps/catalog/src/mapGridPolicy.mjs";
const seed = JSON.parse(await readFile(new URL("../runtime-seed/page-layouts/map.json", import.meta.url), "utf8"));
const serverSource = await readFile(new URL("./catalog-server.mjs", import.meta.url), "utf8");
const mcpSource = await readFile(new URL("./foundry-mcp.mjs", import.meta.url), "utf8");
const extensionKeys = [
"majorLinesEnabled",
"majorLabelsEnabled",
"majorLineWidthMultiplier",
"volumeEnabled",
"volumeMinimumHeightMeters",
"volumeMaximumHeightMeters",
"volumeBandHeightMeters",
];
function applicationError(code, statusCode = 400) {
const error = new Error(code);
error.statusCode = statusCode;
return error;
}
const validators = {
applicationError,
isObject: (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value),
requireNumber(value, minimum, maximum, code) {
if (typeof value !== "number" || !Number.isFinite(value) || value < minimum || value > maximum) {
throw applicationError(code);
}
return value;
},
requireBoolean(value, code) {
if (typeof value !== "boolean") throw applicationError(code);
return value;
},
requireHex(value, code) {
const normalized = String(value || "");
if (!/^#[0-9a-f]{6}$/i.test(normalized)) throw applicationError(code);
return normalized.toLowerCase();
},
};
function loadServerGridContract() {
const start = serverSource.indexOf("const GRID_LOD_PROFILE_KEYS");
const end = serverSource.indexOf("function validateMapPageSettingsPatch");
assert.ok(start >= 0 && end > start, "server grid contract source boundaries");
const context = vm.createContext({ ...validators });
const source = `${serverSource.slice(start, end)}\n;globalThis.gridContract = { DEFAULT_GRID_LOD_PROFILES, promoteLegacyGridLodProfiles, validateGridLodProfiles };`;
new vm.Script(source, { filename: "catalog-server.grid-contract.mjs" }).runInContext(context);
return context.gridContract;
}
const serverGrid = loadServerGridContract();
test("seed, client defaults and server promotion keep the sector-v2 profile contract identical", () => {
assert.deepEqual(seed.settings.gridLodProfiles, DEFAULT_GRID_LOD_PROFILES);
const legacyProfiles = seed.settings.gridLodProfiles.map((profile) => {
const legacy = { ...profile };
for (const key of extensionKeys) delete legacy[key];
return legacy;
});
assert.deepEqual(
structuredClone(serverGrid.validateGridLodProfiles(legacyProfiles)),
DEFAULT_GRID_LOD_PROFILES,
);
});
test("flat layouts migrate mode-aware extensions while explicit invalid hierarchy is rejected", () => {
const flatSettings = {
...seed.settings,
gridLodProfiles: undefined,
gridLod1StepKm: 3,
gridTileSizeKm: 10,
gridLod2Mode: "graticule",
};
const migrated = structuredClone(serverGrid.validateGridLodProfiles(
serverGrid.promoteLegacyGridLodProfiles(flatSettings),
));
assert.equal(migrated[0].majorLinesEnabled, false);
assert.equal(migrated[0].majorLabelsEnabled, false);
assert.equal(migrated[1].mode, "graticule");
assert.equal(migrated[1].volumeEnabled, false);
const invalidExplicit = structuredClone(DEFAULT_GRID_LOD_PROFILES);
invalidExplicit[0].stepKm = 3;
invalidExplicit[0].tileSizeKm = 10;
assert.throws(
() => serverGrid.validateGridLodProfiles(invalidExplicit),
/invalid_map_grid_lod_profile_1_tileSizeKm_step_ratio/,
);
});
test("Foundry MCP exposes every persisted sector-v2 profile field", () => {
for (const key of extensionKeys) assert.match(mcpSource, new RegExp(`\\b${key}: \\{`));
});