""" Tests for webhooks endpoints. """ from __future__ import annotations import uuid import pytest from httpx import AsyncClient from sqlalchemy.ext.asyncio import AsyncSession from cpv3.modules.webhooks.models import Webhook from cpv3.modules.users.models import User @pytest.fixture async def test_webhook(test_db_session: AsyncSession, test_user: User) -> Webhook: """Create a test webhook owned by test_user.""" webhook = Webhook( id=uuid.uuid4(), user_id=test_user.id, event="job.completed", url="https://example.com/webhook", secret="test-secret-123", is_active=True, ) test_db_session.add(webhook) await test_db_session.commit() await test_db_session.refresh(webhook) return webhook @pytest.fixture async def other_webhook(test_db_session: AsyncSession, other_user: User) -> Webhook: """Create a webhook owned by another user.""" webhook = Webhook( id=uuid.uuid4(), user_id=other_user.id, event="job.failed", url="https://other.com/webhook", secret="other-secret-456", is_active=True, ) test_db_session.add(webhook) await test_db_session.commit() await test_db_session.refresh(webhook) return webhook class TestListWebhooksEndpoint: """Tests for GET /api/webhooks/.""" async def test_list_webhooks(self, auth_client: AsyncClient, test_webhook: Webhook): """Test listing webhooks.""" response = await auth_client.get("/api/webhooks/") assert response.status_code == 200 data = response.json() assert isinstance(data, list) async def test_list_webhooks_unauthenticated(self, async_client: AsyncClient): """Test listing webhooks without auth returns 401.""" response = await async_client.get("/api/webhooks/") assert response.status_code == 401 class TestCreateWebhookEndpoint: """Tests for POST /api/webhooks/.""" async def test_create_webhook_success(self, auth_client: AsyncClient): """Test creating a webhook.""" response = await auth_client.post( "/api/webhooks/", json={ "event": "transcription.completed", "url": "https://myapp.com/webhook", "secret": "my-webhook-secret", }, ) assert response.status_code == 201 data = response.json() assert data["event"] == "transcription.completed" assert data["url"] == "https://myapp.com/webhook" async def test_create_webhook_minimal(self, auth_client: AsyncClient): """Test creating a webhook with minimal fields.""" response = await auth_client.post( "/api/webhooks/", json={"url": "https://minimal.com/hook"}, ) assert response.status_code == 201 data = response.json() assert data["url"] == "https://minimal.com/hook" async def test_create_webhook_unauthenticated(self, async_client: AsyncClient): """Test creating webhook without auth returns 401.""" response = await async_client.post( "/api/webhooks/", json={"url": "https://test.com/hook"}, ) assert response.status_code == 401 class TestRetrieveWebhookEndpoint: """Tests for GET /api/webhooks/{webhook_id}/.""" async def test_retrieve_own_webhook( self, auth_client: AsyncClient, test_webhook: Webhook ): """Test retrieving own webhook.""" response = await auth_client.get(f"/api/webhooks/{test_webhook.id}/") assert response.status_code == 200 data = response.json() assert data["id"] == str(test_webhook.id) assert data["url"] == test_webhook.url async def test_retrieve_other_webhook_as_staff( self, staff_client: AsyncClient, test_webhook: Webhook ): """Test staff can retrieve any webhook.""" response = await staff_client.get(f"/api/webhooks/{test_webhook.id}/") assert response.status_code == 200 async def test_retrieve_nonexistent_webhook(self, auth_client: AsyncClient): """Test retrieving nonexistent webhook returns 404.""" fake_id = uuid.uuid4() response = await auth_client.get(f"/api/webhooks/{fake_id}/") assert response.status_code == 404 assert response.json()["detail"] == "Not found" async def test_retrieve_other_webhook_forbidden( self, auth_client: AsyncClient, other_webhook: Webhook ): """Test regular user cannot retrieve other user's webhook.""" response = await auth_client.get(f"/api/webhooks/{other_webhook.id}/") assert response.status_code == 403 assert response.json()["detail"] == "Forbidden" class TestPatchWebhookEndpoint: """Tests for PATCH /api/webhooks/{webhook_id}/.""" async def test_patch_own_webhook( self, auth_client: AsyncClient, test_webhook: Webhook ): """Test updating own webhook.""" response = await auth_client.patch( f"/api/webhooks/{test_webhook.id}/", json={ "url": "https://updated.com/webhook", "event": "job.started", }, ) assert response.status_code == 200 data = response.json() assert data["url"] == "https://updated.com/webhook" assert data["event"] == "job.started" async def test_patch_webhook_deactivate( self, auth_client: AsyncClient, test_webhook: Webhook ): """Test deactivating a webhook.""" response = await auth_client.patch( f"/api/webhooks/{test_webhook.id}/", json={"is_active": False}, ) assert response.status_code == 200 data = response.json() assert data["is_active"] is False async def test_patch_other_webhook_as_staff( self, staff_client: AsyncClient, test_webhook: Webhook ): """Test staff can update any webhook.""" response = await staff_client.patch( f"/api/webhooks/{test_webhook.id}/", json={"event": "staff.updated"}, ) assert response.status_code == 200 async def test_patch_nonexistent_webhook(self, auth_client: AsyncClient): """Test patching nonexistent webhook returns 404.""" fake_id = uuid.uuid4() response = await auth_client.patch( f"/api/webhooks/{fake_id}/", json={"url": "https://test.com"}, ) assert response.status_code == 404 async def test_patch_other_webhook_forbidden( self, auth_client: AsyncClient, other_webhook: Webhook ): """Test regular user cannot update other user's webhook.""" response = await auth_client.patch( f"/api/webhooks/{other_webhook.id}/", json={"url": "https://hacked.com"}, ) assert response.status_code == 403 class TestDeleteWebhookEndpoint: """Tests for DELETE /api/webhooks/{webhook_id}/.""" async def test_delete_own_webhook( self, auth_client: AsyncClient, test_webhook: Webhook ): """Test deleting own webhook.""" response = await auth_client.delete(f"/api/webhooks/{test_webhook.id}/") assert response.status_code == 204 async def test_delete_other_webhook_as_staff( self, staff_client: AsyncClient, test_webhook: Webhook ): """Test staff can delete any webhook.""" response = await staff_client.delete(f"/api/webhooks/{test_webhook.id}/") assert response.status_code == 204 async def test_delete_nonexistent_webhook(self, auth_client: AsyncClient): """Test deleting nonexistent webhook returns 404.""" fake_id = uuid.uuid4() response = await auth_client.delete(f"/api/webhooks/{fake_id}/") assert response.status_code == 404 async def test_delete_other_webhook_forbidden( self, auth_client: AsyncClient, other_webhook: Webhook ): """Test regular user cannot delete other user's webhook.""" response = await auth_client.delete(f"/api/webhooks/{other_webhook.id}/") assert response.status_code == 403