"""Integration tests for webhooks API endpoints. Tests webhook endpoints with mocked services. """ from datetime import datetime from unittest.mock import AsyncMock, patch from uuid import uuid4 import pytest from fastapi.testclient import TestClient from notebooklm_agent.api.main import app from notebooklm_agent.api.models.responses import Webhook @pytest.mark.unit class TestRegisterWebhookEndpoint: """Test suite for POST /api/v1/webhooks endpoint.""" def test_register_webhook_returns_201(self): """Should return 201 for successful registration.""" # Arrange client = TestClient(app) webhook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() mock_webhook = Webhook( id=webhook_id, url="https://example.com/webhook", events=["artifact.completed", "source.ready"], secret=True, active=True, created_at=datetime.utcnow(), last_triggered=None, failure_count=0, ) mock_service.register.return_value = mock_webhook mock_service_class.return_value = mock_service # Act response = client.post( "/api/v1/webhooks", json={ "url": "https://example.com/webhook", "events": ["artifact.completed", "source.ready"], "secret": "my-secret-key", }, ) # Assert assert response.status_code == 201 data = response.json() assert data["success"] is True assert data["data"]["url"] == "https://example.com/webhook" assert data["data"]["events"] == ["artifact.completed", "source.ready"] def test_register_webhook_http_url_returns_400(self): """Should return 400 for HTTP URL (not HTTPS).""" # Arrange client = TestClient(app) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() from notebooklm_agent.core.exceptions import ValidationError mock_service.register.side_effect = ValidationError("URL must use HTTPS") mock_service_class.return_value = mock_service # Act response = client.post( "/api/v1/webhooks", json={ "url": "http://example.com/webhook", "events": ["artifact.completed"], }, ) # Assert assert response.status_code in [400, 422] def test_register_webhook_invalid_event_returns_400(self): """Should return 400 for invalid event type.""" # Arrange client = TestClient(app) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() from notebooklm_agent.core.exceptions import ValidationError mock_service.register.side_effect = ValidationError("Invalid events") mock_service_class.return_value = mock_service # Act response = client.post( "/api/v1/webhooks", json={ "url": "https://example.com/webhook", "events": ["invalid.event"], }, ) # Assert assert response.status_code in [400, 422] @pytest.mark.unit class TestListWebhooksEndpoint: """Test suite for GET /api/v1/webhooks endpoint.""" def test_list_webhooks_returns_200(self): """Should return 200 with list of webhooks.""" # Arrange client = TestClient(app) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() mock_webhook = Webhook( id=uuid4(), url="https://example.com/webhook", events=["artifact.completed"], secret=False, active=True, created_at=datetime.utcnow(), last_triggered=None, failure_count=0, ) mock_service.list.return_value = [mock_webhook] mock_service_class.return_value = mock_service # Act response = client.get("/api/v1/webhooks") # Assert assert response.status_code == 200 data = response.json() assert data["success"] is True assert len(data["data"]) == 1 assert data["data"][0]["url"] == "https://example.com/webhook" def test_list_webhooks_empty_returns_empty_list(self): """Should return empty list if no webhooks.""" # Arrange client = TestClient(app) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() mock_service.list.return_value = [] mock_service_class.return_value = mock_service # Act response = client.get("/api/v1/webhooks") # Assert assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["data"] == [] @pytest.mark.unit class TestGetWebhookEndpoint: """Test suite for GET /api/v1/webhooks/{id} endpoint.""" def test_get_webhook_returns_200(self): """Should return 200 with webhook details.""" # Arrange client = TestClient(app) webhook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() mock_webhook = Webhook( id=webhook_id, url="https://example.com/webhook", events=["artifact.completed"], secret=False, active=True, created_at=datetime.utcnow(), last_triggered=None, failure_count=0, ) mock_service.get.return_value = mock_webhook mock_service_class.return_value = mock_service # Act response = client.get(f"/api/v1/webhooks/{webhook_id}") # Assert assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["data"]["id"] == webhook_id def test_get_webhook_not_found_returns_404(self): """Should return 404 when webhook not found.""" # Arrange client = TestClient(app) webhook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() from notebooklm_agent.core.exceptions import NotFoundError mock_service.get.side_effect = NotFoundError("Webhook", webhook_id) mock_service_class.return_value = mock_service # Act response = client.get(f"/api/v1/webhooks/{webhook_id}") # Assert assert response.status_code == 404 @pytest.mark.unit class TestDeleteWebhookEndpoint: """Test suite for DELETE /api/v1/webhooks/{id} endpoint.""" def test_delete_webhook_returns_204(self): """Should return 204 No Content for successful deletion.""" # Arrange client = TestClient(app) webhook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() mock_service.delete.return_value = None mock_service_class.return_value = mock_service # Act response = client.delete(f"/api/v1/webhooks/{webhook_id}") # Assert assert response.status_code == 204 assert response.content == b"" def test_delete_webhook_not_found_returns_404(self): """Should return 404 when webhook not found.""" # Arrange client = TestClient(app) webhook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() from notebooklm_agent.core.exceptions import NotFoundError mock_service.delete.side_effect = NotFoundError("Webhook", webhook_id) mock_service_class.return_value = mock_service # Act response = client.delete(f"/api/v1/webhooks/{webhook_id}") # Assert assert response.status_code == 404 @pytest.mark.unit class TestTestWebhookEndpoint: """Test suite for POST /api/v1/webhooks/{id}/test endpoint.""" def test_test_webhook_returns_200(self): """Should return 200 with test result.""" # Arrange client = TestClient(app) webhook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() mock_service.test_webhook.return_value = True mock_service_class.return_value = mock_service # Act response = client.post(f"/api/v1/webhooks/{webhook_id}/test") # Assert assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["data"]["test"] is True def test_test_webhook_not_found_returns_404(self): """Should return 404 when webhook not found.""" # Arrange client = TestClient(app) webhook_id = str(uuid4()) with patch("notebooklm_agent.api.routes.webhooks.WebhookService") as mock_service_class: mock_service = AsyncMock() from notebooklm_agent.core.exceptions import NotFoundError mock_service.test_webhook.side_effect = NotFoundError("Webhook", webhook_id) mock_service_class.return_value = mock_service # Act response = client.post(f"/api/v1/webhooks/{webhook_id}/test") # Assert assert response.status_code == 404