Files
Jyotisha/tests/test_rectification_kp_cusp_observation.py
T

182 lines
6.6 KiB
Python

from __future__ import annotations
from datetime import datetime
from kp_system import KP_LORDS
from scripts.active_rectification_event_engine import (
build_candidate_static_context,
compute_event_candidate_rows,
)
from scripts.rectification.decision_policy import (
build_candidate_decisions,
build_decision_receipt,
build_technique_audit,
)
from scripts.rectification.kp_cusp_observation import observe_kp_cusps
from scripts.rectification.refinement_packet import window_scan
def _request() -> dict:
return {
"birth_date": "1993-04-17",
"start_time": "14:29",
"end_time": "14:30",
"lat": 36.683333,
"lon": 114.35,
"tz": 8.0,
"events": [{
"id": "5cb071d6-6d99-46be-85dc-a9bf59ef6ac5",
"domain": "career",
"date": "2019-07-01",
"precision": "day",
}],
}
def _score_request() -> dict:
return {
"events": [
{
"id": "00000000-0000-4000-8000-000000000001",
"domain": "career",
"summary": "入职",
"event_kind": "career_entry",
"precision": "day",
"date_start": "2016-09-15",
"date_end": "2016-09-15",
},
{
"id": "00000000-0000-4000-8000-000000000002",
"domain": "relationship",
"summary": "开始一段关系",
"event_kind": "relationship_start",
"precision": "day",
"date_start": "2018-03-01",
"date_end": "2018-03-01",
},
{
"id": "00000000-0000-4000-8000-000000000003",
"domain": "education",
"summary": "毕业",
"event_kind": "education_completion",
"precision": "day",
"date_start": "2015-06-01",
"date_end": "2015-06-01",
},
{
"id": "00000000-0000-4000-8000-000000000004",
"domain": "family",
"summary": "家人变化",
"event_kind": "family_event",
"precision": "day",
"date_start": "2020-01-01",
"date_end": "2020-01-01",
},
]
}
def _diagnostics() -> dict:
return {
"leave_one_event_out_retention_rate": 1,
"leave_one_domain_out_retention_rate": 1,
"date_sensitivity_retention_rate": 1,
"primary_secondary_margin_percent": 50,
}
def test_observe_kp_cusps_uses_placidus_not_equal_houses() -> None:
context = build_candidate_static_context(_request(), datetime(1993, 4, 17, 14, 29))
feature = context["feature"]
snapshot = feature["kp_cusps"]
natal_houses = context["chart"]["houses"]
assert snapshot["status"] == "executed"
assert snapshot["house_system"] == "placidus"
assert snapshot["ayanamsa"] == "krishnamurti"
assert len(snapshot["houses"]) == 12
tenth = snapshot["houses"]["10"]
equal_tenth = float(natal_houses["house_10"]["cusp_degree"])
assert abs(float(tenth["cusp_degree"]) - equal_tenth) > 0.05
assert tenth["sub_lord"] in KP_LORDS
assert isinstance(feature["kp1_sub_index"], int)
assert 0 <= feature["kp1_sub_index"] <= 8
assert "KP_cusps" not in feature["blocked_layers"]
assert "KP_cusps" in feature["available_layers"]
def test_kp_observation_is_absent_from_scoring_missing_layers() -> None:
request = _request()
context = build_candidate_static_context(request, datetime(1993, 4, 17, 14, 29))
rows = compute_event_candidate_rows(request, static_contexts=[context])
assert rows
assert "KP_cusps" not in rows[0]["missing_layers"]
assert context["feature"]["kp_cusps"]["status"] == "executed"
def test_window_scan_emits_kp_sub_lord_changes_without_confirmation() -> None:
scan = window_scan({
"static_contexts": [
{"feature": {"time": "05:13", "kp1_sub_index": 1, "kp4_sub_index": 2, "kp7_sub_index": 3, "kp10_sub_index": 4}},
{"feature": {"time": "05:14", "kp1_sub_index": 2, "kp4_sub_index": 2, "kp7_sub_index": 3, "kp10_sub_index": 4}},
]
})
meanings = [item["user_meaning"] for item in scan["transitions"]]
assert "KP 1宫子主 在 05:14 发生变化" in meanings
assert "KP 4宫子主 在 05:14 发生变化" not in meanings
encoded = str(scan)
assert "白羊" not in encoded
assert "Aries" not in encoded
assert scan["confirmation_allowed"] is False
assert scan["unique_minute_claim"] is False
def test_technique_audit_kp_executed_when_snapshot_present() -> None:
rows = build_technique_audit(
{
"matrix": {},
"static_contexts": [{
"feature": {
"time": "05:13",
"kp_cusps": {"status": "executed", "house_system": "placidus", "ayanamsa": "krishnamurti"},
}
}],
},
house_table={"time": "05:13", "lagna": "金牛座"},
)
kp = next(row for row in rows if row["technique"] == "KP 宫头")
assert kp["status"] == "executed"
assert "政策跳过" not in kp["note"]
assert "不计分" in kp["note"]
def test_technique_audit_kp_blocked_is_honest_and_does_not_fail_propose() -> None:
rows = build_technique_audit({"matrix": {}, "static_contexts": []}, house_table=None)
kp = next(row for row in rows if row["technique"] == "KP 宫头")
assert kp["status"] == "blocked"
assert "政策跳过" not in kp["note"]
decisions = build_candidate_decisions(
[{"time": "05:13", "score": 20, "evidence": [], "missing_layers": []},
{"time": "05:14", "score": 8, "evidence": [], "missing_layers": []}],
result_id="00000000-0000-4000-8000-000000000099",
)
receipt = build_decision_receipt(
_score_request(),
decisions,
{"missing_layers": [], "matrix": {}, "static_contexts": [{"feature": {"time": "05:13", "kp_cusps": {"status": "blocked"}}}]},
_diagnostics(),
)
assert receipt["gates"]["required_layers"]["passed"] is True
assert receipt["propose_allowed"] is True
audit = next(row for row in receipt["technique_audit_table"] if row["technique"] == "KP 宫头")
assert audit["status"] == "blocked"
assert "政策跳过" not in audit["note"]
def test_observe_kp_cusps_blocks_without_inventing_whole_sign_proxy() -> None:
snapshot = observe_kp_cusps(None, 36.6, 114.3)
assert snapshot["status"] == "blocked"
assert snapshot["houses"] == {}
polar = observe_kp_cusps(2451545.0, 80.0, 0.0)
assert polar["status"] == "blocked"
assert polar["houses"] == {}