Files
Jyotisha/tests/test_mcp_strict_workflow_career.py
T
2026-07-01 19:15:35 +08:00

479 lines
19 KiB
Python

#!/usr/bin/env python3
"""Regression tests for MCP career event adjudication."""
from __future__ import annotations
import jyotish_engine
from mcp_server import _collect_strict_evidence
def _base_career_result() -> dict:
return {
"modules": {
"chart": {
"ascendant": {"sign": "Leo"},
"planets": {
"Moon": {"status": "中性(Neutral)"},
"Venus": {"status": "中性(Neutral)"},
"Saturn": {"status": "中性(Neutral)"},
"Sun": {"status": "中性(Neutral)"},
},
},
"varga_full": {"D10_Dasamsa": {"summary": "career varga present"}},
"special_lagnas": {"A10_Karma_Pada": {"sign": "Capricorn", "lord": "Saturn"}},
"jaimini": {
"karakas": {
"Amatyakaraka": {"planet": "Mercury"},
"Atmakaraka": {"planet": "Sun"},
},
"karakamsha": {"karakamsha_sign": "Leo", "karakamsha_lord": "Sun"},
},
"dasha": {"current_dasha": {"mahadasha": "Mercury", "antardasha": "Sun"}},
"narayana_dasha": {"current_dasha": {"sign": "Capricorn", "lord": "Saturn"}},
"dasa_convergence": {
"domain_activations": {
"career_status": {"convergence_level": "L2", "probability": "35-50%"}
}
},
"argala": {
"houses": {
"house_10": {
"net_result": "supported",
"argala_count": 2,
"virodhargala_count": 0,
}
}
},
}
}
def test_career_collects_a10_amk_karakamsha_as_strict_evidence() -> None:
strict = _collect_strict_evidence("career", _base_career_result())
assert strict["question_type"] == "career"
assert strict["present_evidence"]["d10_dasamsa"] == {"summary": "career varga present"}
assert strict["present_evidence"]["a10_karma_pada"] == {"sign": "Capricorn", "lord": "Saturn"}
assert strict["present_evidence"]["amatyakaraka"] == {"planet": "Mercury"}
assert strict["present_evidence"]["karakamsha"] == {
"karakamsha_sign": "Leo",
"karakamsha_lord": "Sun",
}
assert strict["event_judgement"]["event_family"] == "career"
assert strict["event_judgement"]["dominant_label"] == "career_status"
assert strict["event_judgement"]["secondary_context"] == [
"a10_active",
"amk_active",
"karakamsha_context",
"functional_benefic_malefic_used",
"argala_support",
"vedastro_range_scan_missing",
]
def test_career_blocks_label_when_d10_is_missing_but_preserves_jaimini_context() -> None:
result = _base_career_result()
del result["modules"]["varga_full"]["D10_Dasamsa"]
strict = _collect_strict_evidence("career", result)
assert "d10_dasamsa" in strict["missing_evidence"]
assert strict["blocked"] is True
assert strict["event_judgement"]["dominant_label"] is None
assert strict["event_judgement"]["secondary_context"] == [
"a10_active",
"amk_active",
"karakamsha_context",
"functional_benefic_malefic_used",
"argala_support",
"vedastro_range_scan_missing",
]
def test_career_dignity_guardrail_uses_career_relevant_planets_only() -> None:
result = _base_career_result()
result["modules"]["chart"] = {
"ascendant": {"sign": "Leo"},
"planets": {
"Venus": {"status": "落陷取消(Neecha Bhanga)"},
"Saturn": {"status": "中性(Neutral)"},
"Moon": {"status": "中性(Neutral)"},
"Mars": {"status": "极敌(Great Enemy)"},
},
}
strict = _collect_strict_evidence("career", result)
assert strict["present_evidence"]["dignity_guardrail"]["status"] == "caution"
assert strict["present_evidence"]["dignity_guardrail"]["score_delta"] == 5
assert "dignity_supportive_recovery" in strict["event_judgement"]["secondary_context"]
assert "dignity_high_friction" not in strict["event_judgement"]["secondary_context"]
def test_career_dignity_guardrail_detects_conflict_across_career_significators() -> None:
result = _base_career_result()
result["modules"]["chart"] = {
"ascendant": {"sign": "Leo"},
"planets": {
"Venus": {"status": "落陷取消(Neecha Bhanga)"},
"Saturn": {"status": "极敌(Great Enemy)"},
"Moon": {"status": "中性(Neutral)"},
},
}
strict = _collect_strict_evidence("career", result)
assert strict["present_evidence"]["dignity_guardrail"]["status"] == "conflict"
assert strict["present_evidence"]["dignity_guardrail"]["score_delta"] == 0
assert "dignity_conflict" in strict["event_judgement"]["secondary_context"]
def test_career_argala_bridge_uses_tenth_house_as_modifier_only() -> None:
result = _base_career_result()
strict = _collect_strict_evidence("career", result)
assert strict["present_evidence"]["argala_support"] == {
"level": "supportive",
"target_house": 10,
"source": "argala_house_bridge_v1",
"signals": ["argala_support"],
"raw": {
"net_result": "supported",
"argala_count": 2,
"virodhargala_count": 0,
},
}
assert strict["event_judgement"]["dominant_label"] == "career_status"
assert "argala_support" in strict["event_judgement"]["secondary_context"]
def test_career_kakshya_support_adds_small_score_bump_without_label_override() -> None:
base_result = _base_career_result()
base_result["modules"]["dasa_convergence"]["domain_activations"]["career_status"] = {
"convergence_level": "L1",
"probability": "+15-20%",
}
base = _collect_strict_evidence("career", base_result)
result = _base_career_result()
result["modules"]["dasa_convergence"]["domain_activations"]["career_status"] = {
"convergence_level": "L1",
"probability": "+15-20%",
}
result["modules"]["kakshya"] = {
"summary": {"average_strength": 6.7},
"planets": {"Sun": {"kakshya_strength": 7.0}},
}
strict = _collect_strict_evidence("career", result)
assert strict["present_evidence"]["kakshya_career_support"] == {
"level": "supportive",
"source": "kakshya_career_bridge_v1",
"signals": ["kakshya_career_support"],
"average_strength": 6.7,
}
assert strict["event_judgement"]["dominant_label"] == "career_status"
assert strict["event_judgement"]["score"] >= base["event_judgement"]["score"]
assert "kakshya_career_support" in strict["event_judgement"]["secondary_context"]
def test_career_caps_confidence_when_provided_shadbala_components_are_incomplete() -> None:
result = _base_career_result()
result["modules"]["dasa_convergence"]["domain_activations"]["career_status"] = {
"convergence_level": "L4",
"probability": "70-85%",
}
result["modules"]["shadbala"] = {"planets": {"Mercury": {"total_rupa": 7.5}}}
strict = _collect_strict_evidence("career", result)
assert strict["present_evidence"]["shadbala_component_audit"] == {
"status": "incomplete",
"source": "shadbala.planets",
"required_components": ["sthana", "dig", "kala", "chesta", "naisargika", "drik"],
"missing": {"Mercury": ["sthana", "dig", "kala", "chesta", "naisargika", "drik"]},
}
assert strict["confidence_cap"] == "low"
assert "shadbala_component_gap" in strict["event_judgement"]["secondary_context"]
def test_career_accepts_complete_shadbala_components_without_cap_penalty() -> None:
result = _base_career_result()
result["modules"]["dasa_convergence"]["domain_activations"]["career_status"] = {
"convergence_level": "L4",
"probability": "70-85%",
}
result["modules"]["shadbala"] = {
"planets": {
"Mercury": {
"components": {
"sthana": 1.0,
"dig": 0.8,
"kala": 1.2,
"chesta": 0.7,
"naisargika": 0.5,
"drik": 0.3,
},
"total_rupa": 4.5,
}
}
}
strict = _collect_strict_evidence("career", result)
assert strict["present_evidence"]["shadbala_component_audit"]["status"] == "complete"
assert strict["confidence_cap"] == "medium-high"
assert "shadbala_component_gap" not in strict["event_judgement"]["secondary_context"]
def test_career_strict_contract_marks_a10_as_local_supplement_to_official_primary() -> None:
result = _base_career_result()
result["modules"]["source_priority"] = {"mode": "vedastro_official_primary"}
result["modules"]["vedastro_official_full_snapshot"] = {
"status": "partial",
"available": True,
"official_chart": {"planets": {"Sun": {}}, "ascendant": {"sign": "Leo"}},
"section_statuses": {"chart_core": "ok", "dasha_all": "ok"},
}
strict = _collect_strict_evidence("career", result)
assert strict["official_primary_evidence"]["chart_core"]["status"] == "ok"
assert strict["official_primary_evidence"]["dasha"]["status"] == "ok"
assert strict["local_supplemental_evidence"]["a10_karma_pada"]["role"] == "required_local_supplement"
assert strict["local_supplemental_evidence"]["narayana_current"]["role"] == "required_local_supplement"
def test_career_strict_contract_exposes_adjudication_stages_and_multi_reference_summary() -> None:
strict = _collect_strict_evidence("career", _base_career_result())
assert strict["adjudication_stages"]["promise"]["status"] in {"present", "weak", "missing"}
assert strict["adjudication_stages"]["activation"]["required_timing_systems"] == ["Vimshottari", "Narayana"]
assert strict["adjudication_stages"]["label"]["value"] == strict["event_judgement"]["dominant_label"]
assert "multi_reference_reading_summary" in strict
summary = strict["multi_reference_reading_summary"]
assert "root_frame" in summary
assert "divisional_frame" in summary
assert "visibility_frame" in summary
assert "karaka_frame" in summary
assert "timing_frame" in summary
assert "modifier_frame" in summary
assert "conflict_frame" in summary
def test_career_strict_contract_exposes_monthly_adjudication_summary() -> None:
result = _base_career_result()
result["modules"]["vedastro_range_scan_result"] = {
"backend": "vedastro_service_adapter_candidate",
"status": "ok",
"operation": "range_scan",
"domain": "career",
"evidence_ledger": [],
"daily_windows": [
{
"date": "2026-07-18",
"domain": "career",
"score": 5,
"confidence": "high",
"event_count": 2,
"signal_families": ["career_trigger"],
"event_ids": ["GocharJupiterAspect10th", "CareerExpansionWindow"],
"top_signal_label": "Career expansion window",
},
{
"date": "2026-07-28",
"domain": "career",
"score": 6,
"confidence": "high",
"event_count": 2,
"signal_families": ["career_trigger"],
"event_ids": ["TravelForWork", "GocharJupiterAspect10th"],
"top_signal_label": "Travel for work expansion window",
},
],
"top_daily_window": {
"date": "2026-07-28",
"domain": "career",
"score": 6,
"confidence": "high",
"event_count": 2,
"signal_families": ["career_trigger"],
"event_ids": ["TravelForWork", "GocharJupiterAspect10th"],
"top_signal_label": "Travel for work expansion window",
},
"source_metadata": {},
}
strict = _collect_strict_evidence("career", result)
summary = strict["monthly_adjudication_summary"]
assert summary["route"] == "career"
assert summary["primary_state"]["value"] in {"推进", "启动", "重组", "收束", "观察"}
assert summary["manifestation_mode"]["value"]
assert summary["friction_source"]["value"]
assert summary["time_confidence"]["value"] in {"day_supported", "month_supported", "month_only", "blocked"}
assert isinstance(summary["supporting_days"], list)
assert summary["supporting_days"][0]["date"] == "2026-07-18"
def test_career_strict_contract_exposes_compact_technique_audit_summary() -> None:
strict = _collect_strict_evidence("career", _base_career_result())
audit = strict["technique_audit_summary"]
assert audit["functional_benefic_malefic"]["gate"] == "hard"
assert audit["relevant_vargas"]["gate"] == "hard"
assert audit["vimshottari_narayana_crosscheck"]["gate"] == "hard"
assert audit["source_priority_boundary"]["fallback_used"] == strict["fallback_used"]
def test_career_external_activation_derives_user_readable_day_signals() -> None:
result = _base_career_result()
result["modules"]["vedastro_range_scan_result"] = {
"backend": "vedastro_service_adapter_candidate",
"status": "ok",
"operation": "range_scan",
"domain": "career",
"evidence_ledger": [],
"daily_windows": [
{
"date": "2026-07-18",
"domain": "career",
"score": 5,
"confidence": "high",
"event_count": 2,
"signal_families": ["career_trigger"],
"event_ids": ["GocharJupiterAspect10th", "CareerExpansionWindow"],
"top_signal_label": "Career expansion window",
},
{
"date": "2026-07-26",
"domain": "career",
"score": 2,
"confidence": "medium",
"event_count": 1,
"signal_families": ["career_pressure"],
"event_ids": ["SaturnIn10thCareerWindow"],
"top_signal_label": "Saturn in 10th career window",
},
],
"top_daily_window": {
"date": "2026-07-18",
"domain": "career",
"score": 5,
"confidence": "high",
"event_count": 2,
"signal_families": ["career_trigger"],
"event_ids": ["GocharJupiterAspect10th", "CareerExpansionWindow"],
"top_signal_label": "Career expansion window",
},
"source_metadata": {},
}
strict = _collect_strict_evidence("career", result)
external = strict["present_evidence"]["external_activation"]
assert external["official_day_signals"][0]["date"] == "2026-07-18"
assert external["official_day_signals"][0]["day_type"] == "opportunity_entry"
assert external["official_day_signals"][0]["summary"] == "事业机会进入日"
assert external["official_day_signals"][1]["day_type"] == "pressure_opportunity"
def test_career_official_day_signals_distinguish_motion_and_closure_risk() -> None:
result = _base_career_result()
result["modules"]["vedastro_range_scan_result"] = {
"backend": "vedastro_service_adapter_candidate",
"status": "ok",
"operation": "range_scan",
"domain": "career",
"evidence_ledger": [],
"daily_windows": [
{
"date": "2026-07-28",
"domain": "career",
"score": 6,
"confidence": "high",
"event_count": 2,
"signal_families": [],
"event_ids": ["GoodLunarDayForTravel", "GoodSunSignForBuilding"],
"top_signal_label": "Good lunar day for travel",
},
{
"date": "2025-02-28",
"domain": "career",
"score": 4,
"confidence": "medium",
"event_count": 2,
"signal_families": [],
"event_ids": ["BadLunarDayForTravel", "BadForSellingForProfit"],
"top_signal_label": "Bad lunar day for travel",
},
],
"top_daily_window": {
"date": "2026-07-28",
"domain": "career",
"score": 6,
"confidence": "high",
"event_count": 2,
"signal_families": [],
"event_ids": ["GoodLunarDayForTravel", "GoodSunSignForBuilding"],
"top_signal_label": "Good lunar day for travel",
},
"source_metadata": {},
}
strict = _collect_strict_evidence("career", result)
signals = strict["present_evidence"]["external_activation"]["official_day_signals"]
assert signals[0]["day_type"] == "relocation_motion"
assert signals[0]["summary"] == "事业迁移动作日"
assert signals[1]["day_type"] == "closure_risk"
assert signals[1]["summary"] == "事业真正收尾风险日"
def test_career_narrative_payload_forces_monthly_adjudication_layers_into_final_chinese_conclusion() -> None:
result = _base_career_result()
result["modules"]["vedastro_range_scan_result"] = {
"backend": "vedastro_service_adapter_candidate",
"status": "ok",
"operation": "range_scan",
"domain": "career",
"evidence_ledger": [],
"daily_windows": [
{
"date": "2026-07-18",
"domain": "career",
"score": 5,
"confidence": "high",
"event_count": 2,
"signal_families": ["career_trigger"],
"event_ids": ["GocharJupiterAspect10th", "CareerExpansionWindow"],
"top_signal_label": "Career expansion window",
}
],
"top_daily_window": {
"date": "2026-07-18",
"domain": "career",
"score": 5,
"confidence": "high",
"event_count": 2,
"signal_families": ["career_trigger"],
"event_ids": ["GocharJupiterAspect10th", "CareerExpansionWindow"],
"top_signal_label": "Career expansion window",
},
"source_metadata": {},
}
strict = _collect_strict_evidence("career", result)
payload = jyotish_engine._build_career_narrative_payload(strict)
assert "事业" in payload["headline"]
assert payload["monthly_frame"]["primary_state"]["value"]
assert payload["monthly_frame"]["manifestation_mode"]["value"]
assert payload["monthly_frame"]["friction_source"]["value"]
assert payload["monthly_frame"]["time_confidence"]["value"]
assert any("月度主状态" in item for item in payload["strengths"])
assert any("阻力来源" in item for item in payload["risks"])
assert "时间置信度" in payload["markdown"]