mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-28 19:06:36 +08:00
132 lines
5.8 KiB
Python
132 lines
5.8 KiB
Python
"""
|
|
发布记录的范围、作者、分页与手工摘要保护契约。
|
|
"""
|
|
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from scripts import release_notes
|
|
|
|
|
|
def make_commit(sha: str, subject: str, login: str | None = "contributor", parents: int = 1) -> dict:
|
|
"""
|
|
构造包含 GitHub 作者及原始 Git 显示名的提交响应。
|
|
"""
|
|
return {
|
|
"sha": sha,
|
|
"html_url": f"https://github.com/owner/repo/commit/{sha}",
|
|
"parents": [{}] * parents,
|
|
"author": {"login": login} if login else None,
|
|
"commit": {"message": subject, "author": {"name": "Display Name"}},
|
|
}
|
|
|
|
|
|
def test_changelog_preserves_subjects_and_distinct_commits() -> None:
|
|
"""
|
|
不分类、翻译或按标题去重,真实账号与未关联作者分别展示。
|
|
"""
|
|
commits = [
|
|
make_commit("a" * 40, "fix(scope): 原始标题\n详细正文"),
|
|
make_commit("b" * 40, "fix(scope): 原始标题", login=None),
|
|
make_commit("c" * 40, "Merge pull request #1", parents=2),
|
|
make_commit("d" * 40, "合并提交", parents=2),
|
|
make_commit("e" * 40, "build(plugin-market): sync default from MoviePilot-Wiki@123"),
|
|
make_commit("f" * 40, "ci: improve pipeline"),
|
|
]
|
|
body = release_notes.changelog(commits + commits[:1])
|
|
assert body.count("fix(scope): 原始标题") == 2
|
|
assert "by @contributor" in body
|
|
assert "by Display Name" in body
|
|
assert "@Display Name" not in body
|
|
assert "Merge" not in body and "合并提交" not in body
|
|
assert "plugin-market" not in body
|
|
assert "ci: improve pipeline" in body
|
|
assert "详细正文" not in body
|
|
|
|
|
|
def test_previous_tag_excludes_current_future_and_other_major(monkeypatch) -> None:
|
|
"""
|
|
同版本重打包仍从上一正式版计算,不受较高版本或预发布标签影响。
|
|
"""
|
|
monkeypatch.setattr(release_notes, "command", lambda *_: "v2.9.9\nv3.1.2\nv3.1.2-1\nv3.1.3\nv3.1.4\nv3.1.3-beta.1\n")
|
|
assert release_notes.previous_tag("v3.1.3") == "v3.1.2-1"
|
|
assert release_notes.previous_tag("v3.1.2-2") == "v3.1.2-1"
|
|
assert release_notes.previous_tag("v3.0.0") is None
|
|
|
|
|
|
def test_collect_commits_includes_every_page(monkeypatch) -> None:
|
|
"""
|
|
跨页发布记录包含直接提交,比较终点使用固定 SHA 而非可移动标签。
|
|
"""
|
|
calls = []
|
|
|
|
def fake_command(*args):
|
|
"""
|
|
模拟 GitHub Compare API 的多页响应。
|
|
"""
|
|
calls.append(args)
|
|
return json.dumps([{"commits": [make_commit("a", "first")]}, {"commits": [make_commit("b", "second")]}])
|
|
|
|
monkeypatch.setattr(release_notes, "command", fake_command)
|
|
commits = release_notes.collect_commits("owner/repo", "fixed-sha", "v3.1.2")
|
|
assert [commit["sha"] for commit in commits] == ["a", "b"]
|
|
assert calls == [("gh", "api", "--paginate", "--slurp", "repos/owner/repo/compare/v3.1.2...fixed-sha?per_page=100")]
|
|
|
|
|
|
def test_first_release_collects_entire_history(monkeypatch) -> None:
|
|
"""
|
|
首次发布不凭空选择其它大版本,提交历史按时间正序输出。
|
|
"""
|
|
monkeypatch.setattr(release_notes, "command", lambda *_: json.dumps([[make_commit("b", "new")], [make_commit("a", "old")]]))
|
|
commits = release_notes.collect_commits("owner/repo", "fixed-sha", None)
|
|
assert [commit["sha"] for commit in commits] == ["a", "b"]
|
|
|
|
|
|
@pytest.mark.parametrize("summary", ["", "- 手工功能说明\n\n### 注意事项\n\n原样保留 `代码` 与 @author", "说明\n\n## 手工子标题\n\n细节"])
|
|
def test_rebuild_preserves_manual_summary_and_refreshes_records(summary: str) -> None:
|
|
"""
|
|
重建保留手工摘要,刷新自动章节,正文始终仅包含三个约定章节。
|
|
"""
|
|
old = release_notes.release_body(summary, "old records", "owner/repo", "v3.1.1", "v3.1.2")
|
|
preserved = release_notes.manual_summary(old)
|
|
new = release_notes.release_body(preserved, "new records", "owner/repo", "v3.1.1", "v3.1.2")
|
|
assert preserved == summary
|
|
assert "old records" not in new
|
|
assert "new records" in new
|
|
assert "https://github.com/owner/repo/compare/v3.1.1...v3.1.2" in new
|
|
assert "贡献者" not in new
|
|
|
|
|
|
def test_legacy_manual_body_is_preserved_but_generated_body_is_not_summary() -> None:
|
|
"""
|
|
兼容旧手工公告,避免把原有自动分类记录当作本次用户摘要。
|
|
"""
|
|
assert release_notes.manual_summary("手工公告\n- 新功能") == "手工公告\n- 新功能"
|
|
assert release_notes.manual_summary("### ✨ 新功能\n\n- generated by @author") == ""
|
|
assert release_notes.manual_summary("手工公告\n\n### ✨ 新功能\n- generated") == "手工公告"
|
|
|
|
|
|
@pytest.mark.parametrize("frontend", [False, True])
|
|
def test_main_emits_workflow_body_without_evaluating_commit_text(tmp_path, monkeypatch, frontend) -> None:
|
|
"""
|
|
前端仅输出列表,后端保留手工内容;提交中的工作流分隔符不得截断正文。
|
|
"""
|
|
environment = tmp_path / "github.env"
|
|
monkeypatch.setenv("GITHUB_ENV", str(environment))
|
|
monkeypatch.setenv("RELEASE_BODY", "## 本次更新内容\n\n手工公告\n\n## 完整变更记录\nold")
|
|
monkeypatch.setattr(release_notes, "previous_tag", lambda _: "v3.1.2")
|
|
monkeypatch.setattr(release_notes, "collect_commits", lambda *_: [make_commit("a" * 40, "fix: $() EOF")])
|
|
args = ["release_notes.py", "--repository", "owner/repo", "--tag", "v3.1.3", "--source", "fixed-sha"]
|
|
if frontend:
|
|
args.append("--changelog-only")
|
|
monkeypatch.setattr("sys.argv", args)
|
|
release_notes.main()
|
|
content = environment.read_text(encoding="utf-8")
|
|
delimiter = content.splitlines()[0].split("<<")[1]
|
|
assert content.splitlines()[-1] == delimiter
|
|
assert "fix: $() EOF by @contributor" in content
|
|
assert ("## 本次更新内容" in content) is not frontend
|
|
assert ("手工公告" in content) is not frontend
|
|
assert ("## 版本对比" in content) is not frontend
|