The verified-still-open coverage list: per-type attractor reachability fixtures (LakeShore via enclosed depression, PassEntrance via crafted saddle, PlainCenter via flat terrain, RiverCrossing via confluence) plus thin_by_spacing behavior (collision, strict-< boundary, equirectangular column wrap); heightmap 8-bit decode, sea_level passthrough, downsample identity and zero-target early-return; drainage area_pct bit-for-bit determinism plus the isolated-basin-fallback divergence comment (Tyre N1, citing the pre-#953 behavior it deliberately departs from); the layer1 mountain-branch pairing test (investigated first — the cascade test supplies a mountain pool but only ever asserted river counts, a genuine gap); an importer idempotency test covering atlas_city_names AND atlas_feature_names plus the Sol exemption, wired into make test-tooling; and the oasis_water dilation radius scaled by GRID_W/512 (Tyre N2, hash-stable). One stale item dropped per the refinement trim (test_sim_determinism wiring — already done). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
158 lines
6.4 KiB
Python
158 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Idempotency tests for economy_import.atlas's names-only pool populators
|
|
(T-964 — Phase-4 test hardening).
|
|
|
|
`populate_atlas_city_names` and `populate_atlas_feature_names` both clear
|
|
their target table before reinserting (see each function's own docstring:
|
|
"there is no UNIQUE(body_id, name[, feature_type]) — without the clear a
|
|
re-run would accumulate duplicates"). This test proves that contract holds in
|
|
practice: two back-to-back runs against the same DB must yield an IDENTICAL
|
|
row count, not a doubled one, and Sol ('GJ 0') must remain permanently
|
|
exempt (D-223) on both runs.
|
|
|
|
Runs against a scratch COPY of the committed `server/data/systems.db` (real
|
|
schema + real `bodies`/FK data — not a hand-rolled in-memory schema, which
|
|
would drift from the real FK web this populator depends on). Wiki content
|
|
(`wiki/star-systems/*/bodies/*/markers.json`) is read directly from the repo
|
|
— read-only input, safe to reuse as-is; only the DB connection is scratch.
|
|
Deliberately calls the two populator functions directly rather than the full
|
|
`import_economics.py` CLI: the CLI's first step shells out to the Rust
|
|
`generate_brands` binary and overwrites `generated_brands.toml` on disk
|
|
(`brands.regenerate_brands`), a real side effect on shared repo content that
|
|
has nothing to do with atlas name-pool idempotency and would make this test
|
|
depend on a Rust build.
|
|
|
|
Stdlib only (unittest) — run directly or via `make test-tooling`:
|
|
python3 tooling/economy-db/test_atlas_idempotency.py
|
|
"""
|
|
|
|
import shutil
|
|
import sqlite3
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
from economy_import import atlas # noqa: E402
|
|
from economy_import.paths import DB_PATH # noqa: E402
|
|
|
|
|
|
class AtlasNamePoolIdempotencyTests(unittest.TestCase):
|
|
"""Two runs on a scratch DB copy must yield stable, non-doubled counts."""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
if not DB_PATH.exists():
|
|
raise unittest.SkipTest(f"committed systems.db not found at {DB_PATH}")
|
|
|
|
def setUp(self):
|
|
self._tmp = tempfile.TemporaryDirectory()
|
|
scratch_path = Path(self._tmp.name) / "systems_scratch.db"
|
|
shutil.copyfile(DB_PATH, scratch_path)
|
|
self.conn = sqlite3.connect(str(scratch_path))
|
|
self.conn.execute("PRAGMA foreign_keys=ON")
|
|
|
|
def tearDown(self):
|
|
self.conn.close()
|
|
self._tmp.cleanup()
|
|
|
|
def test_atlas_city_names_count_stable_across_two_runs(self):
|
|
first = atlas.populate_atlas_city_names(self.conn, dry_run=False)
|
|
first_count = self.conn.execute(
|
|
"SELECT COUNT(*) FROM atlas_city_names"
|
|
).fetchone()[0]
|
|
self.assertEqual(
|
|
first, first_count, "populator return value must match the rows it wrote"
|
|
)
|
|
|
|
second = atlas.populate_atlas_city_names(self.conn, dry_run=False)
|
|
second_count = self.conn.execute(
|
|
"SELECT COUNT(*) FROM atlas_city_names"
|
|
).fetchone()[0]
|
|
|
|
self.assertEqual(
|
|
first_count,
|
|
second_count,
|
|
"a second run must yield an IDENTICAL atlas_city_names count, not "
|
|
"a doubled one — the clear-before-reinsert contract must hold",
|
|
)
|
|
self.assertEqual(
|
|
first, second, "the populator's own return value must also be stable"
|
|
)
|
|
self.assertGreater(
|
|
first_count, 0, "fixture sanity: the committed wiki content must yield rows"
|
|
)
|
|
|
|
def test_atlas_feature_names_count_stable_across_two_runs(self):
|
|
# T-1169: atlas_feature_names landed alongside atlas_city_names but
|
|
# is a separate table/populator — must be checked independently, not
|
|
# assumed to share the city-name populator's idempotency by proximity.
|
|
first = atlas.populate_atlas_feature_names(self.conn, dry_run=False)
|
|
first_count = self.conn.execute(
|
|
"SELECT COUNT(*) FROM atlas_feature_names"
|
|
).fetchone()[0]
|
|
self.assertEqual(first, first_count)
|
|
|
|
second = atlas.populate_atlas_feature_names(self.conn, dry_run=False)
|
|
second_count = self.conn.execute(
|
|
"SELECT COUNT(*) FROM atlas_feature_names"
|
|
).fetchone()[0]
|
|
|
|
self.assertEqual(
|
|
first_count,
|
|
second_count,
|
|
"a second run must yield an IDENTICAL atlas_feature_names count, not "
|
|
"a doubled one — the clear-before-reinsert contract must hold",
|
|
)
|
|
self.assertEqual(first, second)
|
|
self.assertGreater(
|
|
first_count, 0, "fixture sanity: the committed wiki content must yield rows"
|
|
)
|
|
|
|
def test_sol_system_gj0_gets_zero_rows_in_both_pools_across_two_runs(self):
|
|
# D-223 permanent exemption: Sol ('GJ 0') keeps authored,
|
|
# geometry-bearing markers.json via sol_import.py — never the names
|
|
# pool. Must hold on the FIRST run (not just "never accumulates") and
|
|
# remain zero on the second.
|
|
for run in (1, 2):
|
|
atlas.populate_atlas_city_names(self.conn, dry_run=False)
|
|
atlas.populate_atlas_feature_names(self.conn, dry_run=False)
|
|
|
|
sol_cities = self.conn.execute(
|
|
"""SELECT COUNT(*) FROM atlas_city_names
|
|
WHERE body_id IN (SELECT body_id FROM bodies WHERE system_id = 'GJ 0')"""
|
|
).fetchone()[0]
|
|
sol_features = self.conn.execute(
|
|
"""SELECT COUNT(*) FROM atlas_feature_names
|
|
WHERE body_id IN (SELECT body_id FROM bodies WHERE system_id = 'GJ 0')"""
|
|
).fetchone()[0]
|
|
|
|
self.assertEqual(
|
|
sol_cities, 0, f"run {run}: Sol (GJ 0) must have zero atlas_city_names rows"
|
|
)
|
|
self.assertEqual(
|
|
sol_features,
|
|
0,
|
|
f"run {run}: Sol (GJ 0) must have zero atlas_feature_names rows",
|
|
)
|
|
|
|
# Fixture sanity: GJ 0 must actually have bodies in this DB, or the
|
|
# zero-rows assertions above would be vacuously true.
|
|
gj0_body_count = self.conn.execute(
|
|
"SELECT COUNT(*) FROM bodies WHERE system_id = 'GJ 0'"
|
|
).fetchone()[0]
|
|
self.assertGreater(
|
|
gj0_body_count,
|
|
0,
|
|
"fixture sanity: GJ 0 (Sol) must have bodies in the committed DB, or "
|
|
"the exemption checks above are vacuous",
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|