7266f671a4
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m30s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m28s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 17s
Testy / Kontrola składni wszystkich warstw (push) Successful in 8s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m28s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m28s
Testy / Testy warstwy bazodanowej (ochrona baz) (pull_request) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 18s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 8s
Etap 2: dwa systemy łuku dobowego — jedyne, które mają miejsca, gdzie po prostu NIE ISTNIEJĄ. Koch zgadza się z wyrocznią co do zera; Placidus do 1,7e-6°, przy czym to próg zbieżności WYROCZNI, nie nasz: nasze cuspy spełniają definicję Placidusa z dokładnością 1e-12° (osobny test, działa bez swissepha). Placidus jako jedyny nie ma wzoru zamkniętego — cusp jest zdefiniowany warunkiem na samego siebie („punkt, który przebył 1/3 swojego półłuku"), więc iterujemy po punkcie stałym do 1e-11°. Brak zbieżności traktujemy jako wyjście poza dziedzinę, nie jako wynik. Koch okazał się natomiast ZAMKNIĘTY: jego kryterium to czas od wschodu stopnia stojącego na MC, a półłuk tego stopnia znamy wprost z jego deklinacji. Definicja za Astrodienst (astro.com/astrowiki/en/Koch_House_System) — nie zgadywana. Powyżej koła podbiegunowego oba odmawiają liczenia i wyrocznia odmawia dokładnie tych samych przypadków (5245/20 000 losowych, zero rozjazdów dziedziny). Odmowa jest wyjątkiem, nie liczbą: cicha podmiana systemu jest niewykrywalna z wykresu. Ustępstwo wobec rzeczywistości siedzi osobno, w cusps_detailed(): podstawia Porphyry'ego i ZAWSZE zostawia ślad. Ten ślad idzie wszystkimi trzema wyjściami — na ekran (ramka, nie „muted"), w prompt do modelu (inaczej napisze „Twój Placidus" o Porphyrym) i do PDF-a, w ramce PRZED rysunkami. Astrolog z Tromsø dostaje wynik i wie, że go dostał inaczej. Przy okazji: nazwy systemów były zaszyte w czterech szablonach naraz. Przy trzech systemach uchodziło to na sucho, przy dziesięciu nie — jest katalog w jednym miejscu, a testy szablonów RENDERUJĄ je zamiast szukać tekstu w źródle, więc łapią też literówki w Jinja. Większe jądro efemeryd (de441): świadomie zdegradowane do „nice to have" — rozszerza wyłącznie zakres dat, nie poprawia niczego w obecnym. Odnotowane w domain.py przy JD_MIN/JD_MAX, żeby nie wróciło po cichu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
4.3 KiB
Python
99 lines
4.3 KiB
Python
"""Porównanie systemów domów naraz (PRE-05).
|
|
|
|
JS-a tu nie ma — sprawdzamy strukturalnie: formularz pozwala zaznaczyć kilka
|
|
systemów, klient przekazuje je do logiki, a szablon pokazuje kuspy obok siebie.
|
|
"""
|
|
import pathlib
|
|
|
|
APP = pathlib.Path(__file__).resolve().parents[1] / "app"
|
|
CHART = (APP / "templates" / "chart.html").read_text(encoding="utf-8")
|
|
COMPILE = (APP / "templates" / "compile.html").read_text(encoding="utf-8")
|
|
OPTS = (APP / "templates" / "_form_options.html").read_text(encoding="utf-8") # wspólne opcje
|
|
TABLES = (APP / "templates" / "_result_tables.html").read_text(encoding="utf-8") # wspólne tabele
|
|
MAIN = (APP / "main.py").read_text(encoding="utf-8")
|
|
|
|
from app.house_systems import HOUSE_SYSTEMS, LIMITED, label as house_label # noqa: E402
|
|
CLIENT = (APP / "clients" / "logic_client.py").read_text(encoding="utf-8")
|
|
|
|
|
|
def test_both_forms_use_shared_option_partial():
|
|
"""Opcje w jednym pliku — oba formularze go dołączają (koniec duplikacji)."""
|
|
assert '{% include "_form_options.html" %}' in CHART
|
|
assert '{% include "_form_options.html" %}' in COMPILE
|
|
|
|
|
|
def _render(name: str, **ctx) -> str:
|
|
"""Renderuje szablon z tymi samymi globalsami, co produkcja.
|
|
|
|
Szablony budują listę systemów PĘTLĄ po katalogu, więc sprawdzanie ich
|
|
surowego tekstu nic już nie mówi — trzeba je wykonać. Przy okazji test
|
|
łapie literówki w Jinja, których grep po źródle nigdy nie wyłapał."""
|
|
from jinja2 import Environment, FileSystemLoader
|
|
|
|
env = Environment(loader=FileSystemLoader(str(APP / "templates")), autoescape=True)
|
|
env.globals.update(HOUSE_SYSTEMS=HOUSE_SYSTEMS, HOUSE_LIMITED=LIMITED,
|
|
house_label=house_label, static=lambda p: p)
|
|
return env.get_template(name).render(**ctx)
|
|
|
|
|
|
def test_form_offers_every_system_the_engine_advertises():
|
|
"""Każdy system z katalogu ma checkbox. Katalog jest jednym źródłem nazw,
|
|
więc dopisanie systemu bez odświeżenia UI zapali się tutaj."""
|
|
html = _render("_form_options.html", form={})
|
|
for value, name in HOUSE_SYSTEMS:
|
|
assert f'name="house_systems" value="{value}"' in html, f"brak checkboxa {value}"
|
|
assert name in html, f"brak nazwy {name}"
|
|
|
|
|
|
def test_form_marks_systems_with_a_domain_limit():
|
|
"""Placidus i Koch nie istnieją za kołem podbiegunowym — UI ma uprzedzać
|
|
ZANIM ktoś policzy horoskop dla Tromsø, nie dopiero w ostrzeżeniu wyniku."""
|
|
html = _render("_form_options.html", form={})
|
|
assert LIMITED, "katalog nie oznacza żadnego systemu jako ograniczonego"
|
|
for value, name in HOUSE_SYSTEMS:
|
|
starred = f"{name} *" in html
|
|
assert starred == (value in LIMITED), f"{value}: gwiazdka={starred}"
|
|
|
|
|
|
def test_handler_accepts_and_passes_house_systems():
|
|
assert "house_systems: list[str] = Form([])" in MAIN
|
|
assert "house_systems=house_systems" in MAIN # przekazane do logiki
|
|
|
|
|
|
def test_client_forwards_house_systems_to_logic():
|
|
assert '"house_systems": house_systems' in CLIENT
|
|
|
|
|
|
def test_comparison_table_renders_only_for_multiple_systems():
|
|
"""Bez dodatkowych systemów nie pokazujemy pustej tabeli porównania."""
|
|
assert "result.house_systems | length > 1" in TABLES
|
|
assert "Porównanie systemów domów" in TABLES
|
|
|
|
|
|
def test_comparison_table_shows_cusp_per_system():
|
|
"""Tabela iteruje po systemach i po 12 domach — kusp w każdej komórce."""
|
|
assert "for hs in result.house_systems" in TABLES
|
|
assert "hs.cusps[i].in_sign" in TABLES
|
|
|
|
|
|
def test_checkbox_state_survives_submit():
|
|
"""Zaznaczone systemy zostają zaznaczone po przeliczeniu."""
|
|
html = _render("_form_options.html", form={"house_systems": ["koch", "campanus"]})
|
|
for value, _ in HOUSE_SYSTEMS:
|
|
marker = f'value="{value}" checked'
|
|
assert (marker in html) == (value in ("koch", "campanus")), value
|
|
|
|
|
|
def test_fallback_notice_is_shown_prominently():
|
|
"""Po fallbacku kosmogram wygląda bezbłędnie — ostrzeżenie jest jedynym
|
|
sygnałem, że podział domów jest z innego systemu."""
|
|
html = _render("_result_tables.html",
|
|
result={"house_warnings": ["UWAGA: system domów nie ma definicji"]})
|
|
assert "house-warning" in html
|
|
assert "UWAGA: system domów nie ma definicji" in html
|
|
|
|
|
|
def test_no_notice_when_nothing_was_substituted():
|
|
html = _render("_result_tables.html", result={"house_warnings": []})
|
|
assert "house-warning" not in html
|