#!/usr/bin/env python3 """Punarphoo observation detector + relationship event-class split.""" from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts")) from punarphoo import CLAIM_BOUNDARY, circular_degree_gap, detect_punarphoo # noqa: E402 from relationship_analysis import analyze_relationship, classify_relationship_event_class # noqa: E402 def test_circular_degree_gap_wraps_around_zero() -> None: assert circular_degree_gap(359.0, 1.0) == 2.0 assert circular_degree_gap(10.0, 20.0) == 10.0 def test_detect_punarphoo_natal_same_house_within_orb() -> None: planets = { "Moon": {"longitude": 311.48, "house": 7}, "Saturn": {"longitude": 319.01, "house": 7}, } result = detect_punarphoo(planets) assert result["natal_present"] is True assert result["same_house"] is True assert result["orb_deg"] < 10.0 assert result["status"] == "observation_only" assert result["production_tuning_allowed"] is False assert result["claim_boundary"] == CLAIM_BOUNDARY assert result["timing_observation_active"] is False def test_detect_punarphoo_saturn_return_to_natal_moon() -> None: planets = { "Moon": {"longitude": 311.48, "house": 7}, "Saturn": {"longitude": 319.01, "house": 7}, } result = detect_punarphoo(planets, transit_saturn_longitude=311.78) assert result["natal_present"] is True assert result["timing_observation_active"] is True assert result["saturn_return_to_natal_moon"]["active"] is True assert result["claim_boundary"] == CLAIM_BOUNDARY def test_natal_punarphoo_alone_does_not_classify_as_romantic_activation() -> None: planets = { "Moon": {"longitude": 311.48, "house": 7, "sign": "Aquarius"}, "Saturn": {"longitude": 319.01, "house": 7, "sign": "Aquarius"}, "Venus": {"longitude": 341.99, "house": 8, "sign": "Pisces"}, "Jupiter": {"longitude": 165.27, "house": 2, "sign": "Virgo"}, } split = classify_relationship_event_class( planets, asc_sign="Leo", dasha_info={"maha_dasha": "Saturn", "antar_dasha": "Mercury"}, ul_sign="Libra", darakaraka="Sun", ) assert "Punarphoo" not in split["romantic_activation"]["hits"] assert "7H_Moon" in split["romantic_activation"]["natal_structure"] assert "natal_Punarphoo" in split["romantic_activation"]["natal_structure"] assert split["event_class"] != "romantic_activation" assert split["claim_status"] == "observation_only" assert split["negative_holdout"]["production_tuning_allowed"] is False def test_saturn_return_to_moon_adds_punarphoo_romantic_hit() -> None: planets = { "Moon": {"longitude": 311.48, "house": 7, "sign": "Aquarius"}, "Saturn": {"longitude": 319.01, "house": 7, "sign": "Aquarius"}, "Venus": {"longitude": 341.99, "house": 8, "sign": "Pisces"}, } split = classify_relationship_event_class( planets, asc_sign="Leo", dasha_info={"maha_dasha": "Saturn", "antar_dasha": "Mercury"}, transit_saturn_longitude=311.48, ) assert "Punarphoo" in split["romantic_activation"]["hits"] assert split["event_class"] == "romantic_activation" def test_pratyantar_does_not_drive_event_class() -> None: planets = { "Moon": {"longitude": 313.26, "house": 7, "sign": "Aquarius"}, "Saturn": {"longitude": 305.73, "house": 7, "sign": "Aquarius"}, "Jupiter": {"longitude": 165.27, "house": 2, "sign": "Virgo"}, "Venus": {"longitude": 341.99, "house": 8, "sign": "Pisces"}, } jupiter_pd = classify_relationship_event_class( planets, asc_sign="Leo", dasha_info={"maha_dasha": "Saturn", "antar_dasha": "Mercury", "pratyantar": "Jupiter"}, ) assert "5L_dasha" not in jupiter_pd["romantic_activation"]["hits"] assert "5L_pd" in jupiter_pd["pd_hits"] assert jupiter_pd["event_class"] != "romantic_activation" venus_pd = classify_relationship_event_class( planets, asc_sign="Leo", dasha_info={"maha_dasha": "Saturn", "antar_dasha": "Ketu", "pratyantar": "Venus"}, ) assert "Venus_dasha" not in venus_pd["legal_marriage"]["hits"] assert "Venus_pd" in venus_pd["pd_hits"] assert venus_pd["event_class"] != "legal_marriage" def test_venus_to_natal_moon_does_not_drive_romantic_class() -> None: planets = { "Moon": {"longitude": 313.26, "house": 7, "sign": "Aquarius"}, "Saturn": {"longitude": 305.73, "house": 7, "sign": "Aquarius"}, "Venus": {"longitude": 341.99, "house": 8, "sign": "Pisces"}, } split = classify_relationship_event_class( planets, asc_sign="Leo", dasha_info={"maha_dasha": "Saturn", "antar_dasha": "Ketu", "pratyantar": "Venus"}, transit_venus_longitude=311.28, ) assert "7H_Venus_to_Moon" not in split["romantic_activation"]["hits"] assert split["event_class"] != "romantic_activation" assert "Venus_dasha" not in split["legal_marriage"]["hits"] venus_obs = split["romantic_activation"]["venus_to_natal_moon"] assert venus_obs["active"] is True assert venus_obs["drives_event_class"] is False assert venus_obs["status"] == "observation_only" def test_analyze_relationship_does_not_flatten_timing_into_one_label() -> None: planets = { "Venus": {"sign": "Pisces", "house": 8}, "Moon": {"sign": "Aquarius", "house": 7, "longitude": 311.48}, "Saturn": {"sign": "Aquarius", "house": 7, "longitude": 319.01}, "Jupiter": {"sign": "Virgo", "house": 2}, } analysis = analyze_relationship( planets, asc_sign="Leo", dasha_info={"maha_dasha": "Saturn"}, ul_sign="Libra", darakaraka="Sun", ) assert analysis["event_class_split"]["claim_status"] == "observation_only" assert analysis["timing"][0]["event_class"] in { "romantic_activation", "relationship_formation", "legal_marriage", "unclassified", } assert "不得合并" in analysis["timing"][0]["note"]