fix(report): use the upstream reader edition for new longform bodies
New reports request reader_main from upstream origin/main 23b9609e. KP and transit no longer copy an empty vars() dict, solar returns keep birth_asc_sign_idx, and ordinary projection deletes internal lines whole.
This commit is contained in:
@@ -17,22 +17,36 @@ from scripts.timing_precision_contract import build_timing_precision_contract
|
||||
from tests.test_report_longform_parity import BEIJING, _producer_packet
|
||||
|
||||
|
||||
def test_kp_error_pack_emits_blocked_heading_instead_of_vanishing() -> None:
|
||||
def test_kp_error_pack_omits_the_section_instead_of_printing_the_error() -> None:
|
||||
"""原值:空 KP 仍输出原始表标题和 kp_lord_sub_unavailable。
|
||||
新值:整节不输出,synthetic_kp_failure / producer failed 也不出现。
|
||||
原因:D3。BUG-561 用 blocked 标题代替消失,但标题里打印了原始 error。
|
||||
"""
|
||||
markdown = render_pl9_markdown(_producer_packet({
|
||||
"advanced_systems": {"kp": {"error": "synthetic_kp_failure", "status": "blocked"}},
|
||||
}))
|
||||
assert "### KP Lord / Sub 原始表(本地计算)" in markdown
|
||||
assert "kp_lord_sub_unavailable" in markdown
|
||||
assert "#### KP 事业结构小表" in markdown
|
||||
assert "#### KP 事业宫位核对(2 / 6 / 10 / 11 宫)" in markdown
|
||||
assert "#### KP 财务结构小表" in markdown
|
||||
assert "#### KP 关系结构小表" in markdown
|
||||
assert "### KP Lord / Sub 原始表(本地计算)" not in markdown
|
||||
assert "kp_lord_sub_unavailable" not in markdown
|
||||
assert "synthetic_kp_failure" not in markdown
|
||||
assert "producer failed" not in markdown
|
||||
assert "#### KP 事业结构小表" not in markdown
|
||||
assert "#### KP 事业宫位核对(2 / 6 / 10 / 11 宫)" not in markdown
|
||||
assert "#### KP 财务结构小表" not in markdown
|
||||
assert "#### KP 关系结构小表" not in markdown
|
||||
|
||||
|
||||
def test_planned_headings_are_present_or_blocked_on_empty_packet() -> None:
|
||||
def test_empty_packet_omits_unavailable_planned_sections() -> None:
|
||||
"""原值:PLANNED_LONGFORM_SECTION_HEADINGS 在空包里必须逐条出现。
|
||||
新值:没有数据的占位节不出现;仍有正文的审计节可以留下。
|
||||
原因:D3,数据不可用的节整节不输出,且不得带原始 error。
|
||||
"""
|
||||
markdown = render_pl9_markdown(_producer_packet({}))
|
||||
missing = [heading for heading in PLANNED_LONGFORM_SECTION_HEADINGS if heading not in markdown]
|
||||
assert missing == [], f"planned headings vanished: {missing}"
|
||||
omitted = [heading for heading in PLANNED_LONGFORM_SECTION_HEADINGS if heading not in markdown]
|
||||
assert "### KP Lord / Sub 原始表(本地计算)" in omitted
|
||||
assert "### Profile-aware Benchmark Boundary" in omitted
|
||||
assert "producer failed" not in markdown
|
||||
assert "结论等级规则" not in markdown
|
||||
assert "This reference export" not in markdown
|
||||
|
||||
|
||||
def test_module_execution_audit_survives_raw_strip() -> None:
|
||||
|
||||
@@ -64,12 +64,12 @@ def test_reading_navigation_emits_required_headings() -> None:
|
||||
for heading in (
|
||||
"## 成品阅读导航",
|
||||
"### 先读什么",
|
||||
"### 结论等级规则",
|
||||
"### 专题判读协议",
|
||||
"### 质量验收矩阵",
|
||||
"### 对照覆盖表",
|
||||
):
|
||||
assert heading in markdown
|
||||
assert "### 结论等级规则" not in markdown
|
||||
assert "| 三年年度展开 | present |" in markdown
|
||||
assert "| KP 三年流月支持 | present |" in markdown
|
||||
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
"""Reader-main export and the KP / Muntha defects behind BUG-1026/1027/1028."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from scripts.annual_tajika_pack import _field_brief, _field_with_conflict_gate
|
||||
from scripts.jyotish_engine import (
|
||||
_copy_call_namespace,
|
||||
_professional_report_reference_boundary_notice,
|
||||
cmd_full_reading,
|
||||
render_pl9_markdown,
|
||||
)
|
||||
from scripts.pl9_reader_export import _pl9_export_markdown_for_edition
|
||||
from scripts.professional_report_reference import (
|
||||
REPORT_VERSION_READER,
|
||||
build_professional_report_reference,
|
||||
)
|
||||
from scripts.shared_full_report_authority import DEFAULT_REPORT_VERSION
|
||||
from scripts.solar_return import calc_solar_return_chart, solar_return_full_report
|
||||
|
||||
# Clearly fictional. Not a user, not a published celebrity case.
|
||||
FICTIONAL = {
|
||||
"name": "虚构甲",
|
||||
"year": 1991,
|
||||
"month": 4,
|
||||
"day": 7,
|
||||
"hour": 9,
|
||||
"minute": 15,
|
||||
"second": 0,
|
||||
"lat": 30.5728,
|
||||
"lon": 104.0668,
|
||||
"tz": 8.0,
|
||||
"node_mode": "mean",
|
||||
"ayanamsa": "lahiri",
|
||||
"today": "2026-04-07",
|
||||
"transit_date": "2026-04-07",
|
||||
"target_year": 2026,
|
||||
"age": 35,
|
||||
"birth_time_accuracy": "confirmed",
|
||||
}
|
||||
|
||||
LEAK_PATTERNS = (
|
||||
re.compile(r"\bblocked\b", re.I),
|
||||
re.compile(r"\bexecuted\b", re.I),
|
||||
re.compile(r"\bpartial_verified\b", re.I),
|
||||
re.compile(r"\bmissing_in_local\b", re.I),
|
||||
re.compile(r"\bproducer\b", re.I),
|
||||
re.compile(r"\bparity\b", re.I),
|
||||
re.compile(r"PL9\s*第\s*\d+\s*页"),
|
||||
re.compile(r"full-report://"),
|
||||
re.compile(r"\bpl9_[A-Za-z0-9_]+\b", re.I),
|
||||
re.compile(r"\b[A-Za-z][A-Za-z0-9]*_unavailable\b"),
|
||||
re.compile(r"\b[A-Za-z][A-Za-z0-9]*_(?:pack|packet)\b", re.I),
|
||||
re.compile(r"object has no attribute"),
|
||||
re.compile(r"\bTraceback\b"),
|
||||
re.compile(r"结论等级规则"),
|
||||
re.compile(r"This reference export"),
|
||||
re.compile(r"producer failed"),
|
||||
re.compile(r"PyJHora"),
|
||||
re.compile(r"\bparameter_sensitive\b"),
|
||||
re.compile(r"\bschema\b", re.I),
|
||||
re.compile(r"\bsource_path\b"),
|
||||
re.compile(r"\{planets\}"),
|
||||
)
|
||||
|
||||
CORE_CHAPTERS = (
|
||||
"出生资料",
|
||||
"Ashtakavarga",
|
||||
"标准大运",
|
||||
"KP",
|
||||
"Year Lord",
|
||||
"Tajika",
|
||||
"Muntha",
|
||||
)
|
||||
|
||||
|
||||
def _class_args(**overrides):
|
||||
fields = {**FICTIONAL, **overrides}
|
||||
return type("Args", (), fields)()
|
||||
|
||||
|
||||
def _leak_hits(markdown: str) -> list[str]:
|
||||
return [pattern.pattern for pattern in LEAK_PATTERNS if pattern.search(markdown)]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def full_reading():
|
||||
return cmd_full_reading(_class_args())
|
||||
|
||||
|
||||
def test_default_edition_stays_reference_and_reader_requests_v3() -> None:
|
||||
assert DEFAULT_REPORT_VERSION == "pl9_personal_long_report.v3"
|
||||
assert REPORT_VERSION_READER == "pl9_personal_long_report.v3"
|
||||
|
||||
class Handler:
|
||||
def _high_rigor_birth_payload(self, body):
|
||||
return dict(FICTIONAL)
|
||||
|
||||
def _compute_full_reading_for_thematic(self, body):
|
||||
return {"modules": {"chart": {}}}
|
||||
|
||||
class Engine:
|
||||
def build_professional_report_reference_packet(self, full_reading, args, packs):
|
||||
return {"schema": "pl9_style_professional_export_v1", "selected_report_pack_ids": packs}
|
||||
|
||||
def render_pl9_markdown(self, packet):
|
||||
return "reference-body"
|
||||
|
||||
handler = Handler()
|
||||
engine = Engine()
|
||||
default = build_professional_report_reference(handler, {"format": "markdown"}, engine=engine)
|
||||
assert default["edition"] == "reference"
|
||||
assert default["markdown"] == "reference-body"
|
||||
reader = build_professional_report_reference(
|
||||
handler,
|
||||
{"format": "markdown", "edition": "reader_main"},
|
||||
engine=engine,
|
||||
)
|
||||
assert reader["edition"] == "reader_main"
|
||||
assert reader["report_version"] == "pl9_personal_long_report.v3"
|
||||
|
||||
|
||||
def test_reference_body_drops_local_leak_phrases() -> None:
|
||||
packet = {
|
||||
"schema": "pl9_style_professional_export_v1",
|
||||
"birth_info": {"name": "虚构甲"},
|
||||
"reader_engine_boundary_notice": _professional_report_reference_boundary_notice(),
|
||||
"worksheets": {},
|
||||
}
|
||||
markdown = render_pl9_markdown(packet)
|
||||
assert "结论等级规则" not in markdown
|
||||
assert "This reference export" not in markdown
|
||||
assert "producer failed" not in markdown
|
||||
assert "多引擎口径说明" in markdown
|
||||
|
||||
|
||||
def test_class_args_copy_keeps_year_for_kp_and_transit(full_reading) -> None:
|
||||
args = _class_args()
|
||||
copied = _copy_call_namespace(args)
|
||||
assert copied.year == 1991
|
||||
assert vars(args) == {}
|
||||
modules = full_reading["modules"]
|
||||
for name in ("kp", "double_transit_pac", "transit_ll7l", "planetary_congregation"):
|
||||
module = modules.get(name)
|
||||
assert isinstance(module, dict), name
|
||||
blob = repr(module)
|
||||
assert "has no attribute" not in blob
|
||||
assert module.get("status") != "blocked", (name, module.get("status"), module.get("reason"))
|
||||
assert "error" not in module or not str(module.get("error", "")).startswith("KP producer failed")
|
||||
|
||||
|
||||
def test_reader_main_hides_audit_vocabulary(full_reading) -> None:
|
||||
from scripts.jyotish_engine import build_professional_report_reference_packet
|
||||
|
||||
packet = build_professional_report_reference_packet(full_reading, _class_args(), ["full"])
|
||||
reference = _pl9_export_markdown_for_edition(packet, "reference")
|
||||
reader = _pl9_export_markdown_for_edition(packet, "reader_main")
|
||||
assert "结论等级规则" not in reference
|
||||
assert "This reference export" not in reference
|
||||
hits = _leak_hits(reader)
|
||||
assert hits == [], hits
|
||||
for chapter in CORE_CHAPTERS:
|
||||
assert chapter in reader, chapter
|
||||
assert reader.strip().startswith("#")
|
||||
|
||||
|
||||
def test_solar_return_restores_birth_asc_and_year_lord_moves() -> None:
|
||||
lords = set()
|
||||
signs = set()
|
||||
for year in (2024, 2026, 2028):
|
||||
chart = calc_solar_return_chart(
|
||||
FICTIONAL["year"], FICTIONAL["month"], FICTIONAL["day"],
|
||||
FICTIONAL["hour"], FICTIONAL["minute"],
|
||||
FICTIONAL["lat"], FICTIONAL["lon"], FICTIONAL["tz"],
|
||||
year,
|
||||
ayanamsa_name="lahiri",
|
||||
)
|
||||
assert "error" not in chart, chart.get("error")
|
||||
assert isinstance(chart.get("birth_asc_sign_idx"), int)
|
||||
report = solar_return_full_report(
|
||||
FICTIONAL["year"], FICTIONAL["month"], FICTIONAL["day"],
|
||||
FICTIONAL["hour"], FICTIONAL["minute"],
|
||||
FICTIONAL["lat"], FICTIONAL["lon"], FICTIONAL["tz"],
|
||||
year,
|
||||
ayanamsa_name="lahiri",
|
||||
)
|
||||
muntha = report["muntha"]
|
||||
assert muntha.get("status") != "blocked", muntha
|
||||
assert isinstance(muntha.get("muntha_sign_idx"), int)
|
||||
year_lord = report["year_lord"]
|
||||
assert year_lord.get("status") != "blocked", year_lord
|
||||
lords.add(year_lord["year_lord"])
|
||||
signs.add(muntha["muntha_sign"])
|
||||
assert len(signs) >= 2
|
||||
assert len(lords) >= 2
|
||||
|
||||
|
||||
def test_blocked_side_does_not_invent_a_conflict() -> None:
|
||||
gated = _field_with_conflict_gate(
|
||||
"muntha",
|
||||
{"muntha": {"status": "blocked", "reason": "natal_ascendant_required_for_muntha"}},
|
||||
{"muntha": {"muntha_sign": "Capricorn", "muntha_lord": "Saturn", "note": "extra"}},
|
||||
)
|
||||
assert gated["status"] == "partial_verified"
|
||||
assert "conflict" not in _field_brief("Muntha", gated)
|
||||
same_sign = _field_with_conflict_gate(
|
||||
"year_lord",
|
||||
{"year_lord": {"year_lord": "Mars", "year_lord_sign": "Aries", "trace": "left"}},
|
||||
{"year_lord": {"year_lord": "Mars", "year_lord_sign": "Aries", "trace": "right"}},
|
||||
)
|
||||
assert same_sign["status"] == "partial_verified"
|
||||
disagree = _field_with_conflict_gate(
|
||||
"year_lord",
|
||||
{"year_lord": {"year_lord": "Mars", "year_lord_sign": "Aries"}},
|
||||
{"year_lord": {"year_lord": "Venus", "year_lord_sign": "Taurus"}},
|
||||
)
|
||||
assert disagree["status"] == "conflict"
|
||||
Reference in New Issue
Block a user