From 184c012aeba6bfe06f728f38a3dee99a94609846 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Wed, 29 Jul 2026 18:52:25 +0800 Subject: [PATCH 1/2] =?UTF-8?q?test(wave190):=20preset=5Fvoices=20?= =?UTF-8?q?=E9=A2=84=E7=BD=AE=E9=9F=B3=E8=89=B2=20+35=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unit/domain/test_preset_voices.py | 246 ++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100755 tests/unit/domain/test_preset_voices.py diff --git a/tests/unit/domain/test_preset_voices.py b/tests/unit/domain/test_preset_voices.py new file mode 100755 index 000000000..2be3790dd --- /dev/null +++ b/tests/unit/domain/test_preset_voices.py @@ -0,0 +1,246 @@ +"""preset_voices 预置音色配置单测.""" + +from dataclasses import FrozenInstanceError + +import pytest + +from domain.preset_voices import ( + PRESET_VOICES, + PresetVoice, + get_preset_voice_by_id, + get_preset_voices, + is_preset_voice, +) + +# ── PresetVoice dataclass ──────────────────────────────────────────────────── + + +class TestPresetVoice: + """PresetVoice dataclass""" + + def test_minimal_creation(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" + assert v.language == "zh-CN" + assert v.preview_url == "" + assert v.tags is None + + def test_full_creation(self): + v = PresetVoice( + voice_id="test_v2", + name="完整音色", + description="完整描述", + gender="male", + language="en-US", + preview_url="https://example.com/preview.mp3", + tags=["沉稳", "男声"], + ) + assert v.gender == "male" + assert v.language == "en-US" + assert v.preview_url == "https://example.com/preview.mp3" + assert v.tags == ["沉稳", "男声"] + + def test_frozen_immutable(self): + v = PresetVoice(voice_id="test", name="Test", description="desc", gender="female") + with pytest.raises(FrozenInstanceError): + v.name = "NewName" + + def test_equality(self): + v1 = PresetVoice(voice_id="same", name="同名", description="d", gender="female") + v2 = PresetVoice(voice_id="same", name="同名", description="d", gender="female") + assert v1 == v2 + + def test_inequality(self): + v1 = PresetVoice(voice_id="a", name="A", description="da", gender="female") + v2 = PresetVoice(voice_id="b", name="B", description="db", gender="male") + assert v1 != v2 + + def test_to_dict(self): + v = PresetVoice( + voice_id="test_v1", + name="测试音色", + description="测试描述", + gender="female", + language="zh-CN", + preview_url="https://x.com/a.mp3", + tags=["温柔", "女声"], + ) + d = v.to_dict() + assert isinstance(d, dict) + assert d["voice_id"] == "test_v1" + assert d["name"] == "测试音色" + assert d["description"] == "测试描述" + assert d["gender"] == "female" + assert d["language"] == "zh-CN" + assert d["preview_url"] == "https://x.com/a.mp3" + assert d["tags"] == ["温柔", "女声"] + + def test_to_dict_none_tags_becomes_empty_list(self): + v = PresetVoice(voice_id="test", name="Test", description="desc", gender="female") + d = v.to_dict() + assert d["tags"] == [] + assert isinstance(d["tags"], list) + + def test_to_dict_has_all_keys(self): + v = PresetVoice(voice_id="test", name="Test", description="desc", gender="female") + d = v.to_dict() + assert set(d.keys()) == { + "voice_id", + "name", + "description", + "gender", + "language", + "preview_url", + "tags", + } + + def test_slots_no_extra_attrs(self): + v = PresetVoice(voice_id="test", name="Test", description="desc", gender="female") + # frozen + slots dataclass 不允许动态添加属性 + with pytest.raises((AttributeError, TypeError)): + v.nonexistent_field = "value" + + +# ── PRESET_VOICES 列表 ─────────────────────────────────────────────────────── + + +class TestPresetVoicesList: + """PRESET_VOICES 预置音色列表""" + + def test_not_empty(self): + assert len(PRESET_VOICES) > 0 + + def test_count(self): + assert len(PRESET_VOICES) == 8 + + def test_all_are_preset_voice(self): + for v in PRESET_VOICES: + assert isinstance(v, PresetVoice) + + def test_unique_voice_ids(self): + ids = [v.voice_id for v in PRESET_VOICES] + assert len(ids) == len(set(ids)) + + def test_unique_names(self): + names = [v.name for v in PRESET_VOICES] + assert len(names) == len(set(names)) + + 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") + + def test_all_chinese(self): + for v in PRESET_VOICES: + assert v.language == "zh-CN" + + def test_longxiaochun_voice(self): + v = next(v for v in PRESET_VOICES if v.voice_id == "longxiaochun_v3") + assert v.name == "龙小淳" + assert v.gender == "female" + assert "温柔" in v.description + + def test_longxiaochen_voice(self): + v = next(v for v in PRESET_VOICES if v.voice_id == "longxiaochen_v3") + assert v.name == "龙小晨" + assert v.gender == "male" + + def test_male_voices_count(self): + males = [v for v in PRESET_VOICES if v.gender == "male"] + assert len(males) == 3 # 龙小晨/龙书/龙博 + + def test_female_voices_count(self): + females = [v for v in PRESET_VOICES if v.gender == "female"] + assert len(females) == 5 # 龙小淳/龙小夏/龙悦/龙静/龙甜 + + def test_all_have_tags(self): + for v in PRESET_VOICES: + assert v.tags is not None + assert len(v.tags) > 0 + + def test_voice_id_pattern(self): + # 所有音色 ID 都以 _v3 结尾 + for v in PRESET_VOICES: + assert v.voice_id.endswith("_v3") + + +# ── 工具函数 ───────────────────────────────────────────────────────────────── + + +class TestGetPresetVoices: + """get_preset_voices 函数""" + + def test_returns_full_list(self): + result = get_preset_voices() + assert len(result) == len(PRESET_VOICES) + assert result is PRESET_VOICES # 返回同一列表引用 + + def test_all_are_preset_voice(self): + result = get_preset_voices() + for v in result: + assert isinstance(v, PresetVoice) + + +class TestGetPresetVoiceById: + """get_preset_voice_by_id 函数""" + + def test_get_existing_female(self): + v = get_preset_voice_by_id("longxiaochun_v3") + assert v is not None + assert v.voice_id == "longxiaochun_v3" + assert v.name == "龙小淳" + + def test_get_existing_male(self): + v = get_preset_voice_by_id("longxiaochen_v3") + assert v is not None + assert v.gender == "male" + + def test_get_nonexistent_returns_none(self): + v = get_preset_voice_by_id("nonexistent_voice") + assert v is None + + def test_get_empty_string_returns_none(self): + v = get_preset_voice_by_id("") + assert v is None + + def test_returns_same_instance(self): + v1 = get_preset_voice_by_id("longyue_v3") + v2 = get_preset_voice_by_id("longyue_v3") + assert v1 is v2 + + def test_all_voices_reachable(self): + for v in PRESET_VOICES: + found = get_preset_voice_by_id(v.voice_id) + assert found is not None + assert found.voice_id == v.voice_id + + +class TestIsPresetVoice: + """is_preset_voice 函数""" + + def test_existing_voice_true(self): + assert is_preset_voice("longxiaochun_v3") is True + + def test_all_existing_are_true(self): + for v in PRESET_VOICES: + assert is_preset_voice(v.voice_id) is True + + def test_nonexistent_voice_false(self): + assert is_preset_voice("fake_voice") is False + + def test_empty_string_false(self): + assert is_preset_voice("") is False + + def test_consistent_with_get_by_id(self): + for v in PRESET_VOICES: + assert is_preset_voice(v.voice_id) == (get_preset_voice_by_id(v.voice_id) is not None) -- 2.54.0 From 70ce8b21f84745ccee72537309b35a754b90839a Mon Sep 17 00:00:00 2001 From: CI Bot Date: Wed, 29 Jul 2026 11:02:56 +0000 Subject: [PATCH 2/2] style: auto-format with black + isort + prettier [skip ci-format-check] --- tests/unit/domain/test_preset_voices.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/domain/test_preset_voices.py b/tests/unit/domain/test_preset_voices.py index 2be3790dd..30d609605 100755 --- a/tests/unit/domain/test_preset_voices.py +++ b/tests/unit/domain/test_preset_voices.py @@ -3,7 +3,6 @@ from dataclasses import FrozenInstanceError import pytest - from domain.preset_voices import ( PRESET_VOICES, PresetVoice, -- 2.54.0