Files
hao-ai-lab-FastVideo/fastvideo/tests/entrypoints/test_openai_api.py
T

258 lines
7.7 KiB
Python

"""Unit tests for the OpenAI-compatible API server helpers (no GPU needed)."""
import os
from unittest.mock import patch
import pytest
from fastvideo.entrypoints.openai.protocol import (
ImageGenerationsRequest,
ImageResponseData,
VideoGenerationsRequest,
VideoListResponse,
VideoResponse,
generate_request_id,
)
from fastvideo.entrypoints.openai.utils import (
choose_image_ext,
merge_image_input_list,
parse_size,
)
# ---------------------------------------------------------------------------
# parse_size
# ---------------------------------------------------------------------------
class TestParseSize:
def test_valid(self):
assert parse_size("1024x768") == (1024, 768)
def test_valid_uppercase(self):
assert parse_size("512X512") == (512, 512)
def test_valid_with_spaces(self):
assert parse_size(" 720 x 480 ") == (720, 480)
def test_invalid_single_number(self):
assert parse_size("1024") == (None, None)
def test_invalid_non_numeric(self):
assert parse_size("widexhigh") == (None, None)
def test_invalid_empty(self):
assert parse_size("") == (None, None)
def test_invalid_triple(self):
assert parse_size("1x2x3") == (None, None)
def test_zero_dimensions(self):
assert parse_size("0x0") == (0, 0)
# ---------------------------------------------------------------------------
# choose_image_ext
# ---------------------------------------------------------------------------
class TestChooseImageExt:
def test_explicit_png(self):
assert choose_image_ext("png", None) == "png"
def test_explicit_webp(self):
assert choose_image_ext("webp", None) == "webp"
def test_explicit_jpeg_normalises(self):
assert choose_image_ext("jpeg", None) == "jpg"
def test_explicit_jpg(self):
assert choose_image_ext("jpg", None) == "jpg"
def test_transparent_background_defaults_png(self):
assert choose_image_ext(None, "transparent") == "png"
def test_opaque_background_defaults_jpg(self):
assert choose_image_ext(None, "opaque") == "jpg"
def test_no_args_defaults_jpg(self):
assert choose_image_ext(None, None) == "jpg"
def test_format_overrides_background(self):
assert choose_image_ext("webp", "transparent") == "webp"
# ---------------------------------------------------------------------------
# merge_image_input_list
# ---------------------------------------------------------------------------
class TestMergeImageInputList:
def test_none_inputs(self):
assert merge_image_input_list(None, None) == []
def test_single_item(self):
assert merge_image_input_list("a") == ["a"]
def test_list_input(self):
assert merge_image_input_list(["a", "b"]) == ["a", "b"]
def test_mixed_inputs(self):
result = merge_image_input_list(None, ["x", "y"], "z")
assert result == ["x", "y", "z"]
def test_empty_list_ignored(self):
assert merge_image_input_list([], "a") == ["a"]
# ---------------------------------------------------------------------------
# image_api._build_generation_kwargs
# ---------------------------------------------------------------------------
class TestImageBuildGenerationKwargs:
@pytest.fixture(autouse=True)
def _patch_output_dir(self, tmp_path):
with patch(
"fastvideo.entrypoints.openai.image_api.get_output_dir",
return_value=str(tmp_path),
):
yield tmp_path
def _build(self, **overrides):
from fastvideo.entrypoints.openai.image_api import (
_build_generation_kwargs, )
defaults = dict(request_id="req-1", prompt="a cat")
defaults.update(overrides)
return _build_generation_kwargs(**defaults)
def test_output_path_under_images_subdir(self, tmp_path):
kw = self._build()
assert kw["output_path"].startswith(
os.path.join(str(tmp_path), "images"))
def test_num_frames_always_one(self):
kw = self._build()
assert kw["num_frames"] == 1
def test_size_parsed(self):
kw = self._build(size="640x480")
assert kw["width"] == 640
assert kw["height"] == 480
def test_seed_forwarded(self):
kw = self._build(seed=42)
assert kw["seed"] == 42
def test_n_clamped(self):
kw = self._build(n=20)
assert kw["num_videos_per_prompt"] == 10
def test_extension_jpg_default(self):
kw = self._build()
assert kw["output_path"].endswith(".jpg")
def test_extension_png_for_transparent(self):
kw = self._build(background="transparent")
assert kw["output_path"].endswith(".png")
# ---------------------------------------------------------------------------
# video_api._build_generation_kwargs
# ---------------------------------------------------------------------------
class TestVideoBuildGenerationKwargs:
@pytest.fixture(autouse=True)
def _patch_output_dir(self, tmp_path):
with patch(
"fastvideo.entrypoints.openai.video_api.get_output_dir",
return_value=str(tmp_path),
):
yield tmp_path
def _build(self, **overrides):
from fastvideo.entrypoints.openai.video_api import (
_build_generation_kwargs, )
defaults = dict(prompt="a running dog", seconds=4)
defaults.update(overrides)
req = VideoGenerationsRequest(**defaults)
return _build_generation_kwargs("req-v1", req)
def test_output_path_under_videos_subdir(self, tmp_path):
kw = self._build()
assert kw["output_path"].startswith(
os.path.join(str(tmp_path), "videos"))
assert kw["output_path"].endswith(".mp4")
def test_fps_defaults_24(self):
kw = self._build()
assert kw["fps"] == 24
def test_num_frames_from_seconds(self):
kw = self._build(seconds=2, fps=30)
assert kw["num_frames"] == 60
def test_explicit_num_frames_overrides_seconds(self):
kw = self._build(seconds=10, num_frames=5)
assert kw["num_frames"] == 5
def test_seed_forwarded(self):
kw = self._build(seed=123)
assert kw["seed"] == 123
def test_custom_output_path_overrides_default(self, tmp_path):
custom = str(tmp_path / "custom")
kw = self._build(output_path=custom)
assert kw["output_path"].startswith(custom)
# ---------------------------------------------------------------------------
# Protocol Pydantic models
# ---------------------------------------------------------------------------
class TestProtocolModels:
def test_image_request_required_fields(self):
req = ImageGenerationsRequest(prompt="test")
assert req.prompt == "test"
assert req.n == 1
assert req.size == "1024x1024"
def test_image_request_missing_prompt_raises(self):
with pytest.raises(Exception):
ImageGenerationsRequest()
def test_video_request_defaults(self):
req = VideoGenerationsRequest(prompt="hello")
assert req.seconds == 4
assert req.seed == 1024
def test_video_response_defaults(self):
resp = VideoResponse(id="v1")
assert resp.status == "queued"
assert resp.object == "video"
def test_image_response_data_optional_fields(self):
d = ImageResponseData()
assert d.b64_json is None
assert d.url is None
def test_generate_request_id_unique(self):
ids = {generate_request_id() for _ in range(100)}
assert len(ids) == 100
def test_video_list_response(self):
resp = VideoListResponse(
data=[VideoResponse(
id="a"), VideoResponse(id="b")])
assert len(resp.data) == 2
assert resp.object == "list"