b4e3bb0fe7
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 47s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 50s
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 / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 3m22s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 3m27s
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 / Validate - Type Check (mypy) (push) Successful in 3m31s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m40s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m4s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Failing after 3m53s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m31s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 30s
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 / ACR Image Cleanup (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m45s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m30s
CI/CD Pipeline / Build Staging API Image (push) Successful in 7m24s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 7m21s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 8m19s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 8m24s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m4s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m21s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 2m25s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m21s
CI/CD Pipeline / Integration Tests (push) Successful in 3m28s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m27s
CI/CD Pipeline / Unit Tests (push) Successful in 15m5s
CI/CD Pipeline / CI Gate (push) Has been skipped
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 / 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
CI/CD Pipeline / Unit Tests (pull_request) Successful in 14m25s
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 Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 9s
136 lines
4.7 KiB
Python
Executable File
136 lines
4.7 KiB
Python
Executable File
"""Tests for PUT /templates/{id}/editor/clips batch update endpoint.
|
|
|
|
Updated for transactional replace_all_clips_transactional method.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing")
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_services():
|
|
plan_svc = MagicMock()
|
|
tpl_svc = MagicMock()
|
|
plan_svc.get_plan_or_raise.return_value = MagicMock(id="plan-1", template_id="tpl-1")
|
|
plan_svc.replace_all_clips_transactional.return_value = 2
|
|
return tpl_svc, plan_svc
|
|
|
|
|
|
class TestBatchUpdateClips:
|
|
def test_batch_update_calls_transactional_replace(self, mock_services):
|
|
"""验证批量更新调用事务性替换方法,传入正确的参数。"""
|
|
from app.api.routes.templates_editor.draft import batch_update_clips
|
|
from app.api.routes.templates_editor.schemas import (
|
|
EditorClipBatchItem,
|
|
EditorClipBatchUpdateRequest,
|
|
)
|
|
|
|
_, plan_svc = mock_services
|
|
req = EditorClipBatchUpdateRequest(
|
|
clips=[
|
|
EditorClipBatchItem(asset_id="a1", start_time=0.0, duration=3.0, order=0),
|
|
EditorClipBatchItem(asset_id="a2", start_time=3.0, duration=5.0, order=1),
|
|
]
|
|
)
|
|
|
|
result = batch_update_clips(
|
|
template_id="tpl-1",
|
|
req=req,
|
|
plan_id="plan-1",
|
|
services=mock_services,
|
|
_=MagicMock(),
|
|
)
|
|
|
|
assert result.plan_id == "plan-1"
|
|
assert result.clip_count == 2
|
|
plan_svc.replace_all_clips_transactional.assert_called_once()
|
|
call_args = plan_svc.replace_all_clips_transactional.call_args
|
|
assert call_args[0][0] == "plan-1"
|
|
clips_data = call_args[0][1]
|
|
assert len(clips_data) == 2
|
|
assert clips_data[0]["asset_id"] == "a1"
|
|
assert clips_data[0]["start_time"] == 0.0
|
|
assert clips_data[0]["duration"] == 3.0
|
|
assert clips_data[1]["asset_id"] == "a2"
|
|
|
|
def test_batch_update_empty_clips(self, mock_services):
|
|
"""空 clips 列表也能正常处理。"""
|
|
from app.api.routes.templates_editor.draft import batch_update_clips
|
|
from app.api.routes.templates_editor.schemas import EditorClipBatchUpdateRequest
|
|
|
|
_, plan_svc = mock_services
|
|
req = EditorClipBatchUpdateRequest(clips=[])
|
|
|
|
result = batch_update_clips(
|
|
template_id="tpl-1",
|
|
req=req,
|
|
plan_id="plan-1",
|
|
services=mock_services,
|
|
_=MagicMock(),
|
|
)
|
|
|
|
assert result.clip_count == 0
|
|
plan_svc.replace_all_clips_transactional.assert_called_once()
|
|
call_args = plan_svc.replace_all_clips_transactional.call_args
|
|
assert call_args[0][1] == []
|
|
|
|
def test_batch_update_passes_order_correctly(self, mock_services):
|
|
"""验证 order 字段正确传递。"""
|
|
from app.api.routes.templates_editor.draft import batch_update_clips
|
|
from app.api.routes.templates_editor.schemas import (
|
|
EditorClipBatchItem,
|
|
EditorClipBatchUpdateRequest,
|
|
)
|
|
|
|
_, plan_svc = mock_services
|
|
req = EditorClipBatchUpdateRequest(
|
|
clips=[
|
|
EditorClipBatchItem(asset_id="a1", start_time=0.0, duration=3.0, order=5),
|
|
]
|
|
)
|
|
|
|
batch_update_clips(
|
|
template_id="tpl-1",
|
|
req=req,
|
|
plan_id="plan-1",
|
|
services=mock_services,
|
|
_=MagicMock(),
|
|
)
|
|
|
|
clips_data = plan_svc.replace_all_clips_transactional.call_args[0][1]
|
|
assert clips_data[0]["order"] == 5
|
|
assert clips_data[0]["asset_id"] == "a1"
|
|
assert clips_data[0]["start_time"] == 0.0
|
|
assert clips_data[0]["duration"] == 3.0
|
|
|
|
|
|
class TestEditorClipBatchItemValidation:
|
|
"""验证 schema 校验规则。"""
|
|
|
|
def test_asset_id_empty_string_allowed(self):
|
|
"""asset_id 空字符串允许通过(占位片段场景)。"""
|
|
from app.api.routes.templates_editor.schemas import EditorClipBatchItem
|
|
|
|
item = EditorClipBatchItem(asset_id="", start_time=0.0, duration=3.0, order=0)
|
|
assert item.asset_id == ""
|
|
|
|
def test_asset_id_valid(self):
|
|
"""有效 asset_id 应通过校验。"""
|
|
from app.api.routes.templates_editor.schemas import EditorClipBatchItem
|
|
|
|
item = EditorClipBatchItem(asset_id="abc123", start_time=0.0, duration=3.0, order=0)
|
|
assert item.asset_id == "abc123"
|
|
|
|
def test_order_none_by_default(self):
|
|
"""order 默认为 None,表示按数组顺序。"""
|
|
from app.api.routes.templates_editor.schemas import EditorClipBatchItem
|
|
|
|
item = EditorClipBatchItem(asset_id="a1", start_time=0.0, duration=3.0)
|
|
assert item.order is None
|