refactor: Phase 3 拆分 generate 流水线并补全测试
提取 collect/formatters/themes 等 pipeline 模块,新增 wecom 分条、RSS、delta、Agent 工作流测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
55
tests/test_agent_workflow.py
Normal file
55
tests/test_agent_workflow.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
|
||||
def test_analyze_trends_parses_json(monkeypatch, isolated_output):
|
||||
import daily.agent_workflow as agent
|
||||
import daily.config as config
|
||||
|
||||
monkeypatch.setattr(config, "OUTPUT_DIR", isolated_output)
|
||||
monkeypatch.setattr(agent, "OUTPUT_DIR", isolated_output)
|
||||
|
||||
monkeypatch.setattr(
|
||||
agent,
|
||||
"llm_chat",
|
||||
lambda system, user: json.dumps(
|
||||
{
|
||||
"headline": "Skills 视频工具升温",
|
||||
"opening": "今日 remotion 相关技能继续走强。",
|
||||
"themes": [{"name": "视频", "summary": "Remotion 生态活跃"}],
|
||||
"top_picks": [],
|
||||
"signals": [],
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
|
||||
llm_input = {"date": "2026-07-03", "skills_trending": []}
|
||||
trends = agent.analyze_trends(llm_input, date_str="2026-07-03")
|
||||
|
||||
assert trends is not None
|
||||
assert trends["headline"] == "Skills 视频工具升温"
|
||||
assert (isolated_output / "2026-07-03.trends.json").exists()
|
||||
|
||||
|
||||
def test_write_wecom_report_extracts_markdown_block(monkeypatch):
|
||||
import daily.agent_workflow as agent
|
||||
|
||||
monkeypatch.setattr(
|
||||
agent,
|
||||
"llm_chat",
|
||||
lambda system, user: "```markdown\n📰 **早报 · 2026-07-03**\n\n正文\n```",
|
||||
)
|
||||
|
||||
md = agent.write_wecom_report(
|
||||
{"date": "2026-07-03"},
|
||||
{"headline": "test", "opening": "open"},
|
||||
date_str="2026-07-03",
|
||||
time_str="09:30 (UTC+8)",
|
||||
updated="2026-07-02",
|
||||
)
|
||||
|
||||
assert md is not None
|
||||
assert md.startswith("📰")
|
||||
assert "正文" in md
|
||||
67
tests/test_delta.py
Normal file
67
tests/test_delta.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_build_movement_context_detects_new_skill(isolated_output, monkeypatch):
|
||||
import daily.config as config
|
||||
import daily.delta as delta
|
||||
|
||||
monkeypatch.setattr(config, "OUTPUT_DIR", isolated_output)
|
||||
monkeypatch.setattr(delta, "OUTPUT_DIR", isolated_output)
|
||||
|
||||
prev_path = isolated_output / "2026-07-02.data.json"
|
||||
prev_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"data": {
|
||||
"date": "2026-07-02",
|
||||
"movement_baseline": {
|
||||
"skills_trending": [{"id": "a/old", "title": "old"}],
|
||||
"skills_hot": [],
|
||||
"github_trending": [],
|
||||
"github_emerging": [],
|
||||
"github_topic": [],
|
||||
},
|
||||
}
|
||||
},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
trending = [
|
||||
{"id": "a/new", "title": "new-skill", "source": "a", "installs": 10, "link": "https://x", "description": ""},
|
||||
{"id": "a/old", "title": "old", "source": "a", "installs": 9, "link": "https://y", "description": ""},
|
||||
]
|
||||
movement = delta.build_movement_context(
|
||||
date_str="2026-07-03",
|
||||
trending=trending,
|
||||
hot=[],
|
||||
github_trending=[],
|
||||
github_emerging=[],
|
||||
github_topic=[],
|
||||
)
|
||||
|
||||
assert movement["baseline_date"] == "2026-07-02"
|
||||
assert len(movement["skills_trending_moves"]) == 1
|
||||
assert movement["skills_trending_moves"][0]["id"] == "a/new"
|
||||
|
||||
|
||||
def test_find_previous_data_skips_missing_days(isolated_output, monkeypatch):
|
||||
import daily.config as config
|
||||
import daily.delta as delta
|
||||
|
||||
monkeypatch.setattr(config, "OUTPUT_DIR", isolated_output)
|
||||
monkeypatch.setattr(delta, "OUTPUT_DIR", isolated_output)
|
||||
|
||||
(isolated_output / "2026-07-01.data.json").write_text(
|
||||
json.dumps({"data": {"date": "2026-07-01", "skills_trending": []}}),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
found = delta.find_previous_data("2026-07-03")
|
||||
assert found is not None
|
||||
assert found[0] == "2026-07-01"
|
||||
54
tests/test_rss_parse.py
Normal file
54
tests/test_rss_parse.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
def test_parse_sample_rss_fixture():
|
||||
from daily.news.feeds import NEWS_CATEGORIES
|
||||
from daily.news.fetch import _parse_feed
|
||||
|
||||
category = NEWS_CATEGORIES[0]
|
||||
feed_name = category.feeds[0].name
|
||||
xml = (FIXTURES / "sample-rss.xml").read_text(encoding="utf-8")
|
||||
items = _parse_feed(xml, feed_name, category)
|
||||
|
||||
assert len(items) == 1
|
||||
assert items[0]["title"] == "Sample AI headline for smoke tests"
|
||||
assert items[0]["link"] == "https://example.com/ai-news/1"
|
||||
assert items[0]["source_name"] == feed_name
|
||||
assert "minimal RSS item" in items[0]["summary"]
|
||||
|
||||
|
||||
def test_fetch_ai_news_offline(monkeypatch):
|
||||
from daily.news import fetch as news_fetch
|
||||
|
||||
sample = {
|
||||
"title": "Sample AI headline for smoke tests",
|
||||
"link": "https://example.com/ai-news/1",
|
||||
"summary": "A minimal RSS item used by offline tests.",
|
||||
"published": "Wed, 02 Jul 2026 08:00:00 GMT",
|
||||
"source_name": "OpenAI",
|
||||
"category_id": "official",
|
||||
"category_name": "厂商官方",
|
||||
"category_icon": "🏢",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
news_fetch,
|
||||
"_fetch_news",
|
||||
lambda categories: {
|
||||
"enabled": True,
|
||||
"hours": 72,
|
||||
"categories": [{"id": "official", "name": "厂商官方", "icon": "🏢", "items": [sample]}],
|
||||
"flat": [sample],
|
||||
"stats": {"feeds_total": 1, "feeds_ok": 1, "items_raw": 1, "feeds_failed": []},
|
||||
},
|
||||
)
|
||||
|
||||
payload = news_fetch.fetch_ai_news()
|
||||
assert payload["enabled"] is True
|
||||
assert payload["flat"][0]["title"].startswith("Sample AI")
|
||||
@@ -36,11 +36,12 @@ def test_generate_report_offline(
|
||||
monkeypatch,
|
||||
):
|
||||
import daily.generate as generate
|
||||
import daily.pipeline.collect as collect
|
||||
|
||||
monkeypatch.setattr(generate, "load_feed", lambda force=False: skills_feed)
|
||||
monkeypatch.setattr(generate, "fetch_github_trending", lambda n: [_sample_github_repo()])
|
||||
monkeypatch.setattr(generate, "fetch_emerging_repos", lambda n, exclude=None: [])
|
||||
monkeypatch.setattr(generate, "fetch_topic_hot_repos", lambda n, exclude=None: ("llm", []))
|
||||
monkeypatch.setattr(collect, "load_feed", lambda force=False: skills_feed)
|
||||
monkeypatch.setattr(collect, "fetch_github_trending", lambda n: [_sample_github_repo()])
|
||||
monkeypatch.setattr(collect, "fetch_emerging_repos", lambda n, exclude=None: [])
|
||||
monkeypatch.setattr(collect, "fetch_topic_hot_repos", lambda n, exclude=None: ("llm", []))
|
||||
monkeypatch.setattr(generate, "_now_cst", lambda: fixed_cst)
|
||||
|
||||
_, wecom_md, out_md, out_wecom = generate.generate_report()
|
||||
|
||||
34
tests/test_wecom_split.py
Normal file
34
tests/test_wecom_split.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from daily.wecom_split import split_wecom_messages
|
||||
|
||||
|
||||
def test_split_wecom_messages_keeps_short_text():
|
||||
text = "📰 **早报 · 2026-07-03**\n\n短内容"
|
||||
parts = split_wecom_messages(text, limit=4096)
|
||||
assert parts == [text]
|
||||
|
||||
|
||||
def test_split_wecom_messages_adds_part_footer():
|
||||
section = "📰 **早报**\n\n" + ("正文行\n" * 200)
|
||||
parts = split_wecom_messages(section, limit=600)
|
||||
assert len(parts) > 1
|
||||
assert all(len(part.encode("utf-8")) <= 600 for part in parts)
|
||||
assert parts[0].endswith("1/" + str(len(parts)))
|
||||
|
||||
|
||||
def test_split_sections_on_emoji_headers():
|
||||
from daily.wecom_split import _split_sections
|
||||
|
||||
text = "\n\n".join(
|
||||
[
|
||||
"📰 **早报 · 2026-07-03**",
|
||||
"💡 **今日速览**\n- item one\n- item two",
|
||||
"🌍 **国际 AI 时讯**\n1. [Headline](https://example.com)",
|
||||
]
|
||||
)
|
||||
sections = _split_sections(text)
|
||||
assert len(sections) == 3
|
||||
assert sections[0].startswith("📰")
|
||||
assert sections[1].startswith("💡")
|
||||
assert sections[2].startswith("🌍")
|
||||
Reference in New Issue
Block a user