""" Tests pour les routes de notifications ntfy. Couvre: - GET /api/notifications/config - POST /api/notifications/test - POST /api/notifications/send - POST /api/notifications/toggle """ import pytest from unittest.mock import patch, MagicMock, AsyncMock from httpx import AsyncClient pytestmark = [pytest.mark.unit, pytest.mark.asyncio] class TestGetNotificationConfig: """Tests pour GET /api/notifications/config.""" async def test_get_config_success(self, client: AsyncClient): """Récupère la configuration des notifications.""" mock_config = MagicMock() mock_config.enabled = True mock_config.base_url = "https://ntfy.sh" mock_config.default_topic = "homelab" mock_config.timeout = 10 mock_config.has_auth = False with patch("app.routes.notifications.notification_service") as mock_service: mock_service.config = mock_config response = await client.get("/api/notifications/config") assert response.status_code == 200 data = response.json() assert data["enabled"] is True assert data["base_url"] == "https://ntfy.sh" assert data["default_topic"] == "homelab" assert data["timeout"] == 10 assert data["has_auth"] is False async def test_get_config_with_auth(self, client: AsyncClient): """Récupère la configuration avec authentification.""" mock_config = MagicMock() mock_config.enabled = True mock_config.base_url = "https://ntfy.example.com" mock_config.default_topic = "alerts" mock_config.timeout = 30 mock_config.has_auth = True with patch("app.routes.notifications.notification_service") as mock_service: mock_service.config = mock_config response = await client.get("/api/notifications/config") assert response.status_code == 200 data = response.json() assert data["has_auth"] is True class TestTestNotification: """Tests pour POST /api/notifications/test.""" async def test_send_test_notification_success(self, client: AsyncClient): """Envoi d'une notification de test réussie.""" mock_config = MagicMock() mock_config.default_topic = "homelab" with patch("app.routes.notifications.notification_service") as mock_service: mock_service.send = AsyncMock(return_value=True) mock_service.config = mock_config response = await client.post("/api/notifications/test") assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["topic"] == "homelab" assert "envoyée" in data["message"] async def test_send_test_notification_with_custom_topic(self, client: AsyncClient): """Envoi d'une notification de test avec topic personnalisé.""" with patch("app.routes.notifications.notification_service") as mock_service: mock_service.send = AsyncMock(return_value=True) mock_service.config.default_topic = "default" response = await client.post("/api/notifications/test?topic=custom-topic") assert response.status_code == 200 data = response.json() assert data["topic"] == "custom-topic" async def test_send_test_notification_with_custom_message(self, client: AsyncClient): """Envoi d'une notification de test avec message personnalisé.""" mock_config = MagicMock() mock_config.default_topic = "homelab" with patch("app.routes.notifications.notification_service") as mock_service: mock_service.send = AsyncMock(return_value=True) mock_service.config = mock_config response = await client.post( "/api/notifications/test?message=Custom%20test%20message" ) assert response.status_code == 200 mock_service.send.assert_called_once() call_args = mock_service.send.call_args assert call_args.kwargs["message"] == "Custom test message" async def test_send_test_notification_failure(self, client: AsyncClient): """Échec de l'envoi d'une notification de test.""" mock_config = MagicMock() mock_config.default_topic = "homelab" with patch("app.routes.notifications.notification_service") as mock_service: mock_service.send = AsyncMock(return_value=False) mock_service.config = mock_config response = await client.post("/api/notifications/test") assert response.status_code == 200 data = response.json() assert data["success"] is False assert "Échec" in data["message"] class TestSendCustomNotification: """Tests pour POST /api/notifications/send.""" async def test_send_custom_notification_success(self, client: AsyncClient): """Envoi d'une notification personnalisée réussie.""" with patch("app.routes.notifications.notification_service") as mock_service: mock_service.send_request = AsyncMock(return_value={ "success": True, "topic": "alerts", "message": "Notification envoyée" }) response = await client.post( "/api/notifications/send", json={ "message": "Alert: Server down!", "title": "Critical Alert", "priority": 5, "tags": ["warning", "server"] } ) assert response.status_code == 200 data = response.json() assert data["success"] is True async def test_send_custom_notification_with_topic(self, client: AsyncClient): """Envoi d'une notification avec topic spécifique.""" with patch("app.routes.notifications.notification_service") as mock_service: mock_service.send_request = AsyncMock(return_value={ "success": True, "topic": "custom-alerts", "message": "Notification envoyée" }) response = await client.post( "/api/notifications/send", json={ "topic": "custom-alerts", "message": "Custom notification" } ) assert response.status_code == 200 async def test_send_custom_notification_minimal(self, client: AsyncClient): """Envoi d'une notification avec payload minimal.""" with patch("app.routes.notifications.notification_service") as mock_service: mock_service.send_request = AsyncMock(return_value={ "success": True, "topic": "default", "message": "OK" }) response = await client.post( "/api/notifications/send", json={"message": "Simple message"} ) assert response.status_code == 200 class TestToggleNotifications: """Tests pour POST /api/notifications/toggle.""" async def test_toggle_notifications_enable(self, client: AsyncClient): """Active les notifications.""" mock_config = MagicMock() mock_config.base_url = "https://ntfy.sh" mock_config.default_topic = "homelab" mock_config.timeout = 10 mock_config.username = None mock_config.password = None mock_config.token = None with patch("app.routes.notifications.notification_service") as mock_service: mock_service.config = mock_config mock_service.reconfigure = MagicMock() response = await client.post("/api/notifications/toggle?enabled=true") assert response.status_code == 200 data = response.json() assert data["enabled"] is True assert "activées" in data["message"] mock_service.reconfigure.assert_called_once() async def test_toggle_notifications_disable(self, client: AsyncClient): """Désactive les notifications.""" mock_config = MagicMock() mock_config.base_url = "https://ntfy.sh" mock_config.default_topic = "homelab" mock_config.timeout = 10 mock_config.username = None mock_config.password = None mock_config.token = None with patch("app.routes.notifications.notification_service") as mock_service: mock_service.config = mock_config mock_service.reconfigure = MagicMock() response = await client.post("/api/notifications/toggle?enabled=false") assert response.status_code == 200 data = response.json() assert data["enabled"] is False assert "désactivées" in data["message"] async def test_toggle_preserves_config(self, client: AsyncClient): """Toggle préserve la configuration existante.""" mock_config = MagicMock() mock_config.base_url = "https://custom.ntfy.sh" mock_config.default_topic = "custom-topic" mock_config.timeout = 30 mock_config.username = "user" mock_config.password = "pass" mock_config.token = "token123" with patch("app.routes.notifications.notification_service") as mock_service: mock_service.config = mock_config mock_service.reconfigure = MagicMock() response = await client.post("/api/notifications/toggle?enabled=true") assert response.status_code == 200 # Verify reconfigure was called with preserved values call_args = mock_service.reconfigure.call_args[0][0] assert call_args.base_url == "https://custom.ntfy.sh" assert call_args.default_topic == "custom-topic" assert call_args.timeout == 30