test(wave158): edit_plan剪辑计划单测 +32
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 20s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 25s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 53s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Successful in 44s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 1201h7m56s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 20s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 25s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 53s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Successful in 44s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 1201h7m56s
为 domain/edit_plan.py 新增 32 个单测: - EditPlanStatus 枚举:6个 - create 工厂/校验:11个(必填/strip/空值/默认值) - 状态流转(draft→editing→rendering→completed/failed):14个 - config 独立性:1个
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user