Spaces:
Running
Running
Adinda Panca Mochamad
Fix Day 2: orbit ring, duplicate name guard, format_panel cleanup, 4 test baru
925c1a7 | """Unit tests for map/generator.py and map/format_panel.py.""" | |
| import sys | |
| sys.path.insert(0, ".") | |
| from map.format_panel import format_panel_entitas | |
| from map.generator import generate_dream_map | |
| from map.themes import PALET_MOOD, gelapkan, get_palette, terangkan | |
| ENTITAS_LENGKAP = { | |
| "title": "The White Deer Forest", | |
| "mood": "mysterious", | |
| "characters": [ | |
| {"name": "Dreamer", "role": "self", "emotion": "afraid"}, | |
| {"name": "White Deer", "role": "creature", "emotion": "elusive"}, | |
| {"name": "Childhood Friend", "role": "known", "emotion": "shifting"}, | |
| ], | |
| "places": [ | |
| {"name": "Night Forest", "type": "fantastical", "feeling": "chasing"}, | |
| ], | |
| "symbols": [ | |
| {"name": "Changing Face", "appearance": "friend face shifts", "significance": "identity"}, | |
| ], | |
| "connections": [ | |
| {"from": "Dreamer", "to": "White Deer", "type": "pursuit"}, | |
| {"from": "Dreamer", "to": "Night Forest", "type": "tension"}, | |
| {"from": "Dreamer", "to": "Changing Face", "type": "tension"}, | |
| ], | |
| "core_theme": "Chasing something familiar that stays out of reach", | |
| } | |
| ENTITAS_MINIMAL = { | |
| "title": "Gray Room", | |
| "mood": "anxious", | |
| "characters": [{"name": "Dreamer", "role": "self", "emotion": "weightless"}], | |
| "places": [], | |
| "symbols": [], | |
| "connections": [], | |
| "core_theme": "Solitude without exit", | |
| } | |
| ENTITAS_KOSONG = { | |
| "title": "Parse Failed", | |
| "mood": "mysterious", | |
| "characters": [], | |
| "places": [], | |
| "symbols": [], | |
| "connections": [], | |
| "core_theme": "", | |
| } | |
| # ββ themes ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_semua_mood_ada(): | |
| for mood in ("mysterious", "joyful", "anxious", "surreal", "peaceful"): | |
| assert mood in PALET_MOOD, f"mood '{mood}' tidak ada di PALET_MOOD" | |
| def test_palette_fallback_mood_invalid(): | |
| p = get_palette("nonexistent") | |
| assert p["bg"] == PALET_MOOD["mysterious"]["bg"] | |
| def test_terangkan_menghasilkan_hex_valid(): | |
| result = terangkan("#9774ff", 0.15) | |
| assert result.startswith("#") and len(result) == 7 | |
| def test_gelapkan_menghasilkan_hex_valid(): | |
| result = gelapkan("#9774ff", 0.15) | |
| assert result.startswith("#") and len(result) == 7 | |
| # ββ generator βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_generate_menghasilkan_svg(): | |
| svg = generate_dream_map(ENTITAS_LENGKAP) | |
| assert svg.strip().startswith("<svg") | |
| assert "</svg>" in svg | |
| def test_generate_nama_entitas_ada_di_svg(): | |
| svg = generate_dream_map(ENTITAS_LENGKAP) | |
| assert "Dreamer" in svg | |
| assert "White Deer" in svg | |
| assert "Night Forest" in svg | |
| assert "Changing Face" in svg | |
| def test_generate_edge_pursuit_dashed(): | |
| svg = generate_dream_map(ENTITAS_LENGKAP) | |
| # pursuit β dashed edge | |
| assert "stroke-dasharray" in svg | |
| def test_generate_minimal_tidak_crash(): | |
| svg = generate_dream_map(ENTITAS_MINIMAL) | |
| assert "<svg" in svg | |
| assert "Dreamer" in svg | |
| def test_generate_kosong_tampilkan_empty_state(): | |
| svg = generate_dream_map(ENTITAS_KOSONG) | |
| assert "<svg" in svg | |
| def test_generate_semua_mood(): | |
| for mood in ("mysterious", "joyful", "anxious", "surreal", "peaceful"): | |
| ent = {**ENTITAS_MINIMAL, "mood": mood} | |
| svg = generate_dream_map(ent) | |
| assert "<svg" in svg, f"SVG kosong untuk mood={mood}" | |
| def test_generate_xss_escape(): | |
| ent = {**ENTITAS_MINIMAL, "title": "<script>alert(1)</script>"} | |
| svg = generate_dream_map(ent) | |
| assert "<script>" not in svg | |
| def test_generate_xss_entity_name(): | |
| ent = {**ENTITAS_MINIMAL, "characters": [ | |
| {"name": '<img src=x onerror=alert(1)>', "role": "self", "emotion": "hacked"} | |
| ]} | |
| svg = generate_dream_map(ent) | |
| assert "<img" not in svg | |
| def test_generate_no_chars_place_sebagai_center(): | |
| """Saat characters=[], places[0] harus di center dan tidak di-overwrite Ring 1.""" | |
| ent = { | |
| "title": "City Dream", "mood": "joyful", | |
| "characters": [], | |
| "places": [ | |
| {"name": "City", "type": "real", "feeling": "busy"}, | |
| {"name": "Park", "type": "real", "feeling": "calm"}, | |
| ], | |
| "symbols": [{"name": "Clock", "appearance": "ticking", "significance": "time"}], | |
| "connections": [], "core_theme": "Urban life", | |
| } | |
| svg = generate_dream_map(ent) | |
| import re | |
| rects = re.findall(r'<rect x="([\d.]+)" y="([\d.]+)"', svg) | |
| # City (center) should be at approximately (300-31, 218-18) = (269, 200) | |
| center_rect = [(x, y) for x, y in rects if abs(float(x) - 269) < 2 and abs(float(y) - 200) < 2] | |
| assert center_rect, f"City tidak di center. Rects: {rects}" | |
| assert "City" in svg | |
| assert "Park" in svg | |
| assert "Clock" in svg | |
| def test_generate_only_symbols(): | |
| """Saat hanya symbols: symbols[0] di center, sisanya di Ring 2.""" | |
| ent = { | |
| "title": "Key Dream", "mood": "surreal", | |
| "characters": [], "places": [], | |
| "symbols": [ | |
| {"name": "Key", "appearance": "golden", "significance": "entry"}, | |
| {"name": "Door", "appearance": "wooden", "significance": "passage"}, | |
| ], | |
| "connections": [], "core_theme": "Gateways", | |
| } | |
| svg = generate_dream_map(ent) | |
| import re | |
| polys = re.findall(r"<polygon points", svg) | |
| assert len(polys) == 2, f"Harus 2 diamond, dapat {len(polys)}" | |
| assert "Key" in svg | |
| assert "Door" in svg | |
| def test_generate_orbit_r1_tidak_tampil_saat_ring1_kosong(): | |
| """Satu karakter saja β Ring 1 orbit tidak boleh muncul (tidak ada node di sana).""" | |
| ent = {**ENTITAS_MINIMAL} # 1 char, no places, no symbols | |
| svg = generate_dream_map(ent) | |
| assert 'r="115"' not in svg, "Ring 1 orbit seharusnya tidak tampil saat ring1 kosong" | |
| # ββ format_panel βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_panel_mengandung_judul(): | |
| md = format_panel_entitas(ENTITAS_LENGKAP) | |
| assert "The White Deer Forest" in md | |
| def test_panel_mengandung_mood_dan_theme(): | |
| md = format_panel_entitas(ENTITAS_LENGKAP) | |
| assert "Mysterious" in md or "mysterious" in md | |
| assert "Chasing something" in md | |
| def test_panel_semua_entitas_ada(): | |
| md = format_panel_entitas(ENTITAS_LENGKAP) | |
| assert "Dreamer" in md | |
| assert "White Deer" in md | |
| assert "Night Forest" in md | |
| assert "Changing Face" in md | |
| def test_panel_kosong_tidak_crash(): | |
| md = format_panel_entitas(ENTITAS_KOSONG) | |
| assert isinstance(md, str) | |
| def test_panel_minimal_tidak_crash(): | |
| md = format_panel_entitas(ENTITAS_MINIMAL) | |
| assert "Dreamer" in md | |
| def run_all(): | |
| tests = [ | |
| test_semua_mood_ada, | |
| test_palette_fallback_mood_invalid, | |
| test_terangkan_menghasilkan_hex_valid, | |
| test_gelapkan_menghasilkan_hex_valid, | |
| test_generate_menghasilkan_svg, | |
| test_generate_nama_entitas_ada_di_svg, | |
| test_generate_edge_pursuit_dashed, | |
| test_generate_minimal_tidak_crash, | |
| test_generate_kosong_tampilkan_empty_state, | |
| test_generate_semua_mood, | |
| test_generate_xss_escape, | |
| test_generate_xss_entity_name, | |
| test_generate_no_chars_place_sebagai_center, | |
| test_generate_only_symbols, | |
| test_generate_orbit_r1_tidak_tampil_saat_ring1_kosong, | |
| test_panel_mengandung_judul, | |
| test_panel_mengandung_mood_dan_theme, | |
| test_panel_semua_entitas_ada, | |
| test_panel_kosong_tidak_crash, | |
| test_panel_minimal_tidak_crash, | |
| ] | |
| for t in tests: | |
| t() | |
| print(f"test_generator: {len(tests)}/{len(tests)} OK") | |
| if __name__ == "__main__": | |
| run_all() | |