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

398 lines
14 KiB
Python

"""
Tests fuer die Abitur-Docs API
Tests fuer:
- NiBiS Dateinamen-Erkennung
- Dokumenten-Metadaten-Verwaltung
- ZIP-Import
- Status-Workflow
- Enum-Endpunkte
"""
import pytest
from unittest.mock import MagicMock, patch
from datetime import datetime
# Import des zu testenden Moduls
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from abitur_docs_api import (
router,
AbiturDokument,
VerarbeitungsStatus,
DocumentMetadata,
RecognitionResult,
Bundesland,
AbiturFach,
Anforderungsniveau,
DokumentTyp,
parse_nibis_filename,
documents_db,
)
class TestNiBiSFilenameRecognition:
"""Tests fuer die automatische NiBiS Dateinamen-Erkennung."""
def test_parse_deutsch_ea_aufgabe(self):
"""Deutsch eA Aufgabe I sollte erkannt werden."""
result = parse_nibis_filename("2025_Deutsch_eA_I.pdf")
assert result.confidence > 0.5
assert result.extracted.get("jahr") == 2025
assert result.extracted.get("fach") == "deutsch"
assert result.extracted.get("niveau") == "eA"
assert result.extracted.get("aufgaben_nummer") == "I"
def test_parse_deutsch_ea_ewh(self):
"""Deutsch eA Erwartungshorizont sollte erkannt werden."""
result = parse_nibis_filename("2025_Deutsch_eA_I_EWH.pdf")
assert result.confidence > 0.5
assert result.extracted.get("typ") == "erwartungshorizont"
def test_parse_englisch_ga(self):
"""Englisch gA sollte erkannt werden."""
result = parse_nibis_filename("2025_Englisch_gA_II.pdf")
assert result.extracted.get("fach") == "englisch"
assert result.extracted.get("niveau") == "gA"
assert result.extracted.get("aufgaben_nummer") == "II"
def test_parse_mathematik(self):
"""Mathematik eA sollte erkannt werden."""
result = parse_nibis_filename("2025_Mathematik_eA_III.pdf")
assert result.extracted.get("fach") == "mathematik"
def test_parse_with_hoerverstehen(self):
"""Hoerverstehen sollte erkannt werden."""
result = parse_nibis_filename("2025_Englisch_eA_Hoerverstehen.pdf")
assert result.extracted.get("typ") == "hoerverstehen"
def test_parse_with_sprachmittlung(self):
"""Sprachmittlung sollte erkannt werden."""
result = parse_nibis_filename("2025_Spanisch_gA_Sprachmittlung.pdf")
assert result.extracted.get("typ") == "sprachmittlung"
def test_parse_deckblatt(self):
"""Deckblatt sollte erkannt werden."""
result = parse_nibis_filename("2025_Geschichte_eA_Deckblatt.pdf")
assert result.extracted.get("typ") == "deckblatt"
def test_parse_unknown_format(self):
"""Unbekanntes Format sollte niedrige Confidence haben."""
result = parse_nibis_filename("random_file.pdf")
assert result.confidence < 0.3
def test_parse_year_extraction(self):
"""Jahr sollte aus dem Dateinamen extrahiert werden."""
result_2024 = parse_nibis_filename("2024_Deutsch_eA_I.pdf")
result_2025 = parse_nibis_filename("2025_Deutsch_eA_I.pdf")
assert result_2024.extracted.get("jahr") == 2024
assert result_2025.extracted.get("jahr") == 2025
def test_parse_case_insensitive(self):
"""Erkennung sollte case-insensitive sein."""
result_upper = parse_nibis_filename("2025_DEUTSCH_EA_I.pdf")
result_lower = parse_nibis_filename("2025_deutsch_ea_i.pdf")
assert result_upper.extracted.get("fach") == "deutsch"
assert result_lower.extracted.get("fach") == "deutsch"
class TestVerarbeitungsStatus:
"""Tests fuer den Dokument-Status-Workflow."""
def test_pending_status(self):
"""Pending Status sollte existieren."""
assert VerarbeitungsStatus.PENDING.value == "pending"
def test_recognized_status(self):
"""Recognized Status sollte existieren."""
assert VerarbeitungsStatus.RECOGNIZED.value == "recognized"
def test_confirmed_status(self):
"""Confirmed Status sollte existieren."""
assert VerarbeitungsStatus.CONFIRMED.value == "confirmed"
def test_indexed_status(self):
"""Indexed Status sollte existieren."""
assert VerarbeitungsStatus.INDEXED.value == "indexed"
def test_error_status(self):
"""Error Status sollte existieren."""
assert VerarbeitungsStatus.ERROR.value == "error"
def test_status_workflow_order(self):
"""Status-Workflow sollte die richtige Reihenfolge haben."""
statuses = list(VerarbeitungsStatus)
expected_order = [
VerarbeitungsStatus.PENDING,
VerarbeitungsStatus.PROCESSING,
VerarbeitungsStatus.RECOGNIZED,
VerarbeitungsStatus.CONFIRMED,
VerarbeitungsStatus.INDEXED,
VerarbeitungsStatus.ERROR,
]
assert statuses == expected_order
class TestBundesland:
"""Tests fuer die Bundesland-Enumeration."""
def test_niedersachsen(self):
"""Niedersachsen sollte existieren."""
assert Bundesland.NIEDERSACHSEN.value == "niedersachsen"
def test_nrw(self):
"""Nordrhein-Westfalen sollte existieren."""
assert Bundesland.NORDRHEIN_WESTFALEN.value == "nordrhein_westfalen"
def test_bayern(self):
"""Bayern sollte existieren."""
assert Bundesland.BAYERN.value == "bayern"
def test_all_bundeslaender_present(self):
"""Alle wichtigen Bundeslaender sollten vorhanden sein."""
bundeslaender = [b.value for b in Bundesland]
assert "niedersachsen" in bundeslaender
assert "nordrhein_westfalen" in bundeslaender
assert "bayern" in bundeslaender
assert "baden_wuerttemberg" in bundeslaender
class TestAbiturFach:
"""Tests fuer die Fach-Enumeration."""
def test_deutsch(self):
"""Deutsch sollte existieren."""
assert AbiturFach.DEUTSCH.value == "deutsch"
def test_mathematik(self):
"""Mathematik sollte existieren."""
assert AbiturFach.MATHEMATIK.value == "mathematik"
def test_englisch(self):
"""Englisch sollte existieren."""
assert AbiturFach.ENGLISCH.value == "englisch"
def test_sprachen_present(self):
"""Alle Hauptsprachen sollten vorhanden sein."""
faecher = [f.value for f in AbiturFach]
assert "deutsch" in faecher
assert "englisch" in faecher
assert "franzoesisch" in faecher
assert "spanisch" in faecher
def test_naturwissenschaften_present(self):
"""Naturwissenschaften sollten vorhanden sein."""
faecher = [f.value for f in AbiturFach]
assert "biologie" in faecher
assert "chemie" in faecher
assert "physik" in faecher
class TestAnforderungsniveau:
"""Tests fuer die Anforderungsniveau-Enumeration."""
def test_ea(self):
"""eA (erhoehtes Anforderungsniveau) sollte existieren."""
assert Anforderungsniveau.EA.value == "eA"
def test_ga(self):
"""gA (grundlegendes Anforderungsniveau) sollte existieren."""
assert Anforderungsniveau.GA.value == "gA"
class TestDokumentTyp:
"""Tests fuer die Dokumenttyp-Enumeration."""
def test_aufgabe(self):
"""Aufgabe sollte existieren."""
assert DokumentTyp.AUFGABE.value == "aufgabe"
def test_erwartungshorizont(self):
"""Erwartungshorizont sollte existieren."""
assert DokumentTyp.ERWARTUNGSHORIZONT.value == "erwartungshorizont"
def test_all_types_present(self):
"""Alle Dokumenttypen sollten vorhanden sein."""
typen = [t.value for t in DokumentTyp]
assert "aufgabe" in typen
assert "erwartungshorizont" in typen
assert "deckblatt" in typen
assert "hoerverstehen" in typen
assert "sprachmittlung" in typen
class TestDocumentMetadata:
"""Tests fuer die Dokumenten-Metadaten."""
def test_create_metadata(self):
"""Metadaten sollten erstellt werden koennen."""
metadata = DocumentMetadata(
jahr=2025,
bundesland="niedersachsen",
fach="deutsch",
niveau="eA",
dokument_typ="aufgabe",
aufgaben_nummer="I"
)
assert metadata.jahr == 2025
assert metadata.fach == "deutsch"
assert metadata.niveau == "eA"
def test_optional_fields(self):
"""Optionale Felder sollten None sein koennen."""
metadata = DocumentMetadata(
jahr=None,
bundesland=None,
fach=None,
niveau=None,
dokument_typ=None,
aufgaben_nummer=None
)
assert metadata.jahr is None
assert metadata.fach is None
class TestAbiturDokument:
"""Tests fuer das AbiturDokument-Modell."""
def test_create_document(self):
"""Ein Dokument sollte erstellt werden koennen."""
# Use internal AbiturDokument dataclass with correct field names
doc = AbiturDokument(
id="doc-123",
dateiname="doc-123.pdf",
original_dateiname="2025_Deutsch_eA_I.pdf",
bundesland=Bundesland.NIEDERSACHSEN,
fach=AbiturFach.DEUTSCH,
jahr=2025,
niveau=Anforderungsniveau.EA,
typ=DokumentTyp.AUFGABE,
aufgaben_nummer="I",
status=VerarbeitungsStatus.PENDING,
confidence=0.85,
file_path="/data/docs/doc-123.pdf",
file_size=1024,
indexed=False,
vector_ids=[],
created_at=datetime.now(),
updated_at=datetime.now()
)
assert doc.id == "doc-123"
assert doc.status == VerarbeitungsStatus.PENDING
def test_document_with_recognition_result(self):
"""Ein Dokument mit Erkennungsergebnis sollte erstellt werden koennen."""
# Test that RecognitionResult works with extracted property
recognition = parse_nibis_filename("2025_Deutsch_eA_I.pdf")
assert recognition.confidence > 0.5
assert recognition.extracted.get("fach") == "deutsch"
doc = AbiturDokument(
id="doc-456",
dateiname="doc-456.pdf",
original_dateiname="2025_Deutsch_eA_I.pdf",
bundesland=Bundesland.NIEDERSACHSEN,
fach=AbiturFach.DEUTSCH,
jahr=2025,
niveau=Anforderungsniveau.EA,
typ=DokumentTyp.AUFGABE,
aufgaben_nummer="I",
status=VerarbeitungsStatus.RECOGNIZED,
confidence=recognition.confidence,
file_path="/data/docs/doc-456.pdf",
file_size=1024,
indexed=False,
vector_ids=[],
created_at=datetime.now(),
updated_at=datetime.now()
)
assert doc.confidence > 0.5
class TestRecognitionResult:
"""Tests fuer das Erkennungsergebnis."""
def test_create_recognition_result(self):
"""Ein Erkennungsergebnis sollte erstellt werden koennen."""
result = parse_nibis_filename("2025_Deutsch_eA_I.pdf")
assert result.confidence > 0.5
assert result.method == "filename_pattern"
assert result.extracted.get("jahr") == 2025
assert result.extracted.get("fach") == "deutsch"
def test_confidence_range(self):
"""Confidence sollte zwischen 0 und 1 liegen."""
result_low = parse_nibis_filename("random_file.pdf")
result_high = parse_nibis_filename("2025_Deutsch_eA_I_EWH.pdf")
assert result_low.confidence >= 0.0
assert result_high.confidence <= 1.0
class TestDocumentsDB:
"""Tests fuer die In-Memory Datenbank."""
def setup_method(self):
"""Setup vor jedem Test - leere die DB."""
documents_db.clear()
def test_empty_db(self):
"""Eine leere DB sollte leer sein."""
assert len(documents_db) == 0
def test_add_document_to_db(self):
"""Ein Dokument sollte zur DB hinzugefuegt werden koennen."""
doc = AbiturDokument(
id="test-1",
dateiname="test-1.pdf",
original_dateiname="test.pdf",
bundesland=Bundesland.NIEDERSACHSEN,
fach=AbiturFach.DEUTSCH,
jahr=2025,
niveau=Anforderungsniveau.EA,
typ=DokumentTyp.AUFGABE,
aufgaben_nummer="I",
status=VerarbeitungsStatus.PENDING,
confidence=0.85,
file_path="/data/test.pdf",
file_size=1024,
indexed=False,
vector_ids=[],
created_at=datetime.now(),
updated_at=datetime.now()
)
documents_db["test-1"] = doc
assert "test-1" in documents_db
assert documents_db["test-1"].original_dateiname == "test.pdf"
class TestFilenamePatterns:
"""Tests fuer verschiedene Dateinamen-Muster."""
def test_pattern_with_underscore(self):
"""Unterstrich-Trennzeichen sollten erkannt werden."""
result = parse_nibis_filename("2025_Biologie_eA_II.pdf")
assert result.extracted.get("fach") == "biologie"
def test_pattern_with_aufgabe_nummer(self):
"""Roemische Zahlen fuer Aufgaben sollten erkannt werden."""
for num in ["I", "II", "III", "IV"]:
result = parse_nibis_filename(f"2025_Deutsch_eA_{num}.pdf")
assert result.extracted.get("aufgaben_nummer") == num
def test_pattern_ewh_suffix(self):
"""EWH-Suffix sollte als Erwartungshorizont erkannt werden."""
result = parse_nibis_filename("2025_Deutsch_eA_I_EWH.pdf")
assert result.extracted.get("typ") == "erwartungshorizont"
def test_pattern_without_aufgabe_nummer(self):
"""Dateien ohne Aufgaben-Nummer sollten auch erkannt werden."""
result = parse_nibis_filename("2025_Deutsch_eA.pdf")
assert result.extracted.get("jahr") == 2025
assert result.extracted.get("fach") == "deutsch"
if __name__ == "__main__":
pytest.main([__file__, "-v"])