297 lines
9.1 KiB
Python
297 lines
9.1 KiB
Python
"""edit_plan 单测.
|
|
|
|
domain 层剪辑计划纯逻辑模块,0 外部依赖。
|
|
覆盖:枚举常量、create工厂/校验、完整状态流转、重置。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from packages.domain.edit_plan import EditPlan, EditPlanStatus
|
|
|
|
|
|
class TestEditPlanStatus:
|
|
"""EditPlanStatus 枚举测试."""
|
|
|
|
def test_five_statuses(self):
|
|
"""五种状态."""
|
|
assert len(EditPlanStatus) == 5
|
|
|
|
def test_draft(self):
|
|
assert EditPlanStatus.DRAFT == "draft"
|
|
|
|
def test_editing(self):
|
|
assert EditPlanStatus.EDITING == "editing"
|
|
|
|
def test_rendering(self):
|
|
assert EditPlanStatus.RENDERING == "rendering"
|
|
|
|
def test_completed(self):
|
|
assert EditPlanStatus.COMPLETED == "completed"
|
|
|
|
def test_failed(self):
|
|
assert EditPlanStatus.FAILED == "failed"
|
|
|
|
|
|
class TestEditPlanCreate:
|
|
"""EditPlan.create 工厂测试."""
|
|
|
|
def test_create_minimal(self):
|
|
"""最简创建."""
|
|
plan = EditPlan.create(template_id="tpl1", name=" 我的计划 ")
|
|
assert plan.template_id == "tpl1"
|
|
assert plan.name == "我的计划"
|
|
assert plan.status == EditPlanStatus.DRAFT
|
|
assert plan.total_duration == 0.0
|
|
assert plan.config == {}
|
|
assert plan.project_id == ""
|
|
assert plan.created_by_user_id == ""
|
|
assert isinstance(plan.id, str)
|
|
assert len(plan.id) > 0
|
|
|
|
def test_create_full(self):
|
|
"""带全部字段."""
|
|
plan = EditPlan.create(
|
|
template_id="tpl1",
|
|
name="口播视频计划",
|
|
config={"bgm": "rock"},
|
|
total_duration=120.5,
|
|
source_edit_plan_id="src1",
|
|
project_id="proj1",
|
|
created_by_user_id="user1",
|
|
)
|
|
assert plan.name == "口播视频计划"
|
|
assert plan.total_duration == 120.5
|
|
assert plan.source_edit_plan_id == "src1"
|
|
assert plan.project_id == "proj1"
|
|
assert plan.created_by_user_id == "user1"
|
|
assert plan.config == {"bgm": "rock"}
|
|
|
|
def test_create_empty_name(self):
|
|
"""空名称无效."""
|
|
try:
|
|
EditPlan.create(template_id="t1", name="")
|
|
assert False
|
|
except ValueError as e:
|
|
assert "名称" in str(e)
|
|
|
|
def test_create_whitespace_name(self):
|
|
"""空白名称无效."""
|
|
try:
|
|
EditPlan.create(template_id="t1", name=" ")
|
|
assert False
|
|
except ValueError as e:
|
|
assert "名称" in str(e)
|
|
|
|
def test_create_empty_template_id(self):
|
|
"""空 template_id 无效."""
|
|
try:
|
|
EditPlan.create(template_id="", name="计划")
|
|
assert False
|
|
except ValueError as e:
|
|
assert "template_id" in str(e)
|
|
|
|
def test_create_whitespace_template_id(self):
|
|
"""空白 template_id 无效."""
|
|
try:
|
|
EditPlan.create(template_id=" ", name="计划")
|
|
assert False
|
|
except ValueError as e:
|
|
assert "template_id" in str(e)
|
|
|
|
def test_config_none_defaults_empty(self):
|
|
"""config=None 默认为空 dict."""
|
|
plan = EditPlan.create(template_id="t1", name="p", config=None)
|
|
assert plan.config == {}
|
|
|
|
def test_unique_id(self):
|
|
"""不同计划 id 不同."""
|
|
p1 = EditPlan.create("t", "p1")
|
|
p2 = EditPlan.create("t", "p2")
|
|
assert p1.id != p2.id
|
|
|
|
def test_has_timestamps(self):
|
|
"""有创建和更新时间."""
|
|
plan = EditPlan.create("t", "p")
|
|
assert plan.created_at is not None
|
|
assert plan.updated_at is not None
|
|
|
|
def test_strips_string_fields(self):
|
|
"""字符串字段 strip."""
|
|
plan = EditPlan.create(
|
|
template_id=" t1 ",
|
|
name=" n ",
|
|
source_edit_plan_id=" s ",
|
|
project_id=" p ",
|
|
created_by_user_id=" u ",
|
|
)
|
|
assert plan.template_id == "t1"
|
|
assert plan.name == "n"
|
|
assert plan.source_edit_plan_id == "s"
|
|
assert plan.project_id == "p"
|
|
assert plan.created_by_user_id == "u"
|
|
|
|
|
|
class TestEditPlanStatusFlow:
|
|
"""状态流转测试."""
|
|
|
|
def _make_plan(self):
|
|
return EditPlan.create(template_id="t1", name="测试计划")
|
|
|
|
def test_initial_status_draft(self):
|
|
"""初始状态 draft."""
|
|
plan = self._make_plan()
|
|
assert plan.status == EditPlanStatus.DRAFT
|
|
|
|
def test_draft_to_editing(self):
|
|
"""draft -> editing."""
|
|
plan = self._make_plan()
|
|
old = plan.updated_at
|
|
plan.start_editing()
|
|
assert plan.status == EditPlanStatus.EDITING
|
|
assert plan.updated_at >= old
|
|
|
|
def test_editing_to_rendering(self):
|
|
"""editing -> rendering."""
|
|
plan = self._make_plan()
|
|
plan.start_editing()
|
|
plan.start_rendering()
|
|
assert plan.status == EditPlanStatus.RENDERING
|
|
|
|
def test_rendering_to_completed(self):
|
|
"""rendering -> completed."""
|
|
plan = self._make_plan()
|
|
plan.start_editing()
|
|
plan.start_rendering()
|
|
plan.mark_completed()
|
|
assert plan.status == EditPlanStatus.COMPLETED
|
|
|
|
def test_rendering_to_failed(self):
|
|
"""rendering -> failed."""
|
|
plan = self._make_plan()
|
|
plan.start_editing()
|
|
plan.start_rendering()
|
|
plan.mark_failed()
|
|
assert plan.status == EditPlanStatus.FAILED
|
|
|
|
def test_failed_to_draft_reset(self):
|
|
"""failed -> draft 重置."""
|
|
plan = self._make_plan()
|
|
plan.start_editing()
|
|
plan.start_rendering()
|
|
plan.mark_failed()
|
|
plan.reset_to_draft()
|
|
assert plan.status == EditPlanStatus.DRAFT
|
|
|
|
def test_cannot_editing_from_completed(self):
|
|
"""completed 不能 start_editing."""
|
|
plan = self._make_plan()
|
|
plan.start_editing()
|
|
plan.start_rendering()
|
|
plan.mark_completed()
|
|
try:
|
|
plan.start_editing()
|
|
assert False
|
|
except ValueError as e:
|
|
assert "draft" in str(e).lower()
|
|
|
|
def test_cannot_rendering_from_draft(self):
|
|
"""draft 不能直接 start_rendering."""
|
|
plan = self._make_plan()
|
|
try:
|
|
plan.start_rendering()
|
|
assert False
|
|
except ValueError as e:
|
|
assert "editing" in str(e).lower()
|
|
|
|
def test_cannot_complete_from_editing(self):
|
|
"""editing 不能直接完成."""
|
|
plan = self._make_plan()
|
|
plan.start_editing()
|
|
try:
|
|
plan.mark_completed()
|
|
assert False
|
|
except ValueError as e:
|
|
assert "rendering" in str(e).lower()
|
|
|
|
def test_cannot_fail_from_editing(self):
|
|
"""editing 不能直接失败."""
|
|
plan = self._make_plan()
|
|
plan.start_editing()
|
|
try:
|
|
plan.mark_failed()
|
|
assert False
|
|
except ValueError as e:
|
|
assert "rendering" in str(e).lower()
|
|
|
|
def test_cannot_reset_from_draft(self):
|
|
"""draft 不能重置."""
|
|
plan = self._make_plan()
|
|
try:
|
|
plan.reset_to_draft()
|
|
assert False
|
|
except ValueError as e:
|
|
assert "failed" in str(e).lower()
|
|
|
|
def test_cannot_reset_from_completed(self):
|
|
"""completed 不能重置."""
|
|
plan = self._make_plan()
|
|
plan.start_editing()
|
|
plan.start_rendering()
|
|
plan.mark_completed()
|
|
try:
|
|
plan.reset_to_draft()
|
|
assert False
|
|
except ValueError as e:
|
|
assert "failed" in str(e).lower()
|
|
|
|
def test_full_happy_path(self):
|
|
"""完整成功路径."""
|
|
plan = self._make_plan()
|
|
assert plan.status == EditPlanStatus.DRAFT
|
|
plan.start_editing()
|
|
assert plan.status == EditPlanStatus.EDITING
|
|
plan.start_rendering()
|
|
assert plan.status == EditPlanStatus.RENDERING
|
|
plan.mark_completed()
|
|
assert plan.status == EditPlanStatus.COMPLETED
|
|
|
|
def test_failed_reset_retry(self):
|
|
"""失败后重置重试完整路径."""
|
|
plan = self._make_plan()
|
|
plan.start_editing()
|
|
plan.start_rendering()
|
|
plan.mark_failed()
|
|
assert plan.status == EditPlanStatus.FAILED
|
|
plan.reset_to_draft()
|
|
assert plan.status == EditPlanStatus.DRAFT
|
|
# 重新走一遍
|
|
plan.start_editing()
|
|
plan.start_rendering()
|
|
plan.mark_completed()
|
|
assert plan.status == EditPlanStatus.COMPLETED
|
|
|
|
def test_each_transition_updates_timestamp(self):
|
|
"""每次状态变更都更新 updated_at."""
|
|
plan = self._make_plan()
|
|
timestamps = [plan.updated_at]
|
|
plan.start_editing()
|
|
timestamps.append(plan.updated_at)
|
|
plan.start_rendering()
|
|
timestamps.append(plan.updated_at)
|
|
plan.mark_completed()
|
|
timestamps.append(plan.updated_at)
|
|
# 单调递增
|
|
for i in range(1, len(timestamps)):
|
|
assert timestamps[i] >= timestamps[i - 1]
|
|
|
|
|
|
class TestEditPlanConfig:
|
|
"""config 独立性测试."""
|
|
|
|
def test_config_independent(self):
|
|
"""不同计划的 config 独立."""
|
|
p1 = EditPlan.create(template_id="t", name="p1")
|
|
p2 = EditPlan.create(template_id="t", name="p2")
|
|
p1.config["key"] = "value"
|
|
assert "key" not in p2.config
|