diff --git a/tests/integration/test_scenario_issue_pull.py b/tests/integration/test_scenario_issue_pull.py index 23cc6c6..9e90da1 100644 --- a/tests/integration/test_scenario_issue_pull.py +++ b/tests/integration/test_scenario_issue_pull.py @@ -221,5 +221,75 @@ async def test_pull_request_review_and_merge( assert merged.data["state"] == "closed" readme = await mcp.ok("gitea_repo_get_contents", filepath="greeting.txt", **repo) assert readme.data["path"] == "greeting.txt" + + # The merge commit resolves back to the pull request. + merge_sha = merged.data["merge_commit_sha"] + by_commit = await mcp.ok("gitea_repo_get_commit_pull_request", sha=merge_sha, **repo) + assert by_commit.data["number"] == index + + # Merging it again is refused with the documented status, not silently accepted. + again = await mcp.fail( + "gitea_pull_merge_pull_request", 405, index=index, body={"do": "merge"}, **repo + ) + assert again.error_kind == "method_not_allowed" + assert "already merged" in again.payload["error"]["message"] finally: gitea.api("DELETE", f"/repos/{fresh_repo.owner}/{fresh_repo.name}/collaborators/{reviewer}") + + +async def test_scheduled_auto_merge_can_be_cancelled( + mcp: McpSession, gitea: GiteaInstance, fresh_repo: RepoRef +) -> None: + """A required status check that is still pending turns the merge into a scheduled one.""" + repo = fresh_repo.args + await mcp.ok( + "gitea_repo_create_branch", + status=201, + body={"new_branch_name": "feat/auto", "old_branch_name": "main"}, + **repo, + ) + await mcp.ok( + "gitea_repo_create_file", + status=201, + filepath="auto.txt", + body={"content": b64("auto\n"), "message": "add auto", "branch": "feat/auto"}, + **repo, + ) + await mcp.ok( + "gitea_repo_create_branch_protection", + status=201, + body={ + "rule_name": "main", + "enable_status_check": True, + "status_check_contexts": ["ci/required"], + }, + **repo, + ) + pull = await mcp.ok( + "gitea_pull_create_pull_request", + status=201, + body={"title": "Auto merge", "base": "main", "head": "feat/auto"}, + **repo, + ) + index = pull.data["number"] + await mcp.ok( + "gitea_repo_create_status", + status=201, + sha=pull.data["head"]["sha"], + body={"state": "pending", "context": "ci/required"}, + **repo, + ) + scheduled = await mcp.ok( + "gitea_pull_merge_pull_request", + status=201, + index=index, + body={"do": "merge", "merge_when_checks_succeed": True}, + **repo, + ) + assert scheduled.status == 201 # scheduled, not merged + assert (await mcp.ok("gitea_pull_get_pull_request", index=index, **repo)).data[ + "merged" + ] is False + await mcp.ok("gitea_pull_cancel_scheduled_auto_merge", index=index, **repo) + # Nothing is scheduled any more. + await mcp.fail("gitea_pull_cancel_scheduled_auto_merge", 404, index=index, **repo) diff --git a/tests/integration/test_scenario_org_repo.py b/tests/integration/test_scenario_org_repo.py index 1b7bfef..ad4134d 100644 --- a/tests/integration/test_scenario_org_repo.py +++ b/tests/integration/test_scenario_org_repo.py @@ -7,7 +7,14 @@ import base64 import time from typing import Any -from tests.integration.conftest import McpSession, RegularUser, RepoRef, b64, drop_org +from tests.integration.conftest import ( + McpSession, + RegularUser, + RepoRef, + b64, + drop_org, + drop_user, +) from tests.integration.harness import GiteaInstance @@ -230,3 +237,55 @@ async def test_repo_collaborators_fork_transfer_archive_search( assert (await mcp.ok("gitea_repo_get", owner=org, repo=fresh_repo.name)).data["archived"] finally: drop_org(gitea, org) + + +async def test_repository_transfer_accept_and_reject( + mcp: McpSession, gitea: GiteaInstance, regular_user: RegularUser +) -> None: + """A transfer started by a non-admin stays pending until the receiving user answers it.""" + owner = regular_user.login + recipient, _ = gitea.create_user(gitea.unique("it-recv")) + accepted = gitea.unique("it-xfer-ok") + rejected = gitea.unique("it-xfer-no") + gitea.create_repo(owner, accepted) + gitea.create_repo(owner, rejected) + try: + for name in (accepted, rejected): + # Started by the owner (through sudo): not an admin, so Gitea keeps the + # transfer pending (answering 201 although the swagger lists 202). + await mcp.ok( + "gitea_repo_transfer", + status=(201, 202), + sudo=owner, + owner=owner, + repo=name, + body={"new_owner": recipient}, + ) + # Still the owner's repository until the recipient answers. + assert (await mcp.ok("gitea_repo_get", owner=owner, repo=name)).data["owner"][ + "login" + ] == owner + took = await mcp.ok( + "gitea_repo_accept_repo_transfer", + status=202, + sudo=recipient, + owner=owner, + repo=accepted, + ) + assert took.identity["sudo"] == recipient + assert (await mcp.ok("gitea_repo_get", owner=recipient, repo=accepted)).data["owner"][ + "login" + ] == recipient + await mcp.ok("gitea_repo_reject_repo_transfer", sudo=recipient, owner=owner, repo=rejected) + assert (await mcp.ok("gitea_repo_get", owner=owner, repo=rejected)).data["owner"][ + "login" + ] == owner + # The transfer is gone: answering it again finds nothing. + await mcp.fail( + "gitea_repo_accept_repo_transfer", 404, sudo=recipient, owner=owner, repo=rejected + ) + finally: + gitea.api("DELETE", f"/repos/{owner}/{rejected}") + gitea.api("DELETE", f"/repos/{owner}/{accepted}") + gitea.api("DELETE", f"/repos/{recipient}/{accepted}") + drop_user(gitea, recipient)