/** * scripts/region-keys.test.ts — validation for the committed `public/region-keys.json`. * * The manifest maps each sub-region's **stable feature id** (from * `public/regions.geo.json`) to a deterministic, human-readable, globally * unique region key of the form `-<4char>` (built by * `scripts/build-region-keys.mjs`). Region-level ownership (v0.19) keys both * `Nation.territory` and the `RegionMap` on these keys, so the manifest is a * load-bearing data artifact — this test pins its contract. * * Deliberately dependency-free: it reads both JSON files with plain * `fs`/`JSON.parse` and never imports `@turf`/geojson types (importing one * pulls `@types/geojson` into the tsc program and breaks `WorldMap.tsx`; see * the v0.17 changelog note). Mirrors the style of `scripts/regions.test.ts`. */ import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; const here = path.dirname(fileURLToPath(import.meta.url)); const root = path.resolve(here, '..'); function readJson(rel: string): Record { return JSON.parse(readFileSync(path.join(root, rel), 'utf8')); } describe('region-keys.json', () => { const manifest = readJson('public/region-keys.json') as Record; const geo = readJson('public/regions.geo.json') as { features?: Array<{ id?: string | number }>; }; const keys = Object.values(manifest); const featureIds = (geo.features ?? []).map((f) => String(f.id)); it('has exactly 1,796 entries (one per region)', () => { expect(Object.keys(manifest)).toHaveLength(1796); }); it('has 1,796 unique keys (no collisions)', () => { expect(new Set(keys).size).toBe(keys.length); expect(new Set(keys).size).toBe(1796); }); it('keys each end in -[a-z]{4} and contain at least one hyphen', () => { const ending = /-[a-z]{4}$/; for (const k of keys) { expect(k, `key does not end in -[a-z]{4}: ${k}`).toMatch(ending); expect(k, `key has no hyphen: ${k}`).toContain('-'); } }); it('is a stable join: its keys are exactly the regions.geo.json feature ids', () => { // The manifest is looked up by feature id, so its key set must line up // 1:1 with the geo file (catches drift if the geo is regenerated without // rebuilding the keys). expect(featureIds).toHaveLength(1796); const manifestIds = Object.keys(manifest); expect(new Set(manifestIds)).toEqual(new Set(featureIds)); }); });