From d21b16aac7caaf024e251d4d4df32b504332c358 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Tue, 21 Jul 2026 19:43:46 +0800 Subject: [PATCH] =?UTF-8?q?test(unit):=20P3-1=20=E7=AC=AC=E4=BA=94?= =?UTF-8?q?=E6=B3=A2=20=E6=96=B0=E5=A2=9E3=E4=B8=AA=E9=A2=86=E5=9F=9F?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95(54?= =?UTF-8?q?=E4=B8=AA=E7=94=A8=E4=BE=8B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - preset_voices: 22个 - 数据类属性/默认值/frozen不可变/to_dict序列化/预置列表/按ID查询/8音色参数化 - template: 20个 - TemplateSegment/Template/TemplateCategory 三类实体完整覆盖 - template_version: 12个 - 创建工厂/默认值/config与clip_configs空值处理/完整字段/slots约束 已rebase到最新develop(含第四波),解决合并冲突 --- tests/unit/test_preset_voices_domain.py | 166 +++++++++++++++++++++ tests/unit/test_template_domain.py | 153 +++++++++++++++++++ tests/unit/test_template_version_domain.py | 127 ++++++++++++++++ 3 files changed, 446 insertions(+) create mode 100644 tests/unit/test_preset_voices_domain.py create mode 100644 tests/unit/test_template_domain.py create mode 100644 tests/unit/test_template_version_domain.py diff --git a/tests/unit/test_preset_voices_domain.py b/tests/unit/test_preset_voices_domain.py new file mode 100644 index 000000000..04534e963 --- /dev/null +++ b/tests/unit/test_preset_voices_domain.py @@ -0,0 +1,166 @@ +""" +PresetVoice 预置音色领域模型单元测试 +""" + +import pytest +from domain.preset_voices import ( + PRESET_VOICES, + PresetVoice, + get_preset_voice_by_id, + get_preset_voices, + is_preset_voice, +) + + +class TestPresetVoice: + """PresetVoice 数据类测试""" + + def test_create_required_fields(self): + v = PresetVoice( + voice_id="test_v1", + name="测试音色", + description="测试描述", + gender="female", + ) + assert v.voice_id == "test_v1" + assert v.name == "测试音色" + assert v.description == "测试描述" + assert v.gender == "female" + + def test_default_language(self): + v = PresetVoice(voice_id="v1", name="n", description="d", gender="female") + assert v.language == "zh-CN" + + def test_default_preview_url(self): + v = PresetVoice(voice_id="v1", name="n", description="d", gender="female") + assert v.preview_url == "" + + def test_default_tags_none(self): + v = PresetVoice(voice_id="v1", name="n", description="d", gender="female") + assert v.tags is None + + def test_custom_tags(self): + v = PresetVoice( + voice_id="v1", + name="n", + description="d", + gender="female", + tags=["温柔", "女声"], + ) + assert v.tags == ["温柔", "女声"] + + def test_is_frozen(self): + v = PresetVoice(voice_id="v1", name="n", description="d", gender="female") + with pytest.raises(AttributeError): + v.name = "改了" + + +class TestPresetVoiceToDict: + """to_dict 序列化测试""" + + def test_to_dict_basic(self): + v = PresetVoice( + voice_id="longxiaochun_v3", + name="龙小淳", + description="温柔女声", + gender="female", + language="zh-CN", + preview_url="https://example.com/audio.mp3", + tags=["温柔", "女声"], + ) + d = v.to_dict() + assert d["voice_id"] == "longxiaochun_v3" + assert d["name"] == "龙小淳" + assert d["description"] == "温柔女声" + assert d["gender"] == "female" + assert d["language"] == "zh-CN" + assert d["preview_url"] == "https://example.com/audio.mp3" + assert d["tags"] == ["温柔", "女声"] + + def test_to_dict_tags_none_becomes_empty_list(self): + v = PresetVoice(voice_id="v1", name="n", description="d", gender="female") + d = v.to_dict() + assert d["tags"] == [] + + +class TestPresetVoiceList: + """预置音色列表测试""" + + def test_list_not_empty(self): + voices = get_preset_voices() + assert len(voices) > 0 + + def test_all_are_preset_voice_instances(self): + for v in PRESET_VOICES: + assert isinstance(v, PresetVoice) + + def test_voice_ids_unique(self): + ids = [v.voice_id for v in PRESET_VOICES] + assert len(ids) == len(set(ids)) + + def test_all_have_required_fields(self): + for v in PRESET_VOICES: + assert v.voice_id + assert v.name + assert v.description + assert v.gender in ("male", "female") + assert v.language + + def test_total_count(self): + assert len(PRESET_VOICES) == 8 + + +class TestGetPresetVoiceById: + """按 ID 查询预置音色测试""" + + def test_existing_voice(self): + v = get_preset_voice_by_id("longxiaochun_v3") + assert v is not None + assert v.name == "龙小淳" + assert v.gender == "female" + + def test_nonexistent_voice(self): + v = get_preset_voice_by_id("nonexistent_voice") + assert v is None + + def test_empty_string(self): + v = get_preset_voice_by_id("") + assert v is None + + +class TestIsPresetVoice: + """判断是否预置音色测试""" + + def test_existing_is_preset(self): + assert is_preset_voice("longxiaochen_v3") is True + + def test_nonexistent_not_preset(self): + assert is_preset_voice("custom_voice_123") is False + + def test_empty_not_preset(self): + assert is_preset_voice("") is False + + +class TestPresetVoiceSamples: + """预置音色样本验证""" + + @pytest.mark.parametrize( + "voice_id,expected_name,gender", + [ + ("longxiaochun_v3", "龙小淳", "female"), + ("longxiaoxia_v3", "龙小夏", "female"), + ("longxiaochen_v3", "龙小晨", "male"), + ("longyue_v3", "龙悦", "female"), + ("longshu_v3", "龙书", "male"), + ("longjing_v3", "龙静", "female"), + ("longbo_v3", "龙博", "male"), + ("longtian_v3", "龙甜", "female"), + ], + ) + def test_all_preset_voices_sample(self, voice_id, expected_name, gender): + v = get_preset_voice_by_id(voice_id) + assert v is not None + assert v.name == expected_name + assert v.gender == gender + assert v.language == "zh-CN" + assert len(v.tags or []) >= 2 diff --git a/tests/unit/test_template_domain.py b/tests/unit/test_template_domain.py new file mode 100644 index 000000000..c0bff5c44 --- /dev/null +++ b/tests/unit/test_template_domain.py @@ -0,0 +1,153 @@ +""" +Template 模板领域模型单元测试 +""" + +from domain.template import Template, TemplateCategory, TemplateSegment + + +class TestTemplateSegment: + """TemplateSegment 测试""" + + def test_create_segment(self): + seg = TemplateSegment( + id="seg-1", + template_id="tpl-1", + segment_order=1, + duration_min=3.0, + duration_max=5.0, + ) + assert seg.id == "seg-1" + assert seg.template_id == "tpl-1" + assert seg.segment_order == 1 + assert seg.duration_min == 3.0 + assert seg.duration_max == 5.0 + assert seg.material_type is None + + def test_segment_with_material_type(self): + seg = TemplateSegment( + id="seg-1", + template_id="tpl-1", + segment_order=0, + duration_min=2.0, + duration_max=4.0, + material_type="人物", + ) + assert seg.material_type == "人物" + + def test_segment_has_timestamps(self): + seg = TemplateSegment( + id="seg-1", + template_id="tpl-1", + segment_order=1, + duration_min=1.0, + duration_max=2.0, + ) + assert seg.created_at is not None + assert seg.updated_at is not None + + +class TestTemplate: + """Template 测试""" + + def test_create_template_minimal(self): + t = Template( + id="tpl-1", + user_id="user-1", + name="测试模板", + mode="one_take", + ) + assert t.id == "tpl-1" + assert t.user_id == "user-1" + assert t.name == "测试模板" + assert t.mode == "one_take" + + def test_default_values(self): + t = Template(id="tpl-1", user_id="u1", name="n", mode="one_take") + assert t.category == "" + assert t.tags == [] + assert t.title_config == {} + assert t.subtitle_config == {} + assert t.bgm_config == {} + assert t.estimated_duration == 0.0 + assert t.segments == [] + assert t.is_active is True + + def test_with_segments(self): + segs = [ + TemplateSegment(id="s1", template_id="t1", segment_order=0, duration_min=2, duration_max=4), + TemplateSegment(id="s2", template_id="t1", segment_order=1, duration_min=3, duration_max=5), + ] + t = Template( + id="tpl-1", + user_id="u1", + name="n", + mode="voice_over", + segments=segs, + ) + assert len(t.segments) == 2 + assert t.segments[0].segment_order == 0 + assert t.segments[1].segment_order == 1 + + def test_all_modes(self): + for mode in ["pip", "voice_pip", "one_take", "voice_over"]: + t = Template(id="t1", user_id="u1", name="n", mode=mode) + assert t.mode == mode + + def test_with_configs(self): + t = Template( + id="t1", + user_id="u1", + name="n", + mode="one_take", + title_config={"font_size": 24, "color": "#ffffff"}, + subtitle_config={"style": "bottom"}, + bgm_config={"volume": 0.5}, + ) + assert t.title_config["font_size"] == 24 + assert t.subtitle_config["style"] == "bottom" + assert t.bgm_config["volume"] == 0.5 + + def test_estimated_duration(self): + t = Template( + id="t1", + user_id="u1", + name="n", + mode="one_take", + estimated_duration=30.5, + ) + assert t.estimated_duration == 30.5 + + def test_is_active_false(self): + t = Template(id="t1", user_id="u1", name="n", mode="one_take", is_active=False) + assert t.is_active is False + + def test_has_timestamps(self): + t = Template(id="t1", user_id="u1", name="n", mode="one_take") + assert t.created_at is not None + assert t.updated_at is not None + + def test_tags_list(self): + t = Template( + id="t1", + user_id="u1", + name="n", + mode="one_take", + tags=["风景", "vlog"], + ) + assert "风景" in t.tags + assert "vlog" in t.tags + assert len(t.tags) == 2 + + +class TestTemplateCategory: + """TemplateCategory 测试""" + + def test_create_category(self): + cat = TemplateCategory(id="cat-1", user_id="u1", name="风景") + assert cat.id == "cat-1" + assert cat.user_id == "u1" + assert cat.name == "风景" + + def test_category_has_timestamp(self): + cat = TemplateCategory(id="cat-1", user_id="u1", name="风景") + assert cat.created_at is not None diff --git a/tests/unit/test_template_version_domain.py b/tests/unit/test_template_version_domain.py new file mode 100644 index 000000000..227111955 --- /dev/null +++ b/tests/unit/test_template_version_domain.py @@ -0,0 +1,127 @@ +""" +EditTemplateVersion 模板版本领域模型单元测试 +""" + +import pytest +from domain.template_version import EditTemplateVersion + + +class TestEditTemplateVersionCreate: + """创建模板版本测试""" + + def test_create_required_fields(self): + v = EditTemplateVersion.create(template_id="tpl-1", version=1) + assert v.id is not None + assert len(v.id) == 32 + assert v.template_id == "tpl-1" + assert v.version == 1 + + def test_default_values(self): + v = EditTemplateVersion.create(template_id="tpl-1", version=1) + assert v.name == "" + assert v.editing_mode == "one_take" + assert v.config == {} + assert v.clip_configs == [] + assert v.change_note == "" + assert v.published_by == "" + + def test_with_name_and_mode(self): + v = EditTemplateVersion.create( + template_id="tpl-1", + version=2, + name="风景Vlog模板", + editing_mode="voice_over", + ) + assert v.name == "风景Vlog模板" + assert v.editing_mode == "voice_over" + + def test_with_config(self): + config = { + "title": {"font_size": 24}, + "subtitle": {"style": "bottom"}, + "bgm": {"volume": 0.5}, + } + v = EditTemplateVersion.create( + template_id="tpl-1", + version=1, + config=config, + ) + assert v.config == config + assert v.config["title"]["font_size"] == 24 + + def test_with_clip_configs(self): + clips = [ + {"clip_id": 1, "duration": 3.0, "transition": "fade"}, + {"clip_id": 2, "duration": 5.0, "transition": "slide"}, + ] + v = EditTemplateVersion.create( + template_id="tpl-1", + version=1, + clip_configs=clips, + ) + assert len(v.clip_configs) == 2 + assert v.clip_configs[0]["clip_id"] == 1 + + def test_config_none_defaults_to_empty_dict(self): + v = EditTemplateVersion.create(template_id="tpl-1", version=1, config=None) + assert v.config == {} + + def test_clip_configs_none_defaults_to_empty_list(self): + v = EditTemplateVersion.create(template_id="tpl-1", version=1, clip_configs=None) + assert v.clip_configs == [] + + def test_with_change_note(self): + v = EditTemplateVersion.create( + template_id="tpl-1", + version=3, + change_note="优化转场效果,新增滤镜", + ) + assert v.change_note == "优化转场效果,新增滤镜" + + def test_with_published_by(self): + v = EditTemplateVersion.create( + template_id="tpl-1", + version=1, + published_by="user-123", + ) + assert v.published_by == "user-123" + + def test_full_version(self): + config = {"bgm": {"volume": 0.3}} + clips = [{"clip_id": 1, "duration": 2.5}] + v = EditTemplateVersion.create( + template_id="tpl-abc", + version=5, + name="正式版v5", + editing_mode="one_take", + config=config, + clip_configs=clips, + change_note="第五次发布", + published_by="admin", + ) + assert v.template_id == "tpl-abc" + assert v.version == 5 + assert v.name == "正式版v5" + assert v.editing_mode == "one_take" + assert v.config == config + assert v.clip_configs == clips + assert v.change_note == "第五次发布" + assert v.published_by == "admin" + + def test_version_number(self): + for ver in [1, 2, 5, 10, 99]: + v = EditTemplateVersion.create(template_id="t1", version=ver) + assert v.version == ver + + def test_has_created_at(self): + v = EditTemplateVersion.create(template_id="t1", version=1) + assert v.created_at is not None + + +class TestEditTemplateVersionSlots: + """slots 模式属性测试""" + + def test_cannot_add_new_attribute(self): + v = EditTemplateVersion.create(template_id="t1", version=1) + with pytest.raises(AttributeError): + v.nonexistent_field = "value" # type: ignore[attr-defined] -- 2.54.0