"""voice_presets 配音音色预设单元测试.""" from __future__ import annotations import pytest from packages.domain.voice_presets import ( MOCK_VOICES, VoiceGender, VoicePreset, VoiceStyle, get_default_voice, get_voice, list_voices, ) # ── VoiceGender 枚举测试 ───────────────────────────────────────────────────── class TestVoiceGender: """VoiceGender 枚举测试.""" def test_male(self): """男声.""" assert VoiceGender.MALE.value == "male" def test_female(self): """女声.""" assert VoiceGender.FEMALE.value == "female" def test_child(self): """童声.""" assert VoiceGender.CHILD.value == "child" def test_count(self): """共3种性别.""" assert len(VoiceGender) == 3 def test_is_str_enum(self): """可与字符串比较.""" assert VoiceGender.FEMALE == "female" # ── VoiceStyle 枚举测试 ────────────────────────────────────────────────────── class TestVoiceStyle: """VoiceStyle 枚举测试.""" def test_stable(self): """沉稳.""" assert VoiceStyle.STABLE.value == "stable" def test_lively(self): """活泼.""" assert VoiceStyle.LIVELY.value == "lively" def test_customer_service(self): """客服.""" assert VoiceStyle.CUSTOMER_SERVICE.value == "customer_service" def test_narration(self): """旁白.""" assert VoiceStyle.NARRATION.value == "narration" def test_news(self): """新闻.""" assert VoiceStyle.NEWS.value == "news" def test_story(self): """故事.""" assert VoiceStyle.STORY.value == "story" def test_count(self): """共6种风格.""" assert len(VoiceStyle) == 6 # ── VoicePreset 数据类测试 ─────────────────────────────────────────────────── class TestVoicePreset: """VoicePreset 数据类测试.""" def test_minimal_creation(self): """最小化创建(必填字段).""" v = VoicePreset(voice_id="v1", name="测试音色") assert v.voice_id == "v1" assert v.name == "测试音色" def test_default_values(self): """默认值正确.""" v = VoicePreset(voice_id="v1", name="T") 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="voice_full", name="完整音色", gender=VoiceGender.MALE, style=VoiceStyle.NEWS, description="测试描述", provider="aliyun", provider_voice_id="ali_001", default_speed=0.9, default_pitch=1.5, sample_rate=44100, language="en-US", ) assert v.voice_id == "voice_full" assert v.name == "完整音色" assert v.gender == VoiceGender.MALE assert v.style == VoiceStyle.NEWS assert v.description == "测试描述" assert v.provider == "aliyun" assert v.provider_voice_id == "ali_001" assert v.default_speed == 0.9 assert v.default_pitch == 1.5 assert v.sample_rate == 44100 assert v.language == "en-US" # ── MOCK_VOICES 列表测试 ───────────────────────────────────────────────────── class TestMockVoices: """MOCK_VOICES 预设列表测试.""" def test_not_empty(self): """列表不为空.""" assert len(MOCK_VOICES) > 0 def test_count(self): """共8个Mock音色.""" assert len(MOCK_VOICES) == 8 def test_all_have_voice_id(self): """每个音色都有voice_id.""" for v in MOCK_VOICES: assert v.voice_id, f"音色缺少voice_id: {v}" def test_all_have_name(self): """每个音色都有name.""" for v in MOCK_VOICES: assert v.name, f"{v.voice_id} 缺少name" def test_voice_ids_unique(self): """voice_id唯一.""" ids = [v.voice_id for v in MOCK_VOICES] assert len(ids) == len(set(ids)), f"重复ID: {[i for i in ids if ids.count(i) > 1]}" def test_all_mock_provider(self): """都是mock供应商.""" for v in MOCK_VOICES: assert v.provider == "mock", f"{v.voice_id} provider不是mock" def test_all_chinese(self): """都是中文语言.""" for v in MOCK_VOICES: assert v.language == "zh-CN" def test_sample_rate_consistent(self): """采样率一致为22050.""" for v in MOCK_VOICES: assert v.sample_rate == 22050 def test_gender_distribution(self): """性别分布:至少有男/女/童声.""" genders = {v.gender for v in MOCK_VOICES} assert VoiceGender.MALE in genders assert VoiceGender.FEMALE in genders assert VoiceGender.CHILD in genders def test_style_coverage(self): """覆盖多种风格.""" styles = {v.style for v in MOCK_VOICES} assert len(styles) >= 4 def test_specific_voices_exist(self): """特定音色存在.""" ids = {v.voice_id for v in MOCK_VOICES} assert "female_warm" in ids assert "male_stable" in ids assert "female_lively" in ids assert "child_cute" in ids def test_default_speed_positive(self): """语速都大于0.""" for v in MOCK_VOICES: assert v.default_speed > 0, f"{v.voice_id}语速<=0" def test_descriptions_not_empty(self): """都有描述.""" for v in MOCK_VOICES: assert v.description, f"{v.voice_id}缺少描述" assert len(v.description) > 5 # ── 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 assert v.style == VoiceStyle.STABLE def test_get_nonexistent_voice(self): """获取不存在的音色返回None.""" assert get_voice("nonexistent") is None def test_get_empty_string(self): """空字符串返回None.""" assert get_voice("") is None def test_mock_provider(self): """指定mock provider.""" v = get_voice("female_warm", provider="mock") assert v is not None assert v.provider == "mock" def test_other_provider_returns_none(self): """其他provider返回None.""" assert get_voice("female_warm", provider="aliyun") is None assert get_voice("female_warm", provider="xunfei") is None 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_filter_returns_all(self): """无筛选返回全部.""" result = list_voices() assert len(result) == len(MOCK_VOICES) def test_filter_by_gender_female(self): """按女性筛选.""" result = list_voices(gender="female") assert len(result) > 0 assert all(v.gender == VoiceGender.FEMALE for v in result) def test_filter_by_gender_male(self): """按男性筛选.""" result = list_voices(gender="male") assert len(result) > 0 assert all(v.gender == VoiceGender.MALE for v in result) def test_filter_by_gender_child(self): """按童声筛选.""" result = list_voices(gender="child") assert len(result) >= 1 assert all(v.gender == VoiceGender.CHILD for v in result) def test_filter_by_invalid_gender(self): """无效性别返回空.""" result = list_voices(gender="alien") assert result == [] def test_filter_by_style_narration(self): """按旁白风格筛选.""" result = list_voices(style="narration") assert len(result) > 0 assert all(v.style == VoiceStyle.NARRATION for v in result) def test_filter_by_style_news(self): """按新闻风格筛选.""" result = list_voices(style="news") assert len(result) >= 1 assert all(v.style == VoiceStyle.NEWS for v in result) def test_filter_by_style_story(self): """按故事风格筛选.""" result = list_voices(style="story") assert len(result) >= 1 assert all(v.style == VoiceStyle.STORY for v in result) def test_filter_by_invalid_style(self): """无效风格返回空.""" result = list_voices(style="unknown") assert result == [] def test_filter_by_provider_mock(self): """mock provider返回全部.""" result = list_voices(provider="mock") assert len(result) == len(MOCK_VOICES) def test_filter_by_provider_other(self): """其他provider返回空.""" result = list_voices(provider="aliyun") assert result == [] def test_keyword_search_name(self): """关键词搜索name.""" result = list_voices(keyword="温暖") assert len(result) >= 1 assert any(v.voice_id == "female_warm" for v in result) def test_keyword_search_description(self): """关键词搜索description.""" result = list_voices(keyword="商务") assert len(result) >= 1 assert any("商务" in v.description for v in result) def test_keyword_search_voice_id(self): """关键词搜索voice_id.""" result = list_voices(keyword="male_stable") assert len(result) >= 1 assert result[0].voice_id == "male_stable" def test_keyword_case_insensitive(self): """关键词不区分大小写(英文).""" r1 = list_voices(keyword="Female") r2 = list_voices(keyword="female") assert len(r1) == len(r2) def test_keyword_no_match(self): """无匹配关键词返回空.""" result = list_voices(keyword="完全不存在的音色xyz") assert result == [] def test_gender_and_style_combined(self): """性别+风格组合筛选.""" result = list_voices(gender="female", style="story") assert len(result) >= 1 assert all(v.gender == VoiceGender.FEMALE for v in result) assert all(v.style == VoiceStyle.STORY for v in result) def test_returns_new_list(self): """返回新列表,修改不影响原数据.""" result = list_voices() result.clear() assert len(MOCK_VOICES) == 8 def test_empty_keyword_returns_all(self): """空关键词返回全部.""" result = list_voices(keyword="") assert len(result) == len(MOCK_VOICES) def test_none_keyword_returns_all(self): """None关键词返回全部.""" result = list_voices(keyword=None) assert len(result) == len(MOCK_VOICES) # ── get_default_voice 测试 ─────────────────────────────────────────────────── class TestGetDefaultVoice: """get_default_voice 测试.""" def test_default_voice_exists(self): """默认音色存在.""" v = get_default_voice() assert v is not None assert isinstance(v, VoicePreset) def test_default_is_first_mock(self): """默认音色是列表第一个.""" v = get_default_voice() assert v is MOCK_VOICES[0] def test_default_is_female_warm(self): """默认是温暖女声.""" v = get_default_voice() assert v.voice_id == "female_warm" assert v.name == "温暖女声" assert v.gender == VoiceGender.FEMALE def test_default_same_instance(self): """多次调用返回同一实例.""" v1 = get_default_voice() v2 = get_default_voice() assert v1 is v2