Files
xiaoxia-saas/tests/unit/test_video_response_dup_fields.py
T
xiaoxia 4725d94c7e
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 3s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
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 / Check if frontend-only change (pull_request) Successful in 6s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 14s
CI/CD Pipeline / Check push changed paths (push) Successful in 15s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
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 / PR Build API Image (pull_request) Successful in 28s
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 34s
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 34s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 36s
CI/CD Pipeline / Build Staging API Image (push) Successful in 37s
CI/CD Pipeline / Integration Tests (push) Successful in 1m53s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m17s
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 / Build Staging Worker Image (push) Successful in 39s
CI/CD Pipeline / CI Gate (pull_request) Successful in 5s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Validate - Style (push) Successful in 3m4s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m31s
CI/CD Pipeline / Deploy Production (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 / Retag skipped Staging Web Image (push) 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 / ACR Image Cleanup (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m19s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 54s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m42s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 5m53s
CI/CD Pipeline / Validate - Security (push) Successful in 6m19s
AI Code Review / AI Code Review (pull_request) Failing after 6m31s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m9s
CI/CD Pipeline / Unit Tests (push) Successful in 8m47s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
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 / Canary Release to Production (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 / Staging E2E Tests (push) Successful in 6m8s
feat(api): 成品视频接口补全查重字段 duplicate_rate/visual_similarity/match_count #1660 (#1678)
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-04 01:03:35 +08:00

124 lines
4.3 KiB
Python

"""#1660 成品视频 API 查重字段透传测试。
覆盖两套响应构造路径:
- routes/videos.py::_to_video_response -> VideoItemResponse (/videos 列表)
- routes/generation_tasks.py::_to_generated_video_response -> GeneratedVideoResponse
"""
from types import SimpleNamespace
from unittest.mock import MagicMock
from app.api.routes.generation_tasks import _to_generated_video_response
from app.api.routes.videos import _to_video_response
from app.schemas.generated_video import GeneratedVideoResponse
from app.schemas.video_center import VideoItemResponse
def _make_item(**overrides):
base = dict(
id="v1",
project_id="p1",
generation_task_id="t1",
name="成片",
file_url="oss://bucket/v1.mp4",
file_size=1024,
duration=12.5,
thumbnail_url=None,
width=1080,
height=1920,
fps=30.0,
status="completed",
review_status="pending_review",
generation_params={},
generated_at=None,
duplicate_rate=None,
match_count=None,
visual_similarity=None,
)
base.update(overrides)
return SimpleNamespace(**base)
class TestVideoItemResponseDupFields:
def test_passes_through_all_three_fields(self):
item = _make_item(duplicate_rate=42.5, match_count=7, visual_similarity=0.83)
resp = _to_video_response(item, storage=None)
assert isinstance(resp, VideoItemResponse)
assert resp.duplicate_rate == 42.5
assert resp.match_count == 7
assert resp.visual_similarity == 0.83
def test_legacy_video_without_fields_returns_none(self):
"""老数据/实体无查重字段时保持 None(前端自动隐藏),不报错。"""
item = SimpleNamespace(
id="v2",
project_id="p1",
generation_task_id="t2",
name="老视频",
file_url="oss://bucket/v2.mp4",
file_size=1,
duration=1.0,
thumbnail_url=None,
width=720,
height=1280,
fps=24.0,
status="completed",
review_status="pending_review",
generation_params={},
)
resp = _to_video_response(item, storage=None)
assert resp.duplicate_rate is None
assert resp.match_count is None
assert resp.visual_similarity is None
def test_explicit_none_values_kept(self):
item = _make_item()
resp = _to_video_response(item, storage=None)
assert resp.duplicate_rate is None
assert resp.match_count is None
assert resp.visual_similarity is None
def test_zero_match_count_is_valid_value(self):
"""计算后确无匹配:match_count=0 / visual_similarity=0.0 是合法值,不能变 None。"""
item = _make_item(duplicate_rate=0.0, match_count=0, visual_similarity=0.0)
resp = _to_video_response(item, storage=None)
assert resp.match_count == 0
assert resp.visual_similarity == 0.0
class TestGeneratedVideoResponseDupFields:
def test_passes_through_all_three_fields(self):
item = _make_item(duplicate_rate=15.2, match_count=3, visual_similarity=0.61)
resp = _to_generated_video_response(item, download_url="https://dl/x")
assert isinstance(resp, GeneratedVideoResponse)
assert resp.duplicate_rate == 15.2
assert resp.match_count == 3
assert resp.visual_similarity == 0.61
assert resp.download_url == "https://dl/x"
def test_missing_fields_default_none(self):
item = SimpleNamespace(
id="v3",
project_id="p1",
generation_task_id="t3",
name="x",
file_url="oss://x",
file_size=1,
duration=1.0,
thumbnail_url=None,
width=720,
height=1280,
fps=24.0,
)
resp = _to_generated_video_response(item)
assert resp.duplicate_rate is None
assert resp.match_count is None
assert resp.visual_similarity is None
def test_storage_failure_falls_back_to_file_url(self):
storage = MagicMock()
storage.get_download_url.side_effect = RuntimeError("oss down")
item = _make_item()
resp = _to_video_response(item, storage=storage)
assert resp.download_url == item.file_url