Files
pi-weekly-newspaper/tests/test_cover.py

131 lines
4.3 KiB
Python
Raw Normal View History

import json
import os
from datetime import date
from unittest.mock import patch
from io import BytesIO
from PIL import Image as PILImage, ImageDraw, ImageFont
from src.cover import (
generate_programmatic_cover,
generate_mosaic_cover,
generate_cover,
_truncate_to_width,
_get_font,
)
def test_programmatic_cover_is_portrait(tmp_path):
path = generate_programmatic_cover(
output_dir=str(tmp_path),
week_start=date(2026, 4, 6),
week_end=date(2026, 4, 12),
headlines=["Article One", "Article Two", "Article Three"],
categories=["Government", "Government", "Culture"],
)
assert os.path.exists(path)
img = PILImage.open(path)
assert img.format == "JPEG"
assert img.size == (480, 800)
def test_programmatic_cover_no_headlines(tmp_path):
path = generate_programmatic_cover(
output_dir=str(tmp_path),
week_start=date(2026, 4, 6),
week_end=date(2026, 4, 12),
headlines=[],
categories=[],
)
assert os.path.exists(path)
img = PILImage.open(path)
assert img.size == (480, 800)
def test_generate_mosaic_cover_single_image(tmp_path):
# Create a dummy image
img_path = str(tmp_path / "dummy.jpg")
img = PILImage.new("RGB", (1000, 500), color="red")
img.save(img_path)
output_dir = str(tmp_path)
week_start = date(2026, 4, 6)
week_end = date(2026, 4, 12)
headlines = ["Test Headline"]
cover_path = generate_mosaic_cover(output_dir, week_start, week_end, headlines, [img_path])
assert os.path.exists(cover_path)
with PILImage.open(cover_path) as result_img:
assert result_img.size == (480, 800)
def test_generate_cover_dispatches(tmp_path):
with patch("src.cover.generate_mosaic_cover") as mock_mosaic:
mock_mosaic.return_value = "/fake/mosaic.jpg"
result = generate_cover("mosaic", str(tmp_path), date(2026, 4, 6),
date(2026, 4, 12), ["A"], ["/x/a.jpg"])
assert result == "/fake/mosaic.jpg"
mock_mosaic.assert_called_once()
with patch("src.cover.generate_programmatic_cover") as mock_prog:
mock_prog.return_value = "/fake/text.jpg"
result = generate_cover("text", str(tmp_path), date(2026, 4, 6),
date(2026, 4, 12), ["A"], ["Government"])
assert result == "/fake/text.jpg"
mock_prog.assert_called_once()
def test_truncate_to_width_respects_pixel_budget():
font = _get_font(18)
short = "Short text"
result = _truncate_to_width(short, font, 400)
assert result == short, "Short text should not be truncated"
long = "A" * 200
result = _truncate_to_width(long, font, 400)
assert result.endswith("\u2026"), "Long text should end with ellipsis"
assert len(result) < len(long), "Truncated text should be shorter"
def test_truncate_to_width_fits_within_budget():
font = _get_font(18)
long = "This is a really long headline that should definitely be truncated to fit"
max_width = 448
result = _truncate_to_width(long, font, max_width)
dummy = PILImage.new("RGBA", (1, 1))
draw = ImageDraw.Draw(dummy)
bbox = draw.textbbox((0, 0), result, font=font)
rendered_width = bbox[2] - bbox[0]
assert rendered_width <= max_width, f"Rendered width {rendered_width} exceeds {max_width}"
def test_obituary_headlines_filtered():
"""Verify the filtering pattern used by publish/issues/scheduler call sites."""
class FakeArticle:
def __init__(self, title, categories):
self.title = title
self.categories = json.dumps(categories)
articles = [
FakeArticle("Town Meeting Approves Budget", ["Government"]),
FakeArticle("John Smith, 85, beloved teacher", ["Obituaries"]),
FakeArticle("Harbor Festival This Weekend", ["Culture"]),
FakeArticle("Jane Doe, 92, retired nurse", ["Obituaries"]),
FakeArticle("Panthers Win Championship", ["Sports"]),
]
headlines = [
a.title for a in articles
if "Obituaries" not in json.loads(a.categories)
]
assert len(headlines) == 3
assert "John Smith, 85, beloved teacher" not in headlines
assert "Jane Doe, 92, retired nurse" not in headlines
assert "Town Meeting Approves Budget" in headlines
assert "Harbor Festival This Weekend" in headlines
assert "Panthers Win Championship" in headlines