GRM-20: refactor(scripts): centralize constants, API clients, and HTTP status codes
- Add shared config.py with API URLs, regexes, timeouts, pagination - Add GiteaClient and VikunjaClient in api_clients.py with pooled sessions - Add APIError exception for unified HTTP error handling - Refactor all scripts to use shared modules and http.HTTPStatus - Rewrite unit tests to mock clients and use HTTPStatus constants - Add tests for api_clients and config modules - Achieve 100% test coverage
This commit is contained in:
@@ -1,21 +1,15 @@
|
||||
"""Unit tests for scripts/auto_merge.py."""
|
||||
|
||||
import http
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import click
|
||||
import pytest
|
||||
import requests
|
||||
from click.testing import CliRunner
|
||||
|
||||
from scripts.auto_merge import (
|
||||
CONVENTIONAL_RE,
|
||||
GITEA_API,
|
||||
TASK_ID_RE,
|
||||
extract_task_id,
|
||||
main,
|
||||
merge_pr,
|
||||
validate_pr_title,
|
||||
)
|
||||
from gitea_runner_manager.config import CONVENTIONAL_RE, TASK_ID_RE
|
||||
from gitea_runner_manager.exceptions import APIError
|
||||
from scripts.auto_merge import extract_task_id, main, validate_pr_title
|
||||
|
||||
|
||||
class TestRegexes:
|
||||
@@ -59,32 +53,12 @@ class TestValidatePrTitle:
|
||||
assert "conventional" in str(exc.value)
|
||||
|
||||
|
||||
class TestMergePr:
|
||||
@patch("scripts.auto_merge.requests.post")
|
||||
def test_successful_merge(self, mock_post: MagicMock) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_post.return_value = mock_response
|
||||
merge_pr("tok", "owner/repo", "7", "GRM-19: fix: bug")
|
||||
mock_post.assert_called_once()
|
||||
args, kwargs = mock_post.call_args
|
||||
assert kwargs["headers"]["Authorization"] == "token tok"
|
||||
assert kwargs["json"]["Do"] == "squash"
|
||||
assert kwargs["json"]["MergeTitleField"] == "GRM-19: fix: bug"
|
||||
assert GITEA_API in args[0]
|
||||
|
||||
@patch("scripts.auto_merge.requests.post")
|
||||
def test_merge_raises_on_http_error(self, mock_post: MagicMock) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.raise_for_status.side_effect = requests.HTTPError("500")
|
||||
mock_post.return_value = mock_response
|
||||
with pytest.raises(requests.HTTPError):
|
||||
merge_pr("tok", "owner/repo", "7", "title")
|
||||
|
||||
|
||||
class TestMain:
|
||||
@patch.dict("os.environ", {"GITEA_TOKEN": "tok"})
|
||||
@patch("scripts.auto_merge.merge_pr")
|
||||
def test_successful_flow(self, mock_merge: MagicMock) -> None:
|
||||
@patch("scripts.auto_merge.GiteaClient")
|
||||
def test_successful_flow(self, mock_client_cls: MagicMock) -> None:
|
||||
mock_client = MagicMock()
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
main,
|
||||
@@ -92,7 +66,8 @@ class TestMain:
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "squash-merged" in result.output
|
||||
mock_merge.assert_called_once_with("tok", "owner/repo", "7", "GRM-19: fix: resolve timeout")
|
||||
mock_client_cls.assert_called_once()
|
||||
mock_client.merge_pr.assert_called_once_with("7", "GRM-19: fix: resolve timeout")
|
||||
|
||||
@patch.dict("os.environ", {"GITEA_TOKEN": ""}, clear=True)
|
||||
def test_missing_token_exits(self) -> None:
|
||||
@@ -116,23 +91,23 @@ class TestMain:
|
||||
assert "conventional" in result.output
|
||||
|
||||
@patch.dict("os.environ", {"GITEA_TOKEN": "tok"})
|
||||
@patch("scripts.auto_merge.merge_pr")
|
||||
def test_merge_pr_failure_raises_click(self, mock_merge: MagicMock) -> None:
|
||||
mock_merge.side_effect = requests.HTTPError("500")
|
||||
@patch("scripts.auto_merge.GiteaClient")
|
||||
def test_merge_pr_failure_raises_click(self, mock_client_cls: MagicMock) -> None:
|
||||
mock_client = MagicMock()
|
||||
mock_client.merge_pr.side_effect = APIError(http.HTTPStatus.INTERNAL_SERVER_ERROR, "server error")
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["GRM-19-fix", "fix: bug", "repo", "1"])
|
||||
result = runner.invoke(main, ["GRM-19-fix", "fix: bug", "owner/repo", "1"])
|
||||
assert result.exit_code == 1
|
||||
assert "HTTP" in result.output
|
||||
|
||||
@patch.dict("os.environ", {"GITEA_TOKEN": "tok"})
|
||||
@patch("scripts.auto_merge.merge_pr")
|
||||
def test_merge_pr_json_parse_failure(self, mock_merge: MagicMock) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 502
|
||||
mock_response.json.side_effect = ValueError("not json")
|
||||
err = requests.HTTPError("502", response=mock_response)
|
||||
mock_merge.side_effect = err
|
||||
@patch("scripts.auto_merge.GiteaClient")
|
||||
def test_merge_pr_json_parse_failure(self, mock_client_cls: MagicMock) -> None:
|
||||
mock_client = MagicMock()
|
||||
mock_client.merge_pr.side_effect = APIError(http.HTTPStatus.BAD_GATEWAY, "bad gateway")
|
||||
mock_client_cls.return_value = mock_client
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["GRM-19-fix", "fix: bug", "repo", "1"])
|
||||
result = runner.invoke(main, ["GRM-19-fix", "fix: bug", "owner/repo", "1"])
|
||||
assert result.exit_code == 1
|
||||
assert "502" in result.output
|
||||
assert str(http.HTTPStatus.BAD_GATEWAY) in result.output
|
||||
|
||||
Reference in New Issue
Block a user