# tests/test_pdf_stream.py — Regression tests for BUG-001 (PDF stream HTTP 500) # # BUG-001: opening a PDF whose filename contains accented characters # (e.g. "Bière blonde envoyé par Desja.pdf") returned HTTP 500 because the raw # Unicode name was injected into the Content-Disposition header, producing an # invalid (non-ASCII) header value. /pdf/info worked because it sets no # filename header. # # Fix: RFC 5987 header encoding (ASCII `filename` fallback + `filename*=UTF-8''…`). import os from pathlib import Path # Minimal valid PDF so pypdf/stream treats the bytes as a real PDF. MIN_PDF = ( b"%PDF-1.4\n" b"1 0 obj<>endobj\n" b"2 0 obj<>endobj\n" b"3 0 obj<>endobj\n" b"xref\n0 4\n0000000000 65535 f \n" b"trailer<>\nstartxref\n0\n%%EOF\n" ) def _create_pdf_with_unicode_name(): vault = Path(os.environ["VAULT_1_PATH"]) sub = vault / "98_Boite_Outils" / "98.2_Attachments" sub.mkdir(parents=True, exist_ok=True) pdf = sub / "Bière blonde envoyé par Desja.pdf" pdf.write_bytes(MIN_PDF) return "98_Boite_Outils/98.2_Attachments/Bière blonde envoyé par Desja.pdf" class TestPdfStreamUnicodeFilename: def test_full_stream_ok(self, client): rel = _create_pdf_with_unicode_name() resp = client.get("/api/file/TestVault/pdf/stream", params={"path": rel}) assert resp.status_code == 200, resp.text assert resp.headers["content-type"] == "application/pdf" cd = resp.headers.get("content-disposition", "") # Content-Disposition must be RFC 5987 encoded (ASCII-safe) assert "filename*=UTF-8''" in cd, cd # UTF-8 percent-encoded accented name present (é -> %C3%A8) assert "%C3%A8re%20blonde" in cd def test_range_request_ok(self, client): rel = _create_pdf_with_unicode_name() resp = client.get( "/api/file/TestVault/pdf/stream", params={"path": rel}, headers={"Range": "bytes=0-99"}, ) assert resp.status_code == 206 assert resp.headers.get("content-range", "").startswith("bytes 0-99/") def test_disposition_ascii_safe(self, client): rel = _create_pdf_with_unicode_name() resp = client.get("/api/file/TestVault/pdf/stream", params={"path": rel}) cd = resp.headers.get("content-disposition", "") # Every header field value must be ASCII-encodable (no UnicodeEncodeError) cd.encode("ascii") # ASCII fallback filename present assert 'filename="' in cd