Files
homelable/backend/tests/test_settings.py
T
Pouzor 9437a74147 feat: manual Proxmox re-sync button + make connection config env-only
Add a 'Re-sync now' button to the Proxmox auto-sync settings section that
triggers an immediate inventory import (POST /proxmox/sync-now) using the
server env config — the manual counterpart to scheduled auto-sync.

Also fix a dual-source-of-truth bug: Proxmox connection config (host, port,
token, verify_tls) is now env-only and never persisted to scan_config.json.
Previously save_overrides() dumped host/port/verify alongside scan settings,
so saving an unrelated setting wrote an empty proxmox_host that load_overrides
then clobbered PROXMOX_HOST with on every boot. Only the auto-sync activation
(sync_enabled + sync_interval) stays user-editable and persisted.

ha-relevant: no
2026-07-07 21:20:13 +02:00

133 lines
4.8 KiB
Python

"""Tests for GET/POST /api/v1/settings."""
import json
from unittest.mock import patch
import pytest
from httpx import AsyncClient
from app.core.config import Settings
@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
def test_proxmox_connection_config_is_env_only_never_from_overrides(tmp_path):
"""Connection config (host/port/verify_tls) is env-only: a stale value in
scan_config.json must be ignored so it can never clobber the env. Only the
auto-sync activation is read back."""
s = Settings(secret_key="x", sqlite_path=str(tmp_path / "homelab.db"))
s.proxmox_host = "pve.local" # as if set from env
s.proxmox_port = 8006
s.proxmox_verify_tls = True
(tmp_path / "scan_config.json").write_text(json.dumps({
"proxmox_host": "stale-host",
"proxmox_port": 9999,
"proxmox_verify_tls": False,
"proxmox_sync_enabled": True,
"proxmox_sync_interval": 600,
}))
s.load_overrides()
# Connection config untouched by the file (env values survive).
assert s.proxmox_host == "pve.local"
assert s.proxmox_port == 8006
assert s.proxmox_verify_tls is True
# Auto-sync activation is the only thing loaded.
assert s.proxmox_sync_enabled is True
assert s.proxmox_sync_interval == 600
def test_save_overrides_omits_proxmox_connection_config(tmp_path):
"""save_overrides must never write host/port/verify_tls (nor the token) —
only the sync activation. This is what prevents the dual source of truth."""
s = Settings(secret_key="x", sqlite_path=str(tmp_path / "homelab.db"))
s.proxmox_host = "pve.local"
s.proxmox_sync_enabled = True
s.proxmox_sync_interval = 900
s.save_overrides()
written = json.loads((tmp_path / "scan_config.json").read_text())
assert "proxmox_host" not in written
assert "proxmox_port" not in written
assert "proxmox_verify_tls" not in written
assert "proxmox_token_id" not in written
assert "proxmox_token_secret" not in written
assert written["proxmox_sync_enabled"] is True
assert written["proxmox_sync_interval"] == 900