176 lines
6.1 KiB
Python
176 lines
6.1 KiB
Python
"""
|
|
PDF generation tests for Scenar Creator v4.
|
|
"""
|
|
|
|
import pytest
|
|
from app.core.pdf_generator import generate_pdf
|
|
from app.core.validator import ScenarsError
|
|
from app.models.event import ScenarioDocument, Block, ProgramType, EventInfo
|
|
|
|
|
|
def make_doc(**kwargs):
|
|
defaults = {
|
|
"version": "1.0",
|
|
"event": EventInfo(title="Test PDF", subtitle="Subtitle",
|
|
date_from="2026-03-01", date_to="2026-03-01"),
|
|
"program_types": [
|
|
ProgramType(id="ws", name="Workshop", color="#0070C0"),
|
|
],
|
|
"blocks": [
|
|
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
|
title="Test Program", type_id="ws", responsible="John"),
|
|
]
|
|
}
|
|
defaults.update(kwargs)
|
|
return ScenarioDocument(**defaults)
|
|
|
|
|
|
def test_generate_pdf_basic():
|
|
doc = make_doc()
|
|
pdf_bytes = generate_pdf(doc)
|
|
assert isinstance(pdf_bytes, bytes)
|
|
assert len(pdf_bytes) > 0
|
|
assert pdf_bytes[:5] == b'%PDF-'
|
|
|
|
|
|
def test_generate_pdf_is_single_page():
|
|
import re
|
|
doc = make_doc()
|
|
pdf_bytes = generate_pdf(doc)
|
|
# Count /Type /Page (not /Pages) occurrences
|
|
pages = len(re.findall(rb'/Type\s*/Page[^s]', pdf_bytes))
|
|
assert pages == 1, f"Expected 1 page, got {pages}"
|
|
|
|
|
|
def test_generate_pdf_multiday():
|
|
doc = make_doc(
|
|
event=EventInfo(title="Multi-day", date_from="2026-03-01", date_to="2026-03-02"),
|
|
program_types=[
|
|
ProgramType(id="key", name="Keynote", color="#FF0000"),
|
|
ProgramType(id="ws", name="Workshop", color="#0070C0"),
|
|
],
|
|
blocks=[
|
|
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
|
title="Day 1", type_id="key", responsible="Alice"),
|
|
Block(id="b2", date="2026-03-02", start="14:00", end="15:00",
|
|
title="Day 2", type_id="ws", responsible="Bob"),
|
|
]
|
|
)
|
|
pdf_bytes = generate_pdf(doc)
|
|
assert isinstance(pdf_bytes, bytes)
|
|
assert pdf_bytes[:5] == b'%PDF-'
|
|
|
|
|
|
def test_generate_pdf_overnight_block():
|
|
"""Block crossing midnight (end < start) should render without error."""
|
|
doc = make_doc(
|
|
blocks=[
|
|
Block(id="b1", date="2026-03-01", start="22:00", end="01:30",
|
|
title="Noční hra", type_id="ws", responsible="Honza"),
|
|
]
|
|
)
|
|
pdf_bytes = generate_pdf(doc)
|
|
assert pdf_bytes[:5] == b'%PDF-'
|
|
|
|
|
|
def test_generate_pdf_empty_blocks():
|
|
doc = make_doc(blocks=[])
|
|
with pytest.raises(ScenarsError):
|
|
generate_pdf(doc)
|
|
|
|
|
|
def test_generate_pdf_missing_type():
|
|
doc = make_doc(
|
|
blocks=[
|
|
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
|
title="Test", type_id="UNKNOWN"),
|
|
]
|
|
)
|
|
with pytest.raises(ScenarsError):
|
|
generate_pdf(doc)
|
|
|
|
|
|
def test_generate_pdf_with_full_event_info():
|
|
doc = make_doc(
|
|
event=EventInfo(
|
|
title="Full Event",
|
|
subtitle="With all fields",
|
|
date_from="2026-03-01",
|
|
date_to="2026-03-03",
|
|
location="Prague"
|
|
),
|
|
program_types=[
|
|
ProgramType(id="ws", name="Workshop", color="#0070C0"),
|
|
ProgramType(id="rest", name="Odpočinek", color="#22C55E"),
|
|
],
|
|
blocks=[
|
|
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
|
title="Session 1", type_id="ws"),
|
|
Block(id="b2", date="2026-03-02", start="10:00", end="11:00",
|
|
title="Session 2", type_id="rest"),
|
|
Block(id="b3", date="2026-03-03", start="08:00", end="09:00",
|
|
title="Session 3", type_id="ws"),
|
|
]
|
|
)
|
|
pdf_bytes = generate_pdf(doc)
|
|
assert pdf_bytes[:5] == b'%PDF-'
|
|
|
|
|
|
def test_generate_pdf_multiple_blocks_same_day():
|
|
doc = make_doc(
|
|
blocks=[
|
|
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
|
title="Morning", type_id="ws"),
|
|
Block(id="b2", date="2026-03-01", start="10:00", end="11:30",
|
|
title="Midday", type_id="ws"),
|
|
Block(id="b3", date="2026-03-01", start="14:00", end="16:00",
|
|
title="Afternoon", type_id="ws", responsible="Team"),
|
|
]
|
|
)
|
|
pdf_bytes = generate_pdf(doc)
|
|
assert pdf_bytes[:5] == b'%PDF-'
|
|
|
|
|
|
def test_generate_pdf_many_types_legend():
|
|
"""Many program types should fit in multi-column legend."""
|
|
types = [ProgramType(id=f"t{i}", name=f"Typ {i}", color=f"#{'%06x' % (i * 20000)}")
|
|
for i in range(1, 9)]
|
|
blocks = [Block(id=f"b{i}", date="2026-03-01", start=f"{8+i}:00", end=f"{9+i}:00",
|
|
title=f"Blok {i}", type_id=f"t{i}") for i in range(1, 9)]
|
|
doc = make_doc(program_types=types, blocks=blocks)
|
|
pdf_bytes = generate_pdf(doc)
|
|
assert pdf_bytes[:5] == b'%PDF-'
|
|
|
|
|
|
def test_generate_pdf_with_notes_creates_second_page():
|
|
"""Blocks with notes should produce a 2-page PDF (timetable + notes)."""
|
|
import re
|
|
types = [ProgramType(id="ws", name="Workshop", color="#0070C0")]
|
|
blocks = [
|
|
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
|
title="Opening", type_id="ws", notes="Bring the flipchart and markers."),
|
|
Block(id="b2", date="2026-03-01", start="10:00", end="11:00",
|
|
title="Teambuilding", type_id="ws", notes="Outdoor if weather permits."),
|
|
Block(id="b3", date="2026-03-01", start="11:00", end="12:00",
|
|
title="Lunch", type_id="ws"), # no notes
|
|
]
|
|
doc = make_doc(program_types=types, blocks=blocks)
|
|
pdf_bytes = generate_pdf(doc)
|
|
assert pdf_bytes[:5] == b'%PDF-'
|
|
pages = len(re.findall(rb'/Type\s*/Page[^s]', pdf_bytes))
|
|
assert pages == 2, f"Expected 2 pages (timetable + notes), got {pages}"
|
|
|
|
|
|
def test_generate_pdf_no_notes_single_page():
|
|
"""Without notes, PDF should be exactly 1 page."""
|
|
import re
|
|
doc = make_doc(
|
|
blocks=[
|
|
Block(id="b1", date="2026-03-01", start="09:00", end="10:00",
|
|
title="No notes here", type_id="ws"),
|
|
]
|
|
)
|
|
pdf_bytes = generate_pdf(doc)
|
|
pages = len(re.findall(rb'/Type\s*/Page[^s]', pdf_bytes))
|
|
assert pages == 1, f"Expected 1 page, got {pages}"
|