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
252 lines
8.5 KiB
Python
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)
|