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