""" Tests für die Letters API. Testet: - CRUD-Operationen für Elternbriefe - PDF-Export - GFK-Verbesserungsvorschläge Note: Some tests require WeasyPrint which needs system libraries. """ import pytest from fastapi.testclient import TestClient from unittest.mock import patch, AsyncMock, MagicMock import sys import os # Add parent directory to path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Check if WeasyPrint is available (required for PDF endpoints) try: import weasyprint WEASYPRINT_AVAILABLE = True except (ImportError, OSError): WEASYPRINT_AVAILABLE = False class TestLettersAPIImport: """Tests für Letters API Import.""" def test_import_letters_api(self): """Test that letters_api can be imported.""" from letters_api import router assert router is not None def test_import_enums(self): """Test that enums can be imported.""" from letters_api import LetterType, LetterTone, LetterStatus assert LetterType is not None assert LetterTone is not None assert LetterStatus is not None def test_import_models(self): """Test that Pydantic models can be imported.""" from letters_api import ( LetterCreateRequest, LetterUpdateRequest, LetterResponse, LetterListResponse, ExportPDFRequest, ImproveRequest, ImproveResponse ) assert LetterCreateRequest is not None assert LetterResponse is not None class TestLetterTypes: """Tests für Brieftypen.""" def test_letter_types_values(self): """Test that all letter types have correct values.""" from letters_api import LetterType expected_types = ["general", "halbjahr", "fehlzeiten", "elternabend", "lob", "custom"] actual_types = [t.value for t in LetterType] for expected in expected_types: assert expected in actual_types def test_letter_tones_values(self): """Test that all tones have correct values.""" from letters_api import LetterTone expected_tones = ["formal", "professional", "warm", "concerned", "appreciative"] actual_tones = [t.value for t in LetterTone] for expected in expected_tones: assert expected in actual_tones class TestLetterCreateRequest: """Tests für LetterCreateRequest Model.""" def test_create_minimal_request(self): """Test creating a request with minimal required fields.""" from letters_api import LetterCreateRequest request = LetterCreateRequest( recipient_name="Familie Müller", recipient_address="Musterstraße 1, 12345 Musterstadt", student_name="Max Müller", student_class="5a", subject="Einladung Elternabend", content="Sehr geehrte Familie Müller...", teacher_name="Frau Schmidt" ) assert request.recipient_name == "Familie Müller" assert request.student_name == "Max Müller" assert request.teacher_name == "Frau Schmidt" def test_create_full_request(self): """Test creating a request with all fields.""" from letters_api import LetterCreateRequest, LetterType, LetterTone, SchoolInfoModel school_info = SchoolInfoModel( name="Musterschule", address="Schulweg 1, 12345 Musterstadt", phone="0123-456789", email="info@musterschule.de" ) request = LetterCreateRequest( recipient_name="Familie Müller", recipient_address="Musterstraße 1, 12345 Musterstadt", student_name="Max Müller", student_class="5a", subject="Einladung Elternabend", content="Sehr geehrte Familie Müller...", teacher_name="Frau Schmidt", teacher_title="Klassenlehrerin", letter_type=LetterType.ELTERNABEND, tone=LetterTone.PROFESSIONAL, school_info=school_info, gfk_principles_applied=["Beobachtung", "Bitte"] ) assert request.letter_type == LetterType.ELTERNABEND assert request.tone == LetterTone.PROFESSIONAL assert request.school_info.name == "Musterschule" class TestHelperFunctions: """Tests für Helper-Funktionen.""" def test_get_type_label(self): """Test type label function.""" from letters_api import _get_type_label, LetterType assert "Einladung" in _get_type_label(LetterType.ELTERNABEND) assert "Fehlzeiten" in _get_type_label(LetterType.FEHLZEITEN) assert "Positives" in _get_type_label(LetterType.LOB) def test_get_tone_label(self): """Test tone label function.""" from letters_api import _get_tone_label, LetterTone assert "förmlich" in _get_tone_label(LetterTone.FORMAL) assert "Professionell" in _get_tone_label(LetterTone.PROFESSIONAL) assert "Warmherzig" in _get_tone_label(LetterTone.WARM) @pytest.mark.skipif( not WEASYPRINT_AVAILABLE, reason="WeasyPrint not available (requires system libraries)" ) class TestLettersAPIEndpoints: """Integration tests für Letters API Endpoints.""" @pytest.fixture def client(self): """Create test client.""" try: from main import app return TestClient(app) except ImportError: pytest.skip("main.py not available for testing") @pytest.fixture def sample_letter_data(self): """Sample letter data for tests.""" return { "recipient_name": "Familie Test", "recipient_address": "Teststraße 1\n12345 Teststadt", "student_name": "Test Kind", "student_class": "5a", "subject": "Testbrief", "content": "Dies ist ein Testbrief.", "teacher_name": "Herr Test", "letter_type": "general", "tone": "professional" } def test_create_letter(self, client, sample_letter_data): """Test creating a new letter.""" if not client: pytest.skip("Client not available") response = client.post("/api/letters/", json=sample_letter_data) assert response.status_code == 200 data = response.json() assert data["recipient_name"] == sample_letter_data["recipient_name"] assert data["student_name"] == sample_letter_data["student_name"] assert data["status"] == "draft" assert "id" in data def test_get_letter(self, client, sample_letter_data): """Test getting a letter by ID.""" if not client: pytest.skip("Client not available") # First create a letter create_response = client.post("/api/letters/", json=sample_letter_data) letter_id = create_response.json()["id"] # Then get it response = client.get(f"/api/letters/{letter_id}") assert response.status_code == 200 data = response.json() assert data["id"] == letter_id def test_update_letter(self, client, sample_letter_data): """Test updating a letter.""" if not client: pytest.skip("Client not available") # Create letter create_response = client.post("/api/letters/", json=sample_letter_data) letter_id = create_response.json()["id"] # Update it update_data = {"subject": "Aktualisierter Betreff"} response = client.put(f"/api/letters/{letter_id}", json=update_data) assert response.status_code == 200 data = response.json() assert data["subject"] == "Aktualisierter Betreff" def test_delete_letter(self, client, sample_letter_data): """Test deleting a letter.""" if not client: pytest.skip("Client not available") # Create letter create_response = client.post("/api/letters/", json=sample_letter_data) letter_id = create_response.json()["id"] # Delete it response = client.delete(f"/api/letters/{letter_id}") assert response.status_code == 200 # Verify it's deleted get_response = client.get(f"/api/letters/{letter_id}") assert get_response.status_code == 404 def test_list_letters(self, client, sample_letter_data): """Test listing letters.""" if not client: pytest.skip("Client not available") # Create a letter client.post("/api/letters/", json=sample_letter_data) # List all response = client.get("/api/letters/") assert response.status_code == 200 data = response.json() assert "letters" in data assert "total" in data assert isinstance(data["letters"], list) def test_get_letter_types(self, client): """Test getting available letter types.""" if not client: pytest.skip("Client not available") response = client.get("/api/letters/types") assert response.status_code == 200 data = response.json() assert "types" in data assert len(data["types"]) > 0 def test_get_letter_tones(self, client): """Test getting available tones.""" if not client: pytest.skip("Client not available") response = client.get("/api/letters/tones") assert response.status_code == 200 data = response.json() assert "tones" in data assert len(data["tones"]) > 0 def test_export_pdf(self, client, sample_letter_data): """Test PDF export.""" if not client: pytest.skip("Client not available") # Create letter create_response = client.post("/api/letters/", json=sample_letter_data) letter_id = create_response.json()["id"] # Export as PDF response = client.post(f"/api/letters/{letter_id}/export-pdf") assert response.status_code == 200 assert response.headers["content-type"] == "application/pdf" assert b"%PDF" in response.content[:10] def test_export_pdf_direct(self, client, sample_letter_data): """Test direct PDF export without saving.""" if not client: pytest.skip("Client not available") export_data = {"letter_data": sample_letter_data} response = client.post("/api/letters/export-pdf", json=export_data) assert response.status_code == 200 assert response.headers["content-type"] == "application/pdf" def test_get_nonexistent_letter(self, client): """Test getting a letter that doesn't exist.""" if not client: pytest.skip("Client not available") response = client.get("/api/letters/nonexistent-id") assert response.status_code == 404 class TestLetterImprove: """Tests für GFK-Verbesserungsvorschläge.""" def test_improve_request_model(self): """Test ImproveRequest model.""" from letters_api import ImproveRequest request = ImproveRequest( content="Der Schüler macht nie seine Hausaufgaben.", communication_type="behavior", tone="concerned" ) assert request.content == "Der Schüler macht nie seine Hausaufgaben." assert request.communication_type == "behavior" def test_improve_response_model(self): """Test ImproveResponse model.""" from letters_api import ImproveResponse response = ImproveResponse( improved_content="Ich habe beobachtet, dass die Hausaufgaben...", changes=["'nie' durch konkretes Datum ersetzt", "Ich-Botschaft verwendet"], gfk_score=0.85, gfk_principles_applied=["Beobachtung", "Gefühl", "Bedürfnis"] ) assert response.gfk_score == 0.85 assert "Beobachtung" in response.gfk_principles_applied # Run tests if executed directly if __name__ == "__main__": pytest.main([__file__, "-v"])