- Add hokusai/image.py with Pillow-based format detection and conversion - Pass output_format to OpenAI gpt-image models and BFL API - Convert mismatched images after provider writes (fallback for all providers) - Handle RGBA-to-RGB flattening for JPEG targets - Add Pillow dependency
121 lines
3.6 KiB
Python
121 lines
3.6 KiB
Python
"""Tests for hokusai.image format detection and conversion."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from PIL import Image
|
|
|
|
from hokusai.image import api_format_for_extension, ensure_image_format
|
|
|
|
|
|
class TestApiFormatForExtension:
|
|
"""Tests for mapping extensions to API format strings."""
|
|
|
|
def test_png(self) -> None:
|
|
assert api_format_for_extension(".png") == "png"
|
|
|
|
def test_jpg(self) -> None:
|
|
assert api_format_for_extension(".jpg") == "jpeg"
|
|
|
|
def test_jpeg(self) -> None:
|
|
assert api_format_for_extension(".jpeg") == "jpeg"
|
|
|
|
def test_webp(self) -> None:
|
|
assert api_format_for_extension(".webp") == "webp"
|
|
|
|
def test_txt_returns_none(self) -> None:
|
|
assert api_format_for_extension(".txt") is None
|
|
|
|
def test_case_insensitive(self) -> None:
|
|
assert api_format_for_extension(".PNG") == "png"
|
|
|
|
|
|
class TestEnsureImageFormat:
|
|
"""Tests for in-place image format conversion."""
|
|
|
|
def test_png_file_with_png_extension_unchanged(self, tmp_path: Path) -> None:
|
|
path = tmp_path / "img.png"
|
|
img = Image.new("RGB", (10, 10), color="red")
|
|
img.save(path, format="PNG")
|
|
original_bytes = path.read_bytes()
|
|
|
|
ensure_image_format(path)
|
|
|
|
assert path.read_bytes() == original_bytes
|
|
|
|
def test_jpeg_file_with_jpg_extension_unchanged(self, tmp_path: Path) -> None:
|
|
path = tmp_path / "img.jpg"
|
|
img = Image.new("RGB", (10, 10), color="red")
|
|
img.save(path, format="JPEG")
|
|
original_bytes = path.read_bytes()
|
|
|
|
ensure_image_format(path)
|
|
|
|
assert path.read_bytes() == original_bytes
|
|
|
|
def test_png_file_converted_to_jpg(self, tmp_path: Path) -> None:
|
|
path = tmp_path / "img.jpg"
|
|
img = Image.new("RGB", (10, 10), color="red")
|
|
img.save(path, format="PNG")
|
|
|
|
with Image.open(path) as check:
|
|
assert check.format == "PNG"
|
|
|
|
ensure_image_format(path)
|
|
|
|
with Image.open(path) as check:
|
|
assert check.format == "JPEG"
|
|
|
|
def test_jpeg_file_converted_to_png(self, tmp_path: Path) -> None:
|
|
path = tmp_path / "img.png"
|
|
img = Image.new("RGB", (10, 10), color="red")
|
|
img.save(path, format="JPEG")
|
|
|
|
with Image.open(path) as check:
|
|
assert check.format == "JPEG"
|
|
|
|
ensure_image_format(path)
|
|
|
|
with Image.open(path) as check:
|
|
assert check.format == "PNG"
|
|
|
|
def test_png_file_converted_to_webp(self, tmp_path: Path) -> None:
|
|
path = tmp_path / "img.webp"
|
|
img = Image.new("RGB", (10, 10), color="red")
|
|
img.save(path, format="PNG")
|
|
|
|
ensure_image_format(path)
|
|
|
|
with Image.open(path) as check:
|
|
assert check.format == "WEBP"
|
|
|
|
def test_rgba_png_converted_to_jpg_flattened(self, tmp_path: Path) -> None:
|
|
path = tmp_path / "img.jpg"
|
|
img = Image.new("RGBA", (10, 10), color=(255, 0, 0, 128))
|
|
img.save(path, format="PNG")
|
|
|
|
ensure_image_format(path)
|
|
|
|
with Image.open(path) as check:
|
|
assert check.format == "JPEG"
|
|
assert check.mode == "RGB"
|
|
|
|
def test_text_extension_ignored(self, tmp_path: Path) -> None:
|
|
path = tmp_path / "file.txt"
|
|
_ = path.write_text("hello")
|
|
original = path.read_bytes()
|
|
|
|
ensure_image_format(path)
|
|
|
|
assert path.read_bytes() == original
|
|
|
|
def test_jpeg_extension_alias(self, tmp_path: Path) -> None:
|
|
path = tmp_path / "img.jpeg"
|
|
img = Image.new("RGB", (10, 10), color="red")
|
|
img.save(path, format="PNG")
|
|
|
|
ensure_image_format(path)
|
|
|
|
with Image.open(path) as check:
|
|
assert check.format == "JPEG"
|