""" Tests for Meeting Consent API Tests for DSGVO-compliant consent management for meeting recordings. """ import pytest from fastapi.testclient import TestClient from fastapi import FastAPI # Import the router from meeting_consent_api import router as consent_router app = FastAPI() app.include_router(consent_router) client = TestClient(app) class TestConsentRequest: """Tests for requesting recording consent.""" @pytest.fixture(autouse=True) def setup(self): """Clear consent store before each test.""" from meeting_consent_api import _consent_store, _participant_consents _consent_store.clear() _participant_consents.clear() def test_request_consent_success(self): """Test requesting consent for a meeting.""" response = client.post( "/api/meeting-consent/request", json={ "meeting_id": "test-meeting-123", "consent_type": "opt_in", "participant_count": 3 } ) assert response.status_code == 200 data = response.json() assert data["meeting_id"] == "test-meeting-123" assert data["consent_type"] == "opt_in" assert data["participant_count"] == 3 assert data["all_consented"] is False assert data["can_record"] is False assert data["status"] == "pending" def test_request_consent_duplicate_rejected(self): """Test that duplicate consent requests are rejected.""" # First request client.post( "/api/meeting-consent/request", json={"meeting_id": "dup-meeting", "consent_type": "opt_in"} ) # Second request should fail response = client.post( "/api/meeting-consent/request", json={"meeting_id": "dup-meeting", "consent_type": "opt_in"} ) assert response.status_code == 409 assert "already exists" in response.json()["detail"] def test_request_consent_default_values(self): """Test consent request uses default values.""" response = client.post( "/api/meeting-consent/request", json={"meeting_id": "default-meeting"} ) assert response.status_code == 200 data = response.json() assert data["consent_type"] == "opt_in" class TestConsentStatus: """Tests for checking consent status.""" @pytest.fixture(autouse=True) def setup(self): """Create test consent before each test.""" from meeting_consent_api import _consent_store, _participant_consents _consent_store.clear() _participant_consents.clear() client.post( "/api/meeting-consent/request", json={ "meeting_id": "status-test-meeting", "consent_type": "opt_in", "participant_count": 2 } ) def test_get_consent_status_existing(self): """Test getting status for existing consent.""" response = client.get("/api/meeting-consent/status-test-meeting") assert response.status_code == 200 data = response.json() assert data["meeting_id"] == "status-test-meeting" assert data["status"] == "pending" def test_get_consent_status_not_requested(self): """Test getting status for meeting without consent request.""" response = client.get("/api/meeting-consent/nonexistent-meeting") assert response.status_code == 200 data = response.json() assert data["status"] == "not_requested" assert data["can_record"] is False class TestParticipantConsent: """Tests for recording individual participant consent.""" @pytest.fixture(autouse=True) def setup(self): """Create test consent with 2 participants.""" from meeting_consent_api import _consent_store, _participant_consents _consent_store.clear() _participant_consents.clear() client.post( "/api/meeting-consent/request", json={ "meeting_id": "participant-test", "consent_type": "opt_in", "participant_count": 2 } ) def test_record_participant_consent_positive(self): """Test recording positive consent from participant.""" response = client.post( "/api/meeting-consent/participant-test/participant", json={ "participant_id": "user-1", "participant_name": "Alice", "consented": True } ) assert response.status_code == 200 data = response.json() assert data["consented_count"] == 1 assert data["all_consented"] is False def test_record_participant_consent_negative(self): """Test recording negative consent from participant.""" response = client.post( "/api/meeting-consent/participant-test/participant", json={ "participant_id": "user-1", "consented": False } ) assert response.status_code == 200 data = response.json() assert data["consented"] is False def test_all_participants_consented_auto_approves(self): """Test that recording is approved when all participants consent.""" # First participant client.post( "/api/meeting-consent/participant-test/participant", json={"participant_id": "user-1", "consented": True} ) # Second participant (should trigger approval) response = client.post( "/api/meeting-consent/participant-test/participant", json={"participant_id": "user-2", "consented": True} ) assert response.status_code == 200 data = response.json() assert data["all_consented"] is True assert data["can_record"] is True def test_record_consent_meeting_not_found(self): """Test recording consent for non-existent meeting.""" response = client.post( "/api/meeting-consent/nonexistent/participant", json={"participant_id": "user-1", "consented": True} ) assert response.status_code == 404 def test_update_existing_participant_consent(self): """Test updating consent for same participant.""" # Initial consent client.post( "/api/meeting-consent/participant-test/participant", json={"participant_id": "user-1", "consented": True} ) # Update to negative response = client.post( "/api/meeting-consent/participant-test/participant", json={"participant_id": "user-1", "consented": False} ) assert response.status_code == 200 data = response.json() assert data["consented"] is False class TestConsentWithdrawal: """Tests for withdrawing consent.""" @pytest.fixture(autouse=True) def setup(self): """Create approved consent.""" from meeting_consent_api import _consent_store, _participant_consents _consent_store.clear() _participant_consents.clear() # Create and approve consent client.post( "/api/meeting-consent/request", json={ "meeting_id": "withdraw-test", "consent_type": "opt_in", "participant_count": 1 } ) client.post( "/api/meeting-consent/withdraw-test/participant", json={"participant_id": "user-1", "consented": True} ) def test_withdraw_consent(self): """Test withdrawing consent for a meeting.""" response = client.post( "/api/meeting-consent/withdraw-test/withdraw", json={"reason": "Changed my mind"} ) assert response.status_code == 200 data = response.json() assert data["status"] == "withdrawn" def test_withdraw_consent_stops_recording_capability(self): """Test that withdrawal stops recording capability.""" # Withdraw client.post( "/api/meeting-consent/withdraw-test/withdraw", json={} ) # Check status response = client.get("/api/meeting-consent/withdraw-test") data = response.json() assert data["status"] == "withdrawn" or data["status"] == "not_requested" def test_withdraw_consent_not_found(self): """Test withdrawing consent for non-existent meeting.""" response = client.post( "/api/meeting-consent/nonexistent/withdraw", json={} ) assert response.status_code == 404 class TestAnnouncedRecording: """Tests for announced recording mode.""" @pytest.fixture(autouse=True) def setup(self): """Clear store before each test.""" from meeting_consent_api import _consent_store, _participant_consents _consent_store.clear() _participant_consents.clear() def test_announce_recording(self): """Test announcing a recording.""" response = client.post( "/api/meeting-consent/announce?meeting_id=announced-meeting&announced_by=Teacher" ) assert response.status_code == 200 data = response.json() assert data["consent_type"] == "announced" assert data["can_record"] is True assert data["announced_by"] == "Teacher" def test_announce_recording_duplicate_rejected(self): """Test that duplicate announcements are rejected.""" # First announcement client.post( "/api/meeting-consent/announce?meeting_id=dup-announce&announced_by=Teacher" ) # Second announcement response = client.post( "/api/meeting-consent/announce?meeting_id=dup-announce&announced_by=Teacher" ) assert response.status_code == 409 class TestParticipantsList: """Tests for listing participant consents.""" @pytest.fixture(autouse=True) def setup(self): """Create test consent with participants.""" from meeting_consent_api import _consent_store, _participant_consents _consent_store.clear() _participant_consents.clear() client.post( "/api/meeting-consent/request", json={"meeting_id": "list-test", "participant_count": 2} ) client.post( "/api/meeting-consent/list-test/participant", json={"participant_id": "user-1-uuid-12345678", "consented": True} ) client.post( "/api/meeting-consent/list-test/participant", json={"participant_id": "user-2-uuid-87654321", "consented": False} ) def test_get_participants_list(self): """Test getting list of participant consents.""" response = client.get("/api/meeting-consent/list-test/participants") assert response.status_code == 200 data = response.json() assert len(data["participants"]) == 2 def test_participants_list_anonymized(self): """Test that participant IDs are anonymized.""" response = client.get("/api/meeting-consent/list-test/participants") data = response.json() # IDs should be truncated to last 8 chars for p in data["participants"]: assert len(p["participant_id"]) == 8 class TestHealthCheck: """Tests for health check endpoint.""" def test_health_check(self): """Test health check returns healthy status.""" response = client.get("/api/meeting-consent/health") assert response.status_code == 200 data = response.json() assert data["status"] == "healthy"