GRM-51: fix: API resilience with retry, idempotent releases, and graceful Vikunja errors
This commit is contained in:
@@ -16,6 +16,7 @@ from scripts.ci.auto_merge import (
|
||||
has_approval_review,
|
||||
has_ready_to_merge_label,
|
||||
main,
|
||||
run_cmd,
|
||||
validate_pr_title,
|
||||
validate_pr_title_matches_vikunja,
|
||||
wait_for_ci,
|
||||
@@ -604,3 +605,66 @@ class TestMain:
|
||||
result = runner.invoke(main, ["GRM-19-fix", "GRM-19: Some task title", "owner/repo", "1"])
|
||||
assert result.exit_code == 0
|
||||
assert "squash-merged" in result.output
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.auto_merge.validate_pr_title_matches_vikunja")
|
||||
@patch("scripts.ci.auto_merge.has_approval_review", return_value=True)
|
||||
@patch("scripts.ci.auto_merge.GiteaClient")
|
||||
def test_merge_405_behind_retries(
|
||||
self, mock_client_cls: MagicMock, mock_approval: MagicMock, mock_vikunja: MagicMock
|
||||
) -> None:
|
||||
"""405 'behind' error should trigger rebase and retry."""
|
||||
mock_client = MagicMock()
|
||||
mock_client.get_pr_labels.return_value = [{"name": "ready-to-merge"}]
|
||||
mock_client.get_pr.return_value = _mock_pr()
|
||||
mock_client.get_commit_status.return_value = _mock_ci_passing()
|
||||
mock_client.get_pr_commits.return_value = _mock_commits()
|
||||
# First merge_pr raises 405 "behind", second succeeds
|
||||
mock_client.merge_pr.side_effect = [
|
||||
APIError(http.HTTPStatus.METHOD_NOT_ALLOWED, "head branch is behind base"),
|
||||
None,
|
||||
]
|
||||
mock_client_cls.return_value = mock_client
|
||||
with patch("scripts.ci.auto_merge.run_cmd") as mock_run_cmd:
|
||||
mock_run_cmd.return_value = MagicMock(returncode=0, stdout="", stderr="")
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["GRM-19-fix", "GRM-19: Some task title", "owner/repo", "1"])
|
||||
assert result.exit_code == 0
|
||||
assert "Rebased" in result.output or "rebase" in result.output.lower()
|
||||
assert mock_client.merge_pr.call_count == 2
|
||||
|
||||
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
|
||||
@patch("scripts.ci.auto_merge.validate_pr_title_matches_vikunja")
|
||||
@patch("scripts.ci.auto_merge.has_approval_review", return_value=True)
|
||||
@patch("scripts.ci.auto_merge.GiteaClient")
|
||||
def test_merge_405_behind_rebase_fails(
|
||||
self, mock_client_cls: MagicMock, mock_approval: MagicMock, mock_vikunja: MagicMock
|
||||
) -> None:
|
||||
"""405 'behind' with rebase failure should raise ClickException."""
|
||||
mock_client = MagicMock()
|
||||
mock_client.get_pr_labels.return_value = [{"name": "ready-to-merge"}]
|
||||
mock_client.get_pr.return_value = _mock_pr()
|
||||
mock_client.get_commit_status.return_value = _mock_ci_passing()
|
||||
mock_client.get_pr_commits.return_value = _mock_commits()
|
||||
mock_client.merge_pr.side_effect = APIError(http.HTTPStatus.METHOD_NOT_ALLOWED, "head branch is behind base")
|
||||
mock_client_cls.return_value = mock_client
|
||||
with patch("scripts.ci.auto_merge.run_cmd") as mock_run_cmd:
|
||||
mock_run_cmd.side_effect = click.ClickException("rebase failed")
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["GRM-19-fix", "GRM-19: Some task title", "owner/repo", "1"])
|
||||
assert result.exit_code == 1
|
||||
assert "rebase" in result.output.lower() or "retry" in result.output.lower()
|
||||
|
||||
def test_run_cmd_success(self) -> None:
|
||||
"""run_cmd should return CompletedProcess on success."""
|
||||
with patch("scripts.ci.auto_merge.subprocess.run") as mock_run:
|
||||
mock_run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
|
||||
result = run_cmd(["echo", "ok"])
|
||||
assert result.returncode == 0
|
||||
|
||||
def test_run_cmd_failure_raises(self) -> None:
|
||||
"""run_cmd should raise ClickException on non-zero exit."""
|
||||
with patch("scripts.ci.auto_merge.subprocess.run") as mock_run:
|
||||
mock_run.return_value = MagicMock(returncode=1, stdout="", stderr="error")
|
||||
with pytest.raises(click.ClickException):
|
||||
run_cmd(["false"])
|
||||
|
||||
Reference in New Issue
Block a user