""" Tests pour les routes de gestion des alertes. Couvre: - Création d'alertes - Liste des alertes - Comptage non lues - Marquage comme lues """ import pytest from unittest.mock import patch, AsyncMock from httpx import AsyncClient pytestmark = pytest.mark.unit class TestCreateAlert: """Tests pour POST /api/alerts.""" @pytest.mark.asyncio async def test_create_alert_success(self, client: AsyncClient, db_session): """Création d'alerte réussie.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() response = await client.post( "/api/alerts", json={ "category": "system", "level": "warning", "title": "Test Alert", "message": "This is a test alert" } ) assert response.status_code == 200 data = response.json() assert data["title"] == "Test Alert" assert data["message"] == "This is a test alert" assert data["level"] == "warning" @pytest.mark.asyncio async def test_create_alert_minimal(self, client: AsyncClient, db_session): """Création avec champs minimaux.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() response = await client.post( "/api/alerts", json={"message": "Minimal alert"} ) assert response.status_code == 200 data = response.json() assert data["message"] == "Minimal alert" assert data["category"] == "general" # default @pytest.mark.asyncio async def test_create_alert_broadcasts_ws(self, client: AsyncClient, db_session): """Création broadcast via WebSocket.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() await client.post( "/api/alerts", json={"message": "WS test alert"} ) mock_ws.broadcast.assert_called_once() call_args = mock_ws.broadcast.call_args[0][0] assert call_args["type"] == "alert_created" class TestGetAlerts: """Tests pour GET /api/alerts.""" async def test_list_alerts_empty(self, client: AsyncClient, db_session): """Liste vide quand pas d'alertes.""" response = await client.get("/api/alerts") assert response.status_code == 200 data = response.json() assert "alerts" in data assert "count" in data @pytest.mark.asyncio async def test_list_alerts_with_data(self, client: AsyncClient, db_session): """Liste les alertes depuis la BD.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() await client.post("/api/alerts", json={"message": "Alert 1"}) await client.post("/api/alerts", json={"message": "Alert 2"}) response = await client.get("/api/alerts") assert response.status_code == 200 data = response.json() assert data["count"] >= 2 async def test_list_alerts_with_pagination(self, client: AsyncClient, db_session): """Pagination fonctionne.""" response = await client.get("/api/alerts?limit=5&offset=0") assert response.status_code == 200 data = response.json() assert "alerts" in data async def test_list_alerts_unread_only(self, client: AsyncClient, db_session): """Filtre alertes non lues.""" response = await client.get("/api/alerts?unread_only=true") assert response.status_code == 200 class TestUnreadCount: """Tests pour GET /api/alerts/unread-count.""" @pytest.mark.asyncio async def test_unread_count(self, client: AsyncClient, db_session): """Comptage des alertes non lues.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() # Create an alert first await client.post("/api/alerts", json={"message": "Test alert"}) response = await client.get("/api/alerts/unread-count") assert response.status_code == 200 data = response.json() # Response has 'unread' key instead of 'count' assert "unread" in data or "count" in data class TestMarkAsRead: """Tests pour POST /api/alerts/{alert_id}/read.""" @pytest.mark.asyncio async def test_mark_as_read_success(self, client: AsyncClient, db_session): """Marquer une alerte comme lue.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() create_response = await client.post( "/api/alerts", json={"message": "Alert to mark as read"} ) alert_id = create_response.json()["id"] response = await client.post(f"/api/alerts/{alert_id}/read") assert response.status_code == 200 data = response.json() assert str(data["id"]) == str(alert_id) assert "message" in data @pytest.mark.asyncio async def test_mark_as_read_not_found(self, client: AsyncClient, db_session): """Marquer une alerte inexistante retourne 404.""" response = await client.post("/api/alerts/nonexistent-id/read") assert response.status_code == 404 assert "non trouvée" in response.json()["detail"] class TestMarkAllAsRead: """Tests pour POST /api/alerts/mark-all-read.""" @pytest.mark.asyncio async def test_mark_all_as_read_success(self, client: AsyncClient, db_session): """Marquer toutes les alertes comme lues.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() # Create some alerts await client.post("/api/alerts", json={"message": "Alert 1"}) await client.post("/api/alerts", json={"message": "Alert 2"}) response = await client.post("/api/alerts/mark-all-read") assert response.status_code == 200 data = response.json() assert "message" in data @pytest.mark.asyncio async def test_mark_all_as_read_broadcasts_ws(self, client: AsyncClient, db_session): """Marquer tout comme lu broadcast via WebSocket.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() await client.post("/api/alerts", json={"message": "Alert"}) await client.post("/api/alerts/mark-all-read") # Should have been called for create and for mark-all-read assert mock_ws.broadcast.call_count >= 1 class TestDeleteAlert: """Tests pour DELETE /api/alerts/{alert_id}.""" @pytest.mark.asyncio async def test_delete_alert_success(self, client: AsyncClient, db_session): """Suppression d'une alerte réussie.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() create_response = await client.post( "/api/alerts", json={"message": "Alert to delete"} ) alert_id = create_response.json()["id"] response = await client.delete(f"/api/alerts/{alert_id}") assert response.status_code == 200 data = response.json() assert str(data["id"]) == str(alert_id) assert "supprimée" in data["message"].lower() or "message" in data @pytest.mark.asyncio async def test_delete_alert_not_found(self, client: AsyncClient, db_session): """Suppression d'une alerte inexistante retourne 404.""" response = await client.delete("/api/alerts/nonexistent-id") assert response.status_code == 404 assert "non trouvée" in response.json()["detail"] @pytest.mark.asyncio async def test_delete_alert_verify_removed(self, client: AsyncClient, db_session): """Vérifier que l'alerte est bien supprimée.""" with patch("app.routes.alerts.ws_manager") as mock_ws: mock_ws.broadcast = AsyncMock() create_response = await client.post( "/api/alerts", json={"message": "Alert to verify deletion"} ) alert_id = create_response.json()["id"] # Delete await client.delete(f"/api/alerts/{alert_id}") # Try to mark as read - should fail response = await client.post(f"/api/alerts/{alert_id}/read") assert response.status_code == 404