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_worksheets_api.py
Benjamin Admin bfdaf63ba9 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>
2026-02-09 09:51:32 +01:00

529 lines
17 KiB
Python

"""
Tests für Worksheets API.
Testet alle Content-Generatoren:
- Multiple Choice
- Cloze (Lückentext)
- Mindmap
- Quiz
"""
import pytest
from fastapi.testclient import TestClient
# Importiere main app
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from main import app
client = TestClient(app)
# Sample text für Tests (mind. 50 Zeichen erforderlich)
SAMPLE_TEXT = """
Die Photosynthese ist ein biologischer Prozess, bei dem Pflanzen, Algen und einige Bakterien
Lichtenergie in chemische Energie umwandeln. Dieser Vorgang findet hauptsächlich in den
Chloroplasten der Pflanzenzellen statt. Dabei werden Kohlendioxid und Wasser unter Verwendung
von Sonnenlicht in Glucose und Sauerstoff umgewandelt. Die Photosynthese ist fundamental für
das Leben auf der Erde, da sie Sauerstoff produziert und die Basis der Nahrungskette bildet.
Pflanzen nutzen die produzierte Glucose als Energiequelle für ihr Wachstum.
"""
SHORT_TEXT = "Zu kurz"
class TestMultipleChoiceGeneration:
"""Tests für Multiple Choice Generierung."""
def test_generate_mc_success(self):
"""Testet erfolgreiche MC-Generierung."""
response = client.post(
"/api/worksheets/generate/multiple-choice",
json={
"source_text": SAMPLE_TEXT,
"num_questions": 3,
"difficulty": "medium",
"topic": "Photosynthese",
"subject": "Biologie"
}
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["content"] is not None
assert data["content"]["content_type"] == "multiple_choice"
assert "questions" in data["content"]["data"]
def test_generate_mc_with_h5p(self):
"""Testet H5P-Export für MC."""
response = client.post(
"/api/worksheets/generate/multiple-choice",
json={
"source_text": SAMPLE_TEXT,
"num_questions": 2,
"difficulty": "easy"
}
)
assert response.status_code == 200
data = response.json()
assert data["content"]["h5p_format"] is not None
assert data["content"]["h5p_format"]["library"] == "H5P.MultiChoice"
def test_generate_mc_text_too_short(self):
"""Testet Fehler bei zu kurzem Text."""
response = client.post(
"/api/worksheets/generate/multiple-choice",
json={
"source_text": SHORT_TEXT,
"num_questions": 3
}
)
# Pydantic Validation sollte fehlschlagen
assert response.status_code == 422
def test_generate_mc_difficulty_levels(self):
"""Testet verschiedene Schwierigkeitsgrade."""
for difficulty in ["easy", "medium", "hard"]:
response = client.post(
"/api/worksheets/generate/multiple-choice",
json={
"source_text": SAMPLE_TEXT,
"num_questions": 2,
"difficulty": difficulty
}
)
assert response.status_code == 200
data = response.json()
assert data["content"]["difficulty"] == difficulty
class TestClozeGeneration:
"""Tests für Lückentext-Generierung."""
def test_generate_cloze_fill_in(self):
"""Testet Fill-in Lückentext."""
response = client.post(
"/api/worksheets/generate/cloze",
json={
"source_text": SAMPLE_TEXT,
"num_gaps": 3,
"cloze_type": "fill_in",
"topic": "Photosynthese"
}
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["content"]["content_type"] == "cloze"
assert "gaps" in data["content"]["data"]
assert "text_with_gaps" in data["content"]["data"]
def test_generate_cloze_drag_drop(self):
"""Testet Drag-Drop Lückentext."""
response = client.post(
"/api/worksheets/generate/cloze",
json={
"source_text": SAMPLE_TEXT,
"num_gaps": 4,
"cloze_type": "drag_drop"
}
)
assert response.status_code == 200
data = response.json()
assert data["content"]["data"]["cloze_type"] == "drag_drop"
def test_generate_cloze_dropdown(self):
"""Testet Dropdown Lückentext."""
response = client.post(
"/api/worksheets/generate/cloze",
json={
"source_text": SAMPLE_TEXT,
"num_gaps": 3,
"cloze_type": "dropdown"
}
)
assert response.status_code == 200
data = response.json()
assert data["content"]["data"]["cloze_type"] == "dropdown"
def test_generate_cloze_h5p_format(self):
"""Testet H5P-Export für Cloze."""
response = client.post(
"/api/worksheets/generate/cloze",
json={
"source_text": SAMPLE_TEXT,
"num_gaps": 3
}
)
assert response.status_code == 200
data = response.json()
assert data["content"]["h5p_format"]["library"] == "H5P.Blanks"
class TestMindmapGeneration:
"""Tests für Mindmap-Generierung."""
def test_generate_mindmap_success(self):
"""Testet erfolgreiche Mindmap-Generierung."""
response = client.post(
"/api/worksheets/generate/mindmap",
json={
"source_text": SAMPLE_TEXT,
"topic": "Photosynthese",
"max_depth": 3
}
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["content"]["content_type"] == "mindmap"
assert "mindmap" in data["content"]["data"]
assert "mermaid" in data["content"]["data"]
assert "json_tree" in data["content"]["data"]
def test_generate_mindmap_no_h5p(self):
"""Testet dass Mindmaps kein H5P-Format haben."""
response = client.post(
"/api/worksheets/generate/mindmap",
json={
"source_text": SAMPLE_TEXT,
"max_depth": 2
}
)
assert response.status_code == 200
data = response.json()
assert data["content"]["h5p_format"] is None
def test_generate_mindmap_structure(self):
"""Testet Mindmap-Struktur."""
response = client.post(
"/api/worksheets/generate/mindmap",
json={
"source_text": SAMPLE_TEXT,
"max_depth": 3
}
)
data = response.json()
mindmap = data["content"]["data"]["mindmap"]
assert "root" in mindmap
assert "title" in mindmap
assert "total_nodes" in mindmap
assert mindmap["root"]["level"] == 0
class TestQuizGeneration:
"""Tests für Quiz-Generierung."""
def test_generate_quiz_true_false(self):
"""Testet True/False Quiz."""
response = client.post(
"/api/worksheets/generate/quiz",
json={
"source_text": SAMPLE_TEXT,
"quiz_types": ["true_false"],
"num_items": 3
}
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert "questions" in data["content"]["data"]
# All questions should be of type true_false
questions = data["content"]["data"]["questions"]
assert len(questions) > 0
assert all(q["type"] == "true_false" for q in questions)
def test_generate_quiz_matching(self):
"""Testet Matching Quiz mit definitionsreichem Text."""
# Text with clear definition patterns for matching extraction
definition_text = """
Photosynthese ist der Prozess der Umwandlung von Lichtenergie in chemische Energie.
Chloroplasten bezeichnet die Zellorganellen, in denen die Photosynthese stattfindet.
Glucose bedeutet Traubenzucker, ein einfacher Zucker.
ATP: Adenosintriphosphat, der universelle Energietraeger der Zellen.
Chlorophyll ist der gruene Farbstoff, der Licht absorbiert.
"""
response = client.post(
"/api/worksheets/generate/quiz",
json={
"source_text": definition_text,
"quiz_types": ["matching"],
"num_items": 3
}
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert "questions" in data["content"]["data"]
# All questions should be of type matching
questions = data["content"]["data"]["questions"]
assert len(questions) > 0
assert all(q["type"] == "matching" for q in questions)
def test_generate_quiz_combined(self):
"""Testet kombiniertes Quiz mit mehreren Typen."""
response = client.post(
"/api/worksheets/generate/quiz",
json={
"source_text": SAMPLE_TEXT,
"quiz_types": ["true_false", "matching", "sorting"],
"num_items": 2,
"difficulty": "medium"
}
)
assert response.status_code == 200
data = response.json()
quiz_data = data["content"]["data"]
assert "questions" in quiz_data
assert "quiz_types" in quiz_data
# Should have questions from multiple types
question_types = set(q["type"] for q in quiz_data["questions"])
assert len(question_types) >= 1 # At least one type should have generated questions
class TestBatchGeneration:
"""Tests für Batch-Generierung."""
def test_batch_generate_multiple_types(self):
"""Testet Generierung mehrerer Content-Typen."""
response = client.post(
"/api/worksheets/generate/batch",
json={
"source_text": SAMPLE_TEXT,
"content_types": ["multiple_choice", "cloze"],
"topic": "Photosynthese",
"difficulty": "medium"
}
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert len(data["contents"]) == 2
content_types = [c["content_type"] for c in data["contents"]]
assert "multiple_choice" in content_types
assert "cloze" in content_types
def test_batch_generate_all_types(self):
"""Testet Generierung aller Content-Typen."""
response = client.post(
"/api/worksheets/generate/batch",
json={
"source_text": SAMPLE_TEXT,
"content_types": ["multiple_choice", "cloze", "mindmap", "quiz"],
"topic": "Test"
}
)
assert response.status_code == 200
data = response.json()
assert len(data["contents"]) == 4
def test_batch_generate_partial_failure(self):
"""Testet Batch mit unbekanntem Typ."""
response = client.post(
"/api/worksheets/generate/batch",
json={
"source_text": SAMPLE_TEXT,
"content_types": ["multiple_choice", "unknown_type"],
"topic": "Test"
}
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert len(data["contents"]) == 1
assert len(data["errors"]) == 1
assert "unknown_type" in data["errors"][0]
class TestContentManagement:
"""Tests für Content-Verwaltung."""
def test_get_content(self):
"""Testet Abrufen von gespeichertem Content."""
# Erst generieren
gen_response = client.post(
"/api/worksheets/generate/multiple-choice",
json={
"source_text": SAMPLE_TEXT,
"num_questions": 2
}
)
content_id = gen_response.json()["content"]["id"]
# Dann abrufen
get_response = client.get(f"/api/worksheets/content/{content_id}")
assert get_response.status_code == 200
data = get_response.json()
assert data["content"]["id"] == content_id
def test_get_content_not_found(self):
"""Testet Fehler bei nicht vorhandenem Content."""
response = client.get("/api/worksheets/content/nonexistent-id")
assert response.status_code == 404
def test_get_content_h5p(self):
"""Testet H5P-Format Abruf."""
# Erst generieren
gen_response = client.post(
"/api/worksheets/generate/multiple-choice",
json={
"source_text": SAMPLE_TEXT,
"num_questions": 2
}
)
content_id = gen_response.json()["content"]["id"]
# H5P abrufen
h5p_response = client.get(f"/api/worksheets/content/{content_id}/h5p")
assert h5p_response.status_code == 200
data = h5p_response.json()
assert "library" in data
assert "params" in data
def test_delete_content(self):
"""Testet Löschen von Content."""
# Erst generieren
gen_response = client.post(
"/api/worksheets/generate/cloze",
json={
"source_text": SAMPLE_TEXT,
"num_gaps": 3
}
)
content_id = gen_response.json()["content"]["id"]
# Dann löschen
del_response = client.delete(f"/api/worksheets/content/{content_id}")
assert del_response.status_code == 200
assert del_response.json()["status"] == "deleted"
# Prüfen dass gelöscht
get_response = client.get(f"/api/worksheets/content/{content_id}")
assert get_response.status_code == 404
class TestMetaEndpoints:
"""Tests für Meta-Endpoints."""
def test_list_content_types(self):
"""Testet Auflistung der Content-Typen."""
response = client.get("/api/worksheets/types")
assert response.status_code == 200
data = response.json()
assert "content_types" in data
assert len(data["content_types"]) == 4
type_names = [t["type"] for t in data["content_types"]]
assert "multiple_choice" in type_names
assert "cloze" in type_names
assert "mindmap" in type_names
assert "quiz" in type_names
def test_get_generation_history(self):
"""Testet Generierungs-Historie."""
# Erst etwas generieren
client.post(
"/api/worksheets/generate/mindmap",
json={
"source_text": SAMPLE_TEXT,
"topic": "History Test"
}
)
response = client.get("/api/worksheets/history?limit=5")
assert response.status_code == 200
data = response.json()
assert "total" in data
assert "contents" in data
assert data["total"] >= 1
class TestGeneratorIntegration:
"""Tests für Generator-Integration."""
def test_mc_generator_output_format(self):
"""Testet MC-Generator Output-Format."""
response = client.post(
"/api/worksheets/generate/multiple-choice",
json={
"source_text": SAMPLE_TEXT,
"num_questions": 2
}
)
data = response.json()
questions = data["content"]["data"]["questions"]
for q in questions:
assert "question" in q
assert "options" in q
assert len(q["options"]) == 4
assert "difficulty" in q
# Genau eine richtige Antwort
correct_count = sum(1 for opt in q["options"] if opt["is_correct"])
assert correct_count == 1
def test_cloze_generator_gap_format(self):
"""Testet Cloze-Generator Gap-Format."""
response = client.post(
"/api/worksheets/generate/cloze",
json={
"source_text": SAMPLE_TEXT,
"num_gaps": 3
}
)
data = response.json()
gaps = data["content"]["data"]["gaps"]
for gap in gaps:
assert "position" in gap
assert "answer" in gap
assert "alternatives" in gap
assert "distractors" in gap
def test_mindmap_generator_node_format(self):
"""Testet Mindmap-Generator Node-Format."""
response = client.post(
"/api/worksheets/generate/mindmap",
json={
"source_text": SAMPLE_TEXT,
"max_depth": 3
}
)
data = response.json()
root = data["content"]["data"]["mindmap"]["root"]
assert "id" in root
assert "label" in root
assert "level" in root
assert "children" in root
assert "color" in root