"""Unit tests for gitea_client module.""" from __future__ import annotations import json from unittest.mock import MagicMock, patch import pytest from gitea_runner_manager.gitea_client import GiteaAPIError, GiteaWorkflowClient class TestGiteaWorkflowClient: def _client(self) -> GiteaWorkflowClient: return GiteaWorkflowClient("https://git.example.com", "test-token") def test_list_workflows(self) -> None: client = self._client() mock_response = {"workflows": [{"id": 1, "name": "CI", "path": "ci.yml", "state": "active"}]} with patch.object(client, "_request", return_value=mock_response) as mock_req: result = client.list_workflows("oblachno-oss", "grm") assert len(result) == 1 assert result[0]["name"] == "CI" mock_req.assert_called_once_with("GET", "/repos/oblachno-oss/grm/actions/workflows") def test_list_workflows_empty(self) -> None: client = self._client() with patch.object(client, "_request", return_value=None): result = client.list_workflows("oblachno-oss", "grm") assert result == [] def test_dispatch_workflow(self) -> None: client = self._client() mock_response = {"id": 42, "html_url": "https://git.example.com/oblachno-oss/grm/actions/runs/42"} with patch.object(client, "_request", return_value=mock_response) as mock_req: result = client.dispatch_workflow("oblachno-oss", "grm", "ci.yml", ref="master") assert result is not None assert result["id"] == 42 mock_req.assert_called_once_with( "POST", "/repos/oblachno-oss/grm/actions/workflows/ci.yml/dispatches?return_run_details=true", {"ref": "master"}, ) def test_dispatch_workflow_with_inputs(self) -> None: client = self._client() with patch.object(client, "_request", return_value=None) as mock_req: client.dispatch_workflow("oblachno-oss", "grm", "build.yml", ref="master", inputs={"env": "prod"}) mock_req.assert_called_once_with( "POST", "/repos/oblachno-oss/grm/actions/workflows/build.yml/dispatches?return_run_details=true", {"ref": "master", "inputs": {"env": "prod"}}, ) def test_dispatch_workflow_api_error(self) -> None: client = self._client() with patch.object(client, "_request", side_effect=GiteaAPIError(404, "workflow not found")): with pytest.raises(GiteaAPIError) as exc_info: client.dispatch_workflow("oblachno-oss", "grm", "nonexistent.yml") assert exc_info.value.status == 404 class TestGiteaWorkflowClientRequest: """Test the underlying _request method with mocked urllib.""" def test_request_success(self) -> None: client = GiteaWorkflowClient("https://git.example.com/", "tok") mock_resp = MagicMock() mock_resp.status = 200 mock_resp.read.return_value = json.dumps({"ok": True}).encode() mock_resp.__enter__ = MagicMock(return_value=mock_resp) mock_resp.__exit__ = MagicMock(return_value=False) with patch("urllib.request.urlopen", return_value=mock_resp) as mock_urlopen: result = client._request("GET", "/test") assert result == {"ok": True} mock_urlopen.assert_called_once() def test_request_204_no_content(self) -> None: client = GiteaWorkflowClient("https://git.example.com", "tok") mock_resp = MagicMock() mock_resp.status = 204 mock_resp.__enter__ = MagicMock(return_value=mock_resp) mock_resp.__exit__ = MagicMock(return_value=False) with patch("urllib.request.urlopen", return_value=mock_resp): result = client._request("POST", "/test", {"ref": "master"}) assert result is None def test_request_http_error(self) -> None: import urllib.error client = GiteaWorkflowClient("https://git.example.com", "tok") err = urllib.error.HTTPError( "https://git.example.com/api/v1/test", 404, "Not Found", {}, __import__("io").BytesIO(b'{"message": "resource not found"}'), ) with patch("urllib.request.urlopen", side_effect=err): with pytest.raises(GiteaAPIError) as exc_info: client._request("GET", "/test") assert exc_info.value.status == 404 assert "resource not found" in exc_info.value.message def test_request_http_error_non_json(self) -> None: import urllib.error client = GiteaWorkflowClient("https://git.example.com", "tok") err = urllib.error.HTTPError( "https://git.example.com/api/v1/test", 500, "Internal Server Error", {}, __import__("io").BytesIO(b"plain text error"), ) with patch("urllib.request.urlopen", side_effect=err): with pytest.raises(GiteaAPIError) as exc_info: client._request("GET", "/test") assert exc_info.value.status == 500 assert "plain text error" in exc_info.value.message