Spaces:
Paused
Paused
Claude
Claude Opus 5
Tier 1, step 5: close the coverage gaps and the doc drift (C6-C16, E1, E2)
cce54f5 unverified Download test/data-integrity.test.js from Almaatla/SpaceCities: direct link, hf CLI and curl.
- Browser
- Download file 4.57 kB
-
https://huggingface.co/spaces/Almaatla/SpaceCities/resolve/main/test/data-integrity.test.js
- Command line
-
hf download hf://spaces/Almaatla/SpaceCities/test/data-integrity.test.js
-
curl -L -o data-integrity.test.js https://huggingface.co/spaces/Almaatla/SpaceCities/resolve/main/test/data-integrity.test.js
4.57 kB
| import { test } from "node:test"; | |
| import assert from "node:assert/strict"; | |
| import { BUILDINGS, UNITS, UPGRADES } from "../engine/entities.js"; | |
| import { TECHS } from "../engine/techtree.js"; | |
| import { COM, RECIPES, PLANETS } from "../data.js"; | |
| import { ODYSSEY_WORLDS } from "../engine/galaxy.js"; | |
| import { PLANET_ARCHETYPE, ODYSSEY_EXTRA_ARCHETYPE } from "../engine/aiArchetypes.js"; | |
| // `requires` is a stringly-typed union: a token is EITHER a building-type key (a completed building of | |
| // that type) OR a research/upgrade id (a purchased UPGRADES entry or a researched TECHS node). It's | |
| // resolved only at read time (entities.js prereqsMet) with no lookup table, so a typo β "assmbler" | |
| // for "assembler", "arsenl" for "arsenal" β raises no error: prereqsMet just never matches it, and | |
| // the def becomes PERMANENTLY unbuildable with no diagnostic. This static guard resolves every token | |
| // on every def against the real key sets so a bad `requires` fails the suite loudly, naming the culprit. | |
| test("every `requires` token on a building/unit def resolves to a real building type or research/upgrade token", () => { | |
| // The full set of legal tokens: building types + upgrade ids + tech ids β exactly the universe | |
| // prereqsMet checks (BUILDINGS[req] for the building branch, player.upgrades[req] for the rest, | |
| // which UPGRADES and TECHS both write into on completion). | |
| const known = new Set([...Object.keys(BUILDINGS), ...Object.keys(UPGRADES), ...Object.keys(TECHS)]); | |
| const bad = []; | |
| for (const [group, defs] of [["BUILDINGS", BUILDINGS], ["UNITS", UNITS]]) { | |
| for (const [key, def] of Object.entries(defs)) { | |
| for (const token of def.requires || []) { | |
| if (!known.has(token)) bad.push(`${group}.${key} requires "${token}" (unresolvable β not a building/upgrade/tech)`); | |
| } | |
| } | |
| } | |
| assert.deepEqual(bad, [], "unresolvable `requires` token(s) β a typo makes the def permanently unbuildable:\n" + bad.join("\n")); | |
| }); | |
| /* ---- C11: the other three data<->engine seams ------------------------------------------------ | |
| The `requires`-token test above guards ONE stringly-typed cross-reference, and its own header | |
| explains why that matters: the reference "is resolved only at read time β¦ with no lookup table, | |
| so a typo β¦ raises no error". Three more seams have exactly that shape and nothing guarding | |
| them. All are clean today; these are the tests that keep them that way as the tables grow. */ | |
| test("every commodity key on a cost/upkeep/consumes/inputs resolves to a real COM id", () => { | |
| const bad = []; | |
| const check = (where, bag) => { | |
| if (!bag || typeof bag !== "object") return; | |
| for (const com of Object.keys(bag)) if (!COM[com] && com !== "energy") bad.push(`${where} β ${com}`); | |
| }; | |
| for (const [table, defs] of [["BUILDINGS", BUILDINGS], ["UNITS", UNITS], ["UPGRADES", UPGRADES], ["TECHS", TECHS]]) | |
| for (const [id, def] of Object.entries(defs)) | |
| for (const field of ["cost", "altCost", "upkeep", "consumes", "inputs"]) | |
| check(`${table}.${id}.${field}`, def[field]); | |
| for (const [id, r] of Object.entries(RECIPES)) { | |
| check(`RECIPES.${id}.in`, r.in); | |
| if (r.out && !COM[r.out]) bad.push(`RECIPES.${id}.out β ${r.out}`); | |
| } | |
| assert.deepEqual(bad, [], | |
| "commodity key(s) naming no real COM id β canAfford would compare against an undefined balance:\n" + bad.join("\n")); | |
| }); | |
| test("every id in BUILDINGS[*].produces is a real UNITS key", () => { | |
| const bad = []; | |
| for (const [id, def] of Object.entries(BUILDINGS)) | |
| for (const t of def.produces || []) if (!UNITS[t]) bad.push(`BUILDINGS.${id}.produces β ${t}`); | |
| assert.deepEqual(bad, [], | |
| "produces entr(ies) naming no real unit β the build button would produce nothing:\n" + bad.join("\n")); | |
| }); | |
| test("every ODYSSEY_WORLD is a real PLANETS id with an explicitly-mapped archetype", () => { | |
| // Asserting on archetypeFor's RETURN can't tell "explicitly balanced" from "fell through the | |
| // `|| ARCHETYPES.balanced` default", so check the maps themselves. | |
| const mapped = { ...PLANET_ARCHETYPE, ...ODYSSEY_EXTRA_ARCHETYPE }; | |
| const unmapped = ODYSSEY_WORLDS.filter(w => !(w in mapped)); | |
| const planetIds = new Set(PLANETS.map(p => p.id)); // PLANETS is an ARRAY of defs, not an id-keyed map | |
| const unknown = ODYSSEY_WORLDS.filter(w => !planetIds.has(w)); | |
| assert.deepEqual(unknown, [], "Odyssey world(s) with no PLANETS entry"); | |
| assert.deepEqual(unmapped, [], | |
| "Odyssey world(s) silently falling through to the balanced archetype default:\n" + unmapped.join("\n")); | |
| }); | |