9554373f24
CI/CD Pipeline / Check if frontend-only change (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 / Build Staging Web Image (push) Successful in 37s
CI/CD Pipeline / Build Staging API Image (push) Successful in 1m27s
CI/CD Pipeline / Frontend Lint (push) Successful in 1m31s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m54s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 2m32s
CI/CD Pipeline / Unit Tests (push) Successful in 2m37s
CI/CD Pipeline / Integration Tests (push) Successful in 1m3s
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy 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 / ACR Image Cleanup (push) Has been cancelled
新增7个领域模块单元测试:classification、edit_plan_clip、filter_presets、generated_video、preset_bgm、template_clip_config、transition_presets,共231个用例
263 lines
9.9 KiB
Python
Executable File
263 lines
9.9 KiB
Python
Executable File
"""edit_plan_clip 领域模型单元测试."""
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
import pytest
|
|
from domain.edit_plan_clip import EditPlanClip, EditPlanClipStatus
|
|
|
|
|
|
class TestEditPlanClipStatus:
|
|
"""EditPlanClipStatus 枚举测试."""
|
|
|
|
def test_values(self):
|
|
assert EditPlanClipStatus.PENDING == "pending"
|
|
assert EditPlanClipStatus.READY == "ready"
|
|
assert EditPlanClipStatus.RENDERED == "rendered"
|
|
assert EditPlanClipStatus.FAILED == "failed"
|
|
|
|
|
|
class TestEditPlanClipCreate:
|
|
"""EditPlanClip.create 工厂方法测试."""
|
|
|
|
def test_create_with_required_fields(self):
|
|
clip = EditPlanClip.create(plan_id="plan_001", clip_type="video", order=1)
|
|
assert clip.id # 自动生成的 UUID
|
|
assert len(clip.id) == 32 # hex 格式
|
|
assert clip.plan_id == "plan_001"
|
|
assert clip.clip_type == "video"
|
|
assert clip.order == 1
|
|
assert clip.status == EditPlanClipStatus.PENDING
|
|
assert clip.start_time == 0.0
|
|
assert clip.duration == 0.0
|
|
assert clip.transition_effect == "cut"
|
|
assert clip.playback_speed == 1.0
|
|
assert clip.config == {}
|
|
|
|
def test_create_with_all_fields(self):
|
|
clip = EditPlanClip.create(
|
|
plan_id="plan_002",
|
|
clip_type="audio",
|
|
order=2,
|
|
template_clip_config_id="tpl_001",
|
|
asset_id="asset_001",
|
|
text_content="测试文案",
|
|
start_time=5.0,
|
|
duration=10.0,
|
|
transition_effect="fade",
|
|
transition_duration=0.5,
|
|
playback_speed=1.5,
|
|
config={"key": "value"},
|
|
)
|
|
assert clip.plan_id == "plan_002"
|
|
assert clip.clip_type == "audio"
|
|
assert clip.order == 2
|
|
assert clip.template_clip_config_id == "tpl_001"
|
|
assert clip.asset_id == "asset_001"
|
|
assert clip.text_content == "测试文案"
|
|
assert clip.start_time == 5.0
|
|
assert clip.duration == 10.0
|
|
assert clip.transition_effect == "fade"
|
|
assert clip.transition_duration == 0.5
|
|
assert clip.playback_speed == 1.5
|
|
assert clip.config == {"key": "value"}
|
|
|
|
def test_create_strips_strings(self):
|
|
clip = EditPlanClip.create(
|
|
plan_id=" plan_003 ",
|
|
clip_type=" video ",
|
|
order=1,
|
|
asset_id=" asset_001 ",
|
|
template_clip_config_id=" tpl_001 ",
|
|
text_content=" 测试 ",
|
|
transition_effect=" fade ",
|
|
)
|
|
assert clip.plan_id == "plan_003"
|
|
assert clip.clip_type == "video"
|
|
assert clip.asset_id == "asset_001"
|
|
assert clip.template_clip_config_id == "tpl_001"
|
|
assert clip.text_content == "测试"
|
|
assert clip.transition_effect == "fade"
|
|
|
|
def test_create_empty_plan_id_raises(self):
|
|
with pytest.raises(ValueError, match="plan_id"):
|
|
EditPlanClip.create(plan_id="", clip_type="video", order=1)
|
|
|
|
def test_create_whitespace_plan_id_raises(self):
|
|
with pytest.raises(ValueError, match="plan_id"):
|
|
EditPlanClip.create(plan_id=" ", clip_type="video", order=1)
|
|
|
|
def test_create_empty_clip_type_raises(self):
|
|
with pytest.raises(ValueError, match="clip_type"):
|
|
EditPlanClip.create(plan_id="plan_001", clip_type="", order=1)
|
|
|
|
def test_create_negative_start_time_raises(self):
|
|
with pytest.raises(ValueError, match="start_time"):
|
|
EditPlanClip.create(plan_id="p", clip_type="v", order=1, start_time=-1.0)
|
|
|
|
def test_create_negative_duration_raises(self):
|
|
with pytest.raises(ValueError, match="duration"):
|
|
EditPlanClip.create(plan_id="p", clip_type="v", order=1, duration=-5.0)
|
|
|
|
def test_create_zero_speed_clamps_to_1(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, playback_speed=0.0)
|
|
assert clip.playback_speed == 1.0
|
|
|
|
def test_create_negative_speed_clamps_to_1(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, playback_speed=-1.0)
|
|
assert clip.playback_speed == 1.0
|
|
|
|
def test_create_low_speed_clamps_to_min(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, playback_speed=0.1)
|
|
assert clip.playback_speed == 0.25
|
|
|
|
def test_create_high_speed_clamps_to_max(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, playback_speed=5.0)
|
|
assert clip.playback_speed == 4.0
|
|
|
|
def test_create_speed_at_boundary_values(self):
|
|
# 边界值应该保持不变
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, playback_speed=0.25)
|
|
assert clip.playback_speed == 0.25
|
|
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, playback_speed=4.0)
|
|
assert clip.playback_speed == 4.0
|
|
|
|
def test_create_negative_transition_duration_clamps_to_0(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, transition_duration=-1.0)
|
|
assert clip.transition_duration == 0.0
|
|
|
|
def test_create_empty_transition_effect_defaults_to_cut(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, transition_effect="")
|
|
assert clip.transition_effect == "cut"
|
|
|
|
def test_create_empty_asset_id_stays_empty(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, asset_id="")
|
|
assert clip.asset_id == ""
|
|
|
|
def test_create_none_config_defaults_to_empty_dict(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, config=None)
|
|
assert clip.config == {}
|
|
|
|
def test_create_ids_are_unique(self):
|
|
c1 = EditPlanClip.create(plan_id="p", clip_type="v", order=1)
|
|
c2 = EditPlanClip.create(plan_id="p", clip_type="v", order=2)
|
|
assert c1.id != c2.id
|
|
|
|
def test_create_timestamps_are_utc(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1)
|
|
assert clip.created_at.tzinfo is not None
|
|
assert clip.updated_at.tzinfo is not None
|
|
|
|
|
|
class TestEditPlanClipStateMachine:
|
|
"""状态机流转测试."""
|
|
|
|
@pytest.fixture
|
|
def pending_clip(self):
|
|
return EditPlanClip.create(plan_id="plan_001", clip_type="video", order=1)
|
|
|
|
def test_initial_status_is_pending(self, pending_clip):
|
|
assert pending_clip.status == EditPlanClipStatus.PENDING
|
|
|
|
def test_pending_to_ready(self, pending_clip):
|
|
pending_clip.mark_ready()
|
|
assert pending_clip.status == EditPlanClipStatus.READY
|
|
|
|
def test_pending_cannot_mark_rendered(self, pending_clip):
|
|
with pytest.raises(ValueError, match="只有 ready"):
|
|
pending_clip.mark_rendered()
|
|
|
|
def test_pending_cannot_mark_failed(self, pending_clip):
|
|
with pytest.raises(ValueError, match="只有 ready"):
|
|
pending_clip.mark_failed()
|
|
|
|
def test_ready_to_rendered(self, pending_clip):
|
|
pending_clip.mark_ready()
|
|
pending_clip.mark_rendered()
|
|
assert pending_clip.status == EditPlanClipStatus.RENDERED
|
|
|
|
def test_ready_to_failed(self, pending_clip):
|
|
pending_clip.mark_ready()
|
|
pending_clip.mark_failed()
|
|
assert pending_clip.status == EditPlanClipStatus.FAILED
|
|
|
|
def test_rendered_cannot_mark_ready_again(self, pending_clip):
|
|
pending_clip.mark_ready()
|
|
pending_clip.mark_rendered()
|
|
with pytest.raises(ValueError):
|
|
pending_clip.mark_ready()
|
|
|
|
def test_failed_cannot_mark_ready_again(self, pending_clip):
|
|
pending_clip.mark_ready()
|
|
pending_clip.mark_failed()
|
|
with pytest.raises(ValueError):
|
|
pending_clip.mark_ready()
|
|
|
|
def test_state_transition_updates_updated_at(self, pending_clip):
|
|
old_updated = pending_clip.updated_at
|
|
# 确保时间不同
|
|
import time
|
|
|
|
time.sleep(0.001)
|
|
pending_clip.mark_ready()
|
|
assert pending_clip.updated_at > old_updated
|
|
|
|
|
|
class TestEditPlanClipAssignAsset:
|
|
"""assign_asset 方法测试."""
|
|
|
|
def test_assign_asset(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1)
|
|
assert not clip.has_asset
|
|
clip.assign_asset("asset_001")
|
|
assert clip.asset_id == "asset_001"
|
|
assert clip.has_asset
|
|
|
|
def test_assign_asset_strips_whitespace(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1)
|
|
clip.assign_asset(" asset_001 ")
|
|
assert clip.asset_id == "asset_001"
|
|
|
|
def test_assign_empty_asset_raises(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1)
|
|
with pytest.raises(ValueError, match="asset_id"):
|
|
clip.assign_asset("")
|
|
|
|
def test_assign_whitespace_asset_raises(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1)
|
|
with pytest.raises(ValueError, match="asset_id"):
|
|
clip.assign_asset(" ")
|
|
|
|
def test_assign_updates_updated_at(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1)
|
|
old_updated = clip.updated_at
|
|
import time
|
|
|
|
time.sleep(0.001)
|
|
clip.assign_asset("asset_001")
|
|
assert clip.updated_at > old_updated
|
|
|
|
|
|
class TestEditPlanClipProperties:
|
|
"""属性方法测试."""
|
|
|
|
def test_end_time(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, start_time=5.0, duration=10.0)
|
|
assert clip.end_time == 15.0
|
|
|
|
def test_end_time_zero_duration(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, start_time=3.0, duration=0.0)
|
|
assert clip.end_time == 3.0
|
|
|
|
def test_has_asset_true(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, asset_id="a001")
|
|
assert clip.has_asset is True
|
|
|
|
def test_has_asset_false(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1)
|
|
assert clip.has_asset is False
|
|
|
|
def test_has_asset_empty_string(self):
|
|
clip = EditPlanClip.create(plan_id="p", clip_type="v", order=1, asset_id="")
|
|
assert clip.has_asset is False
|