from pathlib import Path from subprocess import CompletedProcess import pytest from PIL import Image from app.config import Settings from app.engines.base import UpscaleEngineUnavailable from app.image_io import DownloadedImage from app.upscaler import build_upscaler def make_downloaded_image(tmp_path: Path) -> DownloadedImage: source = tmp_path / "source.png" Image.new("RGBA", (12, 8), (25, 50, 75, 255)).save(source, "PNG") return DownloadedImage( path=source, width=12, height=8, mime="image/png", filesize=source.stat().st_size, ) def make_runtime_settings(tmp_path: Path) -> Settings: binary = tmp_path / "bin" / "realesrgan-ncnn-vulkan" binary.parent.mkdir(parents=True) binary.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8") binary.chmod(0o755) model_dir = tmp_path / "models" model_dir.mkdir(parents=True) (model_dir / "realesrgan-x4plus.param").write_text("param", encoding="utf-8") (model_dir / "realesrgan-x4plus.bin").write_text("bin", encoding="utf-8") return Settings( engine="realesrgan-ncnn", device="vulkan", token="secret-token", tmp_dir=str(tmp_path / "tmp"), output_dir=str(tmp_path / "output"), realesrgan_bin=str(binary), realesrgan_model_dir=str(model_dir), realesrgan_anime_model="realesrgan-x4plus-anime", realesrgan_allow_model_fallback=True, ) def test_realesrgan_command_is_built_without_shell_and_2x_records_downsample(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: settings = make_runtime_settings(tmp_path) engine = build_upscaler(settings) downloaded = make_downloaded_image(tmp_path) captured: dict[str, object] = {} def fake_run(command, **kwargs): captured["command"] = command captured["kwargs"] = kwargs output_index = command.index("-o") + 1 output_path = Path(command[output_index]) output_path.parent.mkdir(parents=True, exist_ok=True) Image.new("RGBA", (downloaded.width * 4, downloaded.height * 4), (120, 80, 20, 255)).save(output_path, "PNG") return CompletedProcess(command, 0, stdout="ok", stderr="") monkeypatch.setattr("app.engines.realesrgan_ncnn_engine.subprocess.run", fake_run) result = engine.upscale(downloaded, 2, "illustration", "webp") assert result.image.size == (downloaded.width * 2, downloaded.height * 2) assert result.metadata["requested_scale"] == 2 assert result.metadata["native_model_scale"] == 4 assert result.metadata["post_downsampled"] is True assert result.metadata["model_fallback"] is True assert result.metadata["used_model"] == "realesrgan-x4plus" assert captured["command"][0] == settings.realesrgan_bin assert captured["kwargs"].get("check") is False assert captured["kwargs"].get("shell", False) is False def test_missing_binary_raises_safe_engine_unavailable(tmp_path: Path) -> None: engine = build_upscaler( Settings( engine="realesrgan-ncnn", token="secret-token", tmp_dir=str(tmp_path / "tmp"), output_dir=str(tmp_path / "output"), realesrgan_bin=str(tmp_path / "bin" / "missing-binary"), realesrgan_model_dir=str(tmp_path / "models"), ) ) with pytest.raises(UpscaleEngineUnavailable, match="Upscale engine is not available"): engine.upscale(make_downloaded_image(tmp_path), 4, "artwork", "webp") def test_pillow_engine_still_works(tmp_path: Path) -> None: engine = build_upscaler( Settings( engine="pillow", token="secret-token", tmp_dir=str(tmp_path / "tmp"), output_dir=str(tmp_path / "output"), ) ) result = engine.upscale(make_downloaded_image(tmp_path), 2, "standard", "webp") assert result.metadata["engine"] == "pillow" assert result.metadata["real_ai_upscale"] is False