bcc488993d
Adds optional live status checking per service (not just per node), requested as a follow-up to issue #196. Backend: - New check_service / check_services: HTTP(S) GET for web services, TCP connect otherwise; UDP and port-less non-web services stay 'unknown'. - New scheduler job 'service_checks', independent interval (default 300s), added/removed live via set_service_checks_enabled. - Settings gain service_check_enabled + service_check_interval (>=30s), persisted to scan_config.json. New WS message type 'service_status'. Frontend: - Live per-service status overlay in canvasStore (not persisted, so it never round-trips through canvas save), fed by the WS message. - DetailPanel + canvas node service rows: offline service turns red (#f85149), otherwise keeps its category colour. - SettingsModal: toggle + interval input (default 300s / 5 min). Off by default — no behaviour change until enabled. ha-relevant: yes
87 lines
2.9 KiB
Python
87 lines
2.9 KiB
Python
"""Tests for GET/POST /api/v1/settings."""
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from httpx import AsyncClient
|
|
|
|
|
|
@pytest.fixture
|
|
async def headers(client: AsyncClient):
|
|
res = await client.post("/api/v1/auth/login", json={"username": "admin", "password": "admin"})
|
|
token = res.json()["access_token"]
|
|
return {"Authorization": f"Bearer {token}"}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_settings_requires_auth(client: AsyncClient):
|
|
res = await client.get("/api/v1/settings")
|
|
assert res.status_code == 401
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_settings_returns_interval(client: AsyncClient, headers):
|
|
res = await client.get("/api/v1/settings", headers=headers)
|
|
assert res.status_code == 200
|
|
data = res.json()
|
|
assert "interval_seconds" in data
|
|
assert isinstance(data["interval_seconds"], int)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_settings_saves_interval(client: AsyncClient, headers):
|
|
with patch("app.api.routes.settings.settings") as mock_settings:
|
|
mock_settings.status_checker_interval = 60
|
|
mock_settings.save_overrides = lambda: None
|
|
res = await client.post(
|
|
"/api/v1/settings",
|
|
json={"interval_seconds": 120},
|
|
headers=headers,
|
|
)
|
|
assert res.status_code == 200
|
|
assert res.json()["interval_seconds"] == 120
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_settings_requires_auth(client: AsyncClient):
|
|
res = await client.post("/api/v1/settings", json={"interval_seconds": 30})
|
|
assert res.status_code == 401
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_settings_returns_service_check_fields(client: AsyncClient, headers):
|
|
res = await client.get("/api/v1/settings", headers=headers)
|
|
data = res.json()
|
|
assert "service_check_enabled" in data
|
|
assert "service_check_interval" in data
|
|
assert isinstance(data["service_check_enabled"], bool)
|
|
assert isinstance(data["service_check_interval"], int)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_settings_saves_service_check_fields(client: AsyncClient, headers):
|
|
with patch("app.api.routes.settings.settings") as mock_settings:
|
|
mock_settings.save_overrides = lambda: None
|
|
res = await client.post(
|
|
"/api/v1/settings",
|
|
json={
|
|
"interval_seconds": 60,
|
|
"service_check_enabled": True,
|
|
"service_check_interval": 600,
|
|
},
|
|
headers=headers,
|
|
)
|
|
assert res.status_code == 200
|
|
body = res.json()
|
|
assert body["service_check_enabled"] is True
|
|
assert body["service_check_interval"] == 600
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_settings_rejects_too_short_service_interval(client: AsyncClient, headers):
|
|
res = await client.post(
|
|
"/api/v1/settings",
|
|
json={"interval_seconds": 60, "service_check_enabled": True, "service_check_interval": 5},
|
|
headers=headers,
|
|
)
|
|
assert res.status_code == 422
|