homelab_automation/tests/backend/test_routes_logs.py
Bruno Charest ecefbc8611
Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
Clean up test files and debug artifacts, add node_modules to gitignore, export DashboardManager for testing, and enhance pytest configuration with comprehensive test markers and settings
2025-12-15 08:15:49 -05:00

252 lines
8.5 KiB
Python

"""
Tests pour les routes de gestion des logs.
Couvre:
- Liste des logs
- Création de logs
- Suppression des logs
"""
import pytest
from unittest.mock import patch, AsyncMock
from httpx import AsyncClient
pytestmark = pytest.mark.unit
class TestGetLogs:
"""Tests pour GET /api/logs."""
async def test_list_logs_empty(self, client: AsyncClient, db_session):
"""Liste vide quand pas de logs."""
response = await client.get("/api/logs")
assert response.status_code == 200
assert isinstance(response.json(), list)
async def test_list_logs_with_data(self, client: AsyncClient, db_session):
"""Liste les logs depuis la BD."""
from app.crud.log import LogRepository
repo = LogRepository(db_session)
await repo.create(level="INFO", message="Test log 1", source="test")
await repo.create(level="ERROR", message="Test log 2", source="test")
await db_session.commit()
response = await client.get("/api/logs")
assert response.status_code == 200
logs = response.json()
assert len(logs) >= 2
async def test_list_logs_with_pagination(self, client: AsyncClient, db_session):
"""Pagination fonctionne."""
from app.crud.log import LogRepository
repo = LogRepository(db_session)
for i in range(5):
await repo.create(level="INFO", message=f"Log {i}", source="test")
await db_session.commit()
response = await client.get("/api/logs?limit=2&offset=0")
assert response.status_code == 200
logs = response.json()
assert isinstance(logs, list)
async def test_list_logs_filter_by_level(self, client: AsyncClient, db_session):
"""Filtre par niveau."""
from app.crud.log import LogRepository
repo = LogRepository(db_session)
await repo.create(level="INFO", message="Info log", source="test")
await repo.create(level="ERROR", message="Error log", source="test")
await db_session.commit()
response = await client.get("/api/logs?level=ERROR")
assert response.status_code == 200
async def test_list_logs_structure(self, client: AsyncClient, db_session):
"""Vérifie la structure de réponse."""
from app.crud.log import LogRepository
repo = LogRepository(db_session)
await repo.create(level="INFO", message="Structured log", source="test")
await db_session.commit()
response = await client.get("/api/logs")
assert response.status_code == 200
logs = response.json()
if logs:
log = logs[0]
assert "id" in log
assert "level" in log
assert "message" in log
class TestCreateLog:
"""Tests pour POST /api/logs."""
@pytest.mark.asyncio
async def test_create_log_success(self, client: AsyncClient, db_session):
"""Création de log réussie."""
with patch("app.routes.logs.ws_manager") as mock_ws:
mock_ws.broadcast = AsyncMock()
response = await client.post(
"/api/logs",
params={
"level": "INFO",
"message": "Test log message",
"source": "test"
}
)
assert response.status_code == 200
data = response.json()
assert data["level"] == "INFO"
assert data["message"] == "Test log message"
@pytest.mark.asyncio
async def test_create_log_broadcasts_ws(self, client: AsyncClient, db_session):
"""Création broadcast via WebSocket."""
with patch("app.routes.logs.ws_manager") as mock_ws:
mock_ws.broadcast = AsyncMock()
await client.post(
"/api/logs",
params={
"level": "WARNING",
"message": "Warning message"
}
)
mock_ws.broadcast.assert_called_once()
call_args = mock_ws.broadcast.call_args[0][0]
assert call_args["type"] == "new_log"
class TestClearLogs:
"""Tests pour DELETE /api/logs."""
@pytest.mark.asyncio
async def test_clear_logs_success(self, client: AsyncClient, db_session):
"""Suppression de tous les logs."""
from app.crud.log import LogRepository
repo = LogRepository(db_session)
await repo.create(level="INFO", message="To delete", source="test")
await db_session.commit()
response = await client.delete("/api/logs")
assert response.status_code == 200
assert "supprimés" in response.json()["message"]
class TestCreateLogWithHost:
"""Tests pour POST /api/logs avec host_id."""
@pytest.mark.asyncio
async def test_create_log_with_host_id(self, client: AsyncClient, db_session, host_factory):
"""Création de log avec host_id."""
host = await host_factory.create(db_session, name="log-host")
with patch("app.routes.logs.ws_manager") as mock_ws:
mock_ws.broadcast = AsyncMock()
response = await client.post(
"/api/logs",
params={
"level": "INFO",
"message": "Host log",
"source": "test",
"host_id": host.id
}
)
assert response.status_code == 200
data = response.json()
assert data["host"] == host.id
@pytest.mark.asyncio
async def test_create_log_error_level(self, client: AsyncClient, db_session):
"""Création de log niveau ERROR."""
with patch("app.routes.logs.ws_manager") as mock_ws:
mock_ws.broadcast = AsyncMock()
response = await client.post(
"/api/logs",
params={
"level": "error", # lowercase
"message": "Error message"
}
)
assert response.status_code == 200
data = response.json()
assert data["level"] == "ERROR" # Should be uppercased
class TestListLogsFilterBySource:
"""Tests pour GET /api/logs avec filtre source."""
@pytest.mark.asyncio
async def test_list_logs_filter_by_source(self, client: AsyncClient, db_session):
"""Filtre par source."""
from app.crud.log import LogRepository
repo = LogRepository(db_session)
await repo.create(level="INFO", message="Scheduler log", source="scheduler")
await repo.create(level="INFO", message="Task log", source="task")
await db_session.commit()
response = await client.get("/api/logs?source=scheduler")
assert response.status_code == 200
logs = response.json()
for log in logs:
if log.get("source"):
assert log["source"] == "scheduler"
@pytest.mark.asyncio
async def test_list_logs_combined_filters(self, client: AsyncClient, db_session):
"""Filtres combinés level + source."""
from app.crud.log import LogRepository
repo = LogRepository(db_session)
await repo.create(level="ERROR", message="Error from scheduler", source="scheduler")
await repo.create(level="INFO", message="Info from scheduler", source="scheduler")
await repo.create(level="ERROR", message="Error from task", source="task")
await db_session.commit()
response = await client.get("/api/logs?level=ERROR&source=scheduler")
assert response.status_code == 200
class TestClearLogsVerify:
"""Tests pour vérifier que DELETE /api/logs supprime bien les logs."""
@pytest.mark.asyncio
async def test_clear_logs_verify_empty(self, client: AsyncClient, db_session):
"""Vérifier que les logs sont bien supprimés."""
from app.crud.log import LogRepository
repo = LogRepository(db_session)
await repo.create(level="INFO", message="Log 1", source="test")
await repo.create(level="INFO", message="Log 2", source="test")
await db_session.commit()
# Clear
await client.delete("/api/logs")
# Verify empty
response = await client.get("/api/logs")
assert response.status_code == 200
# Logs should be empty or significantly reduced
logs = response.json()
assert isinstance(logs, list)