From 15e855fbdb50924dd7d6338361a7e54ced88cf58 Mon Sep 17 00:00:00 2001 From: uy/sun Date: Wed, 1 Jan 2025 01:24:03 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9C=A8=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E8=AE=AE=E9=A2=98=E5=90=8E=E7=AB=8B=E5=8D=B3=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E6=8B=89=E5=8F=96=E8=AF=B7=E6=B1=82=EF=BC=8C=E6=8B=89?= =?UTF-8?q?=E5=8F=96=E8=AF=B7=E6=B1=82=E4=BC=9A=E8=A2=AB=E5=86=8D=E6=AC=A1?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 分支和拉取请求绑定起来,分支不存在才能创建拉取请求。 --- CHANGELOG.md | 4 + src/plugins/github/handlers/git.py | 11 +- src/plugins/github/plugins/publish/utils.py | 30 +- .../publish/process/test_publish_check.py | 560 +++++++++--------- .../resolve/test_resolve_pull_request.py | 3 +- tests/plugins/github/utils.py | 35 +- 6 files changed, 330 insertions(+), 313 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79c6199b..cc99efcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/lang/zh-CN/ ## [Unreleased] +### Fixed + +- 修复在评论议题后立即关闭拉取请求,拉取请求会被再次创建的问题 + ## [4.2.3] - 2024-12-30 ### Added diff --git a/src/plugins/github/handlers/git.py b/src/plugins/github/handlers/git.py index dd086488..d586cc3d 100644 --- a/src/plugins/github/handlers/git.py +++ b/src/plugins/github/handlers/git.py @@ -39,13 +39,10 @@ def commit_and_push(self, message: str, branch_name: str, author: str): def remote_branch_exists(self, branch_name: str) -> bool: """检查远程分支是否存在""" - try: - result = run_shell_command( - ["git", "ls-remote", "--heads", "origin", branch_name] - ) - return bool(result.stdout.strip()) - except Exception: - return False + result = run_shell_command( + ["git", "ls-remote", "--heads", "origin", branch_name] + ) + return bool(result.stdout.decode().strip()) def delete_remote_branch(self, branch_name: str): """删除远程分支""" diff --git a/src/plugins/github/plugins/publish/utils.py b/src/plugins/github/plugins/publish/utils.py index b148d01b..246a13a1 100644 --- a/src/plugins/github/plugins/publish/utils.py +++ b/src/plugins/github/plugins/publish/utils.py @@ -211,13 +211,23 @@ async def process_pull_request( """ 根据发布信息合法性创建拉取请求或将请求改为草稿 """ - if result.valid: - commit_message = f"{COMMIT_MESSAGE_PREFIX} {result.type.value.lower()} {result.name} (#{handler.issue_number})" + if not result.valid: + # 如果之前已经创建了拉取请求,则将其转换为草稿 + await handler.draft_pull_request(branch_name) + return + + # 更新文件 + handler.switch_branch(branch_name) + update_file(result) - handler.switch_branch(branch_name) - # 更新文件 - update_file(result) - handler.commit_and_push(commit_message, branch_name, handler.author) + # 只有当远程分支不存在时才创建拉取请求 + # 需要在 commit_and_push 前判断,否则远程一定存在 + remote_branch_exists = handler.remote_branch_exists(branch_name) + + commit_message = f"{COMMIT_MESSAGE_PREFIX} {result.type.value.lower()} {result.name} (#{handler.issue_number})" + handler.commit_and_push(commit_message, branch_name, handler.author) + + if not remote_branch_exists: # 创建拉取请求 try: pull_number = await handler.create_pull_request( @@ -226,13 +236,13 @@ async def process_pull_request( branch_name, ) await handler.add_labels(pull_number, [PUBLISH_LABEL, result.type.value]) + return except RequestFailed: - # 如果之前已经创建了拉取请求,则将其转换为草稿 + # 如果之前已经创建了拉取请求,则将其转换为可评审 logger.info("该分支的拉取请求已创建,请前往查看") - await handler.update_pull_request_status(title, branch_name) else: - # 如果之前已经创建了拉取请求,则将其转换为草稿 - await handler.draft_pull_request(branch_name) + logger.info("远程分支已存在,跳过创建拉取请求") + await handler.update_pull_request_status(title, branch_name) async def trigger_registry_update(handler: IssueHandler, publish_type: PublishType): diff --git a/tests/plugins/github/publish/process/test_publish_check.py b/tests/plugins/github/publish/process/test_publish_check.py index 121cdcd7..69477fd9 100644 --- a/tests/plugins/github/publish/process/test_publish_check.py +++ b/tests/plugins/github/publish/process/test_publish_check.py @@ -14,10 +14,12 @@ from tests.plugins.github.utils import ( MockBody, MockIssue, + assert_subprocess_run_calls, check_json_data, generate_issue_body_bot, get_github_bot, get_issue_labels, + mock_subprocess_run_with_side_effect, ) @@ -31,9 +33,7 @@ async def test_bot_process_publish_check( """测试机器人的发布流程""" from src.plugins.github import plugin_config - mock_subprocess_run = mocker.patch( - "subprocess.run", side_effect=lambda *args, **kwargs: mocker.MagicMock() - ) + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) mock_issue = MockIssue(body=MockBody(type="bot", name="test").generate()).as_mock( mocker @@ -139,52 +139,26 @@ async def test_bot_process_publish_check( ctx.receive_event(bot, event) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( + assert_subprocess_run_calls( + mock_subprocess_run, [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "switch", "-C", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "config", "--global", "user.name", "test"], - check=True, - capture_output=True, - ), - mocker.call( - [ - "git", - "config", - "--global", - "user.email", - "test@users.noreply.github.com", - ], - check=True, - capture_output=True, - ), - mocker.call(["git", "add", "-A"], check=True, capture_output=True), - mocker.call( - ["git", "commit", "-m", ":beers: publish bot test (#80)"], - check=True, - capture_output=True, - ), - mocker.call(["git", "fetch", "origin"], check=True, capture_output=True), - mocker.call( - ["git", "diff", "origin/publish/issue80", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "push", "origin", "publish/issue80", "-f"], - check=True, - capture_output=True, - ), - ] # type: ignore + ["git", "config", "--global", "safe.directory", "*"], + ["git", "switch", "-C", "publish/issue80"], + ["git", "ls-remote", "--heads", "origin", "publish/issue80"], + ["git", "config", "--global", "user.name", "test"], + [ + "git", + "config", + "--global", + "user.email", + "test@users.noreply.github.com", + ], + ["git", "add", "-A"], + ["git", "commit", "-m", ":beers: publish bot test (#80)"], + ["git", "fetch", "origin"], + ["git", "diff", "origin/publish/issue80", "publish/issue80"], + ["git", "push", "origin", "publish/issue80", "-f"], + ], ) # 检查文件是否正确 @@ -215,9 +189,7 @@ async def test_adapter_process_publish_check( """测试适配器的发布流程""" from src.plugins.github import plugin_config - mock_subprocess_run = mocker.patch( - "subprocess.run", side_effect=lambda *args, **kwargs: mocker.MagicMock() - ) + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) mock_issue = MockIssue( body=MockBody(type="adapter", name="test").generate() @@ -336,52 +308,26 @@ async def test_adapter_process_publish_check( ctx.receive_event(bot, event) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( + assert_subprocess_run_calls( + mock_subprocess_run, [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "switch", "-C", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "config", "--global", "user.name", "test"], - check=True, - capture_output=True, - ), - mocker.call( - [ - "git", - "config", - "--global", - "user.email", - "test@users.noreply.github.com", - ], - check=True, - capture_output=True, - ), - mocker.call(["git", "add", "-A"], check=True, capture_output=True), - mocker.call( - ["git", "commit", "-m", snapshot(":beers: publish adapter test (#80)")], - check=True, - capture_output=True, - ), - mocker.call(["git", "fetch", "origin"], check=True, capture_output=True), - mocker.call( - ["git", "diff", "origin/publish/issue80", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "push", "origin", "publish/issue80", "-f"], - check=True, - capture_output=True, - ), - ] # type: ignore + ["git", "config", "--global", "safe.directory", "*"], + ["git", "switch", "-C", "publish/issue80"], + ["git", "ls-remote", "--heads", "origin", "publish/issue80"], + ["git", "config", "--global", "user.name", "test"], + [ + "git", + "config", + "--global", + "user.email", + "test@users.noreply.github.com", + ], + ["git", "add", "-A"], + ["git", "commit", "-m", ":beers: publish adapter test (#80)"], + ["git", "fetch", "origin"], + ["git", "diff", "origin/publish/issue80", "publish/issue80"], + ["git", "push", "origin", "publish/issue80", "-f"], + ], ) # 检查文件是否正确 @@ -417,9 +363,7 @@ async def test_plugin_process_publish_check( from src.plugins.github import plugin_config from src.providers.docker_test import Metadata - mock_subprocess_run = mocker.patch( - "subprocess.run", side_effect=lambda *args, **kwargs: mocker.MagicMock() - ) + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) mock_issue = MockIssue(body=MockBody(type="plugin").generate()).as_mock(mocker) @@ -623,52 +567,26 @@ async def test_plugin_process_publish_check( ctx.receive_event(bot, event) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( + assert_subprocess_run_calls( + mock_subprocess_run, [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "switch", "-C", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "config", "--global", "user.name", "test"], - check=True, - capture_output=True, - ), - mocker.call( - [ - "git", - "config", - "--global", - "user.email", - "test@users.noreply.github.com", - ], - check=True, - capture_output=True, - ), - mocker.call(["git", "add", "-A"], check=True, capture_output=True), - mocker.call( - ["git", "commit", "-m", ":beers: publish plugin name (#80)"], - check=True, - capture_output=True, - ), - mocker.call(["git", "fetch", "origin"], check=True, capture_output=True), - mocker.call( - ["git", "diff", "origin/publish/issue80", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "push", "origin", "publish/issue80", "-f"], - check=True, - capture_output=True, - ), - ] # type: ignore + ["git", "config", "--global", "safe.directory", "*"], + ["git", "switch", "-C", "publish/issue80"], + ["git", "ls-remote", "--heads", "origin", "publish/issue80"], + ["git", "config", "--global", "user.name", "test"], + [ + "git", + "config", + "--global", + "user.email", + "test@users.noreply.github.com", + ], + ["git", "add", "-A"], + ["git", "commit", "-m", ":beers: publish plugin name (#80)"], + ["git", "fetch", "origin"], + ["git", "diff", "origin/publish/issue80", "publish/issue80"], + ["git", "push", "origin", "publish/issue80", "-f"], + ], ) # 检查文件是否正确 @@ -702,9 +620,7 @@ async def test_plugin_process_publish_check_re_run( from src.plugins.github import plugin_config from src.providers.docker_test import Metadata - mock_subprocess_run = mocker.patch( - "subprocess.run", side_effect=lambda *args, **kwargs: mocker.MagicMock() - ) + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) # 这次运行时,议题内容已经包含了插件测试按钮 mock_issue = MockIssue( @@ -912,52 +828,26 @@ async def test_plugin_process_publish_check_re_run( ctx.receive_event(bot, event) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( + assert_subprocess_run_calls( + mock_subprocess_run, [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "switch", "-C", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "config", "--global", "user.name", "test"], - check=True, - capture_output=True, - ), - mocker.call( - [ - "git", - "config", - "--global", - "user.email", - "test@users.noreply.github.com", - ], - check=True, - capture_output=True, - ), - mocker.call(["git", "add", "-A"], check=True, capture_output=True), - mocker.call( - ["git", "commit", "-m", ":beers: publish plugin name (#80)"], - check=True, - capture_output=True, - ), - mocker.call(["git", "fetch", "origin"], check=True, capture_output=True), - mocker.call( - ["git", "diff", "origin/publish/issue80", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "push", "origin", "publish/issue80", "-f"], - check=True, - capture_output=True, - ), - ] # type: ignore + ["git", "config", "--global", "safe.directory", "*"], + ["git", "switch", "-C", "publish/issue80"], + ["git", "ls-remote", "--heads", "origin", "publish/issue80"], + ["git", "config", "--global", "user.name", "test"], + [ + "git", + "config", + "--global", + "user.email", + "test@users.noreply.github.com", + ], + ["git", "add", "-A"], + ["git", "commit", "-m", ":beers: publish plugin name (#80)"], + ["git", "fetch", "origin"], + ["git", "diff", "origin/publish/issue80", "publish/issue80"], + ["git", "push", "origin", "publish/issue80", "-f"], + ], ) # 检查文件是否正确 @@ -993,9 +883,7 @@ async def test_edit_title( """ from src.plugins.github import plugin_config - mock_subprocess_run = mocker.patch( - "subprocess.run", side_effect=lambda *args, **kwargs: mocker.MagicMock() - ) + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) mock_issue = MockIssue(body=MockBody(type="bot", name="test1").generate()).as_mock( mocker @@ -1128,52 +1016,26 @@ async def test_edit_title( ctx.receive_event(bot, event) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( + assert_subprocess_run_calls( + mock_subprocess_run, [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "switch", "-C", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "config", "--global", "user.name", "test"], - check=True, - capture_output=True, - ), - mocker.call( - [ - "git", - "config", - "--global", - "user.email", - "test@users.noreply.github.com", - ], - check=True, - capture_output=True, - ), - mocker.call(["git", "add", "-A"], check=True, capture_output=True), - mocker.call( - ["git", "commit", "-m", ":beers: publish bot test1 (#80)"], - check=True, - capture_output=True, - ), - mocker.call(["git", "fetch", "origin"], check=True, capture_output=True), - mocker.call( - ["git", "diff", "origin/publish/issue80", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "push", "origin", "publish/issue80", "-f"], - check=True, - capture_output=True, - ), - ] # type: ignore + ["git", "config", "--global", "safe.directory", "*"], + ["git", "switch", "-C", "publish/issue80"], + ["git", "ls-remote", "--heads", "origin", "publish/issue80"], + ["git", "config", "--global", "user.name", "test"], + [ + "git", + "config", + "--global", + "user.email", + "test@users.noreply.github.com", + ], + ["git", "add", "-A"], + ["git", "commit", "-m", ":beers: publish bot test1 (#80)"], + ["git", "fetch", "origin"], + ["git", "diff", "origin/publish/issue80", "publish/issue80"], + ["git", "push", "origin", "publish/issue80", "-f"], + ], ) # 检查文件是否正确 @@ -1955,9 +1817,7 @@ async def test_process_publish_check_ready_for_review( """当之前失败后再次通过测试时,应该将拉取请求标记为 ready for review""" from src.plugins.github import plugin_config - mock_subprocess_run = mocker.patch( - "subprocess.run", side_effect=lambda *args, **kwargs: mocker.MagicMock() - ) + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) mock_issue = MockIssue(body=MockBody(type="bot", name="test").generate()).as_mock( mocker @@ -2087,52 +1947,176 @@ async def test_process_publish_check_ready_for_review( ctx.receive_event(bot, event) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( + assert_subprocess_run_calls( + mock_subprocess_run, [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "switch", "-C", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "config", "--global", "user.name", "test"], - check=True, - capture_output=True, - ), - mocker.call( - [ - "git", - "config", - "--global", - "user.email", - "test@users.noreply.github.com", - ], - check=True, - capture_output=True, - ), - mocker.call(["git", "add", "-A"], check=True, capture_output=True), - mocker.call( - ["git", "commit", "-m", ":beers: publish bot test (#80)"], - check=True, - capture_output=True, - ), - mocker.call(["git", "fetch", "origin"], check=True, capture_output=True), - mocker.call( - ["git", "diff", "origin/publish/issue80", "publish/issue80"], - check=True, - capture_output=True, - ), - mocker.call( - ["git", "push", "origin", "publish/issue80", "-f"], - check=True, - capture_output=True, - ), - ] # type: ignore + ["git", "config", "--global", "safe.directory", "*"], + ["git", "switch", "-C", "publish/issue80"], + ["git", "ls-remote", "--heads", "origin", "publish/issue80"], + ["git", "config", "--global", "user.name", "test"], + [ + "git", + "config", + "--global", + "user.email", + "test@users.noreply.github.com", + ], + ["git", "add", "-A"], + ["git", "commit", "-m", ":beers: publish bot test (#80)"], + ["git", "fetch", "origin"], + ["git", "diff", "origin/publish/issue80", "publish/issue80"], + ["git", "push", "origin", "publish/issue80", "-f"], + ], + ) + + # 检查文件是否正确 + check_json_data( + plugin_config.input_config.bot_path, + [ + { + "name": "test", + "desc": "desc", + "author_id": 1, + "homepage": "https://nonebot.dev", + "tags": [{"label": "test", "color": "#ffffff"}], + "is_official": False, + } + ], + ) + + assert mocked_api["homepage"].called + + +async def test_comment_immediate_after_pull_request_closed( + app: App, + mocker: MockerFixture, + mocked_api: MockRouter, + tmp_path: Path, + mock_installation, +) -> None: + """测试在拉取请求关闭后立即评论 + + 此时分支还没有被删除,不应该再次创建拉取请求 + """ + from src.plugins.github import plugin_config + + mock_subprocess_run = mock_subprocess_run_with_side_effect( + mocker, + {"git ls-remote --heads origin publish/issue80": "refs/heads/publish/issue80"}, + ) + + mock_issue = MockIssue(body=MockBody(type="bot", name="test").generate()).as_mock( + mocker + ) + + mock_event = mocker.MagicMock() + mock_event.issue = mock_issue + + mock_issues_resp = mocker.MagicMock() + mock_issues_resp.parsed_data = mock_issue + + mock_comment = mocker.MagicMock() + mock_comment.body = "Bot: test" + mock_list_comments_resp = mocker.MagicMock() + mock_list_comments_resp.parsed_data = [mock_comment] + + mock_pull = mocker.MagicMock() + mock_pull.title = "Bot: test" + mock_pull.draft = False + mock_pulls_resp = mocker.MagicMock() + mock_pulls_resp.parsed_data = [mock_pull] + + with open(tmp_path / "bots.json5", "w") as f: + json.dump([], f) + + check_json_data(plugin_config.input_config.bot_path, []) + + async with app.test_matcher() as ctx: + adapter, bot = get_github_bot(ctx) + event = get_mock_event(IssuesOpened) + + ctx.should_call_api( + "rest.apps.async_get_repo_installation", + {"owner": "he0119", "repo": "action-test"}, + mock_installation, + ) + ctx.should_call_api( + "rest.issues.async_get", + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + mock_issues_resp, + ) + ctx.should_call_api( + "rest.issues.async_list_comments", + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + mock_list_comments_resp, + ) + ctx.should_call_api( + "rest.issues.async_create_comment", + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "body": snapshot( + """\ +# 📃 商店发布检查结果 + +> Bot: test + +**✅ 所有测试通过,一切准备就绪!** + + +
+详情 +
  • ✅ 项目 主页 返回状态码 200。
  • ✅ 标签: test-#ffffff。
  • +
    + +--- + +💡 如需修改信息,请直接修改 issue,机器人会自动更新检查结果。 +💡 当插件加载测试失败时,请发布新版本后勾选插件测试勾选框重新运行插件测试。 + +♻️ 评论已更新至最新检查结果 + +💪 Powered by [NoneFlow](https://github.com/nonebot/noneflow) + +""" + ), + }, + True, + ) + ctx.should_call_api( + "rest.pulls.async_list", + { + "owner": "he0119", + "repo": "action-test", + "head": "he0119:publish/issue80", + }, + mock_pulls_resp, + ) + + ctx.receive_event(bot, event) + + # 测试 git 命令 + assert_subprocess_run_calls( + mock_subprocess_run, + [ + ["git", "config", "--global", "safe.directory", "*"], + ["git", "switch", "-C", "publish/issue80"], + ["git", "ls-remote", "--heads", "origin", "publish/issue80"], + ["git", "config", "--global", "user.name", "test"], + [ + "git", + "config", + "--global", + "user.email", + "test@users.noreply.github.com", + ], + ["git", "add", "-A"], + ["git", "commit", "-m", ":beers: publish bot test (#80)"], + ["git", "fetch", "origin"], + ["git", "diff", "origin/publish/issue80", "publish/issue80"], + ["git", "push", "origin", "publish/issue80", "-f"], + ], ) # 检查文件是否正确 diff --git a/tests/plugins/github/resolve/test_resolve_pull_request.py b/tests/plugins/github/resolve/test_resolve_pull_request.py index c9b80f6f..83ff7117 100644 --- a/tests/plugins/github/resolve/test_resolve_pull_request.py +++ b/tests/plugins/github/resolve/test_resolve_pull_request.py @@ -15,6 +15,7 @@ generate_issue_body_bot, generate_issue_body_remove, get_github_bot, + mock_subprocess_run_with_side_effect, should_call_apis, ) @@ -28,7 +29,7 @@ async def test_resolve_pull_request( """测试能正确处理拉取请求关闭后其他拉取请求的冲突问题""" from src.plugins.github.plugins.resolve import pr_close_matcher - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) mock_issue = MockIssue( body=generate_issue_body_remove(type="Bot"), number=76 diff --git a/tests/plugins/github/utils.py b/tests/plugins/github/utils.py index 475a9d99..fa5e3ef8 100644 --- a/tests/plugins/github/utils.py +++ b/tests/plugins/github/utils.py @@ -2,13 +2,13 @@ from dataclasses import dataclass, field from pathlib import Path from typing import Any, Literal, NotRequired, TypedDict -from unittest.mock import _Call, call +from unittest.mock import MagicMock, call import pyjson5 from githubkit.rest import Issue from nonebug.mixin.call_api import ApiContext from nonebug.mixin.process import MatcherContext -from pytest_mock import MockFixture, MockType +from pytest_mock import MockerFixture, MockFixture, MockType class GitHubApi(TypedDict): @@ -24,17 +24,38 @@ def should_call_apis( ctx.should_call_api(**api, data=data[n]) +def mock_subprocess_run_with_side_effect( + mocker: MockerFixture, commands: dict[str, str] = {} +): + def side_effect(*args, **kwargs): + command_str = " ".join(args[0]) + if command_str in commands: + value = MagicMock() + value.stdout.decode.return_value = commands[command_str] + return value + # 默认返回远程分支不存在 + elif command_str.startswith("git ls-remote --heads origin"): + value = MagicMock() + value.stdout.decode.return_value = "" + return value + return MagicMock() + + mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run.side_effect = side_effect + return mock_subprocess_run + + def assert_subprocess_run_calls(mock: MockType, commands: list[list[str]]): calls = [] for command in commands: - command_str = " ".join(command) + # command_str = " ".join(command) calls.append(call(command, check=True, capture_output=True)) # 暂时不考虑报错的情况,仅涉及到 stdout - calls.append(call().stdout.decode()) - calls.append(_Call(("().stdout.decode().__str__", (), {}))) - if command_str.startswith("git diff"): - calls.append(_Call(("().stdout.__bool__", (), {}))) + # calls.append(call().stdout.decode()) + # calls.append(_Call(("().stdout.decode().__str__", (), {}))) + # if command_str.startswith("git diff"): + # calls.append(_Call(("().stdout.__bool__", (), {}))) mock.assert_has_calls(calls)