From a73f8176cd8ea5a711fd20e6371ae8610142425f Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Tue, 28 Jul 2026 13:50:12 +0800 Subject: [PATCH] =?UTF-8?q?test(wave159):=20template=5Fclip=5Fconfig?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E7=89=87=E6=AE=B5=E9=85=8D=E7=BD=AE=E5=8D=95?= =?UTF-8?q?=E6=B5=8B=20+41?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 domain/template_clip_config.py 新增 41 个单测: - ClipType 枚举:8个 - TransitionEffect 枚举:7个 - create 工厂/校验:17个(必填/边界/字符串枚举转换/默认值) - has_duration_range / default_duration 属性:9个 --- .../unit/domain/test_template_clip_config.py | 347 ++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 tests/unit/domain/test_template_clip_config.py diff --git a/tests/unit/domain/test_template_clip_config.py b/tests/unit/domain/test_template_clip_config.py new file mode 100644 index 000000000..fdc1fc647 --- /dev/null +++ b/tests/unit/domain/test_template_clip_config.py @@ -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