Files
Jyotisha/tests/test_consultation_yoga_detection_merge.py
T
Jesse_ChenandClaude Opus 5.5 df21e591e8 fix(yoga): Neecha Bhanga lists the conditions that hold, never Raja Yoga (T8, BUG-1205)
Astrologer ruling 乙14. One condition source, yogas_doshas.calc_nicha_bhanga_raj_yoga
(conditions A-F, now also structured). New scripts/main_yoga_detectors.py turns it
into yoga_engine-shaped rows (zh + en twins) that YogaEngine.detect appends; the
four neechabhanga_* rules are reference_only (kept, renamed, not evaluated).
Card: traditional_yogas canonical 落陷取消(Neecha Bhanga) with 成立条件; compact
yoga entries keep conditions/reductions. Report: category 落陷取消, mixed group,
not raja; personal-report alias neecha_bhanga -> special.

Changed assertion (test_consultation_yoga_detection_merge): canonical name
'Neecha Bhanga Raja Yoga' -> '落陷取消(Neecha Bhanga)' (ruling 乙14).

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

94 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""T2 / T3 of TASK-consult-career-yoga-functional-20261002 (BUG-1174, BUG-1175)."""
from __future__ import annotations
from pathlib import Path
from scripts.consultation_native_layers import (
TRADITIONAL_YOGA_LABEL,
build_traditional_yogas,
collect_consultation_yogas,
merge_detected_yogas,
)
ROOT = Path(__file__).resolve().parents[1]
def _compact(item):
if isinstance(item, dict) and item.get("name"):
return {"name": item["name"]}
if isinstance(item, str) and item:
return {"name": item}
return None
class _Handler:
def __init__(self):
self.calls = 0
def _compute_yogas_api(self, body):
self.calls += 1
return {"result": {
"extended_yogas": [{"name": "Sasa Yoga"}, {"name": "Amala Yoga"}],
"rule_engine_yogas": [{"name": "Gajakesari Yoga"}],
}}
def test_rule_engine_runs_even_when_chart_already_has_yogas() -> None:
handler = _Handler()
chart = {"yogas": [{"name": "Amala Yoga"}, {"name": "Amala Yoga"}]}
merged = collect_consultation_yogas(handler, chart, {}, {"Sun": {}}, {"sign": "Aries"}, _compact)
assert handler.calls == 1
assert [row["name"] for row in merged] == ["Amala Yoga", "Sasa Yoga", "Gajakesari Yoga"]
def test_merge_dedupes_by_normalised_name() -> None:
merged = merge_detected_yogas([{"name": "Amala Yoga"}], [{"name": "amala yoga"}, {"name": "Amala"}, {"name": "Vosi Yoga"}])
assert [row["name"] for row in merged] == ["Amala Yoga", "Vosi Yoga"]
def test_engine_failure_keeps_the_chart_list() -> None:
class Broken:
def _compute_yogas_api(self, body):
raise RuntimeError("boom")
merged = collect_consultation_yogas(Broken(), {"yogas": ["Adhi Yoga"]}, {}, {}, None, _compact)
assert [row["name"] for row in merged] == ["Adhi Yoga"]
def test_server_no_longer_skips_the_rule_engine() -> None:
source = (ROOT / "scripts" / "jyotish_api_server.py").read_text(encoding="utf-8")
block = source[source.index("if not _consultation_layer_present(modules.get('yogas')):"):]
block = block[: block.index("routes = _consultation_declared_routes(body)")]
assert "collect_consultation_yogas" in block
assert "if not compact:" not in block
def _modules(hits, ungoverned):
return {"raman_support": {"yogas": {"status": "executed", "hits": hits, "ungoverned_detected": ungoverned}}}
def test_traditional_yogas_closed_list_only_and_fixed_label() -> None:
layer = build_traditional_yogas(_modules(
[{"name": "Gaja Kesari", "hit": True}],
[
{"name": "Sasa Yoga"},
{"name": "Neechabhanga Raja Yoga"},
{"name": "Neechabhanga Raja Yoga (Kendra)"},
{"name": "Budhaditya Yoga"},
{"name": "Surya-Budha Yoga"},
{"name": "Kemadruma Yoga"},
{"name": "Raja Yoga (Mutual Aspect)"},
],
))
assert layer["status"] == "executed"
assert layer["label"] == TRADITIONAL_YOGA_LABEL == "传统格局,本站未验证,只作参考"
# BUG-1205: the Neecha Bhanga canonical is no longer called Raja Yoga.
assert layer["names"] == ["Sasa Yoga", "Gajakesari Yoga", "落陷取消(Neecha Bhanga)", "Budha-Aditya Yoga"]
def test_traditional_yogas_skip_misses_and_empty() -> None:
layer = build_traditional_yogas(_modules([{"name": "Gajakesari Yoga", "hit": False}], []))
assert layer["names"] == []
assert build_traditional_yogas({})["status"] == "blocked"