"""渲染结果内部下载接口单元测试。 测试 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 == []