90 lines
3.4 KiB
Python
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
|