The image routes call VideoGenerator.generate with a GenerationRequest instead of passing flat generate_video keywords. The flat projection _build_generation_kwargs in video_api.py had no runtime caller, so it is removed, and its tests exercise build_generation_request instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
102 lines
4.1 KiB
Python
102 lines
4.1 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""Validate image response formats before invoking the generator."""
|
|
|
|
import base64
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock, Mock
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from fastvideo.entrypoints.openai import image_api
|
|
from fastvideo.entrypoints.openai.stores import AsyncDictStore
|
|
|
|
|
|
@pytest.fixture
|
|
def image_client(monkeypatch, tmp_path):
|
|
generate = Mock(side_effect=lambda request: Path(request.output.output_path).write_bytes(b"test-image-content"))
|
|
|
|
async def run_serialized(fn, *args, **kwargs):
|
|
return fn(*args, **kwargs)
|
|
|
|
engine = SimpleNamespace(
|
|
generator=SimpleNamespace(generate=generate),
|
|
run_serialized=AsyncMock(side_effect=run_serialized),
|
|
)
|
|
monkeypatch.setattr(image_api, "get_serving_engine", lambda: engine)
|
|
monkeypatch.setattr(image_api, "get_output_dir", lambda: str(tmp_path))
|
|
monkeypatch.setattr(image_api, "IMAGE_STORE", AsyncDictStore())
|
|
app = FastAPI()
|
|
app.include_router(image_api.router)
|
|
with TestClient(app) as client:
|
|
yield client, engine
|
|
|
|
|
|
@pytest.mark.parametrize("response_format", ["invalid", "INVALID"])
|
|
def test_invalid_response_format_does_not_generate(image_client, response_format, tmp_path):
|
|
client, engine = image_client
|
|
response = client.post("/v1/images", json={"prompt": "a cat", "response_format": response_format})
|
|
|
|
assert response.status_code == 400
|
|
assert response.json()["detail"] == "response_format=invalid is not supported"
|
|
engine.run_serialized.assert_not_awaited()
|
|
engine.generator.generate.assert_not_called()
|
|
assert not (tmp_path / "images").exists()
|
|
|
|
|
|
def test_edits_invalid_response_format_does_not_generate(image_client, tmp_path):
|
|
client, engine = image_client
|
|
response = client.post(
|
|
"/v1/images/edits",
|
|
data={"prompt": "a cat", "response_format": "invalid"},
|
|
files={"image": ("cat.png", b"fake-image", "image/png")},
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
assert response.json()["detail"] == "response_format=invalid is not supported"
|
|
engine.run_serialized.assert_not_awaited()
|
|
engine.generator.generate.assert_not_called()
|
|
assert not (tmp_path / "uploads").exists()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"payload,expected",
|
|
[
|
|
pytest.param({"prompt": "a cat"}, "url", id="omitted-uses-protocol-default"),
|
|
pytest.param({"prompt": "a cat", "response_format": None}, "b64_json", id="null-falls-back-to-b64-json"),
|
|
pytest.param({"prompt": "a cat", "response_format": ""}, "b64_json", id="empty-falls-back-to-b64-json"),
|
|
pytest.param({"prompt": "a cat", "response_format": "b64_json"}, "b64_json", id="b64-json"),
|
|
pytest.param({"prompt": "a cat", "response_format": "B64_JSON"}, "b64_json", id="b64-json-uppercase"),
|
|
pytest.param({"prompt": "a cat", "response_format": "url"}, "url", id="url"),
|
|
pytest.param({"prompt": "a cat", "response_format": "URL"}, "url", id="url-uppercase"),
|
|
],
|
|
)
|
|
def test_supported_response_formats_still_generate(image_client, payload, expected):
|
|
client, engine = image_client
|
|
response = client.post("/v1/images", json=payload)
|
|
|
|
assert response.status_code == 200, response.text
|
|
body = response.json()
|
|
if expected == "url":
|
|
assert body["data"][0]["url"] == f"/v1/images/{body['id']}/content"
|
|
else:
|
|
assert base64.b64decode(body["data"][0]["b64_json"]) == b"test-image-content"
|
|
engine.run_serialized.assert_awaited_once()
|
|
engine.generator.generate.assert_called_once()
|
|
|
|
|
|
def test_edits_supported_response_format_still_generates(image_client):
|
|
client, engine = image_client
|
|
response = client.post(
|
|
"/v1/images/edits",
|
|
data={"prompt": "a cat", "response_format": "b64_json"},
|
|
files={"image": ("cat.png", b"fake-image", "image/png")},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
assert base64.b64decode(response.json()["data"][0]["b64_json"]) == b"test-image-content"
|
|
engine.run_serialized.assert_awaited_once()
|
|
engine.generator.generate.assert_called_once()
|