oneiros / tests /test_generator.py
Adinda Panca Mochamad
Fix Day 2: orbit ring, duplicate name guard, format_panel cleanup, 4 test baru
925c1a7
Raw
History Blame Contribute Delete
8 kB
"""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()