"""Tests for sonarqube_mcp.sonarqube_client."""
from __future__ import annotations
from unittest.mock import MagicMock, patch
import httpx
import pytest
from sonarqube_mcp.errors import SonarQubeError
from sonarqube_mcp.sonarqube_client import SonarQubeClient
class TestClientResourceManagement:
"""Tests for close() and context manager."""
def test_close_delegates(self, mock_client: SonarQubeClient) -> None:
mock_client.close()
mock_client._client.close.assert_called_once()
def test_context_manager(self, mock_client: SonarQubeClient) -> None:
with mock_client as c:
assert c is mock_client
mock_client._client.close.assert_called_once()
class TestGetErrorBranches:
"""Tests for _get error handling."""
def test_connect_error(self, mock_client: SonarQubeClient) -> None:
mock_client._client.get.side_effect = httpx.ConnectError("refused")
with pytest.raises(SonarQubeError, match="Cannot connect"):
mock_client._get("/api/test")
def test_timeout_error(self, mock_client: SonarQubeClient) -> None:
mock_client._client.get.side_effect = httpx.ReadTimeout("timed out")
with pytest.raises(SonarQubeError, match="timed out"):
mock_client._get("/api/test")
def test_generic_http_error(self, mock_client: SonarQubeClient) -> None:
mock_client._client.get.side_effect = httpx.HTTPError("something")
with pytest.raises(SonarQubeError, match="HTTP error"):
mock_client._get("/api/test")
@pytest.mark.parametrize(
("status", "expected_code"),
[
(401, "auth_error"),
(403, "forbidden"),
(404, "not_found"),
(500, "api_error"),
],
)
def test_status_code_errors(
self,
mock_client: SonarQubeClient,
status: int,
expected_code: str,
) -> None:
resp = MagicMock()
resp.status_code = status
resp.text = "error body"
mock_client._client.get.return_value = resp
with pytest.raises(SonarQubeError) as exc_info:
mock_client._get("/api/test")
assert exc_info.value.code == expected_code
def test_success_returns_json(self, mock_client: SonarQubeClient) -> None:
resp = MagicMock()
resp.status_code = 200
resp.json.return_value = {"status": "UP"}
mock_client._client.get.return_value = resp
assert mock_client._get("/api/test") == {"status": "UP"}
class TestPublicMethods:
"""Tests for public API methods."""
def test_get_system_status(self, mock_client: SonarQubeClient) -> None:
resp = MagicMock()
resp.status_code = 200
resp.json.return_value = {"status": "UP", "version": "10.0"}
mock_client._client.get.return_value = resp
result = mock_client.get_system_status()
assert result == {"status": "UP", "version": "10.0"}
mock_client._client.get.assert_called_once_with(
"/api/system/status", params=None
)
def test_search_projects(self, mock_client: SonarQubeClient) -> None:
resp = MagicMock()
resp.status_code = 200
resp.json.return_value = {"components": [], "paging": {}}
mock_client._client.get.return_value = resp
mock_client.search_projects(query="foo", page=2, page_size=10)
call_args = mock_client._client.get.call_args
assert call_args[0][0] == "/api/components/search"
params = call_args[1]["params"]
assert params["q"] == "foo"
assert params["p"] == 2
def test_search_issues(self, mock_client: SonarQubeClient) -> None:
resp = MagicMock()
resp.status_code = 200
resp.json.return_value = {"issues": [], "paging": {}}
mock_client._client.get.return_value = resp
mock_client.search_issues(project_key="proj", severities="MAJOR")
params = mock_client._client.get.call_args[1]["params"]
assert params["componentKeys"] == "proj"
assert params["severities"] == "MAJOR"
def test_get_issue_not_found(self, mock_client: SonarQubeClient) -> None:
resp = MagicMock()
resp.status_code = 200
resp.json.return_value = {"issues": []}
mock_client._client.get.return_value = resp
with pytest.raises(SonarQubeError, match="Issue not found"):
mock_client.get_issue("MISSING-1")
def test_get_measures(self, mock_client: SonarQubeClient) -> None:
resp = MagicMock()
resp.status_code = 200
resp.json.return_value = {"component": {"measures": []}}
mock_client._client.get.return_value = resp
mock_client.get_measures("proj", "bugs,coverage")
params = mock_client._client.get.call_args[1]["params"]
assert params["component"] == "proj"
assert params["metricKeys"] == "bugs,coverage"
def test_get_rule(self, mock_client: SonarQubeClient) -> None:
resp = MagicMock()
resp.status_code = 200
resp.json.return_value = {"rule": {"key": "py:S1192"}}
mock_client._client.get.return_value = resp
result = mock_client.get_rule("py:S1192")
assert result["rule"]["key"] == "py:S1192"