Files
astrololo/services/logic/tests/test_profections.py
T
gitea b013831492 Profekcje roczne (LOG-10) i Solar/Lunar Return (LOG-12)
Pierwsze techniki predykcyjne.

Profekcje (LOG-10):
- engine/profections.py: Whole Sign, wiek mod 12; profektowany Asc + Władca Roku
  (władca domicylowy) + profekcje MC/Su/Mo. Obsługa 29 lutego.
- endpoint /chart/profections (zakres lat: start_age, count).

Returns (LOG-12):
- engine/returns.py: moment powrotu Słońca/Księżyca do długości natalnej;
  skan dobowy + bisekcja do ~sekundy, z pominięciem artefaktu zawinięcia 0/360.
- endpoint /chart/return (kind solar|lunar, around) — zwraca pełny horoskop
  na znaleziony moment (oba warianty użycia po stronie technik wyżej).

Walidacja:
- profekcje zgodne co do joty z tabelą astro-seek z notes3 (wiek 0..42:
  Asc + Władca Roku + MC/Su/Mo).
- Solar Return 2026: Słońce wraca do Tau 10°08'22" = natalny stopień;
  Lunar Return trafia natalny Księżyc <2'; samospójność potwierdzona.
- 62 testy przechodzą (nowe: test_profections, test_returns).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 11:12:49 +02:00

47 lines
1.9 KiB
Python

"""Profekcje (LOG-10) — walidacja względem tabeli astro-seek z notes3."""
import datetime as dt
from app.engine.profections import DOMICILE_RULERS, age_at, profected_sign, profection_rows
BIRTH = dt.datetime(1984, 4, 30, 7, 35, tzinfo=dt.timezone.utc)
NATAL = {"Asc": 112.18, "MC": 352.59, "Sun": 40.14, "Moon": 30.55}
# wiek -> (profektowany Asc, Władca Roku) — tabela referencyjna notes3
REFERENCE = {
0: ("Cancer", "Moon"), 1: ("Leo", "Sun"), 2: ("Virgo", "Mercury"),
3: ("Libra", "Venus"), 4: ("Scorpio", "Mars"), 5: ("Sagittarius", "Jupiter"),
6: ("Capricorn", "Saturn"), 7: ("Aquarius", "Saturn"), 8: ("Pisces", "Jupiter"),
9: ("Aries", "Mars"), 10: ("Taurus", "Venus"), 11: ("Gemini", "Mercury"),
12: ("Cancer", "Moon"), 40: ("Scorpio", "Mars"), 41: ("Sagittarius", "Jupiter"),
42: ("Capricorn", "Saturn"),
}
def test_profections_match_astroseek_table():
rows = {r["age"]: r for r in profection_rows(NATAL, BIRTH, 0, 43)}
for age, (asc, lord) in REFERENCE.items():
assert rows[age]["profected_asc"] == asc, f"wiek {age}"
assert rows[age]["lord_of_year"] == lord, f"wiek {age}"
def test_profected_secondary_points_match_reference():
rows = {r["age"]: r for r in profection_rows(NATAL, BIRTH, 0, 3)}
# notes3: wiek 0 -> MC Pis, Sun Tau, Moon Tau; wiek 1 -> MC Ari, Sun Gem
assert rows[0]["MC"] == "Pisces" and rows[0]["Sun"] == "Taurus"
assert rows[1]["MC"] == "Aries" and rows[1]["Sun"] == "Gemini"
def test_from_dates_are_birthdays():
rows = profection_rows(NATAL, BIRTH, 40, 3)
assert [r["from"] for r in rows] == ["2024-04-30", "2025-04-30", "2026-04-30"]
def test_age_at_boundaries():
assert age_at(BIRTH, dt.datetime(2026, 4, 29, tzinfo=dt.timezone.utc)) == 41
assert age_at(BIRTH, dt.datetime(2026, 4, 30, tzinfo=dt.timezone.utc)) == 42
def test_rulers_cover_all_signs():
assert len(DOMICILE_RULERS) == 12
assert profected_sign(112.18, 12) == "Cancer" # pełny cykl wraca