From fa991831b1996741a6d0d9c65b30903d118bf4c2 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Wed, 29 Jul 2026 18:32:07 +0800 Subject: [PATCH 1/2] =?UTF-8?q?test(wave183):=20voice=5Fpresets=20?= =?UTF-8?q?=E9=85=8D=E9=9F=B3=E9=9F=B3=E8=89=B2=E9=A2=84=E8=AE=BE=20+59?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unit/domain/test_voice_presets.py | 369 ++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100755 tests/unit/domain/test_voice_presets.py diff --git a/tests/unit/domain/test_voice_presets.py b/tests/unit/domain/test_voice_presets.py new file mode 100755 index 000000000..cc21c5864 --- /dev/null +++ b/tests/unit/domain/test_voice_presets.py @@ -0,0 +1,369 @@ +"""voice_presets 配音音色预设模块单测.""" + +import pytest + +from domain.voice_presets import ( + MOCK_VOICES, + VoiceGender, + VoicePreset, + VoiceStyle, + get_default_voice, + get_voice, + list_voices, +) + +# ── 枚举测试 ────────────────────────────────────────────────────────────────── + + +class TestVoiceGender: + """VoiceGender 音色性别枚举""" + + def test_enum_values(self): + assert VoiceGender.MALE.value == "male" + assert VoiceGender.FEMALE.value == "female" + assert VoiceGender.CHILD.value == "child" + + def test_is_str(self): + assert isinstance(VoiceGender.MALE, str) + assert VoiceGender.FEMALE == "female" + + def test_from_string(self): + assert VoiceGender("male") == VoiceGender.MALE + assert VoiceGender("child") == VoiceGender.CHILD + + def test_invalid_raises(self): + with pytest.raises(ValueError): + VoiceGender("unknown") + + +class TestVoiceStyle: + """VoiceStyle 音色风格枚举""" + + def test_enum_values(self): + assert VoiceStyle.STABLE.value == "stable" + assert VoiceStyle.LIVELY.value == "lively" + assert VoiceStyle.CUSTOMER_SERVICE.value == "customer_service" + assert VoiceStyle.NARRATION.value == "narration" + assert VoiceStyle.NEWS.value == "news" + assert VoiceStyle.STORY.value == "story" + + def test_is_str(self): + assert isinstance(VoiceStyle.NARRATION, str) + assert VoiceStyle.STORY == "story" + + def test_from_string(self): + assert VoiceStyle("news") == VoiceStyle.NEWS + + def test_invalid_raises(self): + with pytest.raises(ValueError): + VoiceStyle("rock") + + +# ── VoicePreset dataclass ───────────────────────────────────────────────────── + + +class TestVoicePreset: + """VoicePreset 音色预设 dataclass""" + + def test_minimal_creation(self): + v = VoicePreset(voice_id="test_voice", name="测试音色") + assert v.voice_id == "test_voice" + assert v.name == "测试音色" + # 默认值 + assert v.gender == VoiceGender.FEMALE + assert v.style == VoiceStyle.NARRATION + assert v.description == "" + assert v.provider == "mock" + assert v.provider_voice_id == "" + assert v.default_speed == 1.0 + assert v.default_pitch == 0.0 + assert v.sample_rate == 22050 + assert v.language == "zh-CN" + + def test_full_creation(self): + v = VoicePreset( + voice_id="male_deep", + name="深沉男声", + gender=VoiceGender.MALE, + style=VoiceStyle.STABLE, + description="非常深沉的男声", + provider="aliyun", + provider_voice_id="zhiyuan", + default_speed=0.8, + default_pitch=-1.0, + sample_rate=16000, + language="zh-CN", + ) + assert v.voice_id == "male_deep" + assert v.gender == VoiceGender.MALE + assert v.style == VoiceStyle.STABLE + assert v.provider == "aliyun" + assert v.default_speed == 0.8 + assert v.sample_rate == 16000 + + def test_str_gender_creation(self): + # 用字符串值创建也可以(因为是 StrEnum) + v = VoicePreset(voice_id="v1", name="V1", gender="male") + assert v.gender == VoiceGender.MALE + + def test_str_style_creation(self): + v = VoicePreset(voice_id="v1", name="V1", style="news") + assert v.style == VoiceStyle.NEWS + + def test_equality(self): + v1 = VoicePreset(voice_id="same", name="同名") + v2 = VoicePreset(voice_id="same", name="同名") + assert v1 == v2 + + def test_inequality(self): + v1 = VoicePreset(voice_id="a", name="A") + v2 = VoicePreset(voice_id="b", name="B") + assert v1 != v2 + + def test_slots_no_extra_attrs(self): + v = VoicePreset(voice_id="test", name="Test") + with pytest.raises(AttributeError): + v.nonexistent_field = "value" + + +# ── MOCK_VOICES 列表 ───────────────────────────────────────────────────────── + + +class TestMockVoices: + """Mock 音色预设列表""" + + def test_not_empty(self): + assert len(MOCK_VOICES) > 0 + + def test_count(self): + assert len(MOCK_VOICES) == 8 + + def test_all_are_voice_preset(self): + for v in MOCK_VOICES: + assert isinstance(v, VoicePreset) + + def test_unique_voice_ids(self): + ids = [v.voice_id for v in MOCK_VOICES] + assert len(ids) == len(set(ids)) + + def test_female_warm_preset(self): + v = next(v for v in MOCK_VOICES if v.voice_id == "female_warm") + assert v.name == "温暖女声" + assert v.gender == VoiceGender.FEMALE + assert v.style == VoiceStyle.NARRATION + assert v.default_speed == 1.0 + assert "温柔" in v.description + + def test_male_stable_preset(self): + v = next(v for v in MOCK_VOICES if v.voice_id == "male_stable") + assert v.name == "沉稳男声" + assert v.gender == VoiceGender.MALE + assert v.style == VoiceStyle.STABLE + assert v.default_speed == 0.9 + + def test_female_lively_preset(self): + v = next(v for v in MOCK_VOICES if v.voice_id == "female_lively") + assert v.gender == VoiceGender.FEMALE + assert v.style == VoiceStyle.LIVELY + assert v.default_speed == 1.2 + assert v.default_pitch == 2.0 + + def test_child_cute_preset(self): + v = next(v for v in MOCK_VOICES if v.voice_id == "child_cute") + assert v.gender == VoiceGender.CHILD + assert v.style == VoiceStyle.STORY + assert v.default_pitch == 4.0 + + def test_all_mock_provider(self): + for v in MOCK_VOICES: + assert v.provider == "mock" + + def test_all_have_provider_voice_id(self): + for v in MOCK_VOICES: + assert v.provider_voice_id != "" + + def test_all_chinese(self): + for v in MOCK_VOICES: + assert v.language == "zh-CN" + + +# ── get_voice ───────────────────────────────────────────────────────────────── + + +class TestGetVoice: + """get_voice 函数""" + + def test_get_existing_voice(self): + v = get_voice("female_warm") + assert v is not None + assert v.voice_id == "female_warm" + assert v.name == "温暖女声" + + def test_get_male_stable(self): + v = get_voice("male_stable") + assert v is not None + assert v.gender == VoiceGender.MALE + + def test_get_child_cute(self): + v = get_voice("child_cute") + assert v is not None + assert v.gender == VoiceGender.CHILD + + def test_get_nonexistent_returns_none(self): + v = get_voice("nonexistent_voice") + assert v is None + + def test_get_empty_string_returns_none(self): + v = get_voice("") + assert v is None + + def test_non_mock_provider_returns_none(self): + v = get_voice("female_warm", provider="aliyun") + assert v is None + + def test_non_mock_provider_nonexistent(self): + v = get_voice("whatever", provider="xunfei") + assert v is None + + def test_mock_provider_explicit(self): + v = get_voice("female_warm", provider="mock") + assert v is not None + assert v.voice_id == "female_warm" + + def test_returns_same_instance(self): + # 应该返回同一个对象(缓存的) + v1 = get_voice("female_warm") + v2 = get_voice("female_warm") + assert v1 is v2 + + +# ── list_voices ─────────────────────────────────────────────────────────────── + + +class TestListVoices: + """list_voices 函数""" + + def test_no_filters_returns_all(self): + result = list_voices() + assert len(result) == len(MOCK_VOICES) + assert len(result) == 8 + + def test_filter_by_gender_male(self): + result = list_voices(gender="male") + assert len(result) > 0 + for v in result: + assert v.gender == VoiceGender.MALE + + def test_filter_by_gender_female(self): + result = list_voices(gender="female") + assert len(result) > 0 + for v in result: + assert v.gender == VoiceGender.FEMALE + + def test_filter_by_gender_child(self): + result = list_voices(gender="child") + assert len(result) == 1 + assert result[0].voice_id == "child_cute" + + def test_filter_by_gender_invalid_returns_empty(self): + result = list_voices(gender="alien") + assert len(result) == 0 + + def test_filter_by_style_stable(self): + result = list_voices(style="stable") + assert len(result) > 0 + for v in result: + assert v.style == VoiceStyle.STABLE + + def test_filter_by_style_lively(self): + result = list_voices(style="lively") + assert len(result) == 1 + assert result[0].voice_id == "female_lively" + + def test_filter_by_style_story(self): + result = list_voices(style="story") + assert len(result) >= 2 + for v in result: + assert v.style == VoiceStyle.STORY + + def test_filter_by_style_invalid_returns_empty(self): + result = list_voices(style="punk") + assert len(result) == 0 + + def test_filter_by_provider_mock(self): + result = list_voices(provider="mock") + assert len(result) == len(MOCK_VOICES) + + def test_filter_by_provider_other_returns_empty(self): + result = list_voices(provider="aliyun") + assert len(result) == 0 + + def test_filter_by_keyword_name(self): + result = list_voices(keyword="女声") + assert len(result) > 0 + for v in result: + assert "女声" in v.name or "女声" in v.description or "女声" in v.voice_id + + def test_filter_by_keyword_description(self): + result = list_voices(keyword="商务") + assert len(result) > 0 + # 沉稳男声描述里有"商务" + + def test_filter_by_keyword_voice_id(self): + result = list_voices(keyword="male_stable") + assert len(result) == 1 + assert result[0].voice_id == "male_stable" + + def test_filter_by_keyword_case_insensitive(self): + result1 = list_voices(keyword="Female") + result2 = list_voices(keyword="female") + assert len(result1) == len(result2) + + def test_filter_by_keyword_nonexistent(self): + result = list_voices(keyword="不存在的关键词999") + assert len(result) == 0 + + def test_combined_gender_and_style(self): + result = list_voices(gender="female", style="lively") + assert len(result) == 1 + assert result[0].voice_id == "female_lively" + + def test_combined_gender_style_keyword(self): + result = list_voices(gender="male", style="story", keyword="磁性") + assert len(result) == 1 + assert result[0].voice_id == "male_magnetic" + + def test_combined_no_match(self): + result = list_voices(gender="child", style="news") + assert len(result) == 0 + + def test_returns_new_list(self): + # 修改返回值不应影响原始列表 + result = list_voices() + result.clear() + assert len(MOCK_VOICES) == 8 + + +# ── get_default_voice ───────────────────────────────────────────────────────── + + +class TestGetDefaultVoice: + """get_default_voice 函数""" + + def test_returns_voice_preset(self): + v = get_default_voice() + assert isinstance(v, VoicePreset) + + def test_returns_first_mock_voice(self): + v = get_default_voice() + assert v == MOCK_VOICES[0] + + def test_default_is_female_warm(self): + v = get_default_voice() + assert v.voice_id == "female_warm" + assert v.gender == VoiceGender.FEMALE + + def test_multiple_calls_same(self): + v1 = get_default_voice() + v2 = get_default_voice() + assert v1 is v2 -- 2.54.0 From bb8ed8df0cb8364894ee65d8bc51396a326fb004 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Wed, 29 Jul 2026 10:38:08 +0000 Subject: [PATCH 2/2] style: auto-format with black + isort + prettier [skip ci-format-check] --- tests/unit/domain/test_voice_presets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/domain/test_voice_presets.py b/tests/unit/domain/test_voice_presets.py index cc21c5864..e3ac2da1c 100755 --- a/tests/unit/domain/test_voice_presets.py +++ b/tests/unit/domain/test_voice_presets.py @@ -1,7 +1,6 @@ """voice_presets 配音音色预设模块单测.""" import pytest - from domain.voice_presets import ( MOCK_VOICES, VoiceGender, -- 2.54.0