hokusai/tests/test_image.py
Konstantin Fickel 770f408dad
All checks were successful
Continuous Integration / Build Package (push) Successful in 30s
Continuous Integration / Lint, Check & Test (push) Successful in 45s
feat: ensure image output format matches file extension
- 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
2026-02-21 19:04:33 +01:00

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"