#!/usr/bin/env python3 """Tests for tower_visual_mapper.py — verifies map construction and formatting.""" import json import sys import tempfile from pathlib import Path from unittest.mock import patch sys.path.insert(0, str(Path(__file__).parent.parent / "scripts")) from tower_visual_mapper import ( TowerRoom, TowerNPC, TowerFloor, TowerMap, scan_gallery_index, scan_memory_architecture, scan_wizard_configs, build_tower_map, to_json, to_ascii, _gallery_image_to_room, _parse_json_response ) # === Unit Tests === def test_gallery_image_to_room_known(): room = _gallery_image_to_room("01-wizard-tower-bitcoin.jpg", "The Tower", "The Origin") assert room is not None assert room.name == "The Tower — Exterior" assert room.floor == 0 assert "bitcoin" in room.description.lower() or "sovereign" in room.description.lower() print(" PASS: test_gallery_image_to_room_known") def test_gallery_image_to_room_unknown(): room = _gallery_image_to_room("random-image.jpg", "Something", "The Origin") assert room is None print(" PASS: test_gallery_image_to_room_unknown") def test_gallery_image_to_room_philosophy(): room = _gallery_image_to_room("06-the-paperclip-moment.jpg", "A paperclip", "The Philosophy") assert room is not None assert room.category == "philosophy" print(" PASS: test_gallery_image_to_room_philosophy") def test_parse_json_response_clean(): text = '{"floors": 5, "rooms": [{"name": "Test"}]}' result = _parse_json_response(text) assert result["floors"] == 5 assert result["rooms"][0]["name"] == "Test" print(" PASS: test_parse_json_response_clean") def test_parse_json_response_fenced(): text = '```json\n{"floors": 3}\n```' result = _parse_json_response(text) assert result["floors"] == 3 print(" PASS: test_parse_json_response_fenced") def test_parse_json_response_garbage(): result = _parse_json_response("no json here at all") assert result == {} print(" PASS: test_parse_json_response_garbage") def test_tower_map_structure(): tower = TowerMap() tower.rooms = [ TowerRoom(name="Room A", floor=0, category="test"), TowerRoom(name="Room B", floor=0, category="test"), TowerRoom(name="Room C", floor=1, category="other"), ] tower.npcs = [ TowerNPC(name="NPC1", role="guard", location="Room A"), ] output = json.loads(to_json(tower)) assert output["name"] == "The Tower" assert output["stats"]["total_rooms"] == 3 assert output["stats"]["total_npcs"] == 1 print(" PASS: test_tower_map_structure") def test_to_json(): tower = TowerMap() tower.rooms = [TowerRoom(name="Test Room", floor=1)] output = json.loads(to_json(tower)) assert output["rooms"][0]["name"] == "Test Room" assert output["rooms"][0]["floor"] == 1 print(" PASS: test_to_json") def test_to_ascii(): tower = TowerMap() tower.floors = [TowerFloor(number=0, name="Ground", rooms=["Test Room"])] tower.rooms = [TowerRoom(name="Test Room", floor=0, description="A test")] tower.npcs = [] tower.connections = [] output = to_ascii(tower) assert "THE TOWER" in output assert "Test Room" in output assert "FLOOR 0" in output print(" PASS: test_to_ascii") def test_to_ascii_with_npcs(): tower = TowerMap() tower.floors = [TowerFloor(number=0, name="Ground", rooms=["The Forge"])] tower.rooms = [TowerRoom(name="The Forge", floor=0, occupants=["Bezalel"])] tower.npcs = [TowerNPC(name="Bezalel", role="Builder", location="The Forge")] output = to_ascii(tower) assert "Bezalel" in output print(" PASS: test_to_ascii_with_npcs") def test_scan_gallery_index(tmp_path): # Create mock gallery gallery = tmp_path / "grok-imagine-gallery" gallery.mkdir() index = gallery / "INDEX.md" index.write_text("""# Gallery ### The Origin | 01 | wizard-tower-bitcoin.jpg | The Tower, sovereign | | 02 | soul-inscription.jpg | SOUL.md glowing | ### The Philosophy | 05 | value-drift-battle.jpg | Blue vs red ships | """) rooms = scan_gallery_index(tmp_path) assert len(rooms) >= 2 names = [r.name for r in rooms] assert any("Tower" in n for n in names) assert any("Inscription" in n for n in names) print(" PASS: test_scan_gallery_index") def test_scan_wizard_configs(tmp_path): wizards = tmp_path / "wizards" for name in ["timmy", "bezalel", "ezra"]: wdir = wizards / name wdir.mkdir(parents=True) (wdir / "config.yaml").write_text("model: test\n") npcs = scan_wizard_configs(tmp_path) assert len(npcs) >= 3 names = [n.name for n in npcs] assert any("Timmy" in n for n in names) assert any("Bezalel" in n for n in names) print(" PASS: test_scan_wizard_configs") def test_build_tower_map_empty(tmp_path): tower = build_tower_map(tmp_path, include_vision=False) assert tower.name == "The Tower" # Should still have palace rooms from MEMORY_ARCHITECTURE (won't exist in tmp, but that's fine) assert isinstance(tower.rooms, list) print(" PASS: test_build_tower_map_empty") def test_room_deduplication(): tower = TowerMap() tower.rooms = [ TowerRoom(name="Dup Room", floor=0), TowerRoom(name="Dup Room", floor=1), # same name, different floor TowerRoom(name="Unique Room", floor=0), ] # Deduplicate in build_tower_map — simulate seen = {} deduped = [] for room in tower.rooms: if room.name not in seen: seen[room.name] = True deduped.append(room) assert len(deduped) == 2 print(" PASS: test_room_deduplication") def run_all(): print("=== tower_visual_mapper tests ===") tests = [ test_gallery_image_to_room_known, test_gallery_image_to_room_unknown, test_gallery_image_to_room_philosophy, test_parse_json_response_clean, test_parse_json_response_fenced, test_parse_json_response_garbage, test_tower_map_structure, test_to_json, test_to_ascii, test_to_ascii_with_npcs, test_scan_gallery_index, test_scan_wizard_configs, test_build_tower_map_empty, test_room_deduplication, ] passed = 0 failed = 0 for test in tests: try: if "tmp_path" in test.__code__.co_varnames: with tempfile.TemporaryDirectory() as td: test(Path(td)) else: test() passed += 1 except Exception as e: print(f" FAIL: {test.__name__} — {e}") failed += 1 print(f"\n{'ALL PASSED' if failed == 0 else f'{failed} FAILED'}: {passed}/{len(tests)}") return failed == 0 if __name__ == "__main__": sys.exit(0 if run_all() else 1)