标出研究章和 PyJHora 的已知差别(#92)。
10 张虚构盘逐体系对照。不一致的方法在标题下写明差在哪一类。本命 Tithi 顺行一致,逆行没有公开函数。Windows 上 1970 年以前的日出不再用 fromtimestamp。 自测:合同与研究章测试通过;200 张英文扫描约 1956 秒通过。未跑全量 pytest、快速门和前端。 Co-Authored-By: Grok (agent loop)
This commit is contained in:
co-authored by
Grok (agent loop)
parent
e586886196
commit
a757af6b4e
@@ -88,7 +88,14 @@ def _class_args(**overrides):
|
||||
|
||||
|
||||
def _leak_hits(markdown: str) -> list[str]:
|
||||
return [pattern.pattern for pattern in LEAK_PATTERNS if pattern.search(markdown)]
|
||||
# 原值:整篇里出现 PyJHora 都算泄漏
|
||||
# 新值:以「与 PyJHora 不一致:」或「Differs from PyJHora:」开头的行先拿掉,其余照旧
|
||||
# 原因:#92 研究章要在标题下保留这两句对照;其它 PyJHora 字样仍然失败
|
||||
kept = "\n".join(
|
||||
line for line in (markdown or "").splitlines()
|
||||
if not line.startswith("与 PyJHora 不一致:") and not line.startswith("Differs from PyJHora:")
|
||||
)
|
||||
return [pattern.pattern for pattern in LEAK_PATTERNS if pattern.search(kept)]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
@@ -169,6 +176,15 @@ def test_class_args_copy_keeps_year_for_kp_and_transit(full_reading) -> None:
|
||||
assert "error" not in module or not str(module.get("error", "")).startswith("KP producer failed")
|
||||
|
||||
|
||||
def test_pyjhora_mismatch_lines_are_not_reader_leaks() -> None:
|
||||
# 原值:任何含 PyJHora 的文本都命中泄漏
|
||||
# 新值:只有对照句以外的 PyJHora 命中泄漏
|
||||
# 原因:#92 允许研究章保留这两句,不能因此放行其它位置
|
||||
assert _leak_hits("Differs from PyJHora: start\n与 PyJHora 不一致:差在 起点\n") == []
|
||||
hits = _leak_hits("Calculated by PyJHora.\n")
|
||||
assert any("PyJHora" in hit for hit in hits)
|
||||
|
||||
|
||||
def test_reader_main_hides_audit_vocabulary(full_reading) -> None:
|
||||
from scripts.jyotish_engine import build_professional_report_reference_packet
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import ast
|
||||
import bisect
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
|
||||
@@ -16,7 +17,7 @@ from scripts.research_timing.bundle import chart_from_parts, research_bundle
|
||||
from scripts.research_timing.frame import SiderealFrame, birth_jd
|
||||
from scripts.research_timing.rashi_pd import attach_rashi_pratyantar
|
||||
from scripts.research_timing.render import _heading, render_research_chapters
|
||||
from scripts.research_timing.search import ElongationClock, find_returns
|
||||
from scripts.research_timing.search import ElongationClock, _utc_from_jd, find_returns, hindu_solar_events
|
||||
from scripts.rashi_dashas_native import calculate_rashi_dashas
|
||||
from tests.report_english_fictional import fictional_case
|
||||
|
||||
@@ -125,7 +126,11 @@ def test_rendered_chapters_keep_the_research_banner_and_drop_snake_case() -> Non
|
||||
assert _SNAKE.search(chinese) is None
|
||||
assert _SNAKE.search(english) is None
|
||||
assert "blocked" not in english.lower()
|
||||
assert "PyJHora" not in english
|
||||
# 原值:assert "PyJHora" not in english
|
||||
# 新值:英文里的 PyJHora 只允许出现在「Differs from PyJHora:」开头的对照行
|
||||
# 原因:#92 修复单要求不一致的体系写这句;其它位置仍然不能出现 PyJHora
|
||||
stray = [line for line in english.splitlines() if "PyJHora" in line and not line.startswith("Differs from PyJHora:")]
|
||||
assert stray == []
|
||||
assert "主星/名称" in chinese
|
||||
assert "次小运按简化模型,从现有主运按比例分出。主运和子运与原来的表相同。" in chinese
|
||||
assert "收录的教程没有写明第三层。" in chinese
|
||||
@@ -190,6 +195,101 @@ def test_rectification_modules_do_not_import_the_research_package() -> None:
|
||||
assert all("research_timing" not in module for module in modules), path.name
|
||||
|
||||
|
||||
def _sections(markdown: str) -> list[tuple[str, str]]:
|
||||
parts = re.split(r"(?m)^## ", markdown)
|
||||
rows = []
|
||||
for part in parts[1:]:
|
||||
title, _, body = part.partition("\n")
|
||||
rows.append((title.strip(), body))
|
||||
return rows
|
||||
|
||||
|
||||
def _under(rows: list[tuple[str, str]], heading: str, match: str) -> list[str]:
|
||||
found = []
|
||||
for title, body in rows:
|
||||
if title == heading or (match == "prefix" and title.startswith(heading + " ")):
|
||||
found.append(body)
|
||||
return found
|
||||
|
||||
|
||||
def test_solar_events_before_1970_stay_available_on_windows() -> None:
|
||||
"""BUG-1429. Windows fromtimestamp rejects instants before 1970."""
|
||||
moment = datetime(1950, 6, 15, 12, 0, tzinfo=timezone.utc)
|
||||
events = hindu_solar_events(moment, 0.0, 0.0)
|
||||
assert events
|
||||
assert all(item["event"] in {"sunrise", "sunset"} for item in events)
|
||||
parsed = datetime.fromisoformat(events[0]["datetime_utc"])
|
||||
assert parsed.year == 1950
|
||||
assert parsed.tzinfo is not None
|
||||
after = _utc_from_jd(2451545.0)
|
||||
expect = datetime.fromtimestamp((2451545.0 - 2440587.5) * 86400.0, timezone.utc)
|
||||
assert abs((after - expect).total_seconds()) < 1e-3
|
||||
seen = 0
|
||||
for index in range(200):
|
||||
case = fictional_case(index)
|
||||
if int(case["year"]) >= 1970:
|
||||
continue
|
||||
zone = timezone(timedelta(hours=float(case["tz"])))
|
||||
birth = datetime(
|
||||
int(case["year"]), int(case["month"]), int(case["day"]), 12, 0, tzinfo=zone,
|
||||
)
|
||||
rows = hindu_solar_events(birth, float(case["lat"]), float(case["lon"]))
|
||||
assert rows
|
||||
seen += 1
|
||||
assert seen > 0
|
||||
|
||||
|
||||
def test_pyjhora_mismatch_labels_follow_the_committed_json() -> None:
|
||||
"""Read the offline JSON. This test does not import jhora."""
|
||||
path = ROOT / "docs" / "tasks" / "research-timing-pyjhora-20261023.json"
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
assert payload["charts"] >= 5
|
||||
assert payload["status"] == "compared"
|
||||
systems = payload["systems"]
|
||||
assert isinstance(systems, dict) and systems
|
||||
case, _chart_data = _chart(0)
|
||||
base = {"birth_info": case, "worksheets": {}, "calculation_profile": {}}
|
||||
from scripts.pl9_full_data_export import render_full_data_markdown
|
||||
|
||||
chinese = render_full_data_markdown(dict(base))
|
||||
english_packet = dict(base)
|
||||
english_packet["report_language"] = "en"
|
||||
english = render_full_data_markdown(english_packet)
|
||||
if any(item.get("status") == "不一致" for item in systems.values()):
|
||||
assert "与 PyJHora 不一致:" in chinese
|
||||
assert "Differs from PyJHora:" in english
|
||||
zh_rows = _sections(chinese)
|
||||
en_rows = _sections(english)
|
||||
for name, item in systems.items():
|
||||
status = item["status"]
|
||||
assert status in {"一致", "不一致", "无此方法"}, name
|
||||
same_lord = re.search(r"native (\w+) vs PyJHora (\w+)", str(item.get("example") or ""))
|
||||
if same_lord:
|
||||
assert same_lord.group(1) != same_lord.group(2), (name, item.get("example"))
|
||||
for rows, heading, sentence in (
|
||||
(zh_rows, item["heading_zh"], f"与 PyJHora 不一致:差在 {item.get('category') or ''}"),
|
||||
(en_rows, item["heading_en"], f"Differs from PyJHora: {item.get('category_en') or ''}"),
|
||||
):
|
||||
bodies = _under(rows, heading, item.get("match") or "exact")
|
||||
assert bodies, (name, heading)
|
||||
if status == "不一致":
|
||||
assert all(sentence in body for body in bodies), (name, heading)
|
||||
else:
|
||||
assert all("与 PyJHora 不一致:" not in body and "Differs from PyJHora:" not in body for body in bodies), name
|
||||
tree = ast.parse((ROOT / "scripts" / "research" / "research_timing_pyjhora_compare.py").read_text(encoding="utf-8"))
|
||||
allowed = {"pyjhora_fingerprints", "_py_tithi_return_days"}
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.FunctionDef) and node.name in allowed:
|
||||
continue
|
||||
for child in ast.walk(node):
|
||||
module = ""
|
||||
if isinstance(child, ast.Import):
|
||||
module = " ".join(alias.name for alias in child.names)
|
||||
elif isinstance(child, ast.ImportFrom):
|
||||
module = child.module or ""
|
||||
assert not module.startswith("jhora"), module
|
||||
|
||||
|
||||
def test_rectification_golden_file_is_unchanged() -> None:
|
||||
digest = hashlib.sha256((ROOT / _GOLDEN).read_bytes()).hexdigest()
|
||||
assert digest == "92efd8fdbae43f553b0006cfe28e929d5255fe02f3c36e616475fa149a356212"
|
||||
|
||||
Reference in New Issue
Block a user