Files
xiaoxia-saas/tests/unit/test_video_center_backend.py
T
xiaoxia eb4645314d
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production Runtime Images (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
feat: 成片中心后端升级(封面生成/复核/批量下载) (#287)
feat: 成片中心后端升级(封面生成/复核/批量下载)
2026-07-14 09:49:11 +08:00

217 lines
7.3 KiB
Python
Executable File

"""成片中心新功能单元测试。
覆盖:分页列表、复核状态更新、缩略图更新、批量获取、use case。
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api"))
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from packages.adapters.sqlalchemy_impl.generated_video_repository import SQLAlchemyGeneratedVideoRepository
from packages.adapters.sqlalchemy_impl.models import Base
from packages.application.generated_videos import (
GetVideosByIdsUseCase,
ListGeneratedVideosPaginatedUseCase,
UpdateVideoReviewStatusUseCase,
)
from packages.domain import GeneratedVideo
def _repository():
engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()
return SQLAlchemyGeneratedVideoRepository(session)
def _create_video(repo, project_id="proj-1", status="completed", review_status="pending_review", idx=1):
video = GeneratedVideo.create(
project_id=project_id,
generation_task_id=f"task-{idx}",
name=f"video-{idx}.mp4",
file_url=f"generated/video-{idx}.mp4",
file_size=1024 * idx,
duration=10.0 * idx,
width=1280,
height=720,
fps=25.0,
)
video.status = status
video.review_status = review_status
repo.create(video)
return video
class TestGeneratedVideoRepository:
"""GeneratedVideoRepository 新方法测试。"""
def test_list_paginated_default(self):
repo = _repository()
for i in range(5):
_create_video(repo, idx=i)
items, total = repo.list_paginated(page=1, page_size=3)
assert total == 5
assert len(items) == 3
# 按 generated_at 倒序,最新的在前
assert items[0].name == "video-4.mp4"
def test_list_paginated_by_project(self):
repo = _repository()
_create_video(repo, project_id="proj-a", idx=1)
_create_video(repo, project_id="proj-a", idx=2)
_create_video(repo, project_id="proj-b", idx=3)
items, total = repo.list_paginated(project_id="proj-a")
assert total == 2
assert all(i.project_id == "proj-a" for i in items)
def test_list_paginated_by_status(self):
repo = _repository()
_create_video(repo, status="completed", idx=1)
_create_video(repo, status="completed", idx=2)
_create_video(repo, status="failed", idx=3)
items, total = repo.list_paginated(status="completed")
assert total == 2
assert all(i.status == "completed" for i in items)
def test_list_paginated_by_review_status(self):
repo = _repository()
_create_video(repo, review_status="pending_review", idx=1)
_create_video(repo, review_status="approved", idx=2)
_create_video(repo, review_status="rejected", idx=3)
items, total = repo.list_paginated(review_status="approved")
assert total == 1
assert items[0].review_status == "approved"
def test_list_paginated_multi_filter(self):
repo = _repository()
_create_video(repo, project_id="p1", status="completed", review_status="approved", idx=1)
_create_video(repo, project_id="p1", status="completed", review_status="pending_review", idx=2)
_create_video(repo, project_id="p2", status="completed", review_status="approved", idx=3)
items, total = repo.list_paginated(project_id="p1", review_status="approved")
assert total == 1
assert items[0].project_id == "p1"
assert items[0].review_status == "approved"
def test_update_review_status(self):
repo = _repository()
video = _create_video(repo, idx=1)
result = repo.update_review_status(video.id, "approved")
assert result is not None
assert result.review_status == "approved"
# 验证持久化
saved = repo.get(video.id)
assert saved.review_status == "approved"
def test_update_review_status_not_found(self):
repo = _repository()
result = repo.update_review_status("nonexistent", "approved")
assert result is None
def test_update_thumbnail(self):
repo = _repository()
video = _create_video(repo, idx=1)
assert video.thumbnail_url is None
ok = repo.update_thumbnail(video.id, "https://oss/thumb.jpg")
assert ok is True
saved = repo.get(video.id)
assert saved.thumbnail_url == "https://oss/thumb.jpg"
def test_update_thumbnail_not_found(self):
repo = _repository()
ok = repo.update_thumbnail("nonexistent", "https://oss/thumb.jpg")
assert ok is False
def test_get_by_ids(self):
repo = _repository()
v1 = _create_video(repo, idx=1)
v2 = _create_video(repo, idx=2)
v3 = _create_video(repo, idx=3)
result = repo.get_by_ids([v1.id, v3.id])
assert len(result) == 2
ids = {v.id for v in result}
assert v1.id in ids
assert v3.id in ids
def test_get_by_ids_empty(self):
repo = _repository()
result = repo.get_by_ids([])
assert result == []
class TestGeneratedVideoUseCases:
"""Use case 层测试。"""
def test_list_paginated_use_case(self):
repo = _repository()
for i in range(10):
_create_video(repo, idx=i)
use_case = ListGeneratedVideosPaginatedUseCase(repo)
items, total = use_case.execute(page=2, page_size=3)
assert total == 10
assert len(items) == 3
def test_list_paginated_use_case_page_clamp(self):
repo = _repository()
use_case = ListGeneratedVideosPaginatedUseCase(repo)
# page < 1 应该被修正为 1
items, total = use_case.execute(page=0, page_size=20)
assert total == 0
def test_list_paginated_use_case_page_size_clamp(self):
repo = _repository()
use_case = ListGeneratedVideosPaginatedUseCase(repo)
# page_size > 100 应该被修正为 20
for i in range(30):
_create_video(repo, idx=i)
items, total = use_case.execute(page=1, page_size=200)
assert total == 30
assert len(items) == 20 # clamp 到默认 20
def test_update_review_status_use_case(self):
repo = _repository()
video = _create_video(repo, idx=1)
use_case = UpdateVideoReviewStatusUseCase(repo)
result = use_case.execute(video.id, "approved")
assert result is not None
assert result.review_status == "approved"
def test_update_review_status_use_case_invalid_status(self):
repo = _repository()
video = _create_video(repo, idx=1)
use_case = UpdateVideoReviewStatusUseCase(repo)
with pytest.raises(ValueError, match="无效的 review_status"):
use_case.execute(video.id, "invalid_status")
def test_update_review_status_use_case_empty_id(self):
repo = _repository()
use_case = UpdateVideoReviewStatusUseCase(repo)
with pytest.raises(ValueError, match="video_id 不能为空"):
use_case.execute("", "approved")
def test_get_by_ids_use_case(self):
repo = _repository()
v1 = _create_video(repo, idx=1)
v2 = _create_video(repo, idx=2)
use_case = GetVideosByIdsUseCase(repo)
result = use_case.execute([v1.id, v2.id])
assert len(result) == 2