冻结 _now_cst、mock 网络/文件/LLM(featured_pick 与 push_gate 有独立测试,此处冻结),验证同一输入下完整版与企微版产物逐字节 一致——拆分 _collect/_select/_render 不得改变行为(T1+T4)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
104 lines
4.5 KiB
Python
104 lines
4.5 KiB
Python
# tests/test_generate_golden.py
|
|
"""黄金文件/确定性回归: 冻结时间、mock 网络与 LLM, 验证 generate_report
|
|
在同一输入下产出逐字节一致(拆分 _collect/_select/_render 不得改变行为)。"""
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
from datetime import datetime, timedelta, timezone
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
|
|
import daily.generate as g
|
|
|
|
FIXED_NOW = datetime(2026, 7, 18, 8, 50, tzinfo=timezone(timedelta(hours=8)))
|
|
|
|
|
|
def _feed() -> dict:
|
|
return {
|
|
"updatedAt": "2026-07-18T08:00:00",
|
|
"topTrending": [
|
|
{"id": "a", "source": "src", "title": "alpha", "description": "desc a",
|
|
"installs": 100, "link": "https://x/a"},
|
|
{"id": "b", "source": "src", "title": "beta", "description": "desc b",
|
|
"installs": 90, "link": "https://x/b"},
|
|
],
|
|
"topHot": [
|
|
{"id": "c", "source": "src", "title": "gamma", "description": "desc c",
|
|
"installs": 80, "link": "https://x/c"},
|
|
],
|
|
}
|
|
|
|
|
|
def _patches(tmp: Path):
|
|
"""集中 mock 所有外部边界: 网络/文件/时间/LLM。返回 patcher 列表。"""
|
|
return [
|
|
mock.patch.object(g, "_now_cst", return_value=FIXED_NOW),
|
|
mock.patch.object(g, "load_feed", return_value=_feed()),
|
|
mock.patch.object(g, "_load_snapshot", return_value=set()),
|
|
mock.patch.object(g, "_save_snapshot", lambda *a, **k: None),
|
|
mock.patch.object(g, "load_boards", return_value=(
|
|
_feed()["topTrending"], _feed()["topHot"])),
|
|
mock.patch.object(g, "fetch_github_trending", return_value=[]),
|
|
mock.patch.object(g, "fetch_emerging_repos", return_value=[]),
|
|
mock.patch.object(g, "fetch_topic_hot_repos", return_value=("topic", [])),
|
|
mock.patch.object(g, "fetch_ai_news", return_value={
|
|
"enabled": False, "categories": [], "flat": [], "stats": {}}),
|
|
mock.patch.object(g, "fetch_cn_ai_news", return_value={
|
|
"enabled": False, "categories": [], "flat": [], "stats": {}}),
|
|
mock.patch.object(g, "is_research_mode", return_value=False),
|
|
mock.patch.object(g, "is_agent_mode", return_value=False),
|
|
mock.patch.object(g, "run_editorial", return_value=None),
|
|
mock.patch.object(g, "cursor_editor_enabled", return_value=False),
|
|
mock.patch.object(g, "record_pushed_links", lambda *a, **k: None),
|
|
mock.patch.object(g, "save_json", lambda *a, **k: None),
|
|
mock.patch.object(g, "load_recent_shown_keys", return_value={
|
|
"skills_trending": set(), "skills_hot": set(),
|
|
"github_trending": set(), "github_emerging": set(),
|
|
"github_topic": set()}),
|
|
mock.patch.object(g, "OUTPUT_DIR", tmp),
|
|
mock.patch.object(g, "_localize_descriptions_in_place", lambda *a, **k: None),
|
|
# featured_pick / push_gate 有独立测试;此处冻结以免触网(LLM)与读盘。
|
|
mock.patch.object(g, "apply_featured_pick", return_value={
|
|
"title": "alpha", "url": "https://x/a",
|
|
"command": "npx skills add src/alpha", "why": "昨日 star 增速第一"}),
|
|
mock.patch.object(g, "evaluate_push_gate", return_value=mock.Mock(
|
|
should_push=False, silent=True, reasons=["golden-mock"])),
|
|
]
|
|
|
|
|
|
class GenerateGoldenTests(unittest.TestCase):
|
|
def _run(self, tmp: Path) -> tuple[str, str]:
|
|
patches = _patches(tmp)
|
|
for p in patches:
|
|
p.start()
|
|
try:
|
|
markdown, wecom_md, _md, _we = g.generate_report()
|
|
return markdown, wecom_md
|
|
finally:
|
|
for p in patches:
|
|
try:
|
|
p.stop()
|
|
except Exception:
|
|
pass
|
|
|
|
def test_deterministic_same_input_same_output(self):
|
|
import tempfile
|
|
with tempfile.TemporaryDirectory() as d1, tempfile.TemporaryDirectory() as d2:
|
|
md1, we1 = self._run(Path(d1))
|
|
md2, we2 = self._run(Path(d2))
|
|
self.assertEqual(md1, md2, "完整版 markdown 在相同输入下必须逐字节一致")
|
|
self.assertEqual(we1, we2, "企微 wecom_md 在相同输入下必须逐字节一致")
|
|
|
|
def test_output_contains_core_sections(self):
|
|
import tempfile
|
|
with tempfile.TemporaryDirectory() as d:
|
|
markdown, wecom_md = self._run(Path(d))
|
|
self.assertIn("# 早报 · 2026-07-18", markdown)
|
|
self.assertIn("主题聚类", markdown)
|
|
self.assertIsInstance(wecom_md, str)
|
|
self.assertTrue(len(wecom_md) > 0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|