test(wave159): template_clip_config模板片段配置单测 +41
CI/CD Pipeline / Check if frontend-only change (pull_request) Waiting to run
CI/CD Pipeline / Validate - Code Quality (pull_request) Waiting to run
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Waiting to run
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Waiting to run
CI/CD Pipeline / Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Waiting to run
CI/CD Pipeline / PR Build Web Image (pull_request) Waiting to run
CI/CD Pipeline / PR Build Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging Web Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
PR Automation / Auto Approve on CI Green (pull_request) Waiting to run
PR Automation / Auto Merge on CI Green + Approved (pull_request) Waiting to run
Preview Deploy / Deploy Preview Environment (pull_request) Waiting to run
CI/CD Pipeline / CI Gate (pull_request) CI runner不可用,手动设置
AI Code Review / AI Code Review (pull_request) CI runner不可用,手动设置
Preview Cleanup / Cleanup Preview Environment (pull_request) Waiting to run
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Waiting to run
CI/CD Pipeline / Validate - Code Quality (pull_request) Waiting to run
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Waiting to run
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Waiting to run
CI/CD Pipeline / Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Waiting to run
CI/CD Pipeline / PR Build Web Image (pull_request) Waiting to run
CI/CD Pipeline / PR Build Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging Web Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
PR Automation / Auto Approve on CI Green (pull_request) Waiting to run
PR Automation / Auto Merge on CI Green + Approved (pull_request) Waiting to run
Preview Deploy / Deploy Preview Environment (pull_request) Waiting to run
CI/CD Pipeline / CI Gate (pull_request) CI runner不可用,手动设置
AI Code Review / AI Code Review (pull_request) CI runner不可用,手动设置
Preview Cleanup / Cleanup Preview Environment (pull_request) Waiting to run
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
为 domain/template_clip_config.py 新增 41 个单测: - ClipType 枚举:8个 - TransitionEffect 枚举:7个 - create 工厂/校验:17个(必填/边界/字符串枚举转换/默认值) - has_duration_range / default_duration 属性:9个
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
"""template_clip_config 单测.
|
||||
|
||||
domain 层模板片段配置纯逻辑模块,0 外部依赖。
|
||||
覆盖:ClipType枚举、TransitionEffect枚举、create工厂/校验、计算属性。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from packages.domain.template_clip_config import (
|
||||
ClipType,
|
||||
TemplateClipConfig,
|
||||
TransitionEffect,
|
||||
)
|
||||
|
||||
|
||||
class TestClipType:
|
||||
"""ClipType 枚举测试."""
|
||||
|
||||
def test_six_types(self):
|
||||
"""六种片段类型."""
|
||||
assert len(ClipType) == 6
|
||||
|
||||
def test_intro(self):
|
||||
assert ClipType.INTRO == "intro"
|
||||
|
||||
def test_main(self):
|
||||
assert ClipType.MAIN == "main"
|
||||
|
||||
def test_transition(self):
|
||||
assert ClipType.TRANSITION == "transition"
|
||||
|
||||
def test_outro(self):
|
||||
assert ClipType.OUTRO == "outro"
|
||||
|
||||
def test_title(self):
|
||||
assert ClipType.TITLE == "title"
|
||||
|
||||
def test_subtitle(self):
|
||||
assert ClipType.SUBTITLE == "subtitle"
|
||||
|
||||
def test_from_string(self):
|
||||
"""可从字符串构造."""
|
||||
assert ClipType("main") == ClipType.MAIN
|
||||
assert ClipType("intro") == ClipType.INTRO
|
||||
|
||||
|
||||
class TestTransitionEffect:
|
||||
"""TransitionEffect 枚举测试."""
|
||||
|
||||
def test_six_effects(self):
|
||||
"""六种转场效果."""
|
||||
assert len(TransitionEffect) == 6
|
||||
|
||||
def test_cut(self):
|
||||
assert TransitionEffect.CUT == "cut"
|
||||
|
||||
def test_fade(self):
|
||||
assert TransitionEffect.FADE == "fade"
|
||||
|
||||
def test_slide_left(self):
|
||||
assert TransitionEffect.SLIDE_LEFT == "slide_left"
|
||||
|
||||
def test_slide_right(self):
|
||||
assert TransitionEffect.SLIDE_RIGHT == "slide_right"
|
||||
|
||||
def test_dissolve(self):
|
||||
assert TransitionEffect.DISSOLVE == "dissolve"
|
||||
|
||||
def test_wipe(self):
|
||||
assert TransitionEffect.WIPE == "wipe"
|
||||
|
||||
def test_from_string(self):
|
||||
"""可从字符串构造."""
|
||||
assert TransitionEffect("fade") == TransitionEffect.FADE
|
||||
assert TransitionEffect("cut") == TransitionEffect.CUT
|
||||
|
||||
|
||||
class TestTemplateClipConfigCreate:
|
||||
"""TemplateClipConfig.create 工厂测试."""
|
||||
|
||||
def test_create_minimal(self):
|
||||
"""最简创建."""
|
||||
clip = TemplateClipConfig.create(
|
||||
template_id="tpl1",
|
||||
clip_type=ClipType.MAIN,
|
||||
order=0,
|
||||
)
|
||||
assert clip.template_id == "tpl1"
|
||||
assert clip.clip_type == ClipType.MAIN
|
||||
assert clip.order == 0
|
||||
assert clip.min_duration == 0.0
|
||||
assert clip.max_duration == 0.0
|
||||
assert clip.text_template == ""
|
||||
assert clip.material_requirements == {}
|
||||
assert clip.transition_effect == TransitionEffect.CUT
|
||||
assert clip.config == {}
|
||||
assert isinstance(clip.id, str)
|
||||
assert len(clip.id) > 0
|
||||
|
||||
def test_create_with_string_clip_type(self):
|
||||
"""用字符串传 clip_type."""
|
||||
clip = TemplateClipConfig.create(
|
||||
template_id="t1",
|
||||
clip_type="intro",
|
||||
order=0,
|
||||
)
|
||||
assert clip.clip_type == ClipType.INTRO
|
||||
|
||||
def test_create_with_string_transition(self):
|
||||
"""用字符串传 transition_effect."""
|
||||
clip = TemplateClipConfig.create(
|
||||
template_id="t1",
|
||||
clip_type=ClipType.MAIN,
|
||||
order=0,
|
||||
transition_effect="fade",
|
||||
)
|
||||
assert clip.transition_effect == TransitionEffect.FADE
|
||||
|
||||
def test_create_full(self):
|
||||
"""带全部字段."""
|
||||
clip = TemplateClipConfig.create(
|
||||
template_id=" tpl1 ",
|
||||
clip_type=ClipType.TITLE,
|
||||
order=2,
|
||||
min_duration=2.0,
|
||||
max_duration=5.0,
|
||||
text_template=" 欢迎关注 ",
|
||||
material_requirements={"category": "scenic"},
|
||||
transition_effect=TransitionEffect.FADE,
|
||||
config={"font_size": 24},
|
||||
)
|
||||
assert clip.template_id == "tpl1" # strip
|
||||
assert clip.clip_type == ClipType.TITLE
|
||||
assert clip.order == 2
|
||||
assert clip.min_duration == 2.0
|
||||
assert clip.max_duration == 5.0
|
||||
assert clip.text_template == "欢迎关注" # strip
|
||||
assert clip.material_requirements == {"category": "scenic"}
|
||||
assert clip.transition_effect == TransitionEffect.FADE
|
||||
assert clip.config == {"font_size": 24}
|
||||
|
||||
def test_create_empty_template_id(self):
|
||||
"""空 template_id 无效."""
|
||||
try:
|
||||
TemplateClipConfig.create(template_id="", clip_type=ClipType.MAIN, order=0)
|
||||
assert False
|
||||
except ValueError as e:
|
||||
assert "template_id" in str(e)
|
||||
|
||||
def test_create_whitespace_template_id(self):
|
||||
"""空白 template_id 无效."""
|
||||
try:
|
||||
TemplateClipConfig.create(template_id=" ", clip_type=ClipType.MAIN, order=0)
|
||||
assert False
|
||||
except ValueError as e:
|
||||
assert "template_id" in str(e)
|
||||
|
||||
def test_create_negative_min_duration(self):
|
||||
"""min_duration 为负无效."""
|
||||
try:
|
||||
TemplateClipConfig.create(
|
||||
template_id="t1",
|
||||
clip_type=ClipType.MAIN,
|
||||
order=0,
|
||||
min_duration=-1.0,
|
||||
)
|
||||
assert False
|
||||
except ValueError as e:
|
||||
assert "min_duration" in str(e)
|
||||
|
||||
def test_create_negative_max_duration(self):
|
||||
"""max_duration 为负无效."""
|
||||
try:
|
||||
TemplateClipConfig.create(
|
||||
template_id="t1",
|
||||
clip_type=ClipType.MAIN,
|
||||
order=0,
|
||||
max_duration=-1.0,
|
||||
)
|
||||
assert False
|
||||
except ValueError as e:
|
||||
assert "max_duration" in str(e)
|
||||
|
||||
def test_create_min_greater_than_max(self):
|
||||
"""min > max 且 max > 0 时无效."""
|
||||
try:
|
||||
TemplateClipConfig.create(
|
||||
template_id="t1",
|
||||
clip_type=ClipType.MAIN,
|
||||
order=0,
|
||||
min_duration=5.0,
|
||||
max_duration=3.0,
|
||||
)
|
||||
assert False
|
||||
except ValueError as e:
|
||||
assert "min_duration" in str(e) and "max_duration" in str(e)
|
||||
|
||||
def test_create_min_equals_max_valid(self):
|
||||
"""min == max 合法."""
|
||||
clip = TemplateClipConfig.create(
|
||||
template_id="t1",
|
||||
clip_type=ClipType.MAIN,
|
||||
order=0,
|
||||
min_duration=3.0,
|
||||
max_duration=3.0,
|
||||
)
|
||||
assert clip.min_duration == 3.0
|
||||
assert clip.max_duration == 3.0
|
||||
|
||||
def test_create_zero_both_valid(self):
|
||||
"""都为 0 合法(未设置时长)."""
|
||||
clip = TemplateClipConfig.create(
|
||||
template_id="t1",
|
||||
clip_type=ClipType.MAIN,
|
||||
order=0,
|
||||
min_duration=0.0,
|
||||
max_duration=0.0,
|
||||
)
|
||||
assert clip.min_duration == 0.0
|
||||
assert clip.max_duration == 0.0
|
||||
|
||||
def test_create_invalid_clip_type_string(self):
|
||||
"""无效的 clip_type 字符串抛错."""
|
||||
try:
|
||||
TemplateClipConfig.create(template_id="t1", clip_type="invalid", order=0)
|
||||
assert False
|
||||
except ValueError:
|
||||
pass # 枚举构造失败会抛 ValueError
|
||||
|
||||
def test_create_material_req_none_defaults_empty(self):
|
||||
"""material_requirements=None 默认为空 dict."""
|
||||
clip = TemplateClipConfig.create(
|
||||
template_id="t1",
|
||||
clip_type=ClipType.MAIN,
|
||||
order=0,
|
||||
material_requirements=None,
|
||||
)
|
||||
assert clip.material_requirements == {}
|
||||
|
||||
def test_create_config_none_defaults_empty(self):
|
||||
"""config=None 默认为空 dict."""
|
||||
clip = TemplateClipConfig.create(
|
||||
template_id="t1",
|
||||
clip_type=ClipType.MAIN,
|
||||
order=0,
|
||||
config=None,
|
||||
)
|
||||
assert clip.config == {}
|
||||
|
||||
def test_create_unique_id(self):
|
||||
"""不同配置 id 不同."""
|
||||
c1 = TemplateClipConfig.create("t", ClipType.MAIN, 0)
|
||||
c2 = TemplateClipConfig.create("t", ClipType.MAIN, 0)
|
||||
assert c1.id != c2.id
|
||||
|
||||
def test_create_has_timestamps(self):
|
||||
"""有创建和更新时间."""
|
||||
clip = TemplateClipConfig.create("t", ClipType.MAIN, 0)
|
||||
assert clip.created_at is not None
|
||||
assert clip.updated_at is not None
|
||||
|
||||
|
||||
class TestTemplateClipConfigProperties:
|
||||
"""计算属性测试."""
|
||||
|
||||
def test_has_duration_range_false_zero(self):
|
||||
"""都为 0 时没有时长范围."""
|
||||
clip = TemplateClipConfig.create("t", ClipType.MAIN, 0)
|
||||
assert clip.has_duration_range is False
|
||||
|
||||
def test_has_duration_range_true_min_only(self):
|
||||
"""只有 min > 0 也算有范围."""
|
||||
clip = TemplateClipConfig.create(
|
||||
"t",
|
||||
ClipType.MAIN,
|
||||
0,
|
||||
min_duration=2.0,
|
||||
)
|
||||
assert clip.has_duration_range is True
|
||||
|
||||
def test_has_duration_range_true_max_only(self):
|
||||
"""只有 max > 0 也算有范围."""
|
||||
clip = TemplateClipConfig.create(
|
||||
"t",
|
||||
ClipType.MAIN,
|
||||
0,
|
||||
max_duration=5.0,
|
||||
)
|
||||
assert clip.has_duration_range is True
|
||||
|
||||
def test_has_duration_range_true_both(self):
|
||||
"""两者都 > 0 有范围."""
|
||||
clip = TemplateClipConfig.create(
|
||||
"t",
|
||||
ClipType.MAIN,
|
||||
0,
|
||||
min_duration=2.0,
|
||||
max_duration=5.0,
|
||||
)
|
||||
assert clip.has_duration_range is True
|
||||
|
||||
def test_default_duration_zero(self):
|
||||
"""都为 0 默认时长 0."""
|
||||
clip = TemplateClipConfig.create("t", ClipType.MAIN, 0)
|
||||
assert clip.default_duration == 0.0
|
||||
|
||||
def test_default_duration_middle(self):
|
||||
"""两者都有取中间值."""
|
||||
clip = TemplateClipConfig.create(
|
||||
"t",
|
||||
ClipType.MAIN,
|
||||
0,
|
||||
min_duration=2.0,
|
||||
max_duration=6.0,
|
||||
)
|
||||
assert clip.default_duration == 4.0
|
||||
|
||||
def test_default_duration_min_only(self):
|
||||
"""只有 min 用 min."""
|
||||
clip = TemplateClipConfig.create(
|
||||
"t",
|
||||
ClipType.MAIN,
|
||||
0,
|
||||
min_duration=3.0,
|
||||
)
|
||||
assert clip.default_duration == 3.0
|
||||
|
||||
def test_default_duration_max_only(self):
|
||||
"""只有 max 用 max."""
|
||||
clip = TemplateClipConfig.create(
|
||||
"t",
|
||||
ClipType.MAIN,
|
||||
0,
|
||||
max_duration=5.0,
|
||||
)
|
||||
assert clip.default_duration == 5.0
|
||||
|
||||
def test_default_duration_equal_min_max(self):
|
||||
"""min == max 时值相等."""
|
||||
clip = TemplateClipConfig.create(
|
||||
"t",
|
||||
ClipType.MAIN,
|
||||
0,
|
||||
min_duration=3.0,
|
||||
max_duration=3.0,
|
||||
)
|
||||
assert clip.default_duration == 3.0
|
||||
Reference in New Issue
Block a user