"""Tests for the GODSEED deterministic world engine (Agent A). Covers: clamping, unknown-enum/tool rejection, seed determinism, genesis rebuild equality, queue caps + crash isolation, moderation layers (clean pass, slur/leetspeak block, unicode-confusable block, hate symbol block, judge default-deny), and the <600-char world summary. """ import asyncio import json import sys import zlib from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from engine.genesis import GENESIS_FEATURES, genesis_features from engine.moderation import Moderator from engine.queue_worker import ( QUEUE_MAX, AlreadyPending, QueueFull, QueueWorker, ) from engine.summary import GENESIS_MONOLITH, summarize, town_center, town_name from engine.tools import TOOL_NAMES, validate_call from engine.world import World def make_world() -> World: return World.load(genesis_features()) def ok_raise(lat=10, lon=10): return { "tool": "raise_terrain", "args": {"lat": lat, "lon": lon, "radius_deg": 5, "height": 0.05, "roughness": 0.5}, } # --------------------------------------------------------------------- clamping def test_clamp_out_of_range_numbers(): world = make_world() feature, obs = world.apply( "w_000001", 0, { "tool": "raise_terrain", "args": {"lat": 200, "lon": -999, "radius_deg": 400, "height": 9, "roughness": -3}, }, ) assert obs == "ok: mountains risen at (85,-180)" assert feature.args == { "lat": 85.0, "lon": -180.0, "radius_deg": 45.0, "height": 0.12, "roughness": 0.0, } def test_clamp_moons_to_integer_and_enum_normalization(): world = make_world() feature, obs = world.apply( "w_000001", 0, {"tool": "set_sky", "args": {"palette": " AURORA ", "star_density": -1, "moons": 9.7}}, ) assert obs == "ok: sky set to aurora" assert feature.args == {"palette": "aurora", "star_density": 0.0, "moons": 3} assert isinstance(feature.args["moons"], int) def test_inscribe_text_truncated_to_90(): world = make_world() feature, obs = world.apply( "w_000001", 0, {"tool": "inscribe_wish", "args": {"text": "x" * 200, "style": "stone"}}, ) assert obs == "ok: wish inscribed in stone" assert feature.args["text"] == "x" * 90 def test_numeric_strings_accepted_and_clamped(): args, err = validate_call( "set_weather", {"kind": "rain", "intensity": "1.8"} ) assert err is None assert args == {"kind": "rain", "intensity": 1.0} def test_place_water_stream_and_pool(): # stream: hue clamped, 6 waypoints truncated to 5, no radius in canonical args args, err = validate_call( "place_water", {"kind": "stream", "path": [[0, 0], [10, 10], [20, 20], [30, 30], [40, 40], [50, 50]], "hue": 500, "radius_deg": 5}, ) assert err is None assert args["kind"] == "stream" assert len(args["path"]) == 5 assert args["hue"] == 260.0 assert "radius_deg" not in args # pool: path collapses to center, radius clamped args, err = validate_call( "place_water", {"kind": "pool", "path": [[95, 200], [1, 1]], "radius_deg": 99, "hue": 100} ) assert err is None assert args == {"kind": "pool", "path": [[85.0, 180.0]], "radius_deg": 10.0, "hue": 160.0} # stream with one waypoint is rejected args, err = validate_call("place_water", {"kind": "stream", "path": [[0, 0]], "hue": 200}) assert args is None and err == "rejected: stream needs 2..5 waypoints" # forgiving water (June 12 live traces — models fumble this tool the most): # pool without radius_deg gets the engine default args, err = validate_call("place_water", {"kind": "pool", "path": [[0, 0]], "hue": 200}) assert err is None and args["radius_deg"] == 5.0 # lat/lon (the shape of every other tool) synthesizes the path args, err = validate_call("place_water", {"kind": "pool", "lat": 10, "lon": 20, "hue": 200}) assert err is None and args["path"] == [[10.0, 20.0]] # a flat [lat, lon] pair is wrapped; hue defaults when missing args, err = validate_call("place_water", {"kind": "pool", "path": [12, 30]}) assert err is None and args["path"] == [[12.0, 30.0]] and args["hue"] == 190.0 # no location at all is still refused, with a teaching observation args, err = validate_call("place_water", {"kind": "pool"}) assert args is None and "lat+lon" in err # -------------------------------------------------------------------- rejection def test_unknown_tool_rejected(): world = make_world() before = world.version feature, obs = world.apply("w_000001", 0, {"tool": "summon_demon", "args": {}}) assert feature is None assert obs == "rejected: unknown tool 'summon_demon'" assert world.version == before # append-only list untouched on rejection def test_unknown_enum_rejected(): world = make_world() feature, obs = world.apply( "w_000001", 0, { "tool": "spawn_flora", "args": {"lat": 0, "lon": 0, "radius_deg": 5, "kind": "cactus", "density": 1, "hue": 100}, }, ) assert feature is None assert obs == "rejected: unknown kind 'cactus'" def test_missing_arg_rejected(): world = make_world() feature, obs = world.apply( "w_000001", 0, {"tool": "raise_terrain", "args": {"lat": 0, "lon": 0, "radius_deg": 5, "roughness": 0.5}}, ) assert feature is None assert obs == "rejected: missing arg 'height'" def test_bool_is_not_a_number(): args, err = validate_call("set_weather", {"kind": "rain", "intensity": True}) assert args is None and err == "rejected: bad arg 'intensity'" def test_rejected_call_does_not_consume_feature_id(): world = make_world() world.apply("w_000001", 0, {"tool": "nope", "args": {}}) feature, _ = world.apply("w_000001", 1, ok_raise()) assert feature.id == "f_000004" # genesis is f_000000..f_000003 # ------------------------------------------------------------------ determinism def test_seed_formula_matches_contract(): world = make_world() feature, _ = world.apply("w_000007", 3, ok_raise()) assert feature.seed == zlib.crc32(b"w_000007:3") & 0x7FFFFFFF def test_seed_deterministic_across_worlds(): f1, _ = make_world().apply("w_000005", 0, ok_raise()) f2, _ = make_world().apply("w_000005", 0, ok_raise()) assert f1.seed == f2.seed assert f1.args == f2.args assert f1.id == f2.id # ---------------------------------------------------------------------- genesis def test_genesis_rebuild_equality(): world = World.load(genesis_features()) rebuilt = world.state_dict()["features"] assert rebuilt == GENESIS_FEATURES # byte-for-byte through JSON, not just == (int/float and key fidelity) assert json.dumps(rebuilt, sort_keys=True) == json.dumps(GENESIS_FEATURES, sort_keys=True) assert world.epoch == 0 assert world.version == 4 assert world.sky["palette"] == "void" assert world.weather == {"kind": "clear", "intensity": 0.0} def test_genesis_features_returns_copies(): g = genesis_features() g[0]["args"]["moons"] = 99 assert GENESIS_FEATURES[0]["args"]["moons"] == 1 def test_sky_last_write_wins_but_features_append_only(): world = make_world() world.apply("w_000001", 0, {"tool": "set_sky", "args": {"palette": "gold", "star_density": 0.5, "moons": 2}}) state = world.state_dict() assert state["sky"]["palette"] == "gold" sky_features = [f for f in state["features"] if f["tool"] == "set_sky"] assert len(sky_features) == 2 # both writes kept for replay assert state["epoch"] == 1 # ------------------------------------------------------------------------ queue class GrantPlanner: """Mock planner: crashes on 'explode', otherwise raises terrain + tries a bad call.""" def __init__(self): self.granted = [] async def grant(self, wish, world_summary, act, emit): assert isinstance(world_summary, str) and len(world_summary) < 600 if "explode" in wish: raise RuntimeError("boom") await emit({"type": "thought_token", "text": "hm."}) obs = await act(ok_raise()) assert obs.startswith("ok:") bad = await act( {"tool": "spawn_flora", "args": {"lat": 0, "lon": 0, "radius_deg": 5, "kind": "cactus", "density": 1, "hue": 9}} ) assert bad == "rejected: unknown kind 'cactus'" self.granted.append(wish) return {"reading": "so be it", "turns": [], "epitaph": f"granted: {wish[:20]}"} async def _wait_for(events, type_, wish_id=None, timeout=5.0): loop = asyncio.get_running_loop() deadline = loop.time() + timeout while loop.time() < deadline: for event in events: if event.get("type") == type_ and (wish_id is None or event.get("wish_id") == wish_id): return event await asyncio.sleep(0.01) raise AssertionError(f"timeout waiting for {type_} ({wish_id})") def test_queue_caps(): async def run(): world = make_world() async def emit(event): pass worker = QueueWorker(world, Moderator(), GrantPlanner(), emit) # never started wish_id, position = await worker.submit("first wish", "client-a") assert wish_id == "w_000001" assert position == 1 with pytest.raises(AlreadyPending): await worker.submit("second wish, same soul", "client-a") for i in range(QUEUE_MAX - 1): await worker.submit(f"wish {i}", f"client-{i}") assert worker.queue_length == QUEUE_MAX with pytest.raises(QueueFull) as exc: await worker.submit("one too many", "client-z") assert "overwhelmed" in exc.value.reason asyncio.run(run()) def test_worker_crash_isolation_and_grant_flow(): async def run(): world = make_world() events, traces = [], [] async def emit(event): events.append(event) def persist(trace): traces.append(trace) planner = GrantPlanner() worker = QueueWorker(world, Moderator(), planner, emit, persist) await worker.start() try: crash_id, _ = await worker.submit("explode the heavens", "c1") grant_id, _ = await worker.submit("gentle green hills", "c2") rejected = await _wait_for(events, "wish_rejected", crash_id) assert rejected["reason"] # poetic generic, never a stack trace assert "boom" not in rejected["reason"] granted = await _wait_for(events, "wish_granted", grant_id) assert granted["epitaph"] == "granted: gentle green hills" assert granted["epoch"] == 1 # crash isolated: only the granted wish persisted, enriched by worker assert len(traces) == 1 assert traces[0]["wish_id"] == grant_id assert traces[0]["text"] == "gentle green hills" assert traces[0]["moderation"] == {"allowed": True, "category": None} assert traces[0]["submitted_at"] > 0 # tool_call + world_delta emitted with the contract seed delta = await _wait_for(events, "world_delta") assert delta["feature"]["seed"] == zlib.crc32(f"{grant_id}:0".encode()) & 0x7FFFFFFF call = await _wait_for(events, "tool_call", grant_id) assert call["call_index"] == 0 and call["tool"] == "raise_terrain" # client slot freed after completion (even after a crash) next_id, _ = await worker.submit("a quiet lighthouse", "c1") await _wait_for(events, "wish_granted", next_id) assert world.epoch == 2 assert planner.granted == ["gentle green hills", "a quiet lighthouse"] # moderation block: planner never called, poetic rejection emitted blocked_id, _ = await worker.submit("build a n1gg3r statue", "c3") blocked = await _wait_for(events, "wish_rejected", blocked_id) assert "declined" in blocked["reason"] assert len(planner.granted) == 2 # queue + wish_started events flowed assert any(e["type"] == "queue" and "length" in e for e in events) assert any(e["type"] == "wish_started" for e in events) finally: await worker.stop() assert not worker.is_running asyncio.run(run()) def test_wish_counter_resumes_from_persisted_world(): world = make_world() world.apply("w_000041", 0, ok_raise()) async def run(): async def emit(event): pass worker = QueueWorker(world, Moderator(), GrantPlanner(), emit) wish_id, _ = await worker.submit("continue the count", "c1") assert wish_id == "w_000042" asyncio.run(run()) # ------------------------------------------------------------------- moderation def test_moderation_clean_pass(): verdict = Moderator().precheck("a lighthouse on the northern shore, please") assert verdict.allowed assert verdict.category is None and verdict.poetic_reason is None def test_moderation_slur_leetspeak_block(): verdict = Moderator().precheck("build a n1gg3r statue") assert not verdict.allowed assert verdict.category == "hate" assert verdict.poetic_reason def test_moderation_unicode_confusable_block(): # Cyrillic і (U+0456) and е (U+0435) verdict = Moderator().precheck("make a nіggеr forest") assert not verdict.allowed assert verdict.category == "hate" def test_moderation_spaced_out_slur_block(): verdict = Moderator().precheck("a f.a.g.g.o.t shrine") assert not verdict.allowed assert verdict.category == "hate" def test_moderation_cyrillic_transliteration_block(): mod = Moderator() for wish in ("ХИТЛЕР shrine", "гитлер monument", "свастика lake"): verdict = mod.precheck(wish) assert not verdict.allowed and verdict.category == "hate", wish # benign Cyrillic still passes assert mod.precheck("снежные горы и тихое море").allowed def test_moderation_hate_symbol_block(): verdict = Moderator().precheck("plant a 卐 shaped forest") assert not verdict.allowed assert verdict.category == "hate" def test_moderation_violent_phrase_block(): verdict = Moderator().precheck("kill all the villagers") assert not verdict.allowed assert verdict.category == "violence" def test_moderation_no_scunthorpe_false_positives(): mod = Moderator() for wish in ( "a raccoon village under the analysis tower", "glass cliffs of sussex", "cucumber vines along the equator", "a peacock garden by the cockpit ruins", ): assert mod.precheck(wish).allowed, wish def test_moderation_length_and_charset(): mod = Moderator() assert mod.precheck("a" * 140).allowed assert mod.precheck("a" * 141).category == "length" assert mod.precheck("wish\x00wish").category == "charset" assert mod.precheck("").category == "empty" assert mod.precheck(" ").category == "empty" assert mod.precheck(None).category == "empty" # benign newlines fold to spaces rather than denying assert mod.precheck("two moons\nand a stream").allowed def test_moderation_judge_paths(): async def run(): calls = [] async def crashing_judge(text): raise RuntimeError("gpu fell over") async def garbage_judge(text): return "banana banana" async def allow_judge(text): calls.append(text) return {"allowed": True, "category": None} async def deny_judge(text): return '{"allowed": false, "category": "sad"}' async def fenced_judge(text): return '```json\n{"allowed": true}\n```' wish = "a calm violet sea" # default-deny on judge exception verdict = await Moderator(judge=crashing_judge).check(wish) assert not verdict.allowed and verdict.category == "uncertain" # default-deny on unparseable reply verdict = await Moderator(judge=garbage_judge).check(wish) assert not verdict.allowed and verdict.category == "uncertain" # judge allow passes through verdict = await Moderator(judge=allow_judge).check(wish) assert verdict.allowed and calls == [wish] # judge deny is honored, category preserved, reason poetic verdict = await Moderator(judge=deny_judge).check(wish) assert not verdict.allowed and verdict.category == "sad" and verdict.poetic_reason # tolerant of code fences around valid JSON verdict = await Moderator(judge=fenced_judge).check(wish) assert verdict.allowed # no judge configured: layers 1-2 decide verdict = await Moderator().check(wish) assert verdict.allowed asyncio.run(run()) def test_moderation_judge_skipped_when_blocklisted(): async def run(): calls = [] async def judge(text): calls.append(text) return {"allowed": True} verdict = await Moderator(judge=judge).check("a n1gger forest") assert not verdict.allowed assert calls == [] # wordlist layer denies before the judge spends tokens asyncio.run(run()) # ---------------------------------------------------------------------- summary def test_summary_compact_and_complete(): world = make_world() wish = "w_000001" world.apply(wish, 0, ok_raise(22, -40)) world.apply(wish, 1, {"tool": "spawn_flora", "args": {"lat": 22, "lon": -40, "radius_deg": 8, "kind": "glowgrass", "density": 0.7, "hue": 160}}) world.apply(wish, 2, {"tool": "place_structure", "args": {"lat": 5, "lon": 5, "kind": "lighthouse", "scale": 1.0, "hue": 40}}) world.apply(wish, 3, {"tool": "set_weather", "args": {"kind": "mist", "intensity": 0.4}}) world.record_epitaph("the hills learned to glow") world.record_epitaph("a lighthouse for the lost") text = world.summary() assert len(text) < 600 assert "epoch 1" in text assert "glowgrass" in text assert "lighthouse(5,5)" in text assert "mist" in text assert "a lighthouse for the lost" in text assert summarize(world) == text def test_summary_stays_under_cap_with_many_features(): world = make_world() for i in range(40): wish = f"w_{i + 1:06d}" world.apply(wish, 0, ok_raise((i * 7) % 80 - 40, (i * 13) % 300 - 150)) world.apply(wish, 1, {"tool": "place_structure", "args": {"lat": i % 60, "lon": -i % 120, "kind": "shrine", "scale": 1.2, "hue": i * 8 % 360}}) world.apply(wish, 2, {"tool": "spawn_flora", "args": {"lat": 0, "lon": 0, "radius_deg": 10, "kind": ["trees", "vines", "reeds", "flowers", "mushrooms", "glowgrass"][i % 6], "density": 0.5, "hue": 100}}) world.record_epitaph("an unreasonably long epitaph about the slow work of small gods " + "x" * 60) assert len(world.summary()) < 600 # ------------------------------------------------------------------------ misc def test_tool_surface_is_exactly_eleven(): assert TOOL_NAMES == ( "raise_terrain", "lower_terrain", "spawn_flora", "place_structure", "place_water", "set_weather", "set_sky", "inscribe_wish", "spawn_life", "build_district", "place_road", ) # --------------------------------------------------------------------- city update def test_new_structure_kinds_accepted(): world = make_world() for kind in ("tower", "warehouse", "cafe"): feature, obs = world.apply( "w_000001", 0, {"tool": "place_structure", "args": {"lat": 5, "lon": 5, "kind": kind, "scale": 1.0, "hue": 40}}, ) assert feature is not None, kind assert feature.args["kind"] == kind assert obs == f"ok: {kind} placed at (5,5)" def test_place_structure_forgives_unknown_and_synonym_kinds(): # June 12: rejecting model-invented kinds made town wishes build nothing. # Now place_structure NEVER rejects a kind — it maps it. args, err = validate_call( "place_structure", {"lat": 0, "lon": 0, "kind": "castle", "scale": 1.0, "hue": 100}) assert err is None and args["kind"] == "house" # unknown -> humble house for given, expected in [("market_square", "market"), ("skyscraper", "tower"), ("coffeehouse", "cafe"), ("cottage", "house"), ("church", "shrine")]: args, err = validate_call( "place_structure", {"lat": 1, "lon": 1, "kind": given, "scale": 1.0, "hue": 50}) assert err is None and args["kind"] == expected, f"{given} -> {args}" # a "district"-ish kind reroutes to a dense build_district args, err = validate_call( "place_structure", {"lat": 2, "lon": 3, "kind": "neighborhood", "scale": 1.0, "hue": 40}) assert err is None and "density" in args and "kind" not in args # build_district shape def test_spawn_life_valid_and_clamped(): world = make_world() feature, obs = world.apply( "w_000001", 0, {"tool": "spawn_life", "args": {"lat": 200, "lon": -999, "radius_deg": 99, "kind": "CARTS", "count": 50, "hue": 720}}, ) assert feature is not None assert feature.args == { "lat": 85.0, "lon": -180.0, "radius_deg": 20.0, # clamped to spawn_life max "kind": "carts", # enum lowercased "count": 12, # clamped to max, integer "hue": 360.0, } assert isinstance(feature.args["count"], int) assert obs == "ok: 12 carts stirring at (85,-180)" def test_spawn_life_count_is_integer_and_low_clamp(): args, err = validate_call( "spawn_life", {"lat": 0, "lon": 0, "radius_deg": 0.1, "kind": "fireflies", "count": 0.4, "hue": 200}, ) assert err is None assert args["radius_deg"] == 1.0 # clamped up to min assert args["count"] == 1 # rounds/clamps up to min, integer assert isinstance(args["count"], int) assert args["kind"] == "fireflies" def test_spawn_life_rejects_unknown_kind(): args, err = validate_call( "spawn_life", {"lat": 0, "lon": 0, "radius_deg": 5, "kind": "dragons", "count": 3, "hue": 100}, ) assert args is None and err == "rejected: unknown kind 'dragons'" def test_spawn_life_missing_arg_rejected(): args, err = validate_call( "spawn_life", {"lat": 0, "lon": 0, "radius_deg": 5, "kind": "birds", "hue": 100}, ) assert args is None and err == "rejected: missing arg 'count'" # --------------------------------------------------------------------- town mode def test_build_district_valid_and_clamped(): world = make_world() feature, obs = world.apply( "w_000001", 0, {"tool": "build_district", "args": {"lat": 14, "lon": 38, "radius_deg": 99, "density": 9, "hue": 720}}, ) assert feature is not None assert feature.args == { "lat": 14.0, "lon": 38.0, "radius_deg": 15.0, # clamped to build_district max "density": 1.0, # clamped to max "hue": 360.0, } assert obs == "ok: a district rises near (14,38)" def test_build_district_radius_clamps_up_and_missing_arg_rejected(): args, err = validate_call( "build_district", {"lat": 0, "lon": 0, "radius_deg": 0.5, "density": 0.4, "hue": 200}, ) assert err is None assert args["radius_deg"] == 2.0 # clamped up to min args, err = validate_call( "build_district", {"lat": 0, "lon": 0, "density": 0.4, "hue": 200} ) assert args is None and err == "rejected: missing arg 'radius_deg'" def test_place_road_valid_and_clamped(): world = make_world() # 7 waypoints truncated to 6; out-of-range coords clamped feature, obs = world.apply( "w_000001", 0, {"tool": "place_road", "args": {"path": [[200, -999], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6]]}}, ) assert feature is not None assert len(feature.args["path"]) == 6 assert feature.args["path"][0] == [85.0, -180.0] assert feature.args == {"path": [[85.0, -180.0], [1.0, 1.0], [2.0, 2.0], [3.0, 3.0], [4.0, 4.0], [5.0, 5.0]]} assert obs == "ok: a road laid through 6 points" def test_place_road_flat_pair_and_dict_points(): # a flat [lat, lon] pair is NOT enough — a road needs two waypoints args, err = validate_call("place_road", {"path": [12, 30]}) assert args is None and err == "rejected: road needs 2..6 waypoints" # dict waypoints are accepted (like place_water) args, err = validate_call( "place_road", {"path": [{"lat": 10, "lon": 20}, {"lat": 12, "lon": 22}]} ) assert err is None and args == {"path": [[10.0, 20.0], [12.0, 22.0]]} def test_place_road_rejects_missing_and_short_path(): args, err = validate_call("place_road", {}) assert args is None and "road needs path" in err args, err = validate_call("place_road", {"path": [[10, 20]]}) assert args is None and err == "rejected: road needs 2..6 waypoints" args, err = validate_call("place_road", {"path": [[10, 20], ["x", "y"]]}) assert args is None and "bad waypoint" in err def test_new_civic_structure_kinds_accepted(): world = make_world() for kind in ("bank", "market", "house"): feature, obs = world.apply( "w_000001", 0, {"tool": "place_structure", "args": {"lat": 5, "lon": 5, "kind": kind, "scale": 1.0, "hue": 40}}, ) assert feature is not None, kind assert feature.args["kind"] == kind assert obs == f"ok: {kind} placed at (5,5)" def test_town_center_genesis_fallback(): # A fresh genesis world has no non-genesis town tools -> monolith fallback. world = make_world() assert town_center(world) == GENESIS_MONOLITH == (14.0, 38.0) # Terrain / flora alone do NOT anchor the town (only structures + districts). world.apply("w_000001", 0, ok_raise(50, -50)) world.apply("w_000001", 1, {"tool": "spawn_flora", "args": {"lat": 60, "lon": 60, "radius_deg": 8, "kind": "trees", "density": 0.5, "hue": 120}}) assert town_center(world) == GENESIS_MONOLITH def test_town_center_unit_vector_centroid(): world = make_world() world.apply("w_000001", 0, {"tool": "place_structure", "args": {"lat": 10, "lon": 20, "kind": "tower", "scale": 1.0, "hue": 200}}) world.apply("w_000001", 1, {"tool": "build_district", "args": {"lat": 20, "lon": 40, "radius_deg": 8, "density": 0.6, "hue": 40}}) # Unit-vector mean of (10,20) and (20,40), renormalized (computed offline). assert town_center(world) == (15.2206, 29.7632) def test_town_center_excludes_genesis_monolith(): # The genesis monolith (14,38) is NOT counted; one built tower defines the town. world = make_world() world.apply("w_000001", 0, {"tool": "place_structure", "args": {"lat": -30, "lon": 100, "kind": "house", "scale": 1.0, "hue": 30}}) lat, lon = town_center(world) assert round(lat) == -30 and round(lon) == 100 def test_town_name_is_deterministic(): # Same rounded center -> same name; from the curated pool. from engine.summary import TOWN_NAMES n1 = town_name(14.0, 38.0) n2 = town_name(14.49, 37.51) # rounds to the same (14, 38) assert n1 == n2 == "Emberlyn" assert n1 in TOWN_NAMES # The genesis-fallback town is always named the same. assert town_name(*GENESIS_MONOLITH) == "Emberlyn" def test_summary_names_the_town_and_tally(): world = make_world() wish = "w_000001" world.apply(wish, 0, {"tool": "place_structure", "args": {"lat": 14, "lon": 38, "kind": "tower", "scale": 1.4, "hue": 200}}) world.apply(wish, 1, {"tool": "place_structure", "args": {"lat": 15, "lon": 39, "kind": "tower", "scale": 1.4, "hue": 200}}) world.apply(wish, 2, {"tool": "place_structure", "args": {"lat": 13, "lon": 37, "kind": "cafe", "scale": 0.9, "hue": 40}}) world.apply(wish, 3, {"tool": "build_district", "args": {"lat": 14, "lon": 38, "radius_deg": 8, "density": 0.7, "hue": 30}}) text = world.summary() assert len(text) < 600 assert "The town of" in text assert "stands near" in text assert "2 towers" in text assert "a cafe" in text assert "a district" in text # The town line leads the summary (steering: where to keep building). assert text.startswith("The town of") def test_summary_town_line_survives_truncation(): world = make_world() for i in range(40): wish = f"w_{i + 1:06d}" world.apply(wish, 0, {"tool": "place_structure", "args": {"lat": i % 60 - 30, "lon": (i * 9) % 300 - 150, "kind": "house", "scale": 1.0, "hue": i * 8 % 360}}) world.record_epitaph("a long epitaph about the slow work of small gods " + "x" * 60) text = world.summary() assert len(text) < 600 assert text.startswith("The town of")