dd16f8f783
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 4s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 11s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 46s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 57s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 54s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 32s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 35s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m3s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 55s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m15s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m29s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m0s
CI/CD Pipeline / Validate - Style (push) Successful in 4m37s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m51s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m30s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m50s
CI/CD Pipeline / Integration Tests (push) Successful in 6m32s
AI Code Review / AI Code Review (pull_request) Successful in 6m55s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 9m22s
CI/CD Pipeline / CI Gate (pull_request) Failing after 1s
CI/CD Pipeline / Unit Tests (push) Successful in 10m54s
CI/CD Pipeline / Validate - Security (push) Successful in 11m32s
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production API Image (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 / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
168 lines
6.3 KiB
Python
168 lines
6.3 KiB
Python
"""#2028: generation_cover._get_task_video_url 兜底逻辑测试。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing")
|
|
os.environ.setdefault("DATABASE_URL", "sqlite:///test.db")
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api"))
|
|
|
|
import pytest
|
|
|
|
|
|
class TestGetTaskVideoUrlAwaitingCoverFallback:
|
|
def test_returns_url_from_rendered_output_when_awaiting_cover(self):
|
|
from app.api.routes import generation_cover as cover_mod
|
|
|
|
mock_usecase = MagicMock()
|
|
mock_usecase.execute.return_value = []
|
|
|
|
mock_task = MagicMock()
|
|
mock_task.status.value = "awaiting_cover"
|
|
mock_task.extra_meta = {"rendered_output": {"file_url": "oss://generated/awaiting.mp4"}}
|
|
|
|
mock_task_repo_instance = MagicMock()
|
|
mock_task_repo_instance.get.return_value = mock_task
|
|
mock_db = MagicMock()
|
|
|
|
with (
|
|
patch.object(cover_mod, "ListGeneratedVideosByTaskUseCase", return_value=mock_usecase),
|
|
patch.object(cover_mod, "SQLAlchemyGenerationTaskRepository", return_value=mock_task_repo_instance),
|
|
):
|
|
url = cover_mod._get_task_video_url("task-aw1", mock_db)
|
|
assert url == "oss://generated/awaiting.mp4"
|
|
|
|
def test_returns_none_when_status_not_awaiting_cover(self):
|
|
from app.api.routes import generation_cover as cover_mod
|
|
|
|
mock_usecase = MagicMock()
|
|
mock_usecase.execute.return_value = []
|
|
mock_task = MagicMock()
|
|
mock_task.status.value = "running"
|
|
mock_task.extra_meta = {"rendered_output": {"file_url": "oss://x.mp4"}}
|
|
mock_task_repo_instance = MagicMock()
|
|
mock_task_repo_instance.get.return_value = mock_task
|
|
mock_db = MagicMock()
|
|
with (
|
|
patch.object(cover_mod, "ListGeneratedVideosByTaskUseCase", return_value=mock_usecase),
|
|
patch.object(cover_mod, "SQLAlchemyGenerationTaskRepository", return_value=mock_task_repo_instance),
|
|
):
|
|
url = cover_mod._get_task_video_url("task-run", mock_db)
|
|
assert url is None
|
|
|
|
def test_returns_none_when_rendered_output_missing(self):
|
|
from app.api.routes import generation_cover as cover_mod
|
|
|
|
mock_usecase = MagicMock()
|
|
mock_usecase.execute.return_value = []
|
|
mock_task = MagicMock()
|
|
mock_task.status.value = "awaiting_cover"
|
|
mock_task.extra_meta = {}
|
|
mock_task_repo_instance = MagicMock()
|
|
mock_task_repo_instance.get.return_value = mock_task
|
|
mock_db = MagicMock()
|
|
with (
|
|
patch.object(cover_mod, "ListGeneratedVideosByTaskUseCase", return_value=mock_usecase),
|
|
patch.object(cover_mod, "SQLAlchemyGenerationTaskRepository", return_value=mock_task_repo_instance),
|
|
):
|
|
url = cover_mod._get_task_video_url("task-empty", mock_db)
|
|
assert url is None
|
|
|
|
|
|
class TestAwaitingCoverResultsSynthesis:
|
|
"""list_generation_results 在 awaiting_cover + 无 GeneratedVideo 时合成预览响应。"""
|
|
|
|
def _invoke(self, task, storage_service):
|
|
from app.api.routes import generation_tasks as gt_mod
|
|
|
|
mock_auth = MagicMock()
|
|
mock_auth.user.id = "u1"
|
|
mock_task_repo = MagicMock()
|
|
mock_task_repo.get.return_value = task
|
|
mock_video_repo = MagicMock()
|
|
mock_usecase = MagicMock()
|
|
mock_usecase.execute.return_value = []
|
|
mock_project_repo = MagicMock()
|
|
|
|
with (
|
|
patch.object(gt_mod, "check_project_access", return_value=None),
|
|
patch.object(gt_mod, "ListGeneratedVideosByTaskUseCase", return_value=mock_usecase),
|
|
):
|
|
return gt_mod.list_generation_results(
|
|
task_id=task.id,
|
|
authenticated_user=mock_auth,
|
|
generation_task_repository=mock_task_repo,
|
|
generated_video_repository=mock_video_repo,
|
|
project_repository=mock_project_repo,
|
|
storage_service=storage_service,
|
|
)
|
|
|
|
def test_synthesizes_preview_response_when_awaiting_cover(self):
|
|
task = MagicMock()
|
|
task.id = "task-syn-1"
|
|
task.project_id = "proj1"
|
|
task.status.value = "awaiting_cover"
|
|
task.cover_url = ""
|
|
task.extra_meta = {
|
|
"rendered_output": {
|
|
"file_url": "oss://bucket/v.mp4",
|
|
"file_size": 2048,
|
|
"duration": 10.5,
|
|
"width": 1080,
|
|
"height": 1920,
|
|
"fps": 30.0,
|
|
"name": "合成预览",
|
|
"mode": "random",
|
|
"thumbnail_url": "https://cdn/t.jpg",
|
|
}
|
|
}
|
|
task.updated_at = None
|
|
task.created_at = None
|
|
storage = MagicMock()
|
|
storage.get_download_url.return_value = "https://signed/v.mp4"
|
|
resp = self._invoke(task, storage)
|
|
assert len(resp.items) == 1
|
|
item = resp.items[0]
|
|
assert item.id == "preview-task-syn-1"
|
|
assert item.name == "合成预览"
|
|
assert item.file_size == 2048
|
|
assert item.duration == 10.5
|
|
assert item.width == 1080
|
|
assert item.height == 1920
|
|
assert item.fps == 30.0
|
|
assert item.download_url == "https://signed/v.mp4"
|
|
|
|
def test_http_file_url_used_directly(self):
|
|
task = MagicMock()
|
|
task.id = "task-httpx"
|
|
task.project_id = "p"
|
|
task.status.value = "awaiting_cover"
|
|
task.cover_url = ""
|
|
task.extra_meta = {"rendered_output": {"file_url": "https://cdn.example.com/v.mp4", "name": ""}}
|
|
task.updated_at = None
|
|
task.created_at = None
|
|
storage = MagicMock()
|
|
resp = self._invoke(task, storage)
|
|
assert resp.items[0].download_url == "https://cdn.example.com/v.mp4"
|
|
storage.get_download_url.assert_not_called()
|
|
assert resp.items[0].name.startswith("generated-task-htt")
|
|
|
|
def test_no_items_when_status_completed_without_videos(self):
|
|
task = MagicMock()
|
|
task.id = "task-done"
|
|
task.project_id = "p"
|
|
task.status.value = "completed"
|
|
task.extra_meta = {"rendered_output": {"file_url": "oss://x.mp4"}}
|
|
storage = MagicMock()
|
|
resp = self._invoke(task, storage)
|
|
assert resp.items == []
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(pytest.main([__file__, "-v"]))
|