"""Icon normalization — anything decodable becomes a 256x256 PNG.""" from __future__ import annotations import io import pytest from PIL import Image from imptune.services.image_utils import ICON_SIZE, ImageError, normalize_icon def _png(width: int, height: int, mode: str = "RGBA") -> bytes: buf = io.BytesIO() Image.new(mode, (width, height), color="red").save(buf, format="PNG") return buf.getvalue() def _jpeg(width: int, height: int) -> bytes: buf = io.BytesIO() Image.new("RGB", (width, height), color="blue").save(buf, format="JPEG") return buf.getvalue() def test_exact_png_passes_through_byte_identical(): """The icon store is content-addressed — re-encoding would move the file.""" data = _png(*ICON_SIZE) assert normalize_icon(data) is data @pytest.mark.parametrize( "source", [_png(64, 64), _png(1024, 1024), _png(1024, 128), _jpeg(300, 200)], ids=["small", "large", "wide", "jpeg"], ) def test_everything_else_becomes_a_256_png(source): out = normalize_icon(source) with Image.open(io.BytesIO(out)) as img: assert img.format == "PNG" assert img.size == ICON_SIZE def test_aspect_ratio_is_kept_not_stretched(): """A 400x100 source keeps its 4:1 shape, letterboxed in a square canvas. Checked through the alpha channel: the padding stays fully transparent, so the opaque band is 64px tall in a 256px canvas. """ out = normalize_icon(_png(400, 100)) with Image.open(io.BytesIO(out)) as img: alpha = img.convert("RGBA").split()[3] opaque_rows = [ y for y in range(256) if any(alpha.getpixel((x, y)) for x in range(256)) ] assert len(opaque_rows) == 64 # ...and it is centered, not flush to the top. assert opaque_rows[0] == 96 def test_undecodable_bytes_raise(): with pytest.raises(ImageError): normalize_icon(b"this is not an image") def test_empty_bytes_raise(): with pytest.raises(ImageError): normalize_icon(b"")