338ec24e90
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m20s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m29s
Testy / Testy warstwy bazodanowej (ochrona baz) (pull_request) Successful in 9m26s
Testy / Testy astrodemo (pull_request) Successful in 9m25s
Testy / Testy astroklient (pull_request) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 6s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 5s
build / build (push) Successful in 12s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m21s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m29s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m25s
Testy / Testy astrodemo (push) Successful in 9m25s
Testy / Testy astroklient (push) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 6s
Testy / Kontrola składni wszystkich warstw (push) Successful in 4s
Astroklient przedstawiał się na stronie głównej jako „astrololo" — w tytule karty i w nagłówku. To najpoważniejszy z możliwych śladów: mówi klientowi wprost, że trzyma okrojoną wersję czegoś większego, i podaje frazę, po której da się wyszukać, czego mu nie dano. Cała reszta izolacji przestaje mieć znaczenie, gdy produkt sam się przedstawia nazwą rodzica. Wzięło się stąd, że nazwa była wpisana na sztywno w szablony, a szablony są WSPÓLNE — dokładnie ta sama przyczyna, co przy zaszytym w base.html warunku na administratora. Nazwa mieszka teraz w module `produkt.py`, który nakładka produktu nadpisuje; szablony o nią pytają, zamiast ją znać. astrodemo NIE było dotknięte: ma własne kopie szablonów i pokazuje „astrodemo". DRUGIE ŹRÓDŁO WYCIEKU, mniej oczywiste: nazwa siedziała też w identyfikatorach JavaScriptu, które widzi każdy, kto otworzy narzędzia deweloperskie — zdarzenie `astrololo:coords` i klucz `astrololo.form.v1` w localStorage. Przemianowane na neutralne `kosmogram:*`; te identyfikatory nie mają powodu nosić nazwy rodziny produktów. SKUTEK UBOCZNY: zmiana klucza w localStorage kasuje raz zapamiętane wersje robocze formularza. Sprawdzone, że NIE wyciekają: nagłówki X-Astrololo-* jadą wyłącznie między warstwami, a dokumentacja OpenAPI (gdzie zostaje tytuł aplikacji FastAPI) oddaje 404 także zalogowanemu. TEST rozdziela to, co widzi człowiek, od identyfikatorów wewnętrznych. Nazwa rdzenia wolno występować w nazwach loggerów, materiale do wyprowadzania kluczy i nagłówkach międzywarstwowych; nie wolno w szablonach, zasobach ani w treści odpowiedzi. Zakaz na wszystko byłby albo obejściem przez zmianę nazw kluczy kryptograficznych, albo martwą regułą wyłączaną przy pierwszej okazji. Sprawdzony w obie strony: po cofnięciu nakładki test pada. Testy: astroklient 15, presentation 368, astrodemo 28, logic 342, data 42, render 41. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
96 lines
3.6 KiB
Python
96 lines
3.6 KiB
Python
"""Zapamiętane predykcje okresowe (PRE-22).
|
|
|
|
Magazyn żyje w JS (localStorage), więc znów pilnujemy go strukturalnie. Sedno:
|
|
predykcje mają PRZEŻYĆ przejście na inną zakładkę i trafić do raportu, a lista
|
|
nie może puchnąć od powtórzonych prób na tym samym okresie.
|
|
"""
|
|
import pathlib
|
|
|
|
APP = pathlib.Path(__file__).resolve().parents[1] / "app"
|
|
# Skrypty generowania mieszkają w module `app/ai/` — katalogu, którego
|
|
# w obrazie bez tej funkcji po prostu nie ma.
|
|
JS = (APP / "moduly" / "dodatki" / "static" / "predictions.js").read_text(encoding="utf-8")
|
|
PROGRESS = (APP / "moduly" / "dodatki" / "static" / "progress.js").read_text(encoding="utf-8")
|
|
from tests.skladanie import zloz
|
|
|
|
TIMELINE = zloz("timeline.html")
|
|
|
|
|
|
# ─────────────────────────────── podpięcie ───────────────────────────────
|
|
|
|
def test_script_loaded_on_calendar_tab():
|
|
assert "predictions.js" in TIMELINE
|
|
|
|
|
|
def test_loaded_after_progress_script():
|
|
"""Nasłuchuje zdarzenia, które wysyła progress.js — musi być załadowany po nim,
|
|
inaczej przegapi pierwsze wywołanie."""
|
|
assert TIMELINE.index("progress.js") < TIMELINE.index("predictions.js")
|
|
|
|
|
|
def test_calendar_has_container_for_the_list():
|
|
assert 'id="savedPredictions"' in TIMELINE
|
|
|
|
|
|
def test_progress_announces_finished_horoscope():
|
|
"""Okno postępu ogłasza gotowy horoskop zdarzeniem, zamiast samo go zapisywać
|
|
— to okno postępu, a nie magazyn."""
|
|
assert "astrololo:horoscope" in PROGRESS
|
|
assert "profile" in PROGRESS.split("astrololo:horoscope")[1][:200]
|
|
|
|
|
|
# ─────────────────────────────── zachowanie ──────────────────────────────
|
|
|
|
def test_only_period_horoscopes_are_stored():
|
|
"""Interpretacja natalna ma NIE trafiać na listę predykcji okresowych."""
|
|
assert "'period'" in JS
|
|
|
|
|
|
def test_same_period_replaces_instead_of_duplicating():
|
|
"""Ponowne policzenie tego samego zakresu podmienia wpis. Inaczej lista
|
|
puchłaby przy każdej próbie z innym modelem."""
|
|
assert "function upsert" in JS
|
|
assert "p.from_date === entry.from_date" in JS
|
|
assert "p.to_date === entry.to_date" in JS
|
|
|
|
|
|
def test_entry_carries_period_and_text():
|
|
for field in ("from_date", "to_date", "text", "saved_at"):
|
|
assert field in JS, f"wpis nie niesie pola {field}"
|
|
|
|
|
|
def test_incomplete_result_is_not_stored():
|
|
"""Bez tekstu albo bez dat nie ma czego zapamiętać — nie zapisujemy śmieci."""
|
|
assert "if (!text || !from || !to) return null;" in JS
|
|
|
|
|
|
def test_list_can_be_pruned():
|
|
assert "function remove" in JS
|
|
assert "data-drop" in JS
|
|
|
|
|
|
def test_full_storage_is_reported_not_swallowed():
|
|
"""Horoskopy bywają długie. Gdy magazyn się przepełni, użytkownik musi się
|
|
o tym dowiedzieć, zamiast po cichu stracić wynik."""
|
|
assert "pełny" in JS or "pelny" in JS
|
|
assert "return false;" in JS
|
|
|
|
|
|
def test_corrupt_storage_does_not_break_the_page():
|
|
assert "catch" in JS and "Array.isArray" in JS
|
|
|
|
|
|
def test_storage_is_exposed_for_the_compile_tab():
|
|
"""Zakładka „Skompiluj" (PRE-23) ma czytać to samo miejsce — jedno źródło prawdy."""
|
|
assert "window.kosmogramPredictions" in JS
|
|
|
|
|
|
def test_does_nothing_on_other_tabs():
|
|
assert "if (!document.getElementById('savedPredictions')) return;" in JS
|
|
|
|
|
|
def test_reload_does_not_duplicate():
|
|
"""Wariant bez strumienia zapisuje przy wczytaniu strony — klucz to okres,
|
|
więc odświeżenie nie mnoży wpisów."""
|
|
assert "id: from + '..' + to" in JS
|