TASK-015: Add validation for GlobalConfigUpdate.log_target and log_level

- Add LogLevel Literal type: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG
- Add log_target validation to accept special values (STDOUT, STDERR, SYSLOG)
  or validated file paths within allowed directories
- Update GlobalConfigResponse to use LogLevel type
- Add field_validator for log_target in both GlobalConfigUpdate and
  GlobalConfigResponse following the same pattern as AddLogPathRequest
- Add @autouse fixture to test_config_service.py to mock get_settings
- Update existing tests to use uppercase log level values
- Add 12 comprehensive tests for new validation in test_models.py
- Update Features.md to document valid log_target and log_level values
- Add section to Backend-Development.md documenting Literal types and
  field_validator patterns with examples

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-26 13:57:22 +02:00
parent b9e046bd66
commit d66493f135
6 changed files with 306 additions and 45 deletions

View File

@@ -9,6 +9,7 @@ from unittest.mock import AsyncMock, patch
import pytest
from app.config import Settings
from app.models.config import (
GlobalConfigUpdate,
JailConfigListResponse,
@@ -22,6 +23,25 @@ from app.services.config_service import (
JailNotFoundError,
)
# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
@pytest.fixture(autouse=True)
def _mock_settings(monkeypatch: pytest.MonkeyPatch) -> None:
"""Mock get_settings for all tests in this module."""
def mock_get_settings() -> Settings:
return Settings(
database_path=":memory:",
fail2ban_socket="/tmp/fake.sock",
fail2ban_config_dir="/tmp/fail2ban",
session_secret="test-secret-key-do-not-use",
)
monkeypatch.setattr("app.models.config.get_settings", mock_get_settings)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
@@ -472,7 +492,7 @@ class TestUpdateGlobalConfig:
def __init__(self, **_kw: Any) -> None:
self.send = AsyncMock(side_effect=_send)
update = GlobalConfigUpdate(log_level="debug", db_purge_age=3600)
update = GlobalConfigUpdate(log_level="DEBUG", db_purge_age=3600)
with patch("app.services.config_service.Fail2BanClient", _FakeClient):
await config_service.update_global_config(_SOCKET, update)
@@ -492,7 +512,7 @@ class TestUpdateGlobalConfig:
def __init__(self, **_kw: Any) -> None:
self.send = AsyncMock(side_effect=_send)
update = GlobalConfigUpdate(log_level="debug")
update = GlobalConfigUpdate(log_level="DEBUG")
with patch("app.services.config_service.Fail2BanClient", _FakeClient):
await config_service.update_global_config(_SOCKET, update)