gadm-ts/dist/__tests__/tree.test.js
2026-03-23 17:35:02 +01:00

118 lines
13 KiB
JavaScript

/**
* GADMTree tests — builds trees and saves results to tests/tree/ for inspection.
*/
import { describe, it, expect } from 'vitest';
import { buildTree, findNode, walkDFS, walkLevel, leaves } from '../tree.js';
import { writeFileSync, mkdirSync } from 'fs';
import { resolve } from 'path';
const CACHE_DIR = resolve('tests', 'cache', 'gadm');
const OUT_DIR = resolve('tests', 'tree');
function save(name, data) {
mkdirSync(OUT_DIR, { recursive: true });
const fp = resolve(OUT_DIR, name);
writeFileSync(fp, JSON.stringify(data, null, 2), 'utf-8');
console.log(` → saved tests/tree/${name}`);
}
describe('GADMTree', () => {
it('Spain → Cataluña', async () => {
const tree = await buildTree({ name: 'Cataluña', cacheDir: CACHE_DIR });
expect(tree.root.name).toBe('Cataluña');
expect(tree.root.gid).toBe('ESP.6_1');
expect(tree.root.level).toBe(1);
expect(tree.maxLevel).toBeGreaterThanOrEqual(4);
expect(tree.nodeCount).toBeGreaterThan(900);
// 4 provinces
expect(tree.root.children.length).toBe(4);
expect(tree.root.children.map(c => c.name)).toContain('Barcelona');
// Barcelona sub-regions
const bcn = tree.root.children.find(c => c.name === 'Barcelona');
expect(bcn.children.length).toBeGreaterThan(5);
// Leaves = municipalities
const leafNodes = [...leaves(tree.root)];
expect(leafNodes.length).toBeGreaterThan(900);
console.log(`Cataluña: ${tree.nodeCount} nodes, ${leafNodes.length} leaves, max L${tree.maxLevel}`);
save('test-cataluna.json', tree);
});
it('Germany (full country)', async () => {
const tree = await buildTree({ admin: 'DEU', cacheDir: CACHE_DIR });
expect(tree.root.name).toBe('Germany');
expect(tree.root.level).toBe(0);
expect(tree.root.children.length).toBe(16); // 16 Bundesländer
console.log(`Germany: ${tree.nodeCount} nodes, ${tree.root.children.length} Bundesländer`);
save('test-germany-summary.json', {
name: tree.root.name,
gid: tree.root.gid,
maxLevel: tree.maxLevel,
nodeCount: tree.nodeCount,
level1: tree.root.children.map(c => ({
name: c.name,
gid: c.gid,
childCount: c.children.length,
})),
});
});
it('Germany → Sachsen → Dresden', async () => {
const tree = await buildTree({ admin: 'DEU', cacheDir: CACHE_DIR });
const sachsen = findNode(tree.root, 'Sachsen');
expect(sachsen).toBeDefined();
expect(sachsen.children.length).toBe(13);
const dresden = findNode(tree.root, 'Dresden');
expect(dresden).toBeDefined();
// Walk subtree
const nodes = [...walkDFS(dresden)];
const leafNodes = [...leaves(dresden)];
console.log(`Dresden: ${dresden.gid}, L${dresden.level}, ${nodes.length} nodes, ${leafNodes.length} leaves`);
console.log(` Sachsen L2: ${sachsen.children.map(c => c.name).join(', ')}`);
save('test-dresden.json', {
sachsen: {
name: sachsen.name,
gid: sachsen.gid,
children: sachsen.children.map(c => c.name),
},
dresden: {
name: dresden.name,
gid: dresden.gid,
level: dresden.level,
totalNodes: nodes.length,
leaves: leafNodes.map(l => ({ name: l.name, gid: l.gid })),
children: dresden.children.map(c => ({
name: c.name,
gid: c.gid,
children: c.children.map(gc => ({ name: gc.name, gid: gc.gid })),
})),
},
});
});
it('cache hit is fast', async () => {
const t0 = Date.now();
const tree = await buildTree({ name: 'Cataluña', cacheDir: CACHE_DIR });
const ms = Date.now() - t0;
expect(tree.root.name).toBe('Cataluña');
expect(ms).toBeLessThan(500);
console.log(`Cache hit: ${ms}ms`);
});
it('iterators: DFS, walkLevel, findNode', async () => {
const tree = await buildTree({ name: 'Cataluña', cacheDir: CACHE_DIR });
// DFS visits every node
let count = 0;
for (const _ of walkDFS(tree.root))
count++;
expect(count).toBe(tree.nodeCount);
// Level 2 = provinces
const provinces = [...walkLevel(tree.root, 2)];
expect(provinces.length).toBe(4);
// findNode by name
const bcn = findNode(tree.root, 'Barcelona');
expect(bcn?.gid).toBe('ESP.6.1_1');
// findNode by GID
const girona = findNode(tree.root, 'ESP.6.2_1');
expect(girona?.name).toBe('Girona');
save('test-iterators.json', {
dfsCount: count,
nodeCount: tree.nodeCount,
provinces: provinces.map(p => ({ name: p.name, gid: p.gid })),
barcelona: { name: bcn.name, gid: bcn.gid, children: bcn.children.length },
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlZS50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL19fdGVzdHNfXy90cmVlLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFDSCxPQUFPLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDOUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQWlCLE1BQU0sWUFBWSxDQUFDO0FBQzVGLE9BQU8sRUFBRSxhQUFhLEVBQUUsU0FBUyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQzlDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFL0IsTUFBTSxTQUFTLEdBQUcsT0FBTyxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFDcEQsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztBQUV6QyxTQUFTLElBQUksQ0FBQyxJQUFZLEVBQUUsSUFBYTtJQUNyQyxTQUFTLENBQUMsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDeEMsTUFBTSxFQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztJQUNsQyxhQUFhLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUMxRCxPQUFPLENBQUMsR0FBRyxDQUFDLHdCQUF3QixJQUFJLEVBQUUsQ0FBQyxDQUFDO0FBQ2hELENBQUM7QUFFRCxRQUFRLENBQUMsVUFBVSxFQUFFLEdBQUcsRUFBRTtJQUV0QixFQUFFLENBQUMsa0JBQWtCLEVBQUUsS0FBSyxJQUFJLEVBQUU7UUFDOUIsTUFBTSxJQUFJLEdBQUcsTUFBTSxTQUFTLENBQUMsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBRXhFLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN4QyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDdEMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2hDLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsc0JBQXNCLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDaEQsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFNUMsY0FBYztRQUNkLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDMUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUVuRSx3QkFBd0I7UUFDeEIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxXQUFXLENBQUUsQ0FBQztRQUNsRSxNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFL0MsMEJBQTBCO1FBQzFCLE1BQU0sU0FBUyxHQUFHLENB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