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
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.
358 lines
12 KiB
Python
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"
|