Files
Jyotisha/tests/test_functional_layer_snapshot.py
T

90 lines
3.4 KiB
Python

"""Functional benefic/malefic snapshot is one helper on three call paths."""
from __future__ import annotations
import builtins
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SCRIPTS = ROOT / "scripts"
if str(SCRIPTS) not in sys.path:
sys.path.insert(0, str(SCRIPTS))
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from functional_benefics import SIGNS, derive_functional_benefic_malefic # noqa: E402
from functional_layer import functional_layer_for_ascendant # noqa: E402
from jyotish_api_server import JyotishAPIHandler # noqa: E402
import jyotish_engine # noqa: E402
import mcp_server # noqa: E402
_BLOCKED_KEYS = (
"status",
"ascendant",
"functional_benefics",
"functional_malefics",
"functional_neutrals",
"yogakarakas",
"owned_houses",
"effect_on_confidence",
"source",
)
def _dump(payload: dict) -> str:
return json.dumps(payload, sort_keys=True)
def _paths(ascendant):
handler = object.__new__(JyotishAPIHandler)
return (
jyotish_engine._functional_benefic_malefic_snapshot({}, ascendant),
handler._functional_benefic_malefic_snapshot({}, ascendant),
mcp_server._derive_functional_benefic_malefic({"chart": {"ascendant": ascendant}}),
)
def test_twelve_signs_match_the_core_derivation() -> None:
for sign in SIGNS:
expected = _dump(derive_functional_benefic_malefic(sign))
for actual in _paths({"sign": sign}):
assert _dump(actual) == expected
def test_missing_and_unknown_signs_agree_across_paths() -> None:
# 原值:MCP 在上升不是 dict 时返回 "Missing chart.ascendant; functional layer blocked."
# 新值:三条路径都等于 derive_functional_benefic_malefic 对缺失或非法星座的结果
# 原因:三份兜底合成一份。缺失上升走统一的正常路径,不再单独写英文句子。
cases = (None, {}, {"sign": "NotASign"})
for ascendant in cases:
sign = ascendant.get("sign") if isinstance(ascendant, dict) else None
expected = _dump(derive_functional_benefic_malefic(sign))
dumped = [_dump(item) for item in _paths(ascendant)]
assert dumped[0] == dumped[1] == dumped[2] == expected
assert "Missing chart.ascendant" not in dumped[0]
def test_import_failure_returns_the_engine_blocked_shape(monkeypatch) -> None:
real_import = builtins.__import__
def boom(name, globals=None, locals=None, fromlist=(), level=0): # noqa: A002
if name == "functional_benefics" or name.endswith(".functional_benefics"):
raise ImportError("functional_benefics unavailable")
return real_import(name, globals, locals, fromlist, level)
monkeypatch.setattr(builtins, "__import__", boom)
blocked = functional_layer_for_ascendant({"sign": "Aries"})
assert blocked["status"] == "blocked"
assert blocked["ascendant"] == "Aries"
assert blocked["functional_benefics"] == []
assert blocked["functional_malefics"] == []
assert blocked["functional_neutrals"] == []
assert blocked["yogakarakas"] == []
assert blocked["owned_houses"] == {}
assert blocked["source"] == "strict_functional_benefic_malefic_v2"
assert "未完成功能性吉凶星判定" in blocked["effect_on_confidence"]
for key in _BLOCKED_KEYS:
assert key in blocked