feat: implement mosaic cover generation logic
Made-with: Cursor
This commit is contained in:
@@ -1,19 +1,16 @@
|
||||
import json
|
||||
import os
|
||||
from collections import Counter
|
||||
from datetime import date
|
||||
from unittest.mock import patch, MagicMock
|
||||
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_ai_cover,
|
||||
generate_mosaic_cover,
|
||||
generate_cover,
|
||||
_get_dominant_category,
|
||||
_truncate_to_width,
|
||||
_get_font,
|
||||
CATEGORY_PROMPTS,
|
||||
)
|
||||
|
||||
|
||||
@@ -46,54 +43,31 @@ def test_programmatic_cover_no_headlines(tmp_path):
|
||||
assert img.size == (480, 800)
|
||||
|
||||
|
||||
def test_ai_cover_is_portrait(tmp_path):
|
||||
fake_img = PILImage.new("RGB", (480, 800), color="blue")
|
||||
buf = BytesIO()
|
||||
fake_img.save(buf, format="JPEG")
|
||||
fake_bytes = buf.getvalue()
|
||||
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)
|
||||
|
||||
mock_response = MagicMock()
|
||||
mock_response.content = fake_bytes
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
output_dir = str(tmp_path)
|
||||
week_start = date(2026, 4, 6)
|
||||
week_end = date(2026, 4, 12)
|
||||
headlines = ["Test Headline"]
|
||||
|
||||
with patch("src.cover.requests.get", return_value=mock_response):
|
||||
path = generate_ai_cover(
|
||||
output_dir=str(tmp_path),
|
||||
week_start=date(2026, 4, 6),
|
||||
week_end=date(2026, 4, 12),
|
||||
headlines=["Test Headline"],
|
||||
categories=["Government"],
|
||||
)
|
||||
cover_path = generate_mosaic_cover(output_dir, week_start, week_end, headlines, [img_path])
|
||||
|
||||
assert os.path.exists(path)
|
||||
img = PILImage.open(path)
|
||||
assert img.format == "JPEG"
|
||||
assert img.size == (480, 800)
|
||||
|
||||
|
||||
def test_ai_cover_falls_back_to_programmatic(tmp_path):
|
||||
with patch("src.cover.requests.get", side_effect=Exception("API down")):
|
||||
path = generate_ai_cover(
|
||||
output_dir=str(tmp_path),
|
||||
week_start=date(2026, 4, 6),
|
||||
week_end=date(2026, 4, 12),
|
||||
headlines=["Test"],
|
||||
categories=["Government"],
|
||||
)
|
||||
|
||||
assert os.path.exists(path)
|
||||
img = PILImage.open(path)
|
||||
assert img.format == "JPEG"
|
||||
assert img.size == (480, 800)
|
||||
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_ai_cover") as mock_ai:
|
||||
mock_ai.return_value = "/fake/ai.jpg"
|
||||
result = generate_cover("ai", str(tmp_path), date(2026, 4, 6),
|
||||
date(2026, 4, 12), ["A"], ["Government"])
|
||||
assert result == "/fake/ai.jpg"
|
||||
mock_ai.assert_called_once()
|
||||
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"
|
||||
@@ -103,17 +77,6 @@ def test_generate_cover_dispatches(tmp_path):
|
||||
mock_prog.assert_called_once()
|
||||
|
||||
|
||||
def test_get_dominant_category():
|
||||
assert _get_dominant_category(["Government", "Government", "Culture"]) == "Government"
|
||||
assert _get_dominant_category(["Culture", "Government"]) == "Culture"
|
||||
assert _get_dominant_category([]) == "Default"
|
||||
|
||||
|
||||
def test_category_prompts_include_massachusetts():
|
||||
for category, prompt in CATEGORY_PROMPTS.items():
|
||||
assert "Massachusetts" in prompt, f"Prompt for {category} missing 'Massachusetts'"
|
||||
|
||||
|
||||
def test_truncate_to_width_respects_pixel_budget():
|
||||
font = _get_font(18)
|
||||
short = "Short text"
|
||||
|
||||
Reference in New Issue
Block a user