This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/backend/tests/test_meeting_consent_api.py
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

358 lines
12 KiB
Python

"""
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"