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_letters_api.py
Benjamin Admin 21a844cb8a 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

361 lines
12 KiB
Python

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