"""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