Files
2026-05-22 08:40:04 +02:00

207 lines
7.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Tests für die job_preview-View.
Hängt nicht am echten LibreOffice build_preview wird auf Service-Ebene
gepatcht, sodass die Tests in <1 s laufen.
"""
from __future__ import annotations
from pathlib import Path
import pytest
from django.core.files.uploadedfile import SimpleUploadedFile
from django.urls import reverse
from mailmerge.services.preview import PreviewError, PreviewResult
PREVIEW_URL_NAME = "job-preview"
FAKE_PDF = b"%PDF-1.4 fake preview\n"
# ---------------------------------------------------------------------------
# HTTP-Method / Auth
# ---------------------------------------------------------------------------
class TestPreviewAccess:
def test_login_required(self, client, db):
url = reverse(PREVIEW_URL_NAME)
response = client.post(url, {})
# Django leitet auf Login um (302)
assert response.status_code == 302
assert "/login" in response["Location"].lower() or "login" in response["Location"].lower()
def test_get_not_allowed(self, auth_client):
url = reverse(PREVIEW_URL_NAME)
response = auth_client.get(url)
assert response.status_code == 405
# ---------------------------------------------------------------------------
# Form-Validierung
# ---------------------------------------------------------------------------
class TestPreviewFormValidation:
def test_missing_files_returns_form(self, auth_client):
url = reverse(PREVIEW_URL_NAME)
response = auth_client.post(url, {})
# Kein PDF, sondern Form-Template zurück
assert response.status_code == 200
assert response["Content-Type"].startswith("text/html")
def test_rejects_non_csv_extension(
self, auth_client, letter_template, docx_bytes
):
url = reverse(PREVIEW_URL_NAME)
bogus = SimpleUploadedFile("file.txt", b"foo", content_type="text/plain")
response = auth_client.post(url, {
"template": str(letter_template.pk),
"recipients_csv": bogus,
})
assert response.status_code == 200
assert b"Nur .csv-Dateien" in response.content
# ---------------------------------------------------------------------------
# Happy Path
# ---------------------------------------------------------------------------
@pytest.fixture
def patched_build_preview(monkeypatch):
"""Ersetzt build_preview in der Views-Importebene durch einen Stub."""
def fake_build_preview(template_path: Path, csv_file):
return PreviewResult(
pdf_bytes=FAKE_PDF,
used_row={"nachname": "Huber"},
placeholders=["anrede_brief", "nachname", "vorname", "ort"],
csv_columns=["anrede_brief", "nachname", "vorname", "ort"],
missing_columns=[],
extra_columns=[],
)
monkeypatch.setattr(
"mailmerge.views.build_preview", fake_build_preview
)
return fake_build_preview
class TestPreviewHappyPath:
def test_returns_pdf_content_type(
self, auth_client, letter_template, csv_uploaded_valid, patched_build_preview
):
url = reverse(PREVIEW_URL_NAME)
response = auth_client.post(url, {
"template": str(letter_template.pk),
"recipients_csv": csv_uploaded_valid,
})
assert response.status_code == 200
assert response["Content-Type"] == "application/pdf"
def test_returns_pdf_body(
self, auth_client, letter_template, csv_uploaded_valid, patched_build_preview
):
url = reverse(PREVIEW_URL_NAME)
response = auth_client.post(url, {
"template": str(letter_template.pk),
"recipients_csv": csv_uploaded_valid,
})
assert response.content == FAKE_PDF
def test_content_disposition_inline(
self, auth_client, letter_template, csv_uploaded_valid, patched_build_preview
):
url = reverse(PREVIEW_URL_NAME)
response = auth_client.post(url, {
"template": str(letter_template.pk),
"recipients_csv": csv_uploaded_valid,
})
assert response["Content-Disposition"].startswith("inline")
def test_placeholders_header_set(
self, auth_client, letter_template, csv_uploaded_valid, patched_build_preview
):
url = reverse(PREVIEW_URL_NAME)
response = auth_client.post(url, {
"template": str(letter_template.pk),
"recipients_csv": csv_uploaded_valid,
})
assert "X-Preview-Placeholders" in response
assert "nachname" in response["X-Preview-Placeholders"]
# ---------------------------------------------------------------------------
# Extra-Spalten als Hinweis-Header
# ---------------------------------------------------------------------------
class TestPreviewExtraColumnsHeader:
@pytest.fixture
def patched_with_extras(self, monkeypatch):
def fake_build_preview(template_path, csv_file):
return PreviewResult(
pdf_bytes=FAKE_PDF,
used_row={"nachname": "Huber"},
placeholders=["nachname"],
csv_columns=["nachname", "personalnr"],
missing_columns=[],
extra_columns=["personalnr"],
)
monkeypatch.setattr(
"mailmerge.views.build_preview", fake_build_preview
)
def test_extra_columns_header_contains_value(
self, auth_client, letter_template, csv_uploaded_valid, patched_with_extras
):
url = reverse(PREVIEW_URL_NAME)
response = auth_client.post(url, {
"template": str(letter_template.pk),
"recipients_csv": csv_uploaded_valid,
})
assert response["X-Preview-Extra-Columns"] == "personalnr"
# ---------------------------------------------------------------------------
# Fehlerpfade: PreviewError und unerwartete Exceptions
# ---------------------------------------------------------------------------
class TestPreviewErrorHandling:
def test_preview_error_renders_form_with_message(
self, auth_client, letter_template, csv_uploaded_valid, monkeypatch
):
def fake_build_preview(template_path, csv_file):
raise PreviewError("Spalte 'ort' fehlt")
monkeypatch.setattr(
"mailmerge.views.build_preview", fake_build_preview
)
url = reverse(PREVIEW_URL_NAME)
response = auth_client.post(url, {
"template": str(letter_template.pk),
"recipients_csv": csv_uploaded_valid,
})
assert response.status_code == 200
assert response["Content-Type"].startswith("text/html")
# Django escaped Apostrophe in Templates ("'" -> "&#x27;")
body = response.content.decode("utf-8")
assert "Spalte" in body and "ort" in body and "fehlt" in body
def test_unexpected_exception_renders_form(
self, auth_client, letter_template, csv_uploaded_valid, monkeypatch
):
def fake_build_preview(template_path, csv_file):
raise RuntimeError("LibreOffice timeout")
monkeypatch.setattr(
"mailmerge.views.build_preview", fake_build_preview
)
url = reverse(PREVIEW_URL_NAME)
response = auth_client.post(url, {
"template": str(letter_template.pk),
"recipients_csv": csv_uploaded_valid,
})
assert response.status_code == 200
assert response["Content-Type"].startswith("text/html")
assert b"Vorschau konnte nicht erstellt werden" in response.content
assert b"LibreOffice timeout" in response.content