meal-order-manager/tests/test_pdf.py

139 lines
4.5 KiB
Python
Raw Normal View History

"""Unit tests for src/shared/shared/pdf.py (weekly summary PDF generator)."""
import pytest
from fpdf import FPDF as _RealFPDF
from shared.pdf import build_weekly_summary_pdf
class _NoCompressFPDF(_RealFPDF):
"""FPDF subclass with stream compression off, so the content stream holds
readable text we can assert against without a PDF parser."""
last_pages_count = 0
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_compression(False)
def output(self, *args, **kwargs):
_NoCompressFPDF.last_pages_count = self.pages_count
return super().output(*args, **kwargs)
@pytest.fixture()
def readable_pdf(monkeypatch):
"""Patch the generator to emit uncompressed (searchable) PDFs."""
monkeypatch.setattr("shared.pdf.FPDF", _NoCompressFPDF)
def _orders():
return [
{
"employee_name": "Bob Jones",
"employee_email": "bob@seahavenind.com",
"items": [
{"name": "Chicken Parm", "quantity": 2},
{"name": "Caesar Salad", "quantity": 1},
],
},
{
"employee_name": "Alice Smith",
"employee_email": "alice@seahavenind.com",
"items": [{"name": "Veggie Bowl", "quantity": 3}],
},
]
def _build(orders, **overrides):
kwargs = {
"week": "2026-W19",
"generated_at": "2026-05-22T16:35:40-04:00",
"total_employees": len(orders),
"total_meals": sum(i["quantity"] for o in orders for i in o["items"]),
}
kwargs.update(overrides)
return build_weekly_summary_pdf(orders, **kwargs)
def _volume_orders(count, items_per_employee=2):
"""Synthetic orders for layout/page-count checks."""
return [
{
"employee_name": f"Employee {i:02d}",
"employee_email": f"emp{i:02d}@seahavenind.com",
"items": [
{"name": f"Meal {j}", "quantity": 1} for j in range(items_per_employee)
],
}
for i in range(count)
]
class TestPdfStructure:
def test_returns_pdf_bytes(self):
pdf = _build(_orders())
assert isinstance(pdf, bytes)
assert pdf[:5] == b"%PDF-", "Output should be a valid PDF"
assert len(pdf) > 0
def test_empty_orders_still_valid_pdf(self):
pdf = _build([], total_employees=0, total_meals=0)
assert pdf[:5] == b"%PDF-"
class TestPdfContent:
def test_header_shows_week_and_counts(self, readable_pdf):
pdf = _build(_orders())
text = pdf.decode("latin-1")
assert "Weekly Meal Order Summary - 2026-W19" in text
assert "2 employees" in text
assert "6 meals" in text
def test_each_employee_and_item_present(self, readable_pdf):
orders = _orders()
pdf = _build(orders)
text = pdf.decode("latin-1")
for order in orders:
assert order["employee_name"] in text, f"missing {order['employee_name']}"
assert order["employee_email"] in text
for item in order["items"]:
assert item["name"] in text, f"missing item {item['name']}"
def test_no_prices_anywhere(self, readable_pdf):
"""Per issue #16: strictly employee, item, qty — never a price."""
orders = _orders()
# Even if price fields are present on the items, they must not be rendered.
for o in orders:
for i in o["items"]:
i["price"] = 12.50
i["subtotal"] = 99.99
pdf = _build(orders)
text = pdf.decode("latin-1")
assert "$" not in text, "PDF must contain no currency symbol"
assert "12.50" not in text and "99.99" not in text, "PDF must not render prices"
def test_non_latin1_chars_do_not_crash(self):
orders = [
{
"employee_name": "José 🍜 Ramírez",
"employee_email": "jose@seahavenind.com",
"items": [{"name": "Phở Bowl", "quantity": 1}],
}
]
pdf = _build(orders)
assert pdf[:5] == b"%PDF-"
class TestPdfLayout:
def test_typical_week_fits_on_one_page(self, readable_pdf):
"""12 employees with 2 items each overflowed the pre-DEV-210 layout."""
pdf = _build(_volume_orders(12))
assert pdf[:5] == b"%PDF-"
assert _NoCompressFPDF.last_pages_count == 1
def test_high_volume_still_paginates(self, readable_pdf):
pdf = _build(_volume_orders(40))
assert pdf[:5] == b"%PDF-"
assert _NoCompressFPDF.last_pages_count >= 2