Files
Jyotisha/tests/test_dasha_float_split_bug1719.py
ClaudeandClaude Opus 5.5 0bf8deea49 fix(rectification): birth-moment start and float sub-period split end to end (BUG-1720)
Product approved 2026-11-08. _active_vimshottari, the candidate context
timeline, chart_choice astrologer and every event_probes Vimshottari start
now use birth_moment.mahadashas from the candidate birth moment and
birth_moment.split_period for AD/PD, replacing build_dasha_timeline
(00:00 of the birth date) and the integer-day build_antardasha. Frozen
dasha_analyzer.py / domain_calculation_service.py unchanged. Research
mirrors follow. A bare date string now raises TypeError.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyn7Hj3LCW3BUs5bZ8
2026-11-08 07:11:48 +08:00

78 lines
3.8 KiB
Python

"""BUG-1719: integer-day Vimshottari sub-period split (占星师 r40 审查第 2 条)."""
import sys
from datetime import datetime, timedelta
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "scripts"))
from chart_dasha import birth_moment # noqa: E402
import dasha_analyzer # noqa: E402
START = datetime(2030, 1, 1, 0, 0, 0)
PARENT = {"lord": "Mars", "start": START, "end": START + timedelta(days=100.75)}
def test_her_repro_frozen_build_antardasha_ends_18h_early():
# 冻结文件,不改:记录缺陷本身。
# 原值:产品拍板前生时校正打分仍用它(见 BUG-1719)。
# 新值:没有任何调用方(见下方白名单测试)。
# 原因:产品 2026-11-08 批准;占星师要求整条调用链改浮点切分;BUG-1720
rows = dasha_analyzer.build_antardasha(dict(PARENT))
assert PARENT["end"] - rows[-1]["end"] == timedelta(hours=18)
def test_float_split_ends_at_the_parent_end_and_keeps_proportions():
rows = birth_moment.split_period(dict(PARENT))
assert [r["lord"] for r in rows] == ["Mars", "Rahu", "Jupiter", "Saturn", "Mercury", "Ketu", "Venus", "Sun", "Moon"]
assert rows[0]["start"] == START and rows[-1]["end"] == PARENT["end"]
assert all(a["end"] == b["start"] for a, b in zip(rows, rows[1:]))
total = timedelta(days=100.75).total_seconds()
for row in rows:
expected = total * row["years"] / 120
assert abs((row["end"] - row["start"]).total_seconds() - expected) < 1e-3
def test_reference_transparency_uses_birth_moment_split():
import reference_transparency_contract as rtc
chart = {"birth_info": {"date": "1990-06-15", "time": "17:45:00"}, "planets": {"Moon": {"lon": 123.4}}}
birth = datetime(1990, 6, 15, 17, 45)
for reference in ("2001-03-04", "2019-11-30", "2034-07-01"):
target = datetime.fromisoformat(reference)
mds, *_ = birth_moment.mahadashas(birth, 123.4, through=target)
index, md = next((i, m) for i, m in enumerate(mds) if m["start"] <= target < m["end"])
ads = birth_moment.antardashas(md, visible_from=birth if index == 0 else None)
ad = next(a for a in ads if a["start"] <= target < a["end"])
assert rtc._vimshottari_state(chart, reference) == {"mahadasha": md["lord"], "antardasha": ad["lord"]}
def test_kp_monthly_elapsed_uses_full_precision_start():
from kp_monthly_vimshottari import build_monthly_vimshottari_snapshot
birth = datetime(1990, 6, 15, 17, 45)
anchor = datetime(2026, 11, 1)
snapshot = build_monthly_vimshottari_snapshot(birth_dt=birth, moon_lon=123.4, anchor_dt=anchor)
mds, *_ = birth_moment.mahadashas(birth, 123.4, through=anchor)
md = next(m for m in mds if m["start"] <= anchor < m["end"])
elapsed = (anchor - md["start"]).total_seconds() / 86400 / 365.25
assert snapshot["levels"]["mahadasha"]["lord"] == md["lord"]
assert abs(snapshot["levels"]["mahadasha"]["years_into_period"] - elapsed) < 1e-9
def test_no_product_path_calls_the_integer_day_split():
"""No caller left outside the frozen file itself (BUG-1720 moved rectification to the float split)."""
# 原值:allowed 另含 active_rectification_event_engine.py、rectification/event_probes.py、research/nadi_seconds_lib.py
# 新值:只剩 dasha_analyzer.py 自己
# 原因:产品 2026-11-08 批准;占星师要求整条调用链改浮点切分;BUG-1720
allowed = {
"scripts/dasha_analyzer.py",
}
callers = set()
for path in (ROOT / "scripts").rglob("*.py"):
text = path.read_text(encoding="utf-8", errors="ignore")
code = "\n".join(line for line in text.splitlines() if not line.lstrip().startswith("#"))
if "build_antardasha(" in code.replace("def build_antardasha(", ""):
callers.add(path.relative_to(ROOT).as_posix())
assert callers <= allowed, callers - allowed