fix: Restore all files lost during destructive rebase

A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-02-09 09:51:32 +01:00
parent f7487ee240
commit bfdaf63ba9
2009 changed files with 749983 additions and 1731 deletions

View File

@@ -0,0 +1,294 @@
"""
Tests for Recording API
Tests for Jibri webhook handling, recording management, and transcription endpoints.
"""
import pytest
from datetime import datetime, timedelta
from fastapi.testclient import TestClient
# Import the app (adjust import path as needed)
# In actual test environment, this would be the main FastAPI app
# from main import app
# For now, we create a minimal test setup
from fastapi import FastAPI
from recording_api import router as recording_router
app = FastAPI()
app.include_router(recording_router)
client = TestClient(app)
class TestJibriWebhook:
"""Tests for Jibri webhook endpoint."""
def test_webhook_recording_completed_valid(self):
"""Test webhook with valid recording_completed event."""
payload = {
"event": "recording_completed",
"recording_name": "test-room_20260115_120000",
"storage_path": "recordings/test-room_20260115_120000/video.mp4",
"audio_path": "recordings/test-room_20260115_120000/audio.wav",
"file_size_bytes": 52428800,
"timestamp": "2026-01-15T12:00:00Z"
}
response = client.post("/api/recordings/webhook", json=payload)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["status"] == "uploaded"
assert "recording_id" in data
assert data["meeting_id"] == "test-room"
def test_webhook_unknown_event_rejected(self):
"""Test that unknown event types are rejected."""
payload = {
"event": "unknown_event",
"recording_name": "test",
"storage_path": "test/video.mp4",
"file_size_bytes": 1000,
"timestamp": "2026-01-15T12:00:00Z"
}
response = client.post("/api/recordings/webhook", json=payload)
assert response.status_code == 400
assert "Unknown event type" in response.json()["error"]
def test_webhook_missing_required_fields(self):
"""Test that missing required fields cause validation error."""
payload = {
"event": "recording_completed"
# Missing other required fields
}
response = client.post("/api/recordings/webhook", json=payload)
assert response.status_code == 422 # Validation error
class TestRecordingManagement:
"""Tests for recording CRUD operations."""
@pytest.fixture(autouse=True)
def setup(self):
"""Create a test recording before each test."""
# Clear store and create test recording
from recording_api import _recordings_store
_recordings_store.clear()
payload = {
"event": "recording_completed",
"recording_name": "fixture-room_20260115_100000",
"storage_path": "recordings/fixture-room/video.mp4",
"file_size_bytes": 10000000,
"timestamp": "2026-01-15T10:00:00Z"
}
response = client.post("/api/recordings/webhook", json=payload)
self.recording_id = response.json()["recording_id"]
def test_list_recordings_empty(self):
"""Test listing recordings when empty."""
from recording_api import _recordings_store
_recordings_store.clear()
response = client.get("/api/recordings")
assert response.status_code == 200
data = response.json()
assert data["total"] == 0
assert data["recordings"] == []
def test_list_recordings_with_data(self):
"""Test listing recordings returns created recordings."""
response = client.get("/api/recordings")
assert response.status_code == 200
data = response.json()
assert data["total"] == 1
assert len(data["recordings"]) == 1
def test_list_recordings_filter_by_status(self):
"""Test filtering recordings by status."""
response = client.get("/api/recordings?status=uploaded")
assert response.status_code == 200
data = response.json()
assert all(r["status"] == "uploaded" for r in data["recordings"])
def test_list_recordings_pagination(self):
"""Test pagination of recordings list."""
response = client.get("/api/recordings?page=1&page_size=10")
assert response.status_code == 200
data = response.json()
assert data["page"] == 1
assert data["page_size"] == 10
def test_get_recording_by_id(self):
"""Test getting a specific recording by ID."""
response = client.get(f"/api/recordings/{self.recording_id}")
assert response.status_code == 200
data = response.json()
assert data["id"] == self.recording_id
assert data["status"] == "uploaded"
def test_get_recording_not_found(self):
"""Test getting non-existent recording returns 404."""
response = client.get("/api/recordings/nonexistent-id")
assert response.status_code == 404
assert "not found" in response.json()["detail"].lower()
def test_delete_recording(self):
"""Test soft-deleting a recording."""
response = client.delete(
f"/api/recordings/{self.recording_id}?reason=DSGVO%20request"
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["status"] == "deleted"
def test_delete_recording_requires_reason(self):
"""Test that deletion requires a reason."""
response = client.delete(f"/api/recordings/{self.recording_id}")
assert response.status_code == 422 # Missing required query param
class TestTranscriptionEndpoints:
"""Tests for transcription management."""
@pytest.fixture(autouse=True)
def setup(self):
"""Create test recording and clear transcription store."""
from recording_api import _recordings_store, _transcriptions_store
_recordings_store.clear()
_transcriptions_store.clear()
payload = {
"event": "recording_completed",
"recording_name": "trans-test_20260115_110000",
"storage_path": "recordings/trans-test/video.mp4",
"file_size_bytes": 5000000,
"timestamp": "2026-01-15T11:00:00Z"
}
response = client.post("/api/recordings/webhook", json=payload)
self.recording_id = response.json()["recording_id"]
def test_start_transcription(self):
"""Test starting a transcription job."""
response = client.post(
f"/api/recordings/{self.recording_id}/transcribe",
json={"language": "de", "model": "large-v3"}
)
assert response.status_code == 200
data = response.json()
assert data["status"] == "pending"
assert data["language"] == "de"
assert data["model"] == "large-v3"
def test_start_transcription_default_values(self):
"""Test transcription uses default values when not specified."""
response = client.post(
f"/api/recordings/{self.recording_id}/transcribe",
json={}
)
assert response.status_code == 200
data = response.json()
assert data["language"] == "de"
assert data["model"] == "large-v3"
def test_start_transcription_recording_not_found(self):
"""Test starting transcription for non-existent recording."""
response = client.post(
"/api/recordings/nonexistent/transcribe",
json={"language": "de"}
)
assert response.status_code == 404
def test_start_transcription_duplicate_rejected(self):
"""Test that duplicate transcription requests are rejected."""
# First request
client.post(
f"/api/recordings/{self.recording_id}/transcribe",
json={"language": "de"}
)
# Second request should fail
response = client.post(
f"/api/recordings/{self.recording_id}/transcribe",
json={"language": "de"}
)
assert response.status_code == 409
assert "already exists" in response.json()["detail"]
def test_get_transcription_status(self):
"""Test getting transcription status."""
# Start transcription first
client.post(
f"/api/recordings/{self.recording_id}/transcribe",
json={"language": "de"}
)
response = client.get(
f"/api/recordings/{self.recording_id}/transcription"
)
assert response.status_code == 200
data = response.json()
assert data["recording_id"] == self.recording_id
assert data["status"] == "pending"
def test_get_transcription_not_found(self):
"""Test getting transcription for recording without transcription."""
response = client.get(
f"/api/recordings/{self.recording_id}/transcription"
)
assert response.status_code == 404
class TestAuditLog:
"""Tests for audit log endpoints."""
def test_get_audit_log(self):
"""Test retrieving audit log entries."""
response = client.get("/api/recordings/audit/log")
assert response.status_code == 200
data = response.json()
assert "entries" in data
assert "total" in data
def test_get_audit_log_filter_by_action(self):
"""Test filtering audit log by action."""
response = client.get("/api/recordings/audit/log?action=created")
assert response.status_code == 200
class TestHealthCheck:
"""Tests for health check endpoint."""
def test_health_check(self):
"""Test health check returns healthy status."""
response = client.get("/api/recordings/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "healthy"
assert "recordings_count" in data
assert "minio_endpoint" in data