test: close coverage gaps hidden by seam mocking
Audit of the heavy-mock suites (scanner/status_checker/scheduler) confirmed the mocking is sound layered seam-mocking, not brittle internal patching. It did hide real gaps where a primitive was always mocked and never exercised: - status_checker._http_get: add status-code interpretation tests (2xx/4xx online, 5xx offline) + service-check exception -> offline. Module coverage 95% -> 100%. - scheduler.reschedule_status_checks / reschedule_service_checks were wholly untested; add validation + guard tests. Add _run_proxmox_sync happy-path and error-swallow tests. Module coverage 76% -> 91%. 639 passed. ha-relevant: no
This commit is contained in:
@@ -4,6 +4,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
import pytest
|
||||
|
||||
from app.services.status_checker import (
|
||||
_http_get,
|
||||
_ping,
|
||||
_tcp_connect,
|
||||
check_node,
|
||||
@@ -11,6 +12,18 @@ from app.services.status_checker import (
|
||||
check_services,
|
||||
)
|
||||
|
||||
|
||||
def _mock_httpx_client(status_code):
|
||||
"""Build a stand-in for httpx.AsyncClient whose GET returns status_code."""
|
||||
resp = MagicMock()
|
||||
resp.status_code = status_code
|
||||
client = MagicMock()
|
||||
client.get = AsyncMock(return_value=resp)
|
||||
ctx = MagicMock()
|
||||
ctx.__aenter__ = AsyncMock(return_value=client)
|
||||
ctx.__aexit__ = AsyncMock(return_value=False)
|
||||
return MagicMock(return_value=ctx)
|
||||
|
||||
# --- check_node dispatcher ---
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -462,3 +475,35 @@ async def test_check_services_returns_status_per_service():
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_services_empty_list():
|
||||
assert await check_services("10.0.0.1", []) == []
|
||||
|
||||
|
||||
# --- _http_get status-code interpretation (real primitive, mocked transport) ---
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_http_get_true_on_2xx():
|
||||
with patch("app.services.status_checker.httpx.AsyncClient", _mock_httpx_client(200)):
|
||||
assert await _http_get("http://host") is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_http_get_true_on_4xx():
|
||||
# A 4xx means the server is up and answering — still "online".
|
||||
with patch("app.services.status_checker.httpx.AsyncClient", _mock_httpx_client(404)):
|
||||
assert await _http_get("http://host") is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_http_get_false_on_5xx():
|
||||
with patch("app.services.status_checker.httpx.AsyncClient", _mock_httpx_client(503)):
|
||||
assert await _http_get("http://host") is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_service_http_exception_returns_offline():
|
||||
svc = {"port": 80, "protocol": "tcp", "service_name": "http"}
|
||||
with patch(
|
||||
"app.services.status_checker._http_get",
|
||||
new_callable=AsyncMock,
|
||||
side_effect=RuntimeError("connection refused"),
|
||||
):
|
||||
assert await check_service(svc, "10.0.0.1") == "offline"
|
||||
|
||||
Reference in New Issue
Block a user