diff --git a/CHANGELOG.md b/CHANGELOG.md index e756f6f0..df1ee851 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 + +- 修复 git 没有权限推送的问题 + ## [4.4.4] - 2025-08-10 ### Added diff --git a/src/plugins/github/depends/__init__.py b/src/plugins/github/depends/__init__.py index 88bb9588..3e8f4606 100644 --- a/src/plugins/github/depends/__init__.py +++ b/src/plugins/github/depends/__init__.py @@ -20,17 +20,6 @@ from .utils import extract_issue_number_from_ref -async def bypass_git(): - """绕过检查""" - # https://github.blog/2022-04-18-highlights-from-git-2-36/#stricter-repository-ownership-checks - run_shell_command(["git", "config", "--global", "safe.directory", "*"]) - - -async def install_pre_commit_hooks(): - """安装 pre-commit 钩子""" - run_shell_command(["pre-commit", "install", "--install-hooks"]) - - async def get_labels(event: PullRequestEvent | IssuesEvent): """获取议题或拉取请求的标签""" if isinstance(event, PullRequestClosed | PullRequestReviewSubmitted): @@ -186,3 +175,36 @@ async def is_config_workflow( return False return CONFIG_LABEL in labels + + +async def setup_git( + bot: GitHubBot, installation_id: int = Depends(get_installation_id) +): + """设置 Git 环境""" + # 绕过检查 + # https://github.blog/2022-04-18-highlights-from-git-2-36/#stricter-repository-ownership-checks + run_shell_command(["git", "config", "--global", "safe.directory", "*"]) + + token = ( + await bot.rest.apps.async_create_installation_access_token( + installation_id=installation_id + ) + ).parsed_data.token + + # 设置 GitHub App 令牌 + # https://docs.github.com/zh/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation + # https://github.com/actions/create-github-app-token/issues/204 + run_shell_command( + [ + "git", + "config", + "--global", + f"url.https://x-access-token:{token}@github.com/.insteadOf", + "https://github.com/", + ] + ) + + +# async def install_pre_commit_hooks(): +# """安装 pre-commit 钩子""" +# run_shell_command(["pre-commit", "install", "--install-hooks"]) diff --git a/src/plugins/github/plugins/config/__init__.py b/src/plugins/github/plugins/config/__init__.py index fe105db4..22be4e46 100644 --- a/src/plugins/github/plugins/config/__init__.py +++ b/src/plugins/github/plugins/config/__init__.py @@ -14,12 +14,12 @@ from src.plugins.github.constants import CONFIG_LABEL, TITLE_MAX_LENGTH from src.plugins.github.depends import ( - bypass_git, get_github_handler, get_installation_id, get_issue_handler, is_bot_triggered_workflow, is_config_workflow, + setup_git, ) from src.plugins.github.handlers import GithubHandler, IssueHandler from src.plugins.github.plugins.publish.render import render_comment @@ -62,7 +62,7 @@ async def check_rule( ) -@config_check_matcher.handle(parameterless=[Depends(bypass_git)]) +@config_check_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_remove_check( bot: GitHubBot, installation_id: int = Depends(get_installation_id), @@ -156,7 +156,7 @@ async def review_submitted_rule( auto_merge_matcher = on_type(PullRequestReviewSubmitted, rule=review_submitted_rule) -@auto_merge_matcher.handle(parameterless=[Depends(bypass_git)]) +@auto_merge_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_auto_merge( bot: GitHubBot, event: PullRequestReviewSubmitted, diff --git a/src/plugins/github/plugins/publish/__init__.py b/src/plugins/github/plugins/publish/__init__.py index d9960123..9c6d9d7d 100644 --- a/src/plugins/github/plugins/publish/__init__.py +++ b/src/plugins/github/plugins/publish/__init__.py @@ -19,7 +19,6 @@ TITLE_MAX_LENGTH, ) from src.plugins.github.depends import ( - bypass_git, get_github_handler, get_installation_id, get_issue_handler, @@ -27,6 +26,7 @@ get_related_issue_number, is_bot_triggered_workflow, is_publish_workflow, + setup_git, ) from src.plugins.github.handlers import GithubHandler, IssueHandler from src.providers.validation.models import PublishType, ValidationDict @@ -77,7 +77,7 @@ async def check_rule( ) -@publish_check_matcher.handle(parameterless=[Depends(bypass_git)]) +@publish_check_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_publish_plugin_check( bot: GitHubBot, state: T_State, @@ -109,7 +109,7 @@ async def handle_publish_plugin_check( state["validation"] = result -@publish_check_matcher.handle(parameterless=[Depends(bypass_git)]) +@publish_check_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_adapter_publish_check( bot: GitHubBot, state: T_State, @@ -129,7 +129,7 @@ async def handle_adapter_publish_check( state["validation"] = result -@publish_check_matcher.handle(parameterless=[Depends(bypass_git)]) +@publish_check_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_bot_publish_check( bot: GitHubBot, state: T_State, @@ -149,7 +149,7 @@ async def handle_bot_publish_check( state["validation"] = result -@publish_check_matcher.handle(parameterless=[Depends(bypass_git)]) +@publish_check_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_pull_request_and_update_issue( bot: GitHubBot, validation: ValidationDict = Arg(), @@ -202,7 +202,7 @@ async def pr_close_rule( pr_close_matcher = on_type(PullRequestClosed, rule=Rule(pr_close_rule)) -@pr_close_matcher.handle(parameterless=[Depends(bypass_git)]) +@pr_close_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_pr_close( event: PullRequestClosed, bot: GitHubBot, @@ -239,7 +239,7 @@ async def review_submitted_rule( ) -@auto_merge_matcher.handle(parameterless=[Depends(bypass_git)]) +@auto_merge_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_auto_merge( bot: GitHubBot, event: PullRequestReviewSubmitted, diff --git a/src/plugins/github/plugins/remove/__init__.py b/src/plugins/github/plugins/remove/__init__.py index 7f8a5521..66f7432d 100644 --- a/src/plugins/github/plugins/remove/__init__.py +++ b/src/plugins/github/plugins/remove/__init__.py @@ -13,7 +13,6 @@ from src.plugins.github import plugin_config from src.plugins.github.constants import TITLE_MAX_LENGTH from src.plugins.github.depends import ( - bypass_git, get_github_handler, get_installation_id, get_issue_handler, @@ -21,6 +20,7 @@ get_type_by_labels_name, is_bot_triggered_workflow, is_remove_workflow, + setup_git, ) from src.plugins.github.handlers import GithubHandler, IssueHandler from src.plugins.github.typing import IssuesEvent @@ -69,7 +69,7 @@ async def check_rule( ) -@remove_check_matcher.handle(parameterless=[Depends(bypass_git)]) +@remove_check_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_remove_check( bot: GitHubBot, installation_id: int = Depends(get_installation_id), @@ -136,7 +136,7 @@ async def review_submitted_rule( auto_merge_matcher = on_type(PullRequestReviewSubmitted, rule=review_submitted_rule) -@auto_merge_matcher.handle(parameterless=[Depends(bypass_git)]) +@auto_merge_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_auto_merge( bot: GitHubBot, event: PullRequestReviewSubmitted, diff --git a/src/plugins/github/plugins/resolve/__init__.py b/src/plugins/github/plugins/resolve/__init__.py index 36a2150e..c4c8ad9d 100644 --- a/src/plugins/github/plugins/resolve/__init__.py +++ b/src/plugins/github/plugins/resolve/__init__.py @@ -4,11 +4,11 @@ from src.plugins.github.constants import PUBLISH_LABEL, REMOVE_LABEL from src.plugins.github.depends import ( - bypass_git, get_installation_id, get_related_issue_handler, get_related_issue_number, get_type_by_labels_name, + setup_git, ) from src.plugins.github.handlers import GithubHandler, IssueHandler from src.plugins.github.plugins.publish.utils import ( @@ -57,7 +57,7 @@ async def pr_close_rule( pr_close_matcher = on_type(PullRequestClosed, rule=pr_close_rule, priority=10) -@pr_close_matcher.handle(parameterless=[Depends(bypass_git)]) +@pr_close_matcher.handle(parameterless=[Depends(setup_git)]) async def handle_pr_close( event: PullRequestClosed, bot: GitHubBot, diff --git a/tests/plugins/github/config/process/test_config_auto_merge.py b/tests/plugins/github/config/process/test_config_auto_merge.py index d86a602c..583d940a 100644 --- a/tests/plugins/github/config/process/test_config_auto_merge.py +++ b/tests/plugins/github/config/process/test_config_auto_merge.py @@ -3,7 +3,12 @@ from pytest_mock import MockerFixture from tests.plugins.github.event import get_mock_event -from tests.plugins.github.utils import get_github_bot +from tests.plugins.github.utils import ( + assert_subprocess_run_calls, + get_github_bot, + mock_subprocess_run_with_side_effect, + should_call_apis, +) def get_issue_labels(labels: list[str]): @@ -28,7 +33,7 @@ def get_issue_labels(labels: list[str]): async def test_config_auto_merge( - app: App, mocker: MockerFixture, mock_installation + app: App, mocker: MockerFixture, mock_installation, mock_installation_token ) -> None: """测试审查后自动合并 @@ -36,42 +41,48 @@ async def test_config_auto_merge( """ from src.plugins.github.plugins.config import auto_merge_matcher - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) async with app.test_matcher() as ctx: adapter, bot = get_github_bot(ctx) event = get_mock_event(PullRequestReviewSubmitted) event.payload.pull_request.labels = get_issue_labels(["Config", "Plugin"]) - ctx.should_call_api( - "rest.apps.async_get_repo_installation", - {"owner": "he0119", "repo": "action-test"}, - mock_installation, - ) - ctx.should_call_api( - "rest.pulls.async_merge", - { - "owner": "he0119", - "repo": "action-test", - "pull_number": 100, - "merge_method": "rebase", - }, - True, + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.pulls.async_merge", + "result": True, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + { + "owner": "he0119", + "repo": "action-test", + "pull_number": 100, + "merge_method": "rebase", + }, + ], ) ctx.receive_event(bot, event) ctx.should_pass_rule(auto_merge_matcher) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( - [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), # type: ignore - ], - any_order=True, + assert_subprocess_run_calls( + mock_subprocess_run, + [["git", "config", "--global", "safe.directory", "*"]], ) @@ -82,7 +93,7 @@ async def test_auto_merge_not_remove(app: App, mocker: MockerFixture) -> None: """ from src.plugins.github.plugins.config import auto_merge_matcher - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) async with app.test_matcher() as ctx: adapter, bot = get_github_bot(ctx) @@ -103,7 +114,7 @@ async def test_auto_merge_not_member(app: App, mocker: MockerFixture) -> None: """ from src.plugins.github.plugins.config import auto_merge_matcher - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) async with app.test_matcher() as ctx: adapter, bot = get_github_bot(ctx) @@ -125,7 +136,7 @@ async def test_auto_merge_not_approve(app: App, mocker: MockerFixture) -> None: """ from src.plugins.github.plugins.config import auto_merge_matcher - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) async with app.test_matcher() as ctx: adapter, bot = get_github_bot(ctx) diff --git a/tests/plugins/github/config/process/test_config_check.py b/tests/plugins/github/config/process/test_config_check.py index 4c14ed4c..46c98ea7 100644 --- a/tests/plugins/github/config/process/test_config_check.py +++ b/tests/plugins/github/config/process/test_config_check.py @@ -31,6 +31,7 @@ async def test_process_config_check( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, mock_results: dict[str, Path], ) -> None: """测试发布检查不通过""" @@ -118,6 +119,10 @@ async def test_process_config_check( "api": "rest.apps.async_get_repo_installation", "result": mock_installation, }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, { "api": "rest.issues.async_get", "result": mock_issues_resp, @@ -155,6 +160,7 @@ async def test_process_config_check( # 对应的 API 数据 api_data = [ {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, snapshot( { @@ -282,6 +288,13 @@ async def test_process_config_check( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "fetch", "origin", "results"], ["git", "checkout", "results"], ["git", "switch", "-C", "config/issue80"], diff --git a/tests/plugins/github/config/process/test_config_pull_request.py b/tests/plugins/github/config/process/test_config_pull_request.py index 704b0863..481ba608 100644 --- a/tests/plugins/github/config/process/test_config_pull_request.py +++ b/tests/plugins/github/config/process/test_config_pull_request.py @@ -9,15 +9,21 @@ from tests.plugins.github.resolve.utils import get_pr_labels from tests.plugins.github.utils import ( MockIssue, + assert_subprocess_run_calls, get_github_bot, + mock_subprocess_run_with_side_effect, + should_call_apis, ) async def test_config_process_pull_request( - app: App, mocker: MockerFixture, mock_installation: MagicMock + app: App, + mocker: MockerFixture, + mock_installation: MagicMock, + mock_installation_token: MagicMock, ) -> None: """配置流程的拉取请求关闭流程""" - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) mock_issue = MockIssue(body=generate_issue_body()).as_mock(mocker) @@ -39,56 +45,67 @@ async def test_config_process_pull_request( event.payload.pull_request.labels = get_pr_labels(["Config", "Plugin"]) event.payload.pull_request.merged = True - 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": 76}, - mock_issues_resp, - ) - ctx.should_call_api( - "rest.issues.async_update", - { - "owner": "he0119", - "repo": "action-test", - "issue_number": 80, - "state": "closed", - "state_reason": "completed", - }, - True, - ) - ctx.should_call_api( - "rest.pulls.async_list", - {"owner": "he0119", "repo": "action-test", "state": "open"}, - mock_pulls_resp, + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_update", + "result": True, + }, + { + "api": "rest.pulls.async_list", + "result": mock_pulls_resp, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 76}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "state": "closed", + "state_reason": "completed", + }, + {"owner": "he0119", "repo": "action-test", "state": "open"}, + ], ) 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", "push", "origin", "--delete", "publish/issue76"], - check=True, - capture_output=True, - ), - ], # type: ignore - any_order=True, + ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], + ["git", "push", "origin", "--delete", "publish/issue76"], + ], ) async def test_not_config(app: App, mocker: MockerFixture) -> None: """测试与配置无关的拉取请求""" - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) async with app.test_matcher() as ctx: adapter, bot = get_github_bot(ctx) @@ -102,10 +119,10 @@ async def test_not_config(app: App, mocker: MockerFixture) -> None: async def test_process_config_pull_request_not_merged( - app: App, mocker: MockerFixture, mock_installation + app: App, mocker: MockerFixture, mock_installation, mock_installation_token ) -> None: """删除掉不合并的分支""" - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) mock_issue = MockIssue(body=generate_issue_body()).as_mock(mocker) mock_issues_resp = mocker.MagicMock() @@ -116,43 +133,54 @@ async def test_process_config_pull_request_not_merged( event = get_mock_event(PullRequestClosed) event.payload.pull_request.labels = get_pr_labels(["Config", "Plugin"]) - 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": 76}, - mock_issues_resp, - ) - ctx.should_call_api( - "rest.issues.async_update", - { - "owner": "he0119", - "repo": "action-test", - "issue_number": 80, - "state": "closed", - "state_reason": "not_planned", - }, - True, + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_update", + "result": True, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 76}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "state": "closed", + "state_reason": "not_planned", + }, + ], ) 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", "push", "origin", "--delete", "publish/issue76"], - check=True, - capture_output=True, - ), - ], # type: ignore - any_order=True, + ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], + ["git", "push", "origin", "--delete", "publish/issue76"], + ], ) diff --git a/tests/plugins/github/conftest.py b/tests/plugins/github/conftest.py index 3fce27c2..4bc78959 100644 --- a/tests/plugins/github/conftest.py +++ b/tests/plugins/github/conftest.py @@ -9,3 +9,12 @@ def mock_installation(mocker: MockerFixture): mock_installation_resp = mocker.MagicMock() mock_installation_resp.parsed_data = mock_installation return mock_installation_resp + + +@pytest.fixture +def mock_installation_token(mocker: MockerFixture): + mock_token = mocker.MagicMock() + mock_token.token = "test-token" + mock_token_resp = mocker.MagicMock() + mock_token_resp.parsed_data = mock_token + return mock_token_resp diff --git a/tests/plugins/github/handlers/test_github_handler.py b/tests/plugins/github/handlers/test_github_handler.py index 4bb7694d..0c28abe6 100644 --- a/tests/plugins/github/handlers/test_github_handler.py +++ b/tests/plugins/github/handlers/test_github_handler.py @@ -26,14 +26,14 @@ async def test_update_issue_title(app: App) -> None: GitHubApi(api="rest.issues.async_update", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, "title": "new title", }, - } + ] ), ) await github_handler.update_issue_title("new title", 76) @@ -58,14 +58,14 @@ async def test_update_issue_body(app: App) -> None: GitHubApi(api="rest.issues.async_update", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, "body": "new body", }, - } + ] ), ) await github_handler.update_issue_body("new body", 76) @@ -90,14 +90,14 @@ async def test_create_dispatch_event(app: App) -> None: GitHubApi(api="rest.repos.async_create_dispatch_event", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "event_type": "event", "client_payload": {"key": "value"}, }, - } + ] ), ) await github_handler.create_dispatch_event("event", {"key": "value"}) @@ -127,13 +127,13 @@ async def test_list_comments(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - } + ] ), ) await github_handler.list_comments(76) @@ -158,14 +158,14 @@ async def test_create_comment(app: App) -> None: GitHubApi(api="rest.issues.async_create_comment", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, "body": "new comment", }, - } + ] ), ) await github_handler.create_comment("new comment", 76) @@ -190,14 +190,14 @@ async def test_update_comment(app: App) -> None: GitHubApi(api="rest.issues.async_update_comment", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "comment_id": 123, "body": "updated comment", }, - } + ] ), ) await github_handler.update_comment(123, "updated comment") @@ -228,15 +228,15 @@ async def test_comment_issue(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_create_comment", result=True), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 76}, - 1: { + [ + {"owner": "owner", "repo": "repo", "issue_number": 76}, + { "owner": "owner", "repo": "repo", "issue_number": 76, "body": "new comment", }, - } + ] ), ) await github_handler.resuable_comment_issue("new comment", 76) @@ -270,15 +270,15 @@ async def test_comment_issue_reuse(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_update_comment", result=True), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 76}, - 1: { + [ + {"owner": "owner", "repo": "repo", "issue_number": 76}, + { "owner": "owner", "repo": "repo", "comment_id": 123, "body": "new comment", }, - } + ] ), ) await github_handler.resuable_comment_issue("new comment", 76) @@ -311,9 +311,9 @@ async def test_comment_issue_reuse_no_change(app: App, mocker: MockerFixture) -> ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 76}, - } + [ + {"owner": "owner", "repo": "repo", "issue_number": 76}, + ] ), ) await github_handler.resuable_comment_issue("comment\n", 76) @@ -351,9 +351,9 @@ async def test_get_pull_requests_by_label(app: App, mocker: MockerFixture) -> No GitHubApi(api="rest.pulls.async_list", result=mock_pulls_resp), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "state": "open"}, - } + [ + {"owner": "owner", "repo": "repo", "state": "open"}, + ] ), ) pulls = await github_handler.get_pull_requests_by_label("Plugin") @@ -383,9 +383,9 @@ async def test_get_pull_request_by_branch(app: App, mocker: MockerFixture) -> No GitHubApi(api="rest.pulls.async_list", result=mock_pulls_resp), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "head": "owner:branch"}, - } + [ + {"owner": "owner", "repo": "repo", "head": "owner:branch"}, + ] ), ) pull = await github_handler.get_pull_request_by_branch("branch") @@ -416,9 +416,9 @@ async def test_get_pull_request_by_branch_empty( GitHubApi(api="rest.pulls.async_list", result=mock_pulls_resp), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "head": "owner:branch"}, - } + [ + {"owner": "owner", "repo": "repo", "head": "owner:branch"}, + ] ), ) with pytest.raises(ValueError, match="找不到分支 branch 对应的拉取请求"): @@ -451,9 +451,9 @@ async def test_get_pull_request(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "pull_number": 123}, - } + [ + {"owner": "owner", "repo": "repo", "pull_number": 123}, + ] ), ) pull = await github_handler.get_pull_request(123) @@ -489,9 +489,9 @@ async def test_draft_pull_request(app: App, mocker: MockerFixture) -> None: GitHubApi(api="async_graphql", result=None), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "head": "owner:branch"}, - 1: { + [ + {"owner": "owner", "repo": "repo", "head": "owner:branch"}, + { "query": """\ mutation convertPullRequestToDraft($pullRequestId: ID!) { convertPullRequestToDraft(input: {pullRequestId: $pullRequestId}) { @@ -503,7 +503,7 @@ async def test_draft_pull_request(app: App, mocker: MockerFixture) -> None: "pullRequestId": 123, }, }, - } + ] ), ) await github_handler.draft_pull_request("branch") @@ -534,9 +534,9 @@ async def test_draft_pull_request_no_pr(app: App, mocker: MockerFixture) -> None ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "head": "owner:branch"}, - } + [ + {"owner": "owner", "repo": "repo", "head": "owner:branch"}, + ] ), ) await github_handler.draft_pull_request("branch") @@ -569,9 +569,9 @@ async def test_draft_pull_request_drafted(app: App, mocker: MockerFixture) -> No ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "head": "owner:branch"}, - } + [ + {"owner": "owner", "repo": "repo", "head": "owner:branch"}, + ] ), ) await github_handler.draft_pull_request("branch") @@ -603,14 +603,14 @@ async def test_merge_pull_request(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "pull_number": 123, "merge_method": "rebase", }, - } + ] ), ) await github_handler.merge_pull_request(123, "rebase") @@ -645,15 +645,15 @@ async def test_update_pull_request_status(app: App, mocker: MockerFixture) -> No GitHubApi(api="async_graphql", result=None), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "head": "owner:branch"}, - 1: { + [ + {"owner": "owner", "repo": "repo", "head": "owner:branch"}, + { "owner": "owner", "repo": "repo", "pull_number": 111, "title": "new title", }, - 2: { + { "query": """\ mutation markPullRequestReadyForReview($pullRequestId: ID!) { markPullRequestReadyForReview(input: {pullRequestId: $pullRequestId}) { @@ -663,7 +663,7 @@ async def test_update_pull_request_status(app: App, mocker: MockerFixture) -> No """, "variables": {"pullRequestId": 222}, }, - } + ] ), ) await github_handler.update_pull_request_status("new title", "branch") @@ -692,8 +692,8 @@ async def test_create_pull_request(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.pulls.async_create", result=mock_pull_resp), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "title": "new title", @@ -701,7 +701,7 @@ async def test_create_pull_request(app: App, mocker: MockerFixture) -> None: "base": "main", "head": "branch", }, - } + ] ), ) number = await github_handler.create_pull_request( @@ -729,14 +729,14 @@ async def test_add_labels(app: App) -> None: GitHubApi(api="rest.issues.async_add_labels", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, "labels": ["Publish", "Plugin"], }, - } + ] ), ) await github_handler.add_labels(76, ["Publish", "Plugin"]) @@ -761,8 +761,8 @@ async def test_ready_pull_request(app: App) -> None: GitHubApi(api="async_graphql", result=None), ], snapshot( - { - 0: { + [ + { "query": """\ mutation markPullRequestReadyForReview($pullRequestId: ID!) { markPullRequestReadyForReview(input: {pullRequestId: $pullRequestId}) { @@ -772,7 +772,7 @@ async def test_ready_pull_request(app: App) -> None: """, "variables": {"pullRequestId": "node_id"}, }, - } + ] ), ) await github_handler.ready_pull_request("node_id") @@ -797,14 +797,14 @@ async def test_update_pull_request_title(app: App) -> None: GitHubApi(api="rest.pulls.async_update", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "pull_number": 123, "title": "new title", }, - } + ] ), ) await github_handler.update_pull_request_title("new title", 123) @@ -834,9 +834,9 @@ async def test_get_user_name(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.users.async_get_by_id", result=mock_user_resp), ], snapshot( - { - 0: {"account_id": 1}, - } + [ + {"account_id": 1}, + ] ), ) await github_handler.get_user_name(1) @@ -868,9 +868,9 @@ async def test_get_user_id(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: {"username": "name"}, - } + [ + {"username": "name"}, + ] ), ) await github_handler.get_user_id("name") @@ -899,9 +899,9 @@ async def test_get_issue(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_get", result=mock_issue_resp), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 123}, - } + [ + {"owner": "owner", "repo": "repo", "issue_number": 123}, + ] ), ) issue = await github_handler.get_issue(123) @@ -927,15 +927,15 @@ async def test_close_issue(app: App) -> None: GitHubApi(api="rest.issues.async_update", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 123, "state": "closed", "state_reason": "completed", }, - } + ] ), ) await github_handler.close_issue("completed", 123) @@ -965,9 +965,9 @@ async def test_to_issue_handler(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_get", result=mock_issue_resp), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 123}, - } + [ + {"owner": "owner", "repo": "repo", "issue_number": 123}, + ] ), ) issue_handler = await github_handler.to_issue_handler(123) @@ -1007,13 +1007,13 @@ async def test_get_self_comment(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - } + ] ), ) comment = await github_handler.get_self_comment(76) @@ -1048,13 +1048,13 @@ async def test_get_self_comment_not_found(app: App, mocker: MockerFixture) -> No ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - } + ] ), ) comment = await github_handler.get_self_comment(76) @@ -1080,14 +1080,14 @@ async def test_comment_issue_new(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_create_comment", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, "body": "new comment", }, - } + ] ), ) await github_handler.comment_issue("new comment", 76) @@ -1116,14 +1116,14 @@ async def test_comment_issue_update(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_update_comment", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "comment_id": 123, "body": "new comment", }, - } + ] ), ) await github_handler.comment_issue("new comment", 76, mock_comment) @@ -1175,14 +1175,14 @@ async def test_download_artifact(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "artifact_id": 123, "archive_format": "zip", }, - } + ] ), ) @@ -1220,14 +1220,14 @@ async def test_download_artifact_with_custom_repo( ), ], snapshot( - { - 0: { + [ + { "owner": "custom_owner", "repo": "custom_repo", "artifact_id": 456, "archive_format": "zip", }, - } + ] ), ) diff --git a/tests/plugins/github/handlers/test_issue_handler.py b/tests/plugins/github/handlers/test_issue_handler.py index cf2155f6..c33f73e8 100644 --- a/tests/plugins/github/handlers/test_issue_handler.py +++ b/tests/plugins/github/handlers/test_issue_handler.py @@ -62,14 +62,14 @@ async def test_update_issue_title(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_update", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, "title": "new title", }, - } + ] ), ) await issue_handler.update_issue_title("new title") @@ -102,14 +102,14 @@ async def test_update_issue_body(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_update", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, "body": "new body", }, - } + ] ), ) await issue_handler.update_issue_body("new body") @@ -142,15 +142,15 @@ async def test_close_issue(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_update", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 123, "state": "closed", "state_reason": "completed", }, - } + ] ), ) await issue_handler.close_issue("completed") @@ -183,8 +183,8 @@ async def test_create_pull_request(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.pulls.async_create", result=mock_pull_resp), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "title": "new title", @@ -192,7 +192,7 @@ async def test_create_pull_request(app: App, mocker: MockerFixture) -> None: "base": "main", "head": "branch", }, - } + ] ), ) number = await issue_handler.create_pull_request("main", "new title", "branch") @@ -227,13 +227,13 @@ async def test_list_comments(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - } + ] ), ) await issue_handler.list_comments() @@ -268,15 +268,15 @@ async def test_comment_issue(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_create_comment", result=True), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 76}, - 1: { + [ + {"owner": "owner", "repo": "repo", "issue_number": 76}, + { "owner": "owner", "repo": "repo", "issue_number": 76, "body": "new comment", }, - } + ] ), ) await issue_handler.resuable_comment_issue("new comment") @@ -310,9 +310,9 @@ async def test_should_skip_test(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 76}, - } + [ + {"owner": "owner", "repo": "repo", "issue_number": 76}, + ] ), ) assert await issue_handler.should_skip_test() is False @@ -350,9 +350,9 @@ async def test_should_skip_test_true(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 76}, - } + [ + {"owner": "owner", "repo": "repo", "issue_number": 76}, + ] ), ) assert await issue_handler.should_skip_test() is True @@ -390,9 +390,9 @@ async def test_should_skip_test_not_admin(app: App, mocker: MockerFixture) -> No ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 76}, - } + [ + {"owner": "owner", "repo": "repo", "issue_number": 76}, + ] ), ) assert await issue_handler.should_skip_test() is False @@ -480,9 +480,9 @@ async def test_get_self_comment(app: App, mocker: MockerFixture) -> None: ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 76}, - } + [ + {"owner": "owner", "repo": "repo", "issue_number": 76}, + ] ), ) comment = await issue_handler.get_self_comment() @@ -520,9 +520,9 @@ async def test_get_self_comment_not_found(app: App, mocker: MockerFixture) -> No ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 76}, - } + [ + {"owner": "owner", "repo": "repo", "issue_number": 76}, + ] ), ) comment = await issue_handler.get_self_comment() @@ -552,14 +552,14 @@ async def test_comment_issue_new(app: App, mocker: MockerFixture) -> None: GitHubApi(api="rest.issues.async_create_comment", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, "body": "test comment", }, - } + ] ), ) await issue_handler.comment_issue("test comment") @@ -592,14 +592,14 @@ async def test_comment_issue_update_existing(app: App, mocker: MockerFixture) -> GitHubApi(api="rest.issues.async_update_comment", result=True), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "comment_id": 123, "body": "updated comment", }, - } + ] ), ) await issue_handler.comment_issue("updated comment", self_comment=mock_comment) diff --git a/tests/plugins/github/publish/process/test_auto_merge.py b/tests/plugins/github/publish/process/test_auto_merge.py index 60863e86..e5341a46 100644 --- a/tests/plugins/github/publish/process/test_auto_merge.py +++ b/tests/plugins/github/publish/process/test_auto_merge.py @@ -3,10 +3,17 @@ from pytest_mock import MockerFixture from tests.plugins.github.event import get_mock_event -from tests.plugins.github.utils import get_github_bot +from tests.plugins.github.utils import ( + GitHubApi, + assert_subprocess_run_calls, + get_github_bot, + should_call_apis, +) -async def test_auto_merge(app: App, mocker: MockerFixture, mock_installation) -> None: +async def test_auto_merge( + app: App, mocker: MockerFixture, mock_installation, mock_installation_token +) -> None: """测试审查后自动合并 可直接合并的情况 @@ -24,35 +31,38 @@ async def test_auto_merge(app: App, mocker: MockerFixture, mock_installation) -> adapter, bot = get_github_bot(ctx) event = get_mock_event(PullRequestReviewSubmitted) - ctx.should_call_api( - "rest.apps.async_get_repo_installation", - {"owner": "he0119", "repo": "action-test"}, - mock_installation, - ) - ctx.should_call_api( - "rest.pulls.async_merge", - { - "owner": "he0119", - "repo": "action-test", - "pull_number": 100, - "merge_method": "rebase", - }, - True, + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.apps.async_get_repo_installation", + result=mock_installation, + ), + GitHubApi( + api="rest.apps.async_create_installation_access_token", + result=mock_installation_token, + ), + GitHubApi(api="rest.pulls.async_merge", result=True), + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + { + "owner": "he0119", + "repo": "action-test", + "pull_number": 100, + "merge_method": "rebase", + }, + ], ) ctx.receive_event(bot, event) ctx.should_pass_rule(auto_merge_matcher) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( - [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), # type: ignore - ], - any_order=True, + assert_subprocess_run_calls( + mock_subprocess_run, + [["git", "config", "--global", "safe.directory", "*"]], ) diff --git a/tests/plugins/github/publish/process/test_publish_check.py b/tests/plugins/github/publish/process/test_publish_check.py index 124e429b..c4227001 100644 --- a/tests/plugins/github/publish/process/test_publish_check.py +++ b/tests/plugins/github/publish/process/test_publish_check.py @@ -20,6 +20,7 @@ get_github_bot, get_issue_labels, mock_subprocess_run_with_side_effect, + should_call_apis, ) @@ -29,6 +30,7 @@ async def test_bot_process_publish_check( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试机器人的发布流程""" from src.plugins.github import plugin_config @@ -64,29 +66,49 @@ async def test_bot_process_publish_check( 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( - """\ + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_list_comments", + "result": mock_list_comments_resp, + }, + { + "api": "rest.issues.async_create_comment", + "result": True, + }, + { + "api": "rest.pulls.async_create", + "result": mock_pulls_resp, + }, + { + "api": "rest.issues.async_add_labels", + "result": True, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "body": snapshot( + """\ # 📃 商店发布检查结果 > Bot: test @@ -115,31 +137,23 @@ async def test_bot_process_publish_check( 💪 Powered by [NoneFlow](https://github.com/nonebot/noneflow) """ - ), - }, - True, - ) - ctx.should_call_api( - "rest.pulls.async_create", - { - "owner": "he0119", - "repo": "action-test", - "title": "Bot: test", - "body": "resolve #80", - "base": "master", - "head": "publish/issue80", - }, - mock_pulls_resp, - ) - ctx.should_call_api( - "rest.issues.async_add_labels", - { - "owner": "he0119", - "repo": "action-test", - "issue_number": 2, - "labels": ["Publish", "Bot"], - }, - True, + ), + }, + { + "owner": "he0119", + "repo": "action-test", + "title": "Bot: test", + "body": "resolve #80", + "base": "master", + "head": "publish/issue80", + }, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 2, + "labels": ["Publish", "Bot"], + }, + ], ) ctx.receive_event(bot, event) @@ -149,6 +163,13 @@ async def test_bot_process_publish_check( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "switch", "-C", "publish/issue80"], ["git", "add", str(tmp_path / "bots.json5")], ["git", "ls-remote", "--heads", "origin", "publish/issue80"], @@ -191,6 +212,7 @@ async def test_adapter_process_publish_check( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试适配器的发布流程""" from src.plugins.github import plugin_config @@ -227,29 +249,53 @@ async def test_adapter_process_publish_check( event = get_mock_event(IssuesOpened) event.payload.issue.labels = get_issue_labels(["Adapter", "Publish"]) - 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( - """\ + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_list_comments", + "result": mock_list_comments_resp, + }, + { + "api": "rest.issues.async_create_comment", + "result": True, + }, + { + "api": "rest.issues.async_update", + "result": True, + }, + { + "api": "rest.pulls.async_create", + "result": mock_pulls_resp, + }, + { + "api": "rest.issues.async_add_labels", + "result": True, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "body": snapshot( + """\ # 📃 商店发布检查结果 > Adapter: test @@ -278,43 +324,31 @@ async def test_adapter_process_publish_check( 💪 Powered by [NoneFlow](https://github.com/nonebot/noneflow) """ + ), + }, + snapshot( + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "title": "Adapter: test", + } ), - }, - True, - ) - ctx.should_call_api( - "rest.issues.async_update", - snapshot( { "owner": "he0119", "repo": "action-test", - "issue_number": 80, - "title": "Adapter: test", - } - ), - True, - ) - ctx.should_call_api( - "rest.pulls.async_create", - { - "owner": "he0119", - "repo": "action-test", - "title": snapshot("Adapter: test"), - "body": "resolve #80", - "base": "master", - "head": "publish/issue80", - }, - mock_pulls_resp, - ) - ctx.should_call_api( - "rest.issues.async_add_labels", - { - "owner": "he0119", - "repo": "action-test", - "issue_number": 2, - "labels": ["Publish", "Adapter"], - }, - True, + "title": snapshot("Adapter: test"), + "body": "resolve #80", + "base": "master", + "head": "publish/issue80", + }, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 2, + "labels": ["Publish", "Adapter"], + }, + ], ) ctx.receive_event(bot, event) @@ -324,6 +358,13 @@ async def test_adapter_process_publish_check( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "switch", "-C", "publish/issue80"], ["git", "add", str(tmp_path / "adapters.json5")], ["git", "ls-remote", "--heads", "origin", "publish/issue80"], @@ -370,6 +411,7 @@ async def test_edit_title( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试编辑标题 @@ -409,29 +451,62 @@ async def test_edit_title( 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( - """\ + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_list_comments", + "result": mock_list_comments_resp, + }, + { + "api": "rest.issues.async_create_comment", + "result": True, + }, + { + "api": "rest.issues.async_update", + "result": True, + }, + { + "api": "rest.pulls.async_create", + "exception": RequestFailed( + Response( + httpx.Response(422, request=httpx.Request("test", "test")), + None, # type: ignore + ) + ), + }, + { + "api": "rest.pulls.async_list", + "result": mock_pulls_resp, + }, + { + "api": "rest.pulls.async_update", + "result": True, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "body": snapshot( + """\ # 📃 商店发布检查结果 > Bot: test1 @@ -460,57 +535,34 @@ async def test_edit_title( 💪 Powered by [NoneFlow](https://github.com/nonebot/noneflow) """ - ), - }, - True, - ) - # 修改议题标题 - ctx.should_call_api( - "rest.issues.async_update", - { - "owner": "he0119", - "repo": "action-test", - "issue_number": 80, - "title": "Bot: test1", - }, - True, - ) - ctx.should_call_api( - "rest.pulls.async_create", - { - "owner": "he0119", - "repo": "action-test", - "title": "Bot: test1", - "body": "resolve #80", - "base": "master", - "head": "publish/issue80", - }, - exception=RequestFailed( - Response( - httpx.Response(422, request=httpx.Request("test", "test")), - None, # type: ignore - ) - ), - ) - ctx.should_call_api( - "rest.pulls.async_list", - { - "owner": "he0119", - "repo": "action-test", - "head": "he0119:publish/issue80", - }, - mock_pulls_resp, - ) - # 修改拉取请求标题 - ctx.should_call_api( - "rest.pulls.async_update", - { - "owner": "he0119", - "repo": "action-test", - "pull_number": 2, - "title": "Bot: test1", - }, - True, + ), + }, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "title": "Bot: test1", + }, + { + "owner": "he0119", + "repo": "action-test", + "title": "Bot: test1", + "body": "resolve #80", + "base": "master", + "head": "publish/issue80", + }, + { + "owner": "he0119", + "repo": "action-test", + "head": "he0119:publish/issue80", + }, + { + "owner": "he0119", + "repo": "action-test", + "pull_number": 2, + "title": "Bot: test1", + }, + ], ) ctx.receive_event(bot, event) @@ -520,6 +572,13 @@ async def test_edit_title( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "switch", "-C", "publish/issue80"], ["git", "add", str(tmp_path / "bots.json5")], ["git", "ls-remote", "--heads", "origin", "publish/issue80"], @@ -562,6 +621,7 @@ async def test_edit_title_too_long( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试编辑标题 @@ -602,29 +662,49 @@ async def test_edit_title_too_long( 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( - """\ + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_list_comments", + "result": mock_list_comments_resp, + }, + { + "api": "rest.issues.async_create_comment", + "result": True, + }, + { + "api": "rest.issues.async_update", + "result": True, + }, + { + "api": "rest.pulls.async_list", + "result": mock_pulls_resp, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "body": snapshot( + """\ # 📃 商店发布检查结果 > Bot: looooooooooooooooooooooooooooooooooooooooooooooooooooooong @@ -654,42 +734,28 @@ async def test_edit_title_too_long( 💪 Powered by [NoneFlow](https://github.com/nonebot/noneflow) """ - ), - }, - True, - ) - # 修改标题,应该被截断,且不会更新拉取请求的标题 - ctx.should_call_api( - "rest.issues.async_update", - { - "owner": "he0119", - "repo": "action-test", - "issue_number": 80, - "title": "Bot: looooooooooooooooooooooooooooooooooooooooooooooooo", - }, - True, - ) - ctx.should_call_api( - "rest.pulls.async_list", - { - "owner": "he0119", - "repo": "action-test", - "head": "he0119:publish/issue80", - }, - mock_pulls_resp, + ), + }, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "title": "Bot: looooooooooooooooooooooooooooooooooooooooooooooooo", + }, + { + "owner": "he0119", + "repo": "action-test", + "head": "he0119:publish/issue80", + }, + ], ) ctx.receive_event(bot, event) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( - [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ) # type: ignore - ] + assert_subprocess_run_calls( + mock_subprocess_run, + [["git", "config", "--global", "safe.directory", "*"]], ) # 检查文件是否正确 @@ -704,6 +770,7 @@ async def test_process_publish_check_not_pass( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试发布检查不通过""" from src.plugins.github import plugin_config @@ -736,31 +803,45 @@ async def test_process_publish_check_not_pass( 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( - """\ + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_list_comments", + "result": mock_list_comments_resp, + }, + { + "api": "rest.issues.async_create_comment", + "result": True, + }, + { + "api": "rest.pulls.async_list", + "result": mock_pulls_resp, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "body": snapshot( + """\ # 📃 商店发布检查结果 > Bot: test @@ -788,31 +869,22 @@ async def test_process_publish_check_not_pass( 💪 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, + ), + }, + { + "owner": "he0119", + "repo": "action-test", + "head": "he0119:publish/issue80", + }, + ], ) ctx.receive_event(bot, event) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( - [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), # type: ignore - ] + assert_subprocess_run_calls( + mock_subprocess_run, + [["git", "config", "--global", "safe.directory", "*"]], ) # 检查文件是否正确 @@ -843,7 +915,11 @@ async def test_comment_at_pull_request( async def test_issue_state_closed( - app: App, mocker: MockerFixture, mocked_api: MockRouter, mock_installation + app: App, + mocker: MockerFixture, + mocked_api: MockRouter, + mock_installation, + mock_installation_token, ) -> None: """测试议题已关闭 @@ -861,28 +937,35 @@ async def test_issue_state_closed( 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, + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + ], ) ctx.receive_event(bot, event) assert mocked_api.calls == [] - mock_subprocess_run.assert_has_calls( - [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), # type: ignore - ] + assert_subprocess_run_calls( + mock_subprocess_run, + [["git", "config", "--global", "safe.directory", "*"]], ) @@ -930,6 +1013,7 @@ async def test_convert_pull_request_to_draft( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """未通过时将拉取请求转换为草稿""" from src.plugins.github import plugin_config @@ -968,30 +1052,49 @@ async def test_convert_pull_request_to_draft( 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( - """\ + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_list_comments", + "result": mock_list_comments_resp, + }, + { + "api": "rest.issues.async_create_comment", + "result": True, + }, + { + "api": "rest.pulls.async_list", + "result": mock_pulls_resp, + }, + { + "api": "async_graphql", + "result": True, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "body": snapshot( + """\ # 📃 商店发布检查结果 > Bot: test @@ -1019,40 +1122,26 @@ async def test_convert_pull_request_to_draft( 💪 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.should_call_api( - "async_graphql", - { - "query": "mutation convertPullRequestToDraft($pullRequestId: ID!) {\n convertPullRequestToDraft(input: {pullRequestId: $pullRequestId}) {\n clientMutationId\n }\n }", - "variables": {"pullRequestId": "123"}, - }, - True, + ), + }, + { + "owner": "he0119", + "repo": "action-test", + "head": "he0119:publish/issue80", + }, + { + "query": "mutation convertPullRequestToDraft($pullRequestId: ID!) {\n convertPullRequestToDraft(input: {pullRequestId: $pullRequestId}) {\n clientMutationId\n }\n }", + "variables": {"pullRequestId": "123"}, + }, + ], ) ctx.receive_event(bot, event) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( - [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), # type: ignore - ] + assert_subprocess_run_calls( + mock_subprocess_run, + [["git", "config", "--global", "safe.directory", "*"]], ) # 检查文件是否正确 @@ -1067,6 +1156,7 @@ async def test_process_publish_check_ready_for_review( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """当之前失败后再次通过测试时,应该将拉取请求标记为 ready for review""" from src.plugins.github import plugin_config @@ -1105,29 +1195,58 @@ async def test_process_publish_check_ready_for_review( 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( - """\ + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_list_comments", + "result": mock_list_comments_resp, + }, + { + "api": "rest.issues.async_create_comment", + "result": True, + }, + { + "api": "rest.pulls.async_create", + "exception": RequestFailed( + Response( + httpx.Response(422, request=httpx.Request("test", "test")), + None, # type: ignore + ) + ), + }, + { + "api": "rest.pulls.async_list", + "result": mock_pulls_resp, + }, + { + "api": "async_graphql", + "result": True, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "body": snapshot( + """\ # 📃 商店发布检查结果 > Bot: test @@ -1156,52 +1275,34 @@ async def test_process_publish_check_ready_for_review( 💪 Powered by [NoneFlow](https://github.com/nonebot/noneflow) """ - ), - }, - True, - ) - ctx.should_call_api( - "rest.pulls.async_create", - { - "owner": "he0119", - "repo": "action-test", - "title": "Bot: test", - "body": "resolve #80", - "base": "master", - "head": "publish/issue80", - }, - exception=RequestFailed( - Response( - httpx.Response(422, request=httpx.Request("test", "test")), - None, # type: ignore - ) - ), - ) - ctx.should_call_api( - "rest.pulls.async_list", - { - "owner": "he0119", - "repo": "action-test", - "head": "he0119:publish/issue80", - }, - mock_pulls_resp, - ) - # 将拉取请求标记为可供审阅 - ctx.should_call_api( - "async_graphql", - { - "query": snapshot( - """\ + ), + }, + { + "owner": "he0119", + "repo": "action-test", + "title": "Bot: test", + "body": "resolve #80", + "base": "master", + "head": "publish/issue80", + }, + { + "owner": "he0119", + "repo": "action-test", + "head": "he0119:publish/issue80", + }, + { + "query": snapshot( + """\ mutation markPullRequestReadyForReview($pullRequestId: ID!) { markPullRequestReadyForReview(input: {pullRequestId: $pullRequestId}) { clientMutationId } }\ """ - ), - "variables": {"pullRequestId": "123"}, - }, - True, + ), + "variables": {"pullRequestId": "123"}, + }, + ], ) ctx.receive_event(bot, event) @@ -1211,6 +1312,13 @@ async def test_process_publish_check_ready_for_review( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "switch", "-C", "publish/issue80"], ["git", "add", str(tmp_path / "bots.json5")], ["git", "ls-remote", "--heads", "origin", "publish/issue80"], @@ -1253,6 +1361,7 @@ async def test_comment_immediate_after_pull_request_closed( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试在拉取请求关闭后立即评论 @@ -1294,29 +1403,45 @@ async def test_comment_immediate_after_pull_request_closed( 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( - """\ + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_list_comments", + "result": mock_list_comments_resp, + }, + { + "api": "rest.issues.async_create_comment", + "result": True, + }, + { + "api": "rest.pulls.async_list", + "result": mock_pulls_resp, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 80, + "body": snapshot( + """\ # 📃 商店发布检查结果 > Bot: test @@ -1345,18 +1470,14 @@ async def test_comment_immediate_after_pull_request_closed( 💪 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, + ), + }, + { + "owner": "he0119", + "repo": "action-test", + "head": "he0119:publish/issue80", + }, + ], ) ctx.receive_event(bot, event) @@ -1366,6 +1487,13 @@ async def test_comment_immediate_after_pull_request_closed( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "switch", "-C", "publish/issue80"], ["git", "add", str(tmp_path / "bots.json5")], ["git", "ls-remote", "--heads", "origin", "publish/issue80"], diff --git a/tests/plugins/github/publish/process/test_publish_check_plugin.py b/tests/plugins/github/publish/process/test_publish_check_plugin.py index 9f1febc2..a88b07c9 100644 --- a/tests/plugins/github/publish/process/test_publish_check_plugin.py +++ b/tests/plugins/github/publish/process/test_publish_check_plugin.py @@ -27,6 +27,7 @@ async def test_plugin_process_publish_check( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试插件的发布流程""" from src.plugins.github import plugin_config @@ -72,7 +73,7 @@ async def test_plugin_process_publish_check( check_json_data(plugin_config.input_config.plugin_path, []) async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(IssuesOpened) event.payload.issue.labels = get_issue_labels(["Plugin", "Publish"]) @@ -84,6 +85,10 @@ async def test_plugin_process_publish_check( "api": "rest.apps.async_get_repo_installation", "result": mock_installation, }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, { "api": "rest.issues.async_get", "result": mock_issues_resp, @@ -122,10 +127,11 @@ async def test_plugin_process_publish_check( }, ], snapshot( - { - 0: {"owner": "he0119", "repo": "action-test"}, - 1: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 2: { + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": 123}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { "owner": "he0119", "repo": "action-test", "issue_number": 80, @@ -153,8 +159,8 @@ async def test_plugin_process_publish_check( - [x] 🔥插件测试中,请稍候\ """, }, - 3: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 4: { + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { "owner": "he0119", "repo": "action-test", "issue_number": 80, @@ -182,8 +188,8 @@ async def test_plugin_process_publish_check( - [ ] 如需重新运行插件测试,请勾选左侧勾选框\ """, }, - 5: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 6: { + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { "owner": "he0119", "repo": "action-test", "issue_number": 80, @@ -217,13 +223,13 @@ async def test_plugin_process_publish_check( """, }, - 7: { + { "owner": "he0119", "repo": "action-test", "issue_number": 80, "title": "Plugin: name", }, - 8: { + { "owner": "he0119", "repo": "action-test", "title": "Plugin: name", @@ -231,13 +237,13 @@ async def test_plugin_process_publish_check( "base": "master", "head": "publish/issue80", }, - 9: { + { "owner": "he0119", "repo": "action-test", "issue_number": 2, "labels": ["Publish", "Plugin"], }, - } + ] ), ) @@ -246,6 +252,13 @@ async def test_plugin_process_publish_check( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "switch", "-C", "publish/issue80"], ["git", "add", str(tmp_path / "plugins.json5")], ["git", "ls-remote", "--heads", "origin", "publish/issue80"], @@ -290,6 +303,7 @@ async def test_plugin_process_publish_check_re_run( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试插件的发布流程,重新运行插件测试""" from src.plugins.github import plugin_config @@ -338,7 +352,7 @@ async def test_plugin_process_publish_check_re_run( check_json_data(plugin_config.input_config.plugin_path, []) async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(IssuesOpened) event.payload.issue.labels = get_issue_labels(["Plugin", "Publish"]) @@ -350,6 +364,10 @@ async def test_plugin_process_publish_check_re_run( "api": "rest.apps.async_get_repo_installation", "result": mock_installation, }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, { "api": "rest.issues.async_get", "result": mock_issues_resp, @@ -388,10 +406,11 @@ async def test_plugin_process_publish_check_re_run( }, ], snapshot( - { - 0: {"owner": "he0119", "repo": "action-test"}, - 1: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 2: { + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": 123}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { "owner": "he0119", "repo": "action-test", "issue_number": 80, @@ -419,8 +438,8 @@ async def test_plugin_process_publish_check_re_run( - [x] 🔥插件测试中,请稍候\ """, }, - 3: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 4: { + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { "owner": "he0119", "repo": "action-test", "issue_number": 80, @@ -448,8 +467,8 @@ async def test_plugin_process_publish_check_re_run( - [ ] 如需重新运行插件测试,请勾选左侧勾选框\ """, }, - 5: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 6: { + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { "owner": "he0119", "repo": "action-test", "issue_number": 80, @@ -483,13 +502,13 @@ async def test_plugin_process_publish_check_re_run( """, }, - 7: { + { "owner": "he0119", "repo": "action-test", "issue_number": 80, "title": "Plugin: name", }, - 8: { + { "owner": "he0119", "repo": "action-test", "title": "Plugin: name", @@ -497,13 +516,13 @@ async def test_plugin_process_publish_check_re_run( "base": "master", "head": "publish/issue80", }, - 9: { + { "owner": "he0119", "repo": "action-test", "issue_number": 2, "labels": ["Publish", "Plugin"], }, - } + ] ), ) @@ -512,6 +531,13 @@ async def test_plugin_process_publish_check_re_run( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "switch", "-C", "publish/issue80"], ["git", "add", str(tmp_path / "plugins.json5")], ["git", "ls-remote", "--heads", "origin", "publish/issue80"], @@ -556,6 +582,7 @@ async def test_plugin_process_publish_check_missing_metadata( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试发布检查不通过,测试缺少插件元数据""" from src.plugins.github import plugin_config @@ -592,7 +619,7 @@ async def test_plugin_process_publish_check_missing_metadata( check_json_data(plugin_config.input_config.plugin_path, []) async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(IssuesOpened) event.payload.issue.labels = get_issue_labels(["Plugin", "Publish"]) @@ -604,6 +631,10 @@ async def test_plugin_process_publish_check_missing_metadata( "api": "rest.apps.async_get_repo_installation", "result": mock_installation, }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, { "api": "rest.issues.async_get", "result": mock_issues_resp, @@ -638,10 +669,11 @@ async def test_plugin_process_publish_check_missing_metadata( }, ], snapshot( - { - 0: {"owner": "he0119", "repo": "action-test"}, - 1: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 2: { + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": 123}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { "owner": "he0119", "repo": "action-test", "issue_number": 80, @@ -669,8 +701,8 @@ async def test_plugin_process_publish_check_missing_metadata( - [x] 🔥插件测试中,请稍候\ """, }, - 3: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 4: { + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { "owner": "he0119", "repo": "action-test", "issue_number": 80, @@ -698,8 +730,8 @@ async def test_plugin_process_publish_check_missing_metadata( - [ ] 如需重新运行插件测试,请勾选左侧勾选框\ """, }, - 5: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 6: { + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + { "owner": "he0119", "repo": "action-test", "issue_number": 80, @@ -734,18 +766,18 @@ async def test_plugin_process_publish_check_missing_metadata( """, }, - 7: { + { "owner": "he0119", "repo": "action-test", "issue_number": 80, "title": "Plugin: project_link", }, - 8: { + { "owner": "he0119", "repo": "action-test", "head": "he0119:publish/issue80", }, - } + ] ), ) @@ -769,6 +801,7 @@ async def test_skip_plugin_check( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ) -> None: """测试手动跳过插件测试的流程""" from src.plugins.github import plugin_config @@ -798,7 +831,7 @@ async def test_skip_plugin_check( check_json_data(plugin_config.input_config.plugin_path, []) async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(IssueCommentCreated, "issue-comment-skip") ctx.receive_event(bot, event) @@ -810,6 +843,10 @@ async def test_skip_plugin_check( "api": "rest.apps.async_get_repo_installation", "result": mock_installation, }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, # 获取议题信息 { "api": "rest.issues.async_get", @@ -849,10 +886,11 @@ async def test_skip_plugin_check( }, ], snapshot( - { - 0: {"owner": "he0119", "repo": "action-test"}, - 1: {"owner": "he0119", "repo": "action-test", "issue_number": 70}, - 2: { + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": 123}, + {"owner": "he0119", "repo": "action-test", "issue_number": 70}, + { "owner": "he0119", "repo": "action-test", "issue_number": 70, @@ -880,8 +918,8 @@ async def test_skip_plugin_check( - [x] 🔥插件测试中,请稍候\ """, }, - 3: {"owner": "he0119", "repo": "action-test", "issue_number": 70}, - 4: { + {"owner": "he0119", "repo": "action-test", "issue_number": 70}, + { "owner": "he0119", "repo": "action-test", "issue_number": 70, @@ -919,7 +957,7 @@ async def test_skip_plugin_check( - [x] 🔥插件测试中,请稍候\ """, }, - 5: { + { "owner": "he0119", "repo": "action-test", "issue_number": 70, @@ -957,8 +995,8 @@ async def test_skip_plugin_check( - [ ] 如需重新运行插件测试,请勾选左侧勾选框\ """, }, - 6: {"owner": "he0119", "repo": "action-test", "issue_number": 70}, - 7: { + {"owner": "he0119", "repo": "action-test", "issue_number": 70}, + { "owner": "he0119", "repo": "action-test", "issue_number": 70, @@ -993,18 +1031,18 @@ async def test_skip_plugin_check( """, }, - 8: { + { "owner": "he0119", "repo": "action-test", "issue_number": 70, "title": "Plugin: project_link", }, - 9: { + { "owner": "he0119", "repo": "action-test", "head": "he0119:publish/issue70", }, - } + ] ), ) diff --git a/tests/plugins/github/publish/process/test_publish_pull_request.py b/tests/plugins/github/publish/process/test_publish_pull_request.py index 4f17a566..f6b30c82 100644 --- a/tests/plugins/github/publish/process/test_publish_pull_request.py +++ b/tests/plugins/github/publish/process/test_publish_pull_request.py @@ -19,6 +19,7 @@ async def test_process_pull_request( app: App, mocker: MockerFixture, mock_installation, + mock_installation_token, mocked_api: MockRouter, ) -> None: mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) @@ -61,7 +62,7 @@ async def test_process_pull_request( mock_list_artifacts_resp.parsed_data = mock_list_artifacts_data async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(PullRequestClosed) event.payload.pull_request.merged = True @@ -72,6 +73,10 @@ async def test_process_pull_request( "api": "rest.apps.async_get_repo_installation", "result": mock_installation, }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, { "api": "rest.issues.async_get", "result": mock_issues_resp, @@ -98,12 +103,13 @@ async def test_process_pull_request( }, ], snapshot( - { - 0: {"owner": "he0119", "repo": "action-test"}, - 1: {"owner": "he0119", "repo": "action-test", "issue_number": 76}, - 2: {"owner": "he0119", "repo": "action-test", "issue_number": 80}, - 3: {"owner": "he0119", "repo": "action-test", "run_id": 3}, - 4: { + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 76}, + {"owner": "he0119", "repo": "action-test", "issue_number": 80}, + {"owner": "he0119", "repo": "action-test", "run_id": 3}, + { "owner": "owner", "repo": "registry", "event_type": "registry_update", @@ -112,15 +118,15 @@ async def test_process_pull_request( "artifact_id": 233, }, }, - 5: { + { "owner": "he0119", "repo": "action-test", "issue_number": 80, "state": "closed", "state_reason": "completed", }, - 6: {"owner": "he0119", "repo": "action-test", "state": "open"}, - } + {"owner": "he0119", "repo": "action-test", "state": "open"}, + ] ), ) @@ -131,13 +137,20 @@ async def test_process_pull_request( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "push", "origin", "--delete", "publish/issue76"], ], ) async def test_process_pull_request_not_merged( - app: App, mocker: MockerFixture, mock_installation + app: App, mocker: MockerFixture, mock_installation, mock_installation_token ) -> None: mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) @@ -147,7 +160,7 @@ async def test_process_pull_request_not_merged( mock_issues_resp.parsed_data = mock_issue async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(PullRequestClosed) assert isinstance(event, PullRequestClosed) @@ -158,6 +171,10 @@ async def test_process_pull_request_not_merged( "api": "rest.apps.async_get_repo_installation", "result": mock_installation, }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, { "api": "rest.issues.async_get", "result": mock_issues_resp, @@ -169,6 +186,7 @@ async def test_process_pull_request_not_merged( ], [ {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, {"owner": "he0119", "repo": "action-test", "issue_number": 76}, { "owner": "he0119", @@ -187,6 +205,13 @@ async def test_process_pull_request_not_merged( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "push", "origin", "--delete", "publish/issue76"], ], ) @@ -197,7 +222,7 @@ async def test_not_publish(app: App, mocker: MockerFixture) -> None: mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(PullRequestClosed) event.payload.pull_request.labels = [] @@ -215,7 +240,7 @@ async def test_extract_issue_number_from_ref_failed( mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(PullRequestClosed) event.payload.pull_request.head.ref = "1" diff --git a/tests/plugins/github/publish/utils/test_comment_issue.py b/tests/plugins/github/publish/utils/test_comment_issue.py index a3c83f41..f4981279 100644 --- a/tests/plugins/github/publish/utils/test_comment_issue.py +++ b/tests/plugins/github/publish/utils/test_comment_issue.py @@ -1,7 +1,7 @@ from nonebug import App from pytest_mock import MockerFixture -from tests.plugins.github.utils import get_github_bot +from tests.plugins.github.utils import GitHubApi, get_github_bot, should_call_apis async def test_comment_issue(app: App, mocker: MockerFixture): @@ -22,16 +22,27 @@ async def test_comment_issue(app: App, mocker: MockerFixture): async with app.test_api() as ctx: adapter, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.issues.async_list_comments", - {"owner": "owner", "repo": "repo", "issue_number": 1}, - mock_list_comments_resp, - ) - - ctx.should_call_api( - "rest.issues.async_create_comment", - {"owner": "owner", "repo": "repo", "issue_number": 1, "body": "test"}, - True, + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_list_comments", + result=mock_list_comments_resp, + ), + GitHubApi( + api="rest.issues.async_create_comment", + result=True, + ), + ], + [ + {"owner": "owner", "repo": "repo", "issue_number": 1}, + { + "owner": "owner", + "repo": "repo", + "issue_number": 1, + "body": "test", + }, + ], ) handler = GithubHandler(bot=bot, repo_info=RepoInfo(owner="owner", repo="repo")) @@ -59,16 +70,27 @@ async def test_comment_issue_reuse(app: App, mocker: MockerFixture): async with app.test_api() as ctx: adapter, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.issues.async_list_comments", - {"owner": "owner", "repo": "repo", "issue_number": 1}, - mock_list_comments_resp, - ) - - ctx.should_call_api( - "rest.issues.async_update_comment", - {"owner": "owner", "repo": "repo", "comment_id": 123, "body": "test"}, - True, + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_list_comments", + result=mock_list_comments_resp, + ), + GitHubApi( + api="rest.issues.async_update_comment", + result=True, + ), + ], + [ + {"owner": "owner", "repo": "repo", "issue_number": 1}, + { + "owner": "owner", + "repo": "repo", + "comment_id": 123, + "body": "test", + }, + ], ) handler = GithubHandler(bot=bot, repo_info=RepoInfo(owner="owner", repo="repo")) @@ -93,10 +115,17 @@ async def test_comment_issue_reuse_same(app: App, mocker: MockerFixture): async with app.test_api() as ctx: adapter, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.issues.async_list_comments", - {"owner": "owner", "repo": "repo", "issue_number": 1}, - mock_list_comments_resp, + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_list_comments", + result=mock_list_comments_resp, + ), + ], + [ + {"owner": "owner", "repo": "repo", "issue_number": 1}, + ], ) handler = GithubHandler(bot=bot, repo_info=RepoInfo(owner="owner", repo="repo")) diff --git a/tests/plugins/github/publish/utils/test_ensure_issue_content.py b/tests/plugins/github/publish/utils/test_ensure_issue_content.py index a610dfdc..103cd68b 100644 --- a/tests/plugins/github/publish/utils/test_ensure_issue_content.py +++ b/tests/plugins/github/publish/utils/test_ensure_issue_content.py @@ -2,7 +2,12 @@ from nonebug import App from pytest_mock import MockerFixture -from tests.plugins.github.utils import MockIssue, get_github_bot +from tests.plugins.github.utils import ( + GitHubApi, + MockIssue, + get_github_bot, + should_call_apis, +) async def test_ensure_issue_content(app: App, mocker: MockerFixture): @@ -20,14 +25,21 @@ async def test_ensure_issue_content(app: App, mocker: MockerFixture): issue=issue.as_mock(mocker), ) - ctx.should_call_api( - "rest.issues.async_update", - { - "owner": "owner", - "repo": "repo", - "issue_number": 1, - "body": snapshot( - """\ + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_update", + result=True, + ) + ], + [ + { + "owner": "owner", + "repo": "repo", + "issue_number": 1, + "body": snapshot( + """\ ### 插件名称 ### 插件描述 @@ -40,9 +52,9 @@ async def test_ensure_issue_content(app: App, mocker: MockerFixture): 什么都没有\ """ - ), - }, - True, + ), + } + ], ) await ensure_issue_content(handler) @@ -64,15 +76,22 @@ async def test_ensure_issue_content_partial(app: App, mocker: MockerFixture): issue=issue.as_mock(mocker), ) - ctx.should_call_api( - "rest.issues.async_update", - { - "owner": "owner", - "repo": "repo", - "issue_number": 1, - "body": "### 插件描述\n\n### 插件项目仓库/主页链接\n\n### 插件支持的适配器\n\n### 插件名称\n\nname\n\n### 插件类型\n", - }, - True, + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_update", + result=True, + ) + ], + [ + { + "owner": "owner", + "repo": "repo", + "issue_number": 1, + "body": "### 插件描述\n\n### 插件项目仓库/主页链接\n\n### 插件支持的适配器\n\n### 插件名称\n\nname\n\n### 插件类型\n", + } + ], ) await ensure_issue_content(handler) diff --git a/tests/plugins/github/publish/utils/test_ensure_issue_plugin_test_button.py b/tests/plugins/github/publish/utils/test_ensure_issue_plugin_test_button.py index 56d46e1e..0c0b5943 100644 --- a/tests/plugins/github/publish/utils/test_ensure_issue_plugin_test_button.py +++ b/tests/plugins/github/publish/utils/test_ensure_issue_plugin_test_button.py @@ -2,7 +2,13 @@ from nonebug import App from pytest_mock import MockerFixture -from tests.plugins.github.utils import MockBody, MockIssue, get_github_bot +from tests.plugins.github.utils import ( + GitHubApi, + MockBody, + MockIssue, + get_github_bot, + should_call_apis, +) async def test_ensure_issue_plugin_test_button(app: App, mocker: MockerFixture): @@ -21,14 +27,21 @@ async def test_ensure_issue_plugin_test_button(app: App, mocker: MockerFixture): async with app.test_api() as ctx: _, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.issues.async_update", - snapshot( - { - "owner": "owner", - "repo": "repo", - "issue_number": 1, - "body": """\ + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_update", + result=True, + ) + ], + [ + snapshot( + { + "owner": "owner", + "repo": "repo", + "issue_number": 1, + "body": """\ ### PyPI 项目名 project_link @@ -51,9 +64,9 @@ async def test_ensure_issue_plugin_test_button(app: App, mocker: MockerFixture): - [ ] 如需重新运行插件测试,请勾选左侧勾选框\ """, - } - ), - True, + } + ) + ], ) handler = IssueHandler( @@ -81,14 +94,21 @@ async def test_ensure_issue_plugin_test_button_checked(app: App, mocker: MockerF async with app.test_api() as ctx: _, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.issues.async_update", - snapshot( - { - "owner": "owner", - "repo": "repo", - "issue_number": 1, - "body": """\ + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_update", + result=True, + ) + ], + [ + snapshot( + { + "owner": "owner", + "repo": "repo", + "issue_number": 1, + "body": """\ ### PyPI 项目名 project_link @@ -111,9 +131,9 @@ async def test_ensure_issue_plugin_test_button_checked(app: App, mocker: MockerF - [ ] 如需重新运行插件测试,请勾选左侧勾选框\ """, - } - ), - True, + } + ) + ], ) handler = IssueHandler( @@ -170,14 +190,21 @@ async def test_ensure_issue_plugin_test_button_in_progress( async with app.test_api() as ctx: _, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.issues.async_update", - snapshot( - { - "owner": "owner", - "repo": "repo", - "issue_number": 1, - "body": """\ + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_update", + result=True, + ) + ], + [ + snapshot( + { + "owner": "owner", + "repo": "repo", + "issue_number": 1, + "body": """\ ### PyPI 项目名 project_link @@ -200,9 +227,9 @@ async def test_ensure_issue_plugin_test_button_in_progress( - [x] 🔥插件测试中,请稍候\ """, - } - ), - True, + } + ) + ], ) handler = IssueHandler( diff --git a/tests/plugins/github/publish/utils/test_get_noneflow_artifact.py b/tests/plugins/github/publish/utils/test_get_noneflow_artifact.py index fb55df85..f67942a9 100644 --- a/tests/plugins/github/publish/utils/test_get_noneflow_artifact.py +++ b/tests/plugins/github/publish/utils/test_get_noneflow_artifact.py @@ -83,18 +83,18 @@ async def test_get_noneflow_artifact_success(app: App, mocker: MockerFixture) -> ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - 1: { + { "owner": "owner", "repo": "repo", "run_id": 12345678901, }, - } + ] ), ) @@ -136,13 +136,13 @@ async def test_get_noneflow_artifact_no_comment( ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - } + ] ), ) @@ -189,13 +189,13 @@ async def test_get_noneflow_artifact_empty_comment( ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - } + ] ), ) @@ -249,13 +249,13 @@ async def test_get_noneflow_artifact_no_history( ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - } + ] ), ) @@ -326,18 +326,18 @@ async def test_get_noneflow_artifact_no_noneflow_artifact( ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - 1: { + { "owner": "owner", "repo": "repo", "run_id": 12345678901, }, - } + ] ), ) @@ -393,13 +393,13 @@ async def test_get_noneflow_artifact_invalid_run_id( ), ], snapshot( - { - 0: { + [ + { "owner": "owner", "repo": "repo", "issue_number": 76, }, - } + ] ), ) diff --git a/tests/plugins/github/publish/utils/test_get_pull_requests_by_label.py b/tests/plugins/github/publish/utils/test_get_pull_requests_by_label.py index 3825cf75..b51edc55 100644 --- a/tests/plugins/github/publish/utils/test_get_pull_requests_by_label.py +++ b/tests/plugins/github/publish/utils/test_get_pull_requests_by_label.py @@ -1,7 +1,7 @@ from nonebug import App from pytest_mock import MockerFixture -from tests.plugins.github.utils import get_github_bot +from tests.plugins.github.utils import GitHubApi, get_github_bot, should_call_apis async def test_get_pull_requests_by_label(app: App, mocker: MockerFixture) -> None: @@ -22,10 +22,10 @@ async def test_get_pull_requests_by_label(app: App, mocker: MockerFixture) -> No async with app.test_api() as ctx: adapter, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.pulls.async_list", - {"owner": "owner", "repo": "repo", "state": "open"}, - mock_pulls_resp, + should_call_apis( + ctx, + [GitHubApi(api="rest.pulls.async_list", result=mock_pulls_resp)], + [{"owner": "owner", "repo": "repo", "state": "open"}], ) pulls = await get_pull_requests_by_label( @@ -54,10 +54,10 @@ async def test_get_pull_requests_by_label_not_match( async with app.test_api() as ctx: adapter, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.pulls.async_list", - {"owner": "owner", "repo": "repo", "state": "open"}, - mock_pulls_resp, + should_call_apis( + ctx, + [GitHubApi(api="rest.pulls.async_list", result=mock_pulls_resp)], + [{"owner": "owner", "repo": "repo", "state": "open"}], ) pulls = await get_pull_requests_by_label( diff --git a/tests/plugins/github/publish/utils/test_publish_resolve_conflict_pull_requests.py b/tests/plugins/github/publish/utils/test_publish_resolve_conflict_pull_requests.py index f4e383d7..8a7aa625 100644 --- a/tests/plugins/github/publish/utils/test_publish_resolve_conflict_pull_requests.py +++ b/tests/plugins/github/publish/utils/test_publish_resolve_conflict_pull_requests.py @@ -154,17 +154,17 @@ async def test_resolve_conflict_pull_requests_adapter( ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 1}, - 1: {"owner": "owner", "repo": "repo", "issue_number": 1}, - 2: {"owner": "owner", "repo": "repo", "run_id": 14156878699}, - 3: { + [ + {"owner": "owner", "repo": "repo", "issue_number": 1}, + {"owner": "owner", "repo": "repo", "issue_number": 1}, + {"owner": "owner", "repo": "repo", "run_id": 14156878699}, + { "owner": "owner", "repo": "repo", "artifact_id": 123456789, "archive_format": "zip", }, - } + ] ), ) @@ -540,17 +540,17 @@ async def test_resolve_conflict_pull_requests_plugin( ), ], snapshot( - { - 0: {"owner": "owner", "repo": "repo", "issue_number": 1}, - 1: {"owner": "owner", "repo": "repo", "issue_number": 1}, - 2: {"owner": "owner", "repo": "repo", "run_id": 14156878699}, - 3: { + [ + {"owner": "owner", "repo": "repo", "issue_number": 1}, + {"owner": "owner", "repo": "repo", "issue_number": 1}, + {"owner": "owner", "repo": "repo", "run_id": 14156878699}, + { "owner": "owner", "repo": "repo", "artifact_id": 123456789, "archive_format": "zip", }, - } + ] ), ) diff --git a/tests/plugins/github/publish/utils/test_validate_info_from_issue.py b/tests/plugins/github/publish/utils/test_validate_info_from_issue.py index 59d45b28..c54be6f7 100644 --- a/tests/plugins/github/publish/utils/test_validate_info_from_issue.py +++ b/tests/plugins/github/publish/utils/test_validate_info_from_issue.py @@ -5,11 +5,13 @@ from respx import MockRouter from tests.plugins.github.utils import ( + GitHubApi, generate_issue_body_adapter, generate_issue_body_bot, generate_issue_body_plugin, generate_issue_body_plugin_skip_test, get_github_bot, + should_call_apis, ) @@ -90,10 +92,15 @@ async def test_validate_info_from_issue_plugin( bot=bot, repo_info=RepoInfo(owner="owner", repo="repo"), issue=mock_issue ) - ctx.should_call_api( - "rest.issues.async_list_comments", - {"owner": "owner", "repo": "repo", "issue_number": 1}, - mock_list_comments_resp, + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_list_comments", + result=mock_list_comments_resp, + ) + ], + [{"owner": "owner", "repo": "repo", "issue_number": 1}], ) result = await validate_plugin_info_from_issue(handler) @@ -162,10 +169,15 @@ async def test_validate_info_from_issue_plugin_skip_test( bot=bot, repo_info=RepoInfo(owner="owner", repo="repo"), issue=mock_issue ) - ctx.should_call_api( - "rest.issues.async_list_comments", - {"owner": "owner", "repo": "repo", "issue_number": 1}, - mock_list_comments_resp, + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.issues.async_list_comments", + result=mock_list_comments_resp, + ) + ], + [{"owner": "owner", "repo": "repo", "issue_number": 1}], ) result = await validate_plugin_info_from_issue(handler) diff --git a/tests/plugins/github/remove/process/test_remove_auto_merge.py b/tests/plugins/github/remove/process/test_remove_auto_merge.py index a0298033..9299fbc1 100644 --- a/tests/plugins/github/remove/process/test_remove_auto_merge.py +++ b/tests/plugins/github/remove/process/test_remove_auto_merge.py @@ -3,7 +3,12 @@ from pytest_mock import MockerFixture from tests.plugins.github.event import get_mock_event -from tests.plugins.github.utils import get_github_bot +from tests.plugins.github.utils import ( + GitHubApi, + assert_subprocess_run_calls, + get_github_bot, + should_call_apis, +) def get_issue_labels(labels: list[str]): @@ -28,7 +33,7 @@ def get_issue_labels(labels: list[str]): async def test_remove_auto_merge( - app: App, mocker: MockerFixture, mock_installation + app: App, mocker: MockerFixture, mock_installation, mock_installation_token ) -> None: """测试审查后自动合并 @@ -44,39 +49,42 @@ async def test_remove_auto_merge( mock_pull_resp.parsed_data = mock_pull async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(PullRequestReviewSubmitted) event.payload.pull_request.labels = get_issue_labels(["Remove", "Plugin"]) - ctx.should_call_api( - "rest.apps.async_get_repo_installation", - {"owner": "he0119", "repo": "action-test"}, - mock_installation, - ) - ctx.should_call_api( - "rest.pulls.async_merge", - { - "owner": "he0119", - "repo": "action-test", - "pull_number": 100, - "merge_method": "rebase", - }, - True, + should_call_apis( + ctx, + [ + GitHubApi( + api="rest.apps.async_get_repo_installation", + result=mock_installation, + ), + GitHubApi( + api="rest.apps.async_create_installation_access_token", + result=mock_installation_token, + ), + GitHubApi(api="rest.pulls.async_merge", result=True), + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + { + "owner": "he0119", + "repo": "action-test", + "pull_number": 100, + "merge_method": "rebase", + }, + ], ) ctx.receive_event(bot, event) ctx.should_pass_rule(auto_merge_matcher) # 测试 git 命令 - mock_subprocess_run.assert_has_calls( - [ - mocker.call( - ["git", "config", "--global", "safe.directory", "*"], - check=True, - capture_output=True, - ), # type: ignore - ], - any_order=True, + assert_subprocess_run_calls( + mock_subprocess_run, + [["git", "config", "--global", "safe.directory", "*"]], ) @@ -90,7 +98,7 @@ async def test_auto_merge_not_remove(app: App, mocker: MockerFixture) -> None: mock_subprocess_run = mocker.patch("subprocess.run") async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(PullRequestReviewSubmitted) event.payload.pull_request.labels = [] @@ -111,7 +119,7 @@ async def test_auto_merge_not_member(app: App, mocker: MockerFixture) -> None: mock_subprocess_run = mocker.patch("subprocess.run") async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(PullRequestReviewSubmitted) event.payload.review.author_association = "CONTRIBUTOR" event.payload.pull_request.labels = get_issue_labels(["Remove", "Plugin"]) @@ -133,7 +141,7 @@ async def test_auto_merge_not_approve(app: App, mocker: MockerFixture) -> None: mock_subprocess_run = mocker.patch("subprocess.run") async with app.test_matcher() as ctx: - adapter, bot = get_github_bot(ctx) + _, bot = get_github_bot(ctx) event = get_mock_event(PullRequestReviewSubmitted) event.payload.pull_request.labels = get_issue_labels(["Remove", "Plugin"]) event.payload.review.state = "commented" diff --git a/tests/plugins/github/remove/process/test_remove_check.py b/tests/plugins/github/remove/process/test_remove_check.py index 3d66fd66..5d720cf6 100644 --- a/tests/plugins/github/remove/process/test_remove_check.py +++ b/tests/plugins/github/remove/process/test_remove_check.py @@ -31,6 +31,7 @@ async def test_process_remove_bot_check( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ): """测试正常的删除流程""" from src.plugins.github import plugin_config @@ -80,6 +81,10 @@ async def test_process_remove_bot_check( apis: list[GitHubApi] = [ {"api": "rest.apps.async_get_repo_installation", "result": mock_installation}, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, {"api": "rest.issues.async_get", "result": mock_issues_resp}, {"api": "rest.pulls.async_create", "result": mock_pulls_resp}, {"api": "rest.issues.async_add_labels", "result": True}, @@ -91,6 +96,7 @@ async def test_process_remove_bot_check( data_list = [ {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, snapshot( { @@ -156,6 +162,13 @@ async def test_process_remove_bot_check( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "switch", "-C", "remove/issue80"], ["git", "add", str(tmp_path / "bots.json5")], ["git", "config", "--global", "user.name", "test"], @@ -180,6 +193,7 @@ async def test_process_remove_plugin_check( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ): """测试正常的删除流程""" from src.plugins.github import plugin_config @@ -230,6 +244,10 @@ async def test_process_remove_plugin_check( apis: list[GitHubApi] = [ {"api": "rest.apps.async_get_repo_installation", "result": mock_installation}, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, {"api": "rest.issues.async_get", "result": mock_issues_resp}, {"api": "rest.pulls.async_create", "result": mock_pulls_resp}, {"api": "rest.issues.async_add_labels", "result": True}, @@ -241,6 +259,7 @@ async def test_process_remove_plugin_check( data_list = [ {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, snapshot( { @@ -306,6 +325,13 @@ async def test_process_remove_plugin_check( expected_commands = [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "switch", "-C", "remove/issue80"], ["git", "add", str(tmp_path / "plugins.json5")], ["git", "config", "--global", "user.name", "test"], @@ -325,6 +351,7 @@ async def test_process_remove_not_found_check( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ): """要删除的包不在数据文件中的情况""" from src.plugins.github import plugin_config @@ -362,6 +389,10 @@ async def test_process_remove_not_found_check( apis: list[GitHubApi] = [ {"api": "rest.apps.async_get_repo_installation", "result": mock_installation}, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, {"api": "rest.issues.async_get", "result": mock_issues_resp}, {"api": "rest.issues.async_list_comments", "result": mock_list_comments_resp}, {"api": "rest.issues.async_create_comment", "result": True}, @@ -369,6 +400,7 @@ async def test_process_remove_not_found_check( data_list = [ {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, { @@ -418,6 +450,7 @@ async def test_process_remove_author_info_not_eq( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ): """删除包时作者信息不相等的问题""" from src.plugins.github import plugin_config @@ -467,6 +500,10 @@ async def test_process_remove_author_info_not_eq( apis: list[GitHubApi] = [ {"api": "rest.apps.async_get_repo_installation", "result": mock_installation}, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, {"api": "rest.issues.async_get", "result": mock_issues_resp}, {"api": "rest.issues.async_list_comments", "result": mock_list_comments_resp}, {"api": "rest.issues.async_create_comment", "result": True}, @@ -474,6 +511,7 @@ async def test_process_remove_author_info_not_eq( data_list = [ {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, { @@ -523,6 +561,7 @@ async def test_process_remove_issue_info_not_found( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ): """删除包时无法从议题获取信息的测试""" from src.plugins.github import plugin_config @@ -570,6 +609,10 @@ async def test_process_remove_issue_info_not_found( apis: list[GitHubApi] = [ {"api": "rest.apps.async_get_repo_installation", "result": mock_installation}, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, {"api": "rest.issues.async_get", "result": mock_issues_resp}, {"api": "rest.issues.async_list_comments", "result": mock_list_comments_resp}, {"api": "rest.issues.async_create_comment", "result": True}, @@ -577,6 +620,7 @@ async def test_process_remove_issue_info_not_found( data_list = [ {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, { @@ -626,6 +670,7 @@ async def test_process_remove_driver( mocked_api: MockRouter, tmp_path: Path, mock_installation, + mock_installation_token, ): """不支持驱动器类型的删除""" mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) @@ -654,6 +699,10 @@ async def test_process_remove_driver( apis: list[GitHubApi] = [ {"api": "rest.apps.async_get_repo_installation", "result": mock_installation}, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, {"api": "rest.issues.async_get", "result": mock_issues_resp}, {"api": "rest.issues.async_list_comments", "result": mock_list_comments_resp}, {"api": "rest.issues.async_create_comment", "result": True}, @@ -661,6 +710,7 @@ async def test_process_remove_driver( data_list = [ {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, {"owner": "he0119", "repo": "action-test", "issue_number": 80}, { diff --git a/tests/plugins/github/remove/process/test_remove_pull_request.py b/tests/plugins/github/remove/process/test_remove_pull_request.py index 78a49bd2..28ced938 100644 --- a/tests/plugins/github/remove/process/test_remove_pull_request.py +++ b/tests/plugins/github/remove/process/test_remove_pull_request.py @@ -8,16 +8,22 @@ from tests.plugins.github.resolve.utils import get_pr_labels from tests.plugins.github.utils import ( MockIssue, + assert_subprocess_run_calls, generate_issue_body_remove, get_github_bot, + mock_subprocess_run_with_side_effect, + should_call_apis, ) async def test_remove_process_pull_request( - app: App, mocker: MockerFixture, mock_installation: MagicMock + app: App, + mocker: MockerFixture, + mock_installation: MagicMock, + mock_installation_token: MagicMock, ) -> None: """删除流程的拉取请求关闭流程""" - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) remove_type = "Bot" mock_issue = MockIssue( @@ -42,56 +48,67 @@ async def test_remove_process_pull_request( event.payload.pull_request.labels = get_pr_labels(["Remove", "Bot"]) event.payload.pull_request.merged = True - 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": 76}, - mock_issues_resp, - ) - ctx.should_call_api( - "rest.issues.async_update", - { - "owner": "he0119", - "repo": "action-test", - "issue_number": 76, - "state": "closed", - "state_reason": "completed", - }, - True, - ) - ctx.should_call_api( - "rest.pulls.async_list", - {"owner": "he0119", "repo": "action-test", "state": "open"}, - mock_pulls_resp, + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_update", + "result": True, + }, + { + "api": "rest.pulls.async_list", + "result": mock_pulls_resp, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 76}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 76, + "state": "closed", + "state_reason": "completed", + }, + {"owner": "he0119", "repo": "action-test", "state": "open"}, + ], ) 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", "push", "origin", "--delete", "publish/issue76"], - check=True, - capture_output=True, - ), - ], # type: ignore - any_order=True, + ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], + ["git", "push", "origin", "--delete", "publish/issue76"], + ], ) async def test_not_remove(app: App, mocker: MockerFixture) -> None: """测试与发布无关的拉取请求""" - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) async with app.test_matcher() as ctx: adapter, bot = get_github_bot(ctx) @@ -105,10 +122,10 @@ async def test_not_remove(app: App, mocker: MockerFixture) -> None: async def test_process_remove_pull_request_not_merged( - app: App, mocker: MockerFixture, mock_installation + app: App, mocker: MockerFixture, mock_installation, mock_installation_token ) -> None: """删除掉不合并的分支""" - mock_subprocess_run = mocker.patch("subprocess.run") + mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker) remove_type = "Bot" mock_issue = MockIssue( @@ -122,43 +139,54 @@ async def test_process_remove_pull_request_not_merged( event = get_mock_event(PullRequestClosed) event.payload.pull_request.labels = get_pr_labels(["Remove", "Bot"]) - 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": 76}, - mock_issues_resp, - ) - ctx.should_call_api( - "rest.issues.async_update", - { - "owner": "he0119", - "repo": "action-test", - "issue_number": 76, - "state": "closed", - "state_reason": "not_planned", - }, - True, + should_call_apis( + ctx, + [ + { + "api": "rest.apps.async_get_repo_installation", + "result": mock_installation, + }, + { + "api": "rest.apps.async_create_installation_access_token", + "result": mock_installation_token, + }, + { + "api": "rest.issues.async_get", + "result": mock_issues_resp, + }, + { + "api": "rest.issues.async_update", + "result": True, + }, + ], + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 76}, + { + "owner": "he0119", + "repo": "action-test", + "issue_number": 76, + "state": "closed", + "state_reason": "not_planned", + }, + ], ) 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", "push", "origin", "--delete", "publish/issue76"], - check=True, - capture_output=True, - ), - ], # type: ignore - any_order=True, + ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], + ["git", "push", "origin", "--delete", "publish/issue76"], + ], ) diff --git a/tests/plugins/github/remove/utils/test_remove_resolve_pull_requests.py b/tests/plugins/github/remove/utils/test_remove_resolve_pull_requests.py index 0ac43264..c0931e0a 100644 --- a/tests/plugins/github/remove/utils/test_remove_resolve_pull_requests.py +++ b/tests/plugins/github/remove/utils/test_remove_resolve_pull_requests.py @@ -7,12 +7,14 @@ from respx import MockRouter from tests.plugins.github.utils import ( + GitHubApi, MockIssue, MockUser, assert_subprocess_run_calls, check_json_data, generate_issue_body_remove, get_github_bot, + should_call_apis, ) @@ -78,10 +80,10 @@ async def test_resolve_conflict_pull_requests_bot( handler = GithubHandler(bot=bot, repo_info=RepoInfo(owner="owner", repo="repo")) - ctx.should_call_api( - "rest.issues.async_get", - snapshot({"owner": "owner", "repo": "repo", "issue_number": 1}), - mock_issue_repo, + should_call_apis( + ctx, + [GitHubApi(api="rest.issues.async_get", result=mock_issue_repo)], + snapshot([{"owner": "owner", "repo": "repo", "issue_number": 1}]), ) await resolve_conflict_pull_requests(handler, [mock_pull]) @@ -163,10 +165,10 @@ async def test_resolve_conflict_pull_requests_plugin( async with app.test_api() as ctx: adapter, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.issues.async_get", - snapshot({"owner": "owner", "repo": "repo", "issue_number": 1}), - mock_issue_repo, + should_call_apis( + ctx, + [GitHubApi(api="rest.issues.async_get", result=mock_issue_repo)], + snapshot([{"owner": "owner", "repo": "repo", "issue_number": 1}]), ) handler = GithubHandler(bot=bot, repo_info=RepoInfo(owner="owner", repo="repo")) @@ -238,10 +240,10 @@ async def test_resolve_conflict_pull_requests_not_found( async with app.test_api() as ctx: adapter, bot = get_github_bot(ctx) - ctx.should_call_api( - "rest.issues.async_get", - snapshot({"owner": "owner", "repo": "repo", "issue_number": 1}), - mock_issue_repo, + should_call_apis( + ctx, + [GitHubApi(api="rest.issues.async_get", result=mock_issue_repo)], + snapshot([{"owner": "owner", "repo": "repo", "issue_number": 1}]), ) handler = GithubHandler(bot=bot, repo_info=RepoInfo(owner="owner", repo="repo")) diff --git a/tests/plugins/github/resolve/test_resolve_close_issue.py b/tests/plugins/github/resolve/test_resolve_close_issue.py index 826f9cc1..777407b2 100644 --- a/tests/plugins/github/resolve/test_resolve_close_issue.py +++ b/tests/plugins/github/resolve/test_resolve_close_issue.py @@ -23,6 +23,7 @@ async def test_resolve_close_issue( app: App, mocker: MockerFixture, mock_installation: MagicMock, + mock_installation_token: MagicMock, mocked_api: MockRouter, ) -> None: """测试能正确关闭议题""" @@ -53,6 +54,10 @@ async def test_resolve_close_issue( api="rest.apps.async_get_repo_installation", result=mock_installation, ), + GitHubApi( + api="rest.apps.async_create_installation_access_token", + result=mock_installation_token, + ), GitHubApi( api="rest.issues.async_get", result=mock_issues_resp, @@ -63,17 +68,18 @@ async def test_resolve_close_issue( ), ], snapshot( - { - 0: {"owner": "he0119", "repo": "action-test"}, - 1: {"owner": "he0119", "repo": "action-test", "issue_number": 76}, - 2: { + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 76}, + { "owner": "he0119", "repo": "action-test", "issue_number": 76, "state": "closed", "state_reason": "not_planned", }, - } + ] ), ) ctx.receive_event(bot, event) @@ -84,6 +90,13 @@ async def test_resolve_close_issue( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "push", "origin", "--delete", "publish/issue76"], ], ) @@ -96,6 +109,7 @@ async def test_resolve_close_issue_already_closed( app: App, mocker: MockerFixture, mock_installation: MagicMock, + mock_installation_token: MagicMock, mocked_api: MockRouter, ) -> None: """测试议题已经关闭的情况 @@ -132,6 +146,10 @@ async def test_resolve_close_issue_already_closed( api="rest.apps.async_get_repo_installation", result=mock_installation, ), + GitHubApi( + api="rest.apps.async_create_installation_access_token", + result=mock_installation_token, + ), GitHubApi( api="rest.issues.async_get", result=mock_issues_resp, @@ -142,17 +160,18 @@ async def test_resolve_close_issue_already_closed( ), ], snapshot( - { - 0: {"owner": "he0119", "repo": "action-test"}, - 1: {"owner": "he0119", "repo": "action-test", "issue_number": 76}, - 2: { + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 76}, + { "owner": "he0119", "repo": "action-test", "issue_number": 76, "state": "closed", "state_reason": "not_planned", }, - } + ] ), ) ctx.receive_event(bot, event) @@ -163,6 +182,13 @@ async def test_resolve_close_issue_already_closed( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "push", "origin", "--delete", "publish/issue76"], ], ) diff --git a/tests/plugins/github/resolve/test_resolve_pull_request.py b/tests/plugins/github/resolve/test_resolve_pull_request.py index 834347b1..3b47e28f 100644 --- a/tests/plugins/github/resolve/test_resolve_pull_request.py +++ b/tests/plugins/github/resolve/test_resolve_pull_request.py @@ -27,6 +27,7 @@ async def test_resolve_pull_request( app: App, mocker: MockerFixture, mock_installation: MagicMock, + mock_installation_token: MagicMock, mocked_api: MockRouter, tmp_path: Path, ) -> None: @@ -140,6 +141,10 @@ async def test_resolve_pull_request( api="rest.apps.async_get_repo_installation", result=mock_installation, ), + GitHubApi( + api="rest.apps.async_create_installation_access_token", + result=mock_installation_token, + ), GitHubApi(api="rest.issues.async_get", result=mock_issues_resp), GitHubApi(api="rest.issues.async_update", result=True), GitHubApi(api="rest.pulls.async_list", result=mock_pulls_resp), @@ -159,32 +164,33 @@ async def test_resolve_pull_request( GitHubApi(api="rest.issues.async_get", result=mock_remove_issue_resp), ], snapshot( - { - 0: {"owner": "he0119", "repo": "action-test"}, - 1: {"owner": "he0119", "repo": "action-test", "issue_number": 76}, - 2: { + [ + {"owner": "he0119", "repo": "action-test"}, + {"installation_id": mock_installation.parsed_data.id}, + {"owner": "he0119", "repo": "action-test", "issue_number": 76}, + { "owner": "he0119", "repo": "action-test", "issue_number": 76, "state": "closed", "state_reason": "completed", }, - 3: {"owner": "he0119", "repo": "action-test", "state": "open"}, - 4: {"owner": "he0119", "repo": "action-test", "issue_number": 100}, - 5: {"owner": "he0119", "repo": "action-test", "issue_number": 100}, - 6: { + {"owner": "he0119", "repo": "action-test", "state": "open"}, + {"owner": "he0119", "repo": "action-test", "issue_number": 100}, + {"owner": "he0119", "repo": "action-test", "issue_number": 100}, + { "owner": "he0119", "repo": "action-test", "run_id": 14156878699, }, - 7: { + { "owner": "he0119", "repo": "action-test", "artifact_id": 123456789, "archive_format": "zip", }, - 8: {"owner": "he0119", "repo": "action-test", "issue_number": 101}, - } + {"owner": "he0119", "repo": "action-test", "issue_number": 101}, + ] ), ) ctx.receive_event(bot, event) @@ -195,6 +201,13 @@ async def test_resolve_pull_request( mock_subprocess_run, [ ["git", "config", "--global", "safe.directory", "*"], + [ + "git", + "config", + "--global", + "url.https://x-access-token:test-token@github.com/.insteadOf", + "https://github.com/", + ], ["git", "push", "origin", "--delete", "publish/issue76"], # 处理发布 ["git", "checkout", "master"],