Files
Jyotisha/tests/test_narayana_legacy_label.py
T
Jesse_ChenandClaude Opus 5.5 bf8d704708 feat(narayana): show the Sanjay Rath version beside the legacy one (T4, BUG-1218)
Display only; scoring and timing conclusions keep the legacy algorithm.
narayana_legacy_label attaches a Rath view (seed, deciding level, direction,
24 MDs, running MD/AD) under modules.narayana_dasha.rath for the consultation
modules, full reading Step 4.13 and the CLI. Reference report: one summary
line under the legacy line plus Rath MD / running-MD AD tables (English terms
added). Evidence card: narayana_rath (label, seed sign, level, running MD) on
timing / annual cards only — other cards sit at 11,979 of 12,000 characters.
Goldens re-captured from the real engine.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
2026-10-03 21:09:40 +08:00

117 lines
6.2 KiB
Python

"""The product Narayana Dasha is labelled as the legacy algorithm (BUG-1215, T4).
Round-4 ruling Q4: until the Sanjay Rath profile replaces it (product decision
after the trial), every user-visible Narayana says it is the old algorithm
(from the Lagna, always zodiacal) and the data carry
`profile: legacy_reference_only`. K.N. Rao is a Chara Dasha name only.
Fictional chart.
"""
from __future__ import annotations
import copy
import json
import re
from pathlib import Path
from scripts.narayana_legacy_label import LEGACY_NOTE_EN, LEGACY_NOTE_ZH, LEGACY_PROFILE
from scripts.pl9_reader_export import _pl9_export_markdown_for_edition
from tests.test_report_english_edition import CASES, _packet
ROOT = Path(__file__).resolve().parents[1]
def test_report_module_and_reference_edition_carry_the_label() -> None:
packet = _packet(CASES["day"])
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reference")
assert f"- 算法:{LEGACY_NOTE_ZH}" in zh
from scripts.jyotish_engine import cmd_full_reading
reading = cmd_full_reading(type("Args", (), dict(CASES["day"]))())
module = reading["modules"]["narayana_dasha"]
assert (module["profile"], module["algorithm_note"], module["algorithm_note_en"]) == (LEGACY_PROFILE, LEGACY_NOTE_ZH, LEGACY_NOTE_EN)
def test_english_reader_translates_the_label() -> None:
from scripts.pl9_reader_english_terms import PHRASES_EN
text = f"- 算法:{LEGACY_NOTE_ZH}"
for source, target in PHRASES_EN:
text = text.replace(source, target)
assert text == f"- Algorithm: {LEGACY_NOTE_EN}"
def test_consultation_modules_carry_the_label() -> None:
golden = json.loads((ROOT / "frontend/tests/fixtures/consult-evidence-card-golden.json").read_text(encoding="utf-8"))
for chart in golden["charts"]:
for route in ("workflow", "annual_workflow", "timing_workflow"):
module = chart[route]["chart"]["modules"]["narayana_dasha"]
assert (module["profile"], module["algorithm_note"], module["algorithm_label"]) == (LEGACY_PROFILE, LEGACY_NOTE_ZH, "旧算法(从上升起、一律顺行)")
card = (ROOT / "frontend/src/lib/consultation-evidence-card.ts").read_text(encoding="utf-8")
assert "narayana.algorithm = algorithmLabel" in card and "NARAYANA_SUB_PERIOD_KEYS" in card
workflow = (ROOT / "frontend/src/mastra/consultation-workflow.ts").read_text(encoding="utf-8")
assert '"algorithmlabel"' in workflow
def test_kn_rao_never_names_narayana_on_user_visible_surfaces() -> None:
pattern = re.compile(r"(K\.?\s?N\.?\s?Rao|kn_rao)[^\n]{0,40}Narayana|Narayana[^\n]{0,40}(K\.?\s?N\.?\s?Rao|kn_rao)", re.IGNORECASE)
surfaces = [*(ROOT / "frontend/src").rglob("*.ts"), *(ROOT / "frontend/src").rglob("*.tsx"),
ROOT / "scripts/pl9_reader_export.py", ROOT / "scripts/pl9_reader_english_terms.py",
ROOT / "scripts/consultation_native_layers.py", ROOT / "scripts/jyotish_engine.py",
ROOT / "scripts/narayana_dasha.py", ROOT / "scripts/cmd_narayana_dasha.py"]
hits = [path.name for path in surfaces if pattern.search(path.read_text(encoding="utf-8"))]
assert hits == []
# ── BUG-1218 (T4): Sanjay Rath version shown beside the legacy one ──
def test_full_reading_carries_the_rath_view_and_leaves_the_legacy_result_alone() -> None:
from scripts.jyotish_engine import cmd_full_reading
from scripts.narayana_dasha import narayana_dasha_full_report
from scripts.narayana_legacy_label import RATH_LABEL_ZH
reading = cmd_full_reading(type("Args", (), dict(CASES["day"]))())
module = reading["modules"]["narayana_dasha"]
rath = module["rath"]
assert rath["algorithm_label"] == RATH_LABEL_ZH and rath["used_for_scoring"] is False
assert rath["profile"] == "sanjay_rath" and rath["seed_sign"] and rath["strength_level_decided"] in range(1, 8)
assert len(rath["mahadasha"]) == 24 and len(rath["current_md_antardasha"]) == 12
# Attaching the Rath view leaves every legacy field exactly as it was.
from scripts.narayana_legacy_label import label_legacy_narayana
lons = {"Sun": 10.0, "Moon": 100.0, "Mars": 200.0, "Mercury": 20.0, "Jupiter": 250.0,
"Venus": 340.0, "Saturn": 300.0, "Rahu": 50.0, "Ketu": 230.0} # fictional
base = narayana_dasha_full_report(lagna_sign_idx=3, planet_lons=lons, current_age=40.0, birth_year=1980)
with_rath = label_legacy_narayana(copy.deepcopy(base), lagna_sign_idx=3, planet_lons=lons, current_age=40.0, birth_year=1980)
without = label_legacy_narayana(copy.deepcopy(base))
assert "rath" not in without
assert {key: value for key, value in with_rath.items() if key != "rath"} == without
def test_reference_edition_shows_both_and_english_terms_cover_the_rath_lines() -> None:
from scripts.narayana_legacy_label import rath_report_lines_zh
from scripts.pl9_reader_english_terms import PHRASES_EN, REGEX_EN
packet = _packet(CASES["day"])
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reference")
legacy = zh.index(f"- 算法:{LEGACY_NOTE_ZH}")
rath = zh.index("- 并列:Sanjay Rath 版(并列参考,未进打分与时间轴结论):起运宫 ")
assert legacy < rath
assert "| Rath MD | 星座 | 宫主 | 起始年龄 | 结束年龄 | 年数 |" in zh and "| Rath AD | 星座 | 起始年龄 | 结束年龄 |" in zh
view = packet["worksheets"]["timing_and_predictive_systems"]["narayana_dasha"]["rath"]
for line in rath_report_lines_zh(view):
text = line
for pattern, target in REGEX_EN:
text = pattern.sub(target, text)
for source, target in PHRASES_EN:
text = text.replace(source, target)
assert not re.search(r"[一-鿿]", text), text
def test_consultation_modules_carry_the_rath_view() -> None:
golden = json.loads((ROOT / "frontend/tests/fixtures/consult-evidence-card-golden.json").read_text(encoding="utf-8"))
for chart in golden["charts"]:
for route in ("workflow", "annual_workflow", "timing_workflow"):
module = chart[route]["chart"]["modules"]["narayana_dasha"]
assert module["profile"] == LEGACY_PROFILE
assert module["rath"]["used_for_scoring"] is False and module["rath"]["current_dasha"]["md"]["sign"]