Files
xiaoxia-saas/tests/unit/domain/test_edit_plan.py

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