feat(platform): images port — decode, resolution bar (Q-3), WebP renditions (SD-0002 §6.2)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
"""platform/images — pure decode + resolution bar + WebP renditions (SD-0002 §6.2)."""
|
||||
import io
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from app.platform import images
|
||||
|
||||
|
||||
def _png(width: int, height: int, color=(120, 80, 200)) -> bytes:
|
||||
buf = io.BytesIO()
|
||||
Image.new("RGB", (width, height), color).save(buf, format="PNG")
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def _jpeg(width: int, height: int) -> bytes:
|
||||
buf = io.BytesIO()
|
||||
Image.new("RGB", (width, height), (10, 200, 90)).save(buf, format="JPEG")
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def test_good_image_produces_three_webp_renditions():
|
||||
result = images.process(_png(1200, 900))
|
||||
assert isinstance(result, images.Processed)
|
||||
assert result.source_format == "PNG"
|
||||
assert set(result.renditions) == {"thumb", "card", "detail"}
|
||||
for name, blob in result.renditions.items():
|
||||
with Image.open(io.BytesIO(blob)) as im:
|
||||
assert im.format == "WEBP"
|
||||
with Image.open(io.BytesIO(result.renditions["thumb"])) as im:
|
||||
assert max(im.size) <= images.RENDITIONS["thumb"]
|
||||
with Image.open(io.BytesIO(result.renditions["detail"])) as im:
|
||||
assert max(im.size) <= images.RENDITIONS["detail"]
|
||||
|
||||
|
||||
def test_downscale_only_never_upscales_small_source():
|
||||
result = images.process(_png(520, 520))
|
||||
with Image.open(io.BytesIO(result.renditions["detail"])) as im:
|
||||
assert im.size == (520, 520)
|
||||
|
||||
|
||||
def test_below_resolution_bar_rejected_low_res():
|
||||
result = images.process(_png(300, 1200)) # shorter side 300 < 500
|
||||
assert isinstance(result, images.Rejected)
|
||||
assert result.reason == "rejected_low_res"
|
||||
|
||||
|
||||
def test_not_an_image_rejected_not_image():
|
||||
result = images.process(b"this is not an image")
|
||||
assert isinstance(result, images.Rejected)
|
||||
assert result.reason == "rejected_not_image"
|
||||
|
||||
|
||||
def test_jpeg_source_format_preserved_in_result():
|
||||
result = images.process(_jpeg(800, 800))
|
||||
assert result.source_format == "JPEG"
|
||||
Reference in New Issue
Block a user