Files
Jyotisha/tests/test_full_report_quality_gate.py
T
Jesse_Chen bab0718700
Independent Staging Quality Gate / validate (push) Failing after 9m41s
Independent Staging Quality Gate / publish (push) Has been skipped
feat(report): ship full-mode longform appendix beside the five-chapter report
Web export now calls the same full pack as the long skill report and caches an owner-only Markdown download. Appendix failure stays unavailable and does not change the main report status.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-05 11:10:44 +08:00

344 lines
13 KiB
Python
Executable File
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.
from __future__ import annotations
from scripts.full_report_quality_gate import evaluate_full_report
from scripts.jyotish_engine import render_pl9_markdown
def _complete_markdown() -> str:
return "\n".join(
[
"# 个人印度占星报告",
"## 摘要",
"### 基础资料表",
"#### Birth Particulars / Calculation Details",
"#### 出生 Panchanga(本地计算)",
"#### D1 — Rashi Chart(本命盘)",
"### Vargas I",
"#### D1–D60 完整原始分盘账本(20 传统 + 40 研究型 D-N)",
"### 本命主轴",
"### 2026 年度重点",
"#### 年度专题证据状态",
"### 力量、Ashtakavarga 与功能性吉凶",
"#### 功能性吉凶与 Yogakaraka",
"#### Bhava Lagna",
"#### Hora Lagna",
"#### Ghati Lagna",
"#### ViGhati Lagna",
"#### Sree Lagna",
"#### Indu Lagna",
"本章已调用依据:D2(Hora 财富分盘)、D11(Rudramsa 收益分盘)。",
"### 大运与时间主线",
"#### KP 三年流月支持",
"### 2026 KP 月度支持",
"### 2027 KP 月度支持",
"### 2028 KP 月度支持",
"#### KP 使用边界",
"#### Patyayini Dasha",
"#### Narayana Rashi Dasha",
"### 专业支持专题:第二证据轴",
"#### 辅助大运族:Yogini / Ashtottari / Kala Chakra",
"#### 本命补充因子:Sahams / Avasthas / Upagrahas",
"#### 未闭环专业层:可见但不入判断",
"### 事业与外部发展",
"### 财运与资源使用",
"### 关系与合作模式",
"## Blocked / Audit Appendix",
"## Audit Appendix",
]
)
def _complete_packet() -> dict:
return {
"schema": "pl9_style_professional_export_v1",
"calculation_profile_id": "profile-1993",
"calculation_profile": {
"profile_id": "profile-1993",
"profile_version": "1.0",
"algorithm": "sidereal_natal_chart",
"ayanamsa": "lahiri",
"node_mode": "mean",
"input_hash": "input-hash",
"engine": {"ephemeris_provider": "local"},
},
"result_hash": "result-hash",
"result_binding": {
"input_hash": "input-hash",
"result_hash": "result-hash",
},
"chart_identity": {
"chart_profile_id": "profile-1993",
"birth_data_status": "user_provided",
"rectification_status": "not_reviewed",
"approval_status": "not_approved",
},
"full_report_pack": {
"schema": "pl9.full_report_pack.v1",
"sections": {
key: {"status": "verified"}
for key in (
"base",
"strength",
"dasha",
"annual",
"transit",
"professional_support",
"audit_appendix",
)
} | {
"d1_d60_ledger": {
"status": "available",
"d1_to_d60": {f"D{number}": {"raw": {}} for number in range(1, 61)},
"summary": {
"formal_traditional_division_count": 20,
"research_generic_dn_division_count": 40,
},
}
},
},
"worksheets": {
"divisional_and_special_charts": {
"special_lagnas": {
"Bhava_Lagna": {"sign": "Aries"},
"Hora_Lagna": {"sign": "Aries"},
"Ghati_Lagna": {"sign": "Aries"},
"ViGhati_Lagna": {"sign": "Aries"},
"Sree_Lagna": {"sign": "Aries"},
"Indu_Lagna": {"sign": "Aries"},
},
"varga_full": {
"D2_Hora": {"Ascendant": {"sign": "Aries"}},
"D11_Rudramsa": {"Ascendant": {"sign": "Taurus"}},
},
},
"strengths_and_scores": {
"functional_benefic_malefic": {"status": "used"},
},
"timing_and_predictive_systems": {
"narayana_dasha": {"status": "executed"},
"annual_tajika_pack": {
"patyayini": {"status": "executed"},
"external_engine_comparison": {
"pyjhora": {
"patyayini_dasha": {
"status": "partial_verified",
"normalized_rows": [{"order": 1}],
}
}
},
"audit": {"technique_audit": [{"technique": "Annual", "status": "partial_verified"}]},
"report_sections": {"evidence_appendix": {"status": "partial_verified"}},
},
"dasha_master_pack": {
"families": {"yogini": {"periods": [{"lord": "Sun"}]}}
},
},
"advanced_systems": {
"sahams": {"Punya": {"sign": "Aries"}},
"kp_monthly_report": {
"maturity_profile": {"claim_status": "observation_only_truth_blocked"},
"must_not_claim": ["exact_event_timing"],
},
},
},
}
def test_complete_packet_returns_passed_with_traceable_professional_coverage() -> None:
result = evaluate_full_report(_complete_packet(), _complete_markdown())
assert result["schema_version"] == "jyotish.full_report_quality_gate.v1"
assert result["status"] == "passed"
assert result["professional_coverage_manifest"]
assert {
"material_id",
"source_reference",
"admission_tier",
"report_role",
"surface_location",
"status",
"limitation_reference",
}.issubset(result["professional_coverage_manifest"][0])
assert not result["blocking_reasons"]
assert {
item["material_id"]: item["surface_location"]
for item in result["professional_coverage_manifest"]
}["special_lagnas"] == "professional_support_cross_reference"
def test_missing_provenance_blocks_full_report() -> None:
packet = _complete_packet()
packet.pop("result_hash")
result = evaluate_full_report(packet, _complete_markdown())
assert result["status"] == "blocked"
assert "provenance_missing:result_hash" in result["blocking_reasons"]
def test_missing_chart_identity_blocks_full_report() -> None:
packet = _complete_packet()
packet.pop("chart_identity")
result = evaluate_full_report(packet, _complete_markdown())
assert result["status"] == "blocked"
assert "chart_identity_missing:chart_profile_id" in result["blocking_reasons"]
def test_invalid_result_binding_blocks_full_report() -> None:
packet = _complete_packet()
packet["result_binding"]["result_hash"] = "other-result"
result = evaluate_full_report(packet, _complete_markdown())
assert result["status"] == "blocked"
assert "provenance_lineage_binding_invalid" in result["blocking_reasons"]
def test_missing_or_incomplete_d1_d60_ledger_blocks_full_report() -> None:
packet = _complete_packet()
packet["full_report_pack"]["sections"].pop("d1_d60_ledger")
missing = evaluate_full_report(packet, _complete_markdown())
assert missing["status"] == "blocked"
assert "required_pack_section_missing:d1_d60_ledger" in missing["blocking_reasons"]
packet = _complete_packet()
packet["full_report_pack"]["sections"]["d1_d60_ledger"]["d1_to_d60"].pop("D60")
incomplete = evaluate_full_report(packet, _complete_markdown())
assert incomplete["status"] == "blocked"
assert "d1_d60_ledger_incomplete" in incomplete["blocking_reasons"]
def test_executed_required_support_missing_from_rendering_requires_review() -> None:
markdown = _complete_markdown().replace("#### Hora Lagna\n", "")
result = evaluate_full_report(_complete_packet(), markdown)
assert result["status"] == "review_required"
assert "special_lagna_not_rendered:Hora_Lagna" in result["review_reasons"]
def test_generated_d11_gains_chart_missing_from_rendering_requires_review() -> None:
markdown = _complete_markdown().replace("D11Rudramsa 收益分盘)", "收益分盘已省略")
result = evaluate_full_report(_complete_packet(), markdown)
assert result["status"] == "review_required"
assert "required_support_not_rendered:wealth_d11_gains_chart" in result["review_reasons"]
def test_missing_professional_support_section_requires_review() -> None:
markdown = _complete_markdown().replace("### 专业支持专题:第二证据轴\n", "")
result = evaluate_full_report(_complete_packet(), markdown)
assert result["status"] == "review_required"
assert "reader_section_missing:professional_support" in result["review_reasons"]
def test_missing_restricted_materials_register_requires_review() -> None:
markdown = _complete_markdown().replace("#### 未闭环专业层:可见但不入判断\n", "")
result = evaluate_full_report(_complete_packet(), markdown)
assert result["status"] == "review_required"
assert "reader_section_missing:restricted_materials" in result["review_reasons"]
def test_missing_one_kp_year_requires_review() -> None:
markdown = _complete_markdown().replace("### 2028 KP 月度支持\n", "")
result = evaluate_full_report(_complete_packet(), markdown)
assert result["status"] == "review_required"
assert "three_year_kp_monthly_incomplete" in result["review_reasons"]
def test_missing_special_lagna_value_requires_review() -> None:
packet = _complete_packet()
packet["worksheets"]["divisional_and_special_charts"]["special_lagnas"].pop("Sree_Lagna")
result = evaluate_full_report(packet, _complete_markdown())
assert result["status"] == "review_required"
assert "special_lagna_missing:Sree_Lagna" in result["review_reasons"]
def test_empty_d11_structure_blocks_full_report() -> None:
packet = _complete_packet()
packet["worksheets"]["divisional_and_special_charts"]["varga_full"]["D11_Rudramsa"] = {}
result = evaluate_full_report(packet, _complete_markdown())
assert result["status"] == "blocked"
assert "wealth_varga_structure_missing:D11" in result["blocking_reasons"]
def test_missing_kp_boundary_status_requires_review() -> None:
packet = _complete_packet()
packet["worksheets"]["advanced_systems"]["kp_monthly_report"].pop("must_not_claim")
result = evaluate_full_report(packet, _complete_markdown())
assert result["status"] == "review_required"
assert "kp_evidence_status_missing" in result["review_reasons"]
def test_executed_auxiliary_support_missing_from_rendering_requires_review() -> None:
markdown = _complete_markdown().replace("#### 辅助大运族:Yogini / Ashtottari / Kala Chakra\n", "")
result = evaluate_full_report(_complete_packet(), markdown)
assert result["status"] == "review_required"
assert "required_support_not_rendered:auxiliary_dasha_support" in result["review_reasons"]
def test_restricted_material_cannot_be_promoted_as_confirmed() -> None:
packet = _complete_packet()
packet["professional_coverage_overrides"] = {
"tajika_named_yoga": {"status": "confirmed"},
}
result = evaluate_full_report(packet, _complete_markdown())
assert result["status"] == "blocked"
assert "restricted_material_promoted:tajika_named_yoga" in result["blocking_reasons"]
def test_missing_patyayini_normalized_rows_requires_review() -> None:
packet = _complete_packet()
packet["worksheets"]["timing_and_predictive_systems"]["annual_tajika_pack"]["external_engine_comparison"][
"pyjhora"
]["patyayini_dasha"]["normalized_rows"] = []
result = evaluate_full_report(packet, _complete_markdown())
assert result["status"] == "review_required"
assert "patyayini_normalized_rows_missing" in result["review_reasons"]
def test_quality_gate_reads_product_markdown_markers() -> None:
result = evaluate_full_report(_complete_packet(), "\n".join([
"# 个人印度占星报告",
"## 成品阅读导航",
"### 基础资料表",
"#### Birth Particulars / Calculation Details",
"#### D1 — Rashi Chart(本命盘)",
"### Vargas I",
"#### D1–D60 完整原始分盘账本(20 传统 + 40 研究型 D-N)",
"### 大运与时间主线",
"#### KP 三年流月支持",
"### 2026 年度重点",
"### 专业支持专题:第二证据轴",
"#### 未闭环专业层:可见但不入判断",
"## Blocked / Audit Appendix",
"## Audit Appendix",
]))
markdown = render_pl9_markdown(_complete_packet())
assert "# 个人印度占星报告" in markdown
assert "## 成品阅读导航" in markdown
assert result["schema_version"] == "jyotish.full_report_quality_gate.v1"
assert "rendered_markdown_not_supplied" not in result["warning_reasons"]