"""外部 issue 模板检查的判定;GitHub 写操作在工作流里,这里只验证脚本给出的结论。""" from __future__ import annotations import io import json import sys import tempfile import unittest from contextlib import redirect_stdout from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts")) import issue_intake # noqa: E402 JARGON_BODY = "## 目标\n\n导入资料,不做真实账本写入。\n\n## 授权边界\n\n测试使用合成样本。\n" FEATURE_BODY = "## 使用场景 / Use case\n\n想按女优查看全部作品。\n\n## 期望的行为 / Desired behavior\n\n列出作品。\n" BUG_BODY = "### 问题描述\n\n闪退\n\n### Environment\n\nWindows 11\n" def event(action="opened", body=JARGON_BODY, **issue): return {"action": action, "issue": {"body": body, "author_association": "NONE", "user": {"type": "User"}, "type": None, "state": "open", "labels": [], **issue}} class IssueIntakeTests(unittest.TestCase): def test_templates_supply_issue_type_and_bilingual_section_names(self): templates = dict(issue_intake.load_templates()) self.assertIn(("问题描述", "what happened"), templates["Bug"]) self.assertIn(("使用场景", "use case"), templates["Feature"]) self.assertIn(("补充", "additional context"), templates["Feature"]) def test_command_line_issue_that_follows_a_template_gets_its_type(self): self.assertEqual(issue_intake.verdict(event(body=FEATURE_BODY + "\n不写入账本。\n")), "type Feature") self.assertEqual(issue_intake.verdict(event(body=BUG_BODY)), "type Bug") self.assertEqual(issue_intake.verdict(event("edited", BUG_BODY)), "type Bug") def test_untyped_new_issue_without_template_sections_is_closed(self): for body in (JARGON_BODY, "希望垃圾文件可以批量删除", ""): with self.subTest(body=body[:12]): self.assertEqual(issue_intake.verdict(event(body=body)), "close") self.assertEqual(issue_intake.verdict(event("edited")), "keep") def test_已有类型的新问题缺少模板章节时关闭(self): for kind in ("Bug", "Feature"): for body in (JARGON_BODY, "nas中docker无法自动更新,是不是因为版本号是edge的原因,能不能优化一下", ""): with self.subTest(kind=kind, body=body[:12]): self.assertEqual(issue_intake.verdict(event(body=body, type={"name": kind})), "close") def test_已有类型且符合模板的问题保留类型(self): for action in ("opened", "edited"): for kind, body in (("Bug", BUG_BODY), ("Feature", FEATURE_BODY)): with self.subTest(action=action, kind=kind): self.assertEqual(issue_intake.verdict(event(action, body, type={"name": kind})), "keep") def test_已有类型的待补模板问题补齐后按正文类型重开(self): closed = {"state": "closed", "labels": [{"name": issue_intake.LABEL}], "type": {"name": "Feature"}} self.assertEqual(issue_intake.verdict(event("edited", BUG_BODY, **closed)), "reopen Bug") self.assertEqual(issue_intake.verdict(event("edited", JARGON_BODY, **closed)), "keep") def test_maintainers_bots_and_pull_requests_are_exempt(self): for issue in ({"author_association": "MEMBER"}, {"author_association": "OWNER"}, {"author_association": "COLLABORATOR"}, {"user": {"type": "Bot"}}, {"pull_request": {"url": "x"}}): with self.subTest(issue=issue): self.assertEqual(issue_intake.verdict(event(**issue)), "keep") def test_closed_issue_reopens_with_its_type_once_the_sections_are_filled_in(self): closed = {"state": "closed", "labels": [{"name": issue_intake.LABEL}]} self.assertEqual(issue_intake.verdict(event("edited", FEATURE_BODY, **closed)), "reopen Feature") self.assertEqual(issue_intake.verdict(event("edited", JARGON_BODY, **closed)), "keep") self.assertEqual(issue_intake.verdict(event("edited", FEATURE_BODY, state="closed")), "keep") def test_重开后只折叠工作流留下的模板提示(self): comments = [ {"node_id": "notice", "user": {"login": "github-actions[bot]"}, "body": issue_intake.NOTICE}, {"node_id": "other-bot", "user": {"login": "github-actions[bot]"}, "body": "已加入项目看板。"}, {"node_id": "quote", "user": {"login": "reporter"}, "body": issue_intake.NOTICE}, {"node_id": "deleted-user", "user": None, "body": None}, ] self.assertEqual(issue_intake.notice_ids(comments), ["notice"]) with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "comments.json" path.write_text(json.dumps(comments, ensure_ascii=False), encoding="utf-8") out = io.StringIO() with redirect_stdout(out): self.assertEqual(issue_intake.main(["--notice-ids", str(path)]), 0) self.assertEqual(out.getvalue().split(), ["notice"]) def test_cli_reads_the_event_file_and_prints_the_notice(self): with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "event.json" path.write_text(json.dumps(event(), ensure_ascii=False), encoding="utf-8") out = io.StringIO() with redirect_stdout(out): self.assertEqual(issue_intake.main(["--event", str(path)]), 0) self.assertEqual(out.getvalue().strip(), "close") out = io.StringIO() with redirect_stdout(out): issue_intake.main(["--notice"]) self.assertIn("issues/new/choose", out.getvalue()) self.assertIn("This issue was closed automatically", out.getvalue()) if __name__ == "__main__": unittest.main()