Files
Jellyfish/backend/tests/test_generated_video_api_responses.py

193 lines
6.3 KiB
Python

"""generated_video 接口响应壳测试。"""
from __future__ import annotations
from collections.abc import AsyncGenerator
from types import SimpleNamespace
from fastapi import HTTPException
from fastapi.testclient import TestClient
from app.api.v1.routes.film import generated_video as route
from app.dependencies import get_db
from app.main import app
class _FakeTaskRecord:
def __init__(self, task_id: str) -> None:
self.id = task_id
class _FakeTaskManager:
def __init__(self, *_args, **_kwargs) -> None:
pass
async def create(self, *_args, **_kwargs) -> _FakeTaskRecord:
return _FakeTaskRecord("video-task-1")
class _FakeDB:
def __init__(self) -> None:
self.added: list[object] = []
self.committed = False
def add(self, obj: object) -> None:
self.added.append(obj)
async def commit(self) -> None:
self.committed = True
async def _async_noop(*_args, **_kwargs) -> None:
return None
def _override_db(db: _FakeDB):
async def _get_db() -> AsyncGenerator[_FakeDB, None]:
yield db
return _get_db
def test_preview_video_generation_prompt_returns_success_envelope(client: TestClient, monkeypatch) -> None:
db = _FakeDB()
async def _fake_preview(*_args, **_kwargs):
return "视频预览提示词", ["file-1", "file-2"], {
"shot_id": "shot-1",
"title": "镜头一",
"script_excerpt": "主角转身看向门口。",
"action_beats": ["主角转身", "视线停在门口"],
"action_beat_phases": [
{"text": "主角转身", "phase": "trigger"},
{"text": "视线停在门口", "phase": "aftermath"},
],
"previous_shot_summary": "标题:镜头零;剧本摘录:主角推门进入走廊",
"next_shot_goal": "标题:镜头二;主角停住动作,保持警惕",
"continuity_guidance": "承接上一镜头动作,不要像全新场面重新开局",
"composition_anchor": "以走廊门口作为空间锚点",
"screen_direction_guidance": "保持主角朝向和视线落点连续",
"dialogue_summary": "",
"characters": [],
"scene": None,
"props": [],
"costumes": [],
"camera": {"camera_shot": "MS", "angle": "EYE_LEVEL", "movement": "STATIC", "duration": 4},
"atmosphere": "紧张",
"visual_style": "现实",
"style": "真人都市",
"negative_prompt": "",
}
monkeypatch.setattr(route, "preview_prompt_and_images", _fake_preview)
app.dependency_overrides[get_db] = _override_db(db)
try:
response = client.post(
"/api/v1/film/tasks/video/preview-prompt",
json={
"shot_id": "shot-1",
"reference_mode": "first_last",
"prompt": "生成一个压迫感强的镜头",
"images": [],
"ratio": "9:16",
},
)
finally:
app.dependency_overrides.clear()
assert response.status_code == 200
body = response.json()
assert body["code"] == 200
assert body["message"] == "success"
assert body["data"]["prompt"] == "视频预览提示词"
assert body["data"]["images"] == ["file-1", "file-2"]
assert body["data"]["pack"]["previous_shot_summary"].startswith("标题:镜头零")
assert body["data"]["pack"]["next_shot_goal"].startswith("标题:镜头二")
def test_preview_video_generation_prompt_not_found_returns_api_response(
client: TestClient, monkeypatch
) -> None:
db = _FakeDB()
async def _fake_preview(*_args, **_kwargs):
raise HTTPException(status_code=404, detail="Shot not found")
monkeypatch.setattr(route, "preview_prompt_and_images", _fake_preview)
app.dependency_overrides[get_db] = _override_db(db)
try:
response = client.post(
"/api/v1/film/tasks/video/preview-prompt",
json={
"shot_id": "shot-missing",
"reference_mode": "text_only",
"prompt": "仅文本生成",
"images": [],
"ratio": "16:9",
},
)
finally:
app.dependency_overrides.clear()
assert response.status_code == 404
assert response.json() == {"code": 404, "message": "Shot not found", "data": None, "meta": None}
def test_create_video_generation_task_returns_created_envelope(client: TestClient, monkeypatch) -> None:
db = _FakeDB()
async def _fake_build_run_args(*_args, **_kwargs):
return {"prompt": "最终视频提示词", "images": ["file-1"]}
monkeypatch.setattr(route, "build_run_args", _fake_build_run_args)
monkeypatch.setattr(route, "TaskManager", _FakeTaskManager)
monkeypatch.setattr(route, "enqueue_task_execution", lambda task_id: SimpleNamespace(id=f"celery-{task_id}"))
monkeypatch.setattr(route, "mark_shot_generating", _async_noop)
app.dependency_overrides[get_db] = _override_db(db)
try:
response = client.post(
"/api/v1/film/tasks/video",
json={
"shot_id": "shot-1",
"reference_mode": "first",
"prompt": "生成一个节奏紧张的视频片段",
"images": [],
"ratio": "9:16",
},
)
finally:
app.dependency_overrides.clear()
assert response.status_code == 201
body = response.json()
assert body["code"] == 201
assert body["message"] == "success"
assert body["data"]["task_id"] == "video-task-1"
assert body["meta"] is None
assert db.committed is True
assert len(db.added) == 1
def test_create_video_generation_task_validation_error_returns_api_response(client: TestClient) -> None:
db = _FakeDB()
app.dependency_overrides[get_db] = _override_db(db)
try:
response = client.post(
"/api/v1/film/tasks/video",
json={
"shot_id": "shot-1",
"reference_mode": "invalid-mode",
"prompt": "bad",
"images": [],
"ratio": "16:9",
},
)
finally:
app.dependency_overrides.clear()
assert response.status_code == 422
body = response.json()
assert body["code"] == 422
assert body["data"] is None
assert "reference_mode" in body["message"]