"""Szkielet kosmogramu — SVG po stronie serwera (PRE-12). Sprawdzamy, że SVG jest POPRAWNY (parsuje się jako XML — inaczej wstawiony do strony rozjedzie render) i że niesie to, co ma nieść: pierścień 12 znaków, cztery osie i numery domów. Geometria (Asc po lewej) sprawdzona liczbowo, nie na oko. """ import xml.dom.minidom as minidom from app import chartwheel def _sample_chart(asc=128.9, mc=5.0): """Minimalny wynik z warstwy logiki: osie + cuspy (whole sign) + pierścień znaków.""" signs = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"] glyphs = ["♈", "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓"] asc_sign = int(asc // 30) cusps = [{"house": i + 1, "sign": signs[(asc_sign + i) % 12], "decimal": (asc_sign * 30 + i * 30) % 360, "sign_glyph": glyphs[(asc_sign + i) % 12]} for i in range(12)] return { "angles": { "Asc": {"name": "Asc", "decimal": asc, "sign": signs[asc_sign]}, "MC": {"name": "MC", "decimal": mc}, "Dsc": {"name": "Dsc", "decimal": (asc + 180) % 360}, "IC": {"name": "IC", "decimal": (mc + 180) % 360}, }, "cusps": cusps, "sign_glyphs": [{"sign": s, "glyph": g} for s, g in zip(signs, glyphs)], } def _rendered(): return chartwheel.render(_sample_chart()) def test_output_is_well_formed_xml(): """Wstawiamy surowy SVG do strony — musi być poprawnym XML, bo inaczej zepsuje cały dokument.""" svg = _rendered().replace("var(--line)", "#000").replace( "var(--accent)", "#000").replace("var(--muted)", "#000") doc = minidom.parseString(svg) # rzuci wyjątkiem, gdy błędny assert doc.documentElement.tagName == "svg" def test_has_twelve_sign_glyphs(): assert _rendered().count('class="glyph"') == 12 def test_has_all_four_axis_labels(): svg = _rendered() for label in (">AC<", ">DC<", ">MC<", ">IC<"): assert label in svg def test_has_house_numbers_one_to_twelve(): svg = _rendered() for h in range(1, 13): assert f">{h}<" in svg def test_ascendant_is_on_the_left(): """Konwencja: Asc po lewej. Sprawdzamy oś AC–DC liczbowo — punkt Asc musi mieć x wyraźnie mniejszy od środka (220), a Dsc większy.""" chart = _sample_chart(asc=128.9) asc = chart["angles"]["Asc"]["decimal"] ax, ay = chartwheel._pt(asc, asc, chartwheel.R_ZOD) dx, dy = chartwheel._pt(asc + 180, asc, chartwheel.R_ZOD) assert ax < chartwheel._CX - 100, "Asc powinien być po lewej" assert dx > chartwheel._CX + 100, "Dsc powinien być po prawej" assert abs(ay - chartwheel._CY) < 1e-6, "oś Asc–Dsc jest pozioma" def test_longitude_increases_counterclockwise(): """Asc+90° (II dom) ląduje na DOLE koła (y > środek, bo w SVG y rośnie w dół).""" asc = 100.0 _, y = chartwheel._pt(asc + 90, asc, chartwheel.R_ZOD) assert y > chartwheel._CY + 100 def test_empty_when_no_angles(): """Silnik bez osi/domów (brak sidereal) → brak koła, nie wyjątek.""" assert chartwheel.render({"positions": []}) == "" assert chartwheel.render({"angles": {}, "cusps": [], "sign_glyphs": []}) == "" def test_empty_when_cusps_lack_longitude(): """Starsze wyniki bez `decimal` w cuspach — degradujemy do pustego, nie sypiemy.""" chart = _sample_chart() for c in chart["cusps"]: del c["decimal"] assert chartwheel.render(chart) == ""