mirror of
https://github.com/Sea-Haven-Industries/meal-order-manager.git
synced 2026-09-30 17:03:13 +00:00
Some checks failed
Build Lambda Layer / build (push) Has been cancelled
Helvetica cannot encode an em dash, so the header rendered a question mark. Tighter single-column spacing keeps a 12-person week on page 1 while still paginating large weeks.
138 lines
4.5 KiB
Python
138 lines
4.5 KiB
Python
"""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
|