CI / build + unit + e2e (pull_request) Successful in 2m34s
#41: re-partition the RU-ARK mainland's 28-part lat>=68 Arctic-coast band into two new regions via a new turf-free, idempotent pipeline stage scripts/split-arkhangel-band.mjs (npm run split:regions:band), mirroring the #31 split: - RU-PEO (Pechora, 13 parts, ~37,678 km2) -> key pechora-qwhs - RU-YAM (Yamal, 15 parts, ~51,722 km2) -> key yamal-gosw - RU-ARK keeps its id/name/key arkhangel-sk-lhbk (ownership-safe); the 5-part lat<68 core (~305,229 km2) stays RU-ARK. Pure re-partition: no geometry moved (byte-identical union). The two new features are inserted immediately after RU-ARK-SZ. Manifests regenerated with the real scripts (1,798 -> 1,800 keys + adjacency; adjacency pairs 4,471 -> 4,473). Fixed-point dataset unchanged except feature count (11,794 parts / 11,795 rings / 164,801 verts / 1 KZ-KZY hole). Four dataset/manifest tests updated (RUS 82 -> 84 sub-regions). Version 0.25.0 -> 0.26.0. Fixes #41
77 lines
3.4 KiB
TypeScript
77 lines
3.4 KiB
TypeScript
/**
|
|
* 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 `<name-slug>-<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<string, unknown> {
|
|
return JSON.parse(readFileSync(path.join(root, rel), 'utf8'));
|
|
}
|
|
|
|
describe('region-keys.json', () => {
|
|
const manifest = readJson('public/region-keys.json') as Record<string, string>;
|
|
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,800 entries (one per region)', () => {
|
|
expect(Object.keys(manifest)).toHaveLength(1800);
|
|
});
|
|
|
|
it('has 1,800 unique keys (no collisions)', () => {
|
|
expect(new Set(keys).size).toBe(keys.length);
|
|
expect(new Set(keys).size).toBe(1800);
|
|
});
|
|
|
|
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("keeps RU-ARK's key stable across the #31 + #41 splits and mints the island and band keys", () => {
|
|
// v0.25 (#31) split RU-ARK into main + RU-ARK-NE + RU-ARK-SZ, and v0.26
|
|
// (#41) repartitioned the mainland into the core (RU-ARK) + RU-PEO +
|
|
// RU-YAM. The MAIN feature keeps its id (and therefore its key — FNV-1a
|
|
// of the id), which is what keeps existing Nation.territory / SQLite
|
|
// ownership valid; the new features get fresh keys derived the same way
|
|
// from their ids (no within-country name-collision salt needed — the
|
|
// slugs are unique).
|
|
expect(manifest['RU-ARK']).toBe('arkhangel-sk-lhbk'); // unchanged since v0.19
|
|
expect(manifest['RU-ARK-NE']).toBe('new-earth-eikt');
|
|
expect(manifest['RU-ARK-SZ']).toBe('severnaya-zemlya-awqp');
|
|
expect(manifest['RU-PEO']).toBe('pechora-qwhs');
|
|
expect(manifest['RU-YAM']).toBe('yamal-gosw');
|
|
});
|
|
|
|
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(1800);
|
|
const manifestIds = Object.keys(manifest);
|
|
expect(new Set(manifestIds)).toEqual(new Set(featureIds));
|
|
});
|
|
});
|