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:
Pouzor
2026-07-09 22:14:36 +02:00
parent 991169ba5a
commit 9205f9d36e
2 changed files with 146 additions and 0 deletions
+101
View File
@@ -10,6 +10,8 @@ from app.core.scheduler import (
_run_service_checks,
_run_status_checks,
reschedule_proxmox_sync,
reschedule_service_checks,
reschedule_status_checks,
set_proxmox_sync_enabled,
set_service_checks_enabled,
start_scheduler,
@@ -299,3 +301,102 @@ async def test_run_proxmox_sync_skips_when_no_token():
mock_settings.proxmox_token_id = ""
mock_settings.proxmox_token_secret = ""
await _run_proxmox_sync() # no exception, no fetch
def _proxmox_settings(mock_settings):
mock_settings.proxmox_sync_enabled = True
mock_settings.proxmox_host = "pve"
mock_settings.proxmox_port = 8006
mock_settings.proxmox_token_id = "root@pam!tok"
mock_settings.proxmox_token_secret = "secret"
mock_settings.proxmox_verify_tls = False
@pytest.mark.asyncio
async def test_run_proxmox_sync_persists_inventory():
db = MagicMock()
session_ctx = MagicMock()
session_ctx.__aenter__ = AsyncMock(return_value=db)
session_ctx.__aexit__ = AsyncMock(return_value=False)
result = MagicMock(device_count=3, pending_created=2, pending_updated=1)
with (
patch("app.core.scheduler.settings") as mock_settings,
patch("app.core.scheduler.AsyncSessionLocal", MagicMock(return_value=session_ctx)),
patch(
"app.services.proxmox_service.fetch_proxmox_inventory",
new_callable=AsyncMock,
return_value=(["node-raw"], ["edge-raw"]),
) as mock_fetch,
patch(
"app.api.routes.proxmox._persist_pending_import",
new_callable=AsyncMock,
return_value=result,
) as mock_persist,
):
_proxmox_settings(mock_settings)
await _run_proxmox_sync()
mock_fetch.assert_awaited_once()
mock_persist.assert_awaited_once_with(db, ["node-raw"], ["edge-raw"])
@pytest.mark.asyncio
async def test_run_proxmox_sync_logs_and_swallows_errors():
with (
patch("app.core.scheduler.settings") as mock_settings,
patch(
"app.services.proxmox_service.fetch_proxmox_inventory",
new_callable=AsyncMock,
side_effect=RuntimeError("proxmox api down"),
),
):
_proxmox_settings(mock_settings)
await _run_proxmox_sync() # exception is logged, never propagated
# --- reschedule_* validation and not-running guards ---
def test_reschedule_status_checks_rejects_short_interval():
with pytest.raises(ValueError):
reschedule_status_checks(5)
def test_reschedule_status_checks_noop_when_not_running():
mock_sched = MagicMock()
mock_sched.running = False
with patch("app.core.scheduler.scheduler", mock_sched):
reschedule_status_checks(60)
mock_sched.reschedule_job.assert_not_called()
def test_reschedule_status_checks_updates_running_job():
mock_sched = MagicMock()
mock_sched.running = True
with patch("app.core.scheduler.scheduler", mock_sched):
reschedule_status_checks(60)
mock_sched.reschedule_job.assert_called_once_with(
"status_checks", trigger="interval", seconds=60,
)
def test_reschedule_service_checks_rejects_short_interval():
with pytest.raises(ValueError):
reschedule_service_checks(10)
def test_reschedule_service_checks_updates_when_job_exists():
mock_sched = MagicMock()
mock_sched.running = True
mock_sched.get_job.return_value = MagicMock()
with patch("app.core.scheduler.scheduler", mock_sched):
reschedule_service_checks(60)
mock_sched.reschedule_job.assert_called_once_with(
"service_checks", trigger="interval", seconds=60,
)
def test_reschedule_proxmox_sync_noop_when_not_running():
mock_sched = MagicMock()
mock_sched.running = False
with patch("app.core.scheduler.scheduler", mock_sched):
reschedule_proxmox_sync(600)
mock_sched.reschedule_job.assert_not_called()
+45
View File
@@ -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"