"""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_values(self): assert VoiceGender.MALE == "male" assert VoiceGender.FEMALE == "female" assert VoiceGender.CHILD == "child" def test_is_str_enum(self): # StrEnum 在不同 Python 版本 str() 行为可能不同(3.11+ 返回值,旧版自定义 StrEnum 可能返回类名) # 用 value 比较更稳妥 assert isinstance(VoiceGender.FEMALE, str) assert VoiceGender.MALE.value == "male" assert VoiceGender.FEMALE.value == "female" class TestVoiceStyle: """VoiceStyle 枚举测试.""" def test_values(self): assert VoiceStyle.STABLE == "stable" assert VoiceStyle.LIVELY == "lively" assert VoiceStyle.CUSTOMER_SERVICE == "customer_service" assert VoiceStyle.NARRATION == "narration" assert VoiceStyle.NEWS == "news" assert VoiceStyle.STORY == "story" class TestVoicePreset: """VoicePreset 数据类测试.""" def test_create_with_required_fields(self): v = VoicePreset(voice_id="test_001", name="测试音色") assert v.voice_id == "test_001" assert v.name == "测试音色" # 默认值 assert v.gender == VoiceGender.FEMALE assert v.style == VoiceStyle.NARRATION assert v.provider == "mock" assert v.default_speed == 1.0 assert v.default_pitch == 0.0 assert v.sample_rate == 22050 assert v.language == "zh-CN" def test_create_with_all_fields(self): v = VoicePreset( voice_id="male_news", name="新闻男声", gender=VoiceGender.MALE, style=VoiceStyle.NEWS, description="字正腔圆", provider="aliyun", provider_voice_id="zhiqiang", default_speed=0.9, default_pitch=1.0, sample_rate=16000, language="zh-CN", ) assert v.gender == VoiceGender.MALE assert v.style == VoiceStyle.NEWS assert v.provider == "aliyun" assert v.default_speed == 0.9 assert v.sample_rate == 16000 def test_slots(self): """dataclass slots=True,不能添加新属性.""" v = VoicePreset(voice_id="test", name="测试") with pytest.raises(AttributeError): v.new_field = "value" # type: ignore[attr-defined] class TestMockVoices: """MOCK_VOICES 预设列表测试.""" def test_not_empty(self): assert len(MOCK_VOICES) > 0 def test_all_have_unique_voice_id(self): ids = [v.voice_id for v in MOCK_VOICES] assert len(ids) == len(set(ids)), "voice_id 不能重复" def test_all_are_voice_preset_instances(self): for v in MOCK_VOICES: assert isinstance(v, VoicePreset) assert v.provider == "mock" def test_contains_expected_voices(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_voice_genders_coverage(self): genders = {v.gender for v in MOCK_VOICES} assert VoiceGender.FEMALE in genders assert VoiceGender.MALE in genders assert VoiceGender.CHILD in genders class TestGetVoice: """get_voice 函数测试.""" def test_existing_mock_voice(self): v = get_voice("female_warm") assert v is not None assert v.voice_id == "female_warm" assert v.name == "温暖女声" def test_nonexistent_voice(self): assert get_voice("nonexistent") is None def test_provider_mock(self): v = get_voice("male_stable", provider="mock") assert v is not None assert v.voice_id == "male_stable" def test_unknown_provider_returns_none(self): assert get_voice("female_warm", provider="aliyun") is None def test_empty_string_returns_none(self): assert get_voice("") is None class TestListVoices: """list_voices 函数测试.""" def test_no_filters_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 for v in result: assert v.gender == VoiceGender.FEMALE 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_child(self): result = list_voices(gender="child") assert len(result) > 0 for v in result: assert v.gender == VoiceGender.CHILD def test_filter_by_style(self): result = list_voices(style="story") assert len(result) > 0 for v in result: assert v.style == VoiceStyle.STORY def test_filter_by_style_narration(self): result = list_voices(style="narration") assert len(result) >= 1 def test_filter_by_unknown_provider_returns_empty(self): result = list_voices(provider="aliyun") assert result == [] 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 def test_filter_by_keyword_description(self): result = list_voices(keyword="vlog") assert len(result) > 0 def test_filter_by_keyword_voice_id(self): result = list_voices(keyword="female") assert len(result) > 0 for v in result: assert "female" in v.voice_id.lower() def test_filter_by_keyword_case_insensitive(self): r1 = list_voices(keyword="FEMALE") r2 = list_voices(keyword="female") assert len(r1) == len(r2) def test_filter_keyword_no_match(self): result = list_voices(keyword="xyz_nonexistent_keyword") assert result == [] def test_combined_filters_gender_and_style(self): result = list_voices(gender="female", style="story") assert len(result) > 0 for v in result: assert v.gender == VoiceGender.FEMALE assert v.style == VoiceStyle.STORY def test_combined_filters_gender_and_keyword(self): result = list_voices(gender="male", keyword="新闻") assert len(result) > 0 for v in result: assert v.gender == VoiceGender.MALE def test_combined_no_match(self): result = list_voices(gender="child", style="news") # 童声没有新闻风格 assert result == [] class TestGetDefaultVoice: """get_default_voice 函数测试.""" def test_returns_first_mock_voice(self): v = get_default_voice() assert v == MOCK_VOICES[0] assert v.voice_id == "female_warm" def test_returns_voice_preset(self): assert isinstance(get_default_voice(), VoicePreset)