"""预置音色配置单元测试 — Phase 3 CosyVoice 集成.""" from __future__ import annotations import pytest from packages.domain.preset_voices import ( PRESET_VOICES, PresetVoice, get_preset_voice_by_id, get_preset_voices, is_preset_voice, ) class TestPresetVoice: """测试 PresetVoice 数据类。""" def test_preset_voice_fields(self) -> None: """预置音色字段完整。""" voice = PresetVoice( voice_id="test_voice", name="测试音色", description="测试描述", gender="female", language="zh-CN", preview_url="https://example.com/preview.mp3", tags=["测试"], ) assert voice.voice_id == "test_voice" assert voice.name == "测试音色" assert voice.description == "测试描述" assert voice.gender == "female" assert voice.language == "zh-CN" assert voice.preview_url == "https://example.com/preview.mp3" assert voice.tags == ["测试"] def test_preset_voice_defaults(self) -> None: """预置音色默认值。""" voice = PresetVoice( voice_id="test", name="测试", description="描述", gender="male", ) assert voice.language == "zh-CN" assert voice.preview_url == "" assert voice.tags is None def test_preset_voice_frozen(self) -> None: """预置音色不可变。""" voice = PresetVoice( voice_id="test", name="测试", description="描述", gender="male", ) with pytest.raises(AttributeError): voice.name = "新名称" def test_preset_voice_to_dict(self) -> None: """序列化。""" voice = PresetVoice( voice_id="longxiaochun", name="龙小淳", description="温柔女声", gender="female", tags=["温柔", "女声"], ) result = voice.to_dict() assert result["voice_id"] == "longxiaochun" assert result["name"] == "龙小淳" assert result["description"] == "温柔女声" assert result["gender"] == "female" assert result["language"] == "zh-CN" assert result["preview_url"] == "" assert result["tags"] == ["温柔", "女声"] def test_preset_voice_to_dict_no_tags(self) -> None: """tags 为 None 时序列化为空列表。""" voice = PresetVoice( voice_id="test", name="测试", description="描述", gender="male", ) result = voice.to_dict() assert result["tags"] == [] class TestPresetVoicesConfig: """测试预置音色配置列表。""" def test_preset_voices_not_empty(self) -> None: """预置音色列表不为空。""" assert len(PRESET_VOICES) >= 5 def test_preset_voices_has_8_voices(self) -> None: """应有 8 个预置音色。""" assert len(PRESET_VOICES) == 8 def test_all_voices_have_required_fields(self) -> None: """所有预置音色都有必填字段。""" for voice in PRESET_VOICES: assert voice.voice_id, f"voice_id 为空: {voice}" assert voice.name, f"name 为空: {voice}" assert voice.description, f"description 为空: {voice}" assert voice.gender in ("male", "female"), f"gender 无效: {voice}" assert voice.language == "zh-CN", f"language 应为 zh-CN: {voice}" def test_all_voices_have_unique_ids(self) -> None: """所有预置音色 ID 唯一。""" ids = [v.voice_id for v in PRESET_VOICES] assert len(ids) == len(set(ids)), "存在重复的 voice_id" def test_all_voices_have_unique_names(self) -> None: """所有预置音色名称唯一。""" names = [v.name for v in PRESET_VOICES] assert len(names) == len(set(names)), "存在重复的 name" def test_cosyvoice_voice_ids(self) -> None: """音色 ID 应为 CosyVoice 真实可用的音色名。""" expected_ids = { "longxiaochun", "longxiaoxia", "longxiaochen", "longyue", "longshu", "longjing", "longbo", "longtian", } actual_ids = {v.voice_id for v in PRESET_VOICES} assert actual_ids == expected_ids def test_gender_distribution(self) -> None: """男女音色分布合理。""" male_count = sum(1 for v in PRESET_VOICES if v.gender == "male") female_count = sum(1 for v in PRESET_VOICES if v.gender == "female") assert male_count == 3 assert female_count == 5 def test_all_have_tags(self) -> None: """所有预置音色都有标签。""" for voice in PRESET_VOICES: assert voice.tags is not None, f"tags 为 None: {voice.name}" assert len(voice.tags) >= 2, f"标签太少: {voice.name}" class TestPresetVoiceHelpers: """测试辅助函数。""" def test_get_preset_voices(self) -> None: """get_preset_voices 返回完整列表。""" voices = get_preset_voices() assert voices == PRESET_VOICES assert len(voices) == 8 def test_get_preset_voice_by_id_found(self) -> None: """按 ID 查找存在的音色。""" voice = get_preset_voice_by_id("longxiaochun") assert voice is not None assert voice.name == "龙小淳" assert voice.voice_id == "longxiaochun" def test_get_preset_voice_by_id_not_found(self) -> None: """按 ID 查找不存在的音色。""" voice = get_preset_voice_by_id("nonexistent_voice") assert voice is None def test_is_preset_voice_true(self) -> None: """判断预置音色返回 True。""" assert is_preset_voice("longxiaochun") is True assert is_preset_voice("longxiaoxia") is True assert is_preset_voice("longbo") is True def test_is_preset_voice_false(self) -> None: """判断非预置音色返回 False。""" assert is_preset_voice("nonexistent") is False assert is_preset_voice("") is False assert is_preset_voice("user_custom_voice") is False