a0cac1b75d
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 2m1s
CI/CD Pipeline / Frontend Lint (push) Successful in 2m10s
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 2m3s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (push) Successful in 7m0s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m9s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m7s
191 lines
6.7 KiB
Python
191 lines
6.7 KiB
Python
"""渲染结果内部下载接口单元测试。
|
|
|
|
测试 internal_render 路由的核心逻辑,mock 掉 repository 和 storage 依赖。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
from app.api.routes.internal_render import (
|
|
InternalRenderDownloadUrlResponse,
|
|
InternalRenderTaskVideosResponse,
|
|
_video_to_item,
|
|
get_render_task_videos,
|
|
get_render_video_download_url,
|
|
)
|
|
|
|
# ── Helpers ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
class MockVideo:
|
|
"""模拟 GeneratedVideo 领域对象。"""
|
|
|
|
def __init__(self, **kwargs):
|
|
self.id = kwargs.get("id", "video-1")
|
|
self.generation_task_id = kwargs.get("generation_task_id", "task-1")
|
|
self.project_id = kwargs.get("project_id", "proj-1")
|
|
self.name = kwargs.get("name", "test_video.mp4")
|
|
self.file_url = kwargs.get("file_url", "videos/test/output.mp4")
|
|
self.file_size = kwargs.get("file_size", 1024000)
|
|
self.duration = kwargs.get("duration", 30.5)
|
|
self.width = kwargs.get("width", 1080)
|
|
self.height = kwargs.get("height", 1920)
|
|
self.fps = kwargs.get("fps", 30.0)
|
|
self.status = kwargs.get("status", "completed")
|
|
|
|
|
|
# ── _video_to_item 测试 ────────────────────────────────────────────────────
|
|
|
|
|
|
class TestVideoToItem:
|
|
"""测试视频对象转响应项。"""
|
|
|
|
def test_basic_conversion(self):
|
|
video = MockVideo(id="v1", generation_task_id="t1", status="completed")
|
|
item = _video_to_item(video, "https://oss.example.com/download?v1")
|
|
assert item.video_id == "v1"
|
|
assert item.generation_task_id == "t1"
|
|
assert item.status == "completed"
|
|
assert item.download_url == "https://oss.example.com/download?v1"
|
|
|
|
def test_missing_optional_fields(self):
|
|
"""缺可选字段时返回 None。"""
|
|
video = MockVideo()
|
|
# 去掉可选字段
|
|
del video.file_size
|
|
del video.duration
|
|
item = _video_to_item(video, "https://example.com/dl")
|
|
assert item.file_size is None
|
|
assert item.duration is None
|
|
assert item.width == 1080 # 还在
|
|
|
|
|
|
# ── 路由函数测试 ────────────────────────────────────────────────────────────
|
|
|
|
|
|
class TestGetRenderVideoDownloadUrl:
|
|
"""测试单个视频下载URL接口。"""
|
|
|
|
def test_video_exists(self):
|
|
video = MockVideo(id="v-abc", file_url="videos/abc/out.mp4")
|
|
mock_repo = MagicMock()
|
|
mock_repo.get.return_value = video
|
|
mock_storage = MagicMock()
|
|
mock_storage.get_download_url.return_value = "https://oss.test/signed?v=abc"
|
|
|
|
result = get_render_video_download_url(
|
|
video_id="v-abc",
|
|
_=True,
|
|
generated_video_repository=mock_repo,
|
|
storage_service=mock_storage,
|
|
)
|
|
|
|
assert isinstance(result, InternalRenderDownloadUrlResponse)
|
|
assert result.video_id == "v-abc"
|
|
assert result.download_url == "https://oss.test/signed?v=abc"
|
|
mock_repo.get.assert_called_once_with("v-abc")
|
|
mock_storage.get_download_url.assert_called_once()
|
|
|
|
def test_video_not_found_raises_404(self):
|
|
from fastapi import HTTPException
|
|
|
|
mock_repo = MagicMock()
|
|
mock_repo.get.return_value = None
|
|
mock_storage = MagicMock()
|
|
|
|
with pytest.raises(HTTPException) as exc_info:
|
|
get_render_video_download_url(
|
|
video_id="nonexistent",
|
|
_=True,
|
|
generated_video_repository=mock_repo,
|
|
storage_service=mock_storage,
|
|
)
|
|
assert exc_info.value.status_code == 404
|
|
|
|
def test_download_url_long_expiry(self):
|
|
"""过期时间应为 24 小时(86400s)。"""
|
|
video = MockVideo(id="v1")
|
|
mock_repo = MagicMock()
|
|
mock_repo.get.return_value = video
|
|
mock_storage = MagicMock()
|
|
mock_storage.get_download_url.return_value = "https://oss.test/signed"
|
|
|
|
get_render_video_download_url(
|
|
video_id="v1",
|
|
_=True,
|
|
generated_video_repository=mock_repo,
|
|
storage_service=mock_storage,
|
|
)
|
|
|
|
# 验证 expires_seconds=86400
|
|
call_kwargs = mock_storage.get_download_url.call_args
|
|
assert call_kwargs.kwargs.get("expires_seconds") == 86400 or call_kwargs[1].get("expires_seconds") == 86400
|
|
|
|
|
|
class TestGetRenderTaskVideos:
|
|
"""测试任务视频列表接口。"""
|
|
|
|
def test_list_multiple_videos(self):
|
|
videos = [
|
|
MockVideo(id="v1", status="completed"),
|
|
MockVideo(id="v2", status="completed"),
|
|
MockVideo(id="v3", status="failed"),
|
|
]
|
|
mock_repo = MagicMock()
|
|
mock_repo.list_by_generation_task.return_value = videos
|
|
mock_storage = MagicMock()
|
|
mock_storage.get_download_url.return_value = "https://oss.test/signed"
|
|
|
|
result = get_render_task_videos(
|
|
task_id="task-1",
|
|
status=None,
|
|
_=True,
|
|
generated_video_repository=mock_repo,
|
|
storage_service=mock_storage,
|
|
)
|
|
|
|
assert isinstance(result, InternalRenderTaskVideosResponse)
|
|
assert result.task_id == "task-1"
|
|
assert result.count == 3
|
|
assert len(result.videos) == 3
|
|
|
|
def test_filter_by_status(self):
|
|
videos = [
|
|
MockVideo(id="v1", status="completed"),
|
|
MockVideo(id="v2", status="completed"),
|
|
MockVideo(id="v3", status="failed"),
|
|
]
|
|
mock_repo = MagicMock()
|
|
mock_repo.list_by_generation_task.return_value = videos
|
|
mock_storage = MagicMock()
|
|
mock_storage.get_download_url.return_value = "https://oss.test/signed"
|
|
|
|
result = get_render_task_videos(
|
|
task_id="task-1",
|
|
status="completed",
|
|
_=True,
|
|
generated_video_repository=mock_repo,
|
|
storage_service=mock_storage,
|
|
)
|
|
|
|
assert result.count == 2
|
|
assert all(v.status == "completed" for v in result.videos)
|
|
|
|
def test_empty_task(self):
|
|
mock_repo = MagicMock()
|
|
mock_repo.list_by_generation_task.return_value = []
|
|
mock_storage = MagicMock()
|
|
|
|
result = get_render_task_videos(
|
|
task_id="empty-task",
|
|
status=None,
|
|
_=True,
|
|
generated_video_repository=mock_repo,
|
|
storage_service=mock_storage,
|
|
)
|
|
|
|
assert result.count == 0
|
|
assert result.videos == []
|