test(wave190): preset_voices 预置音色 +35测 #1152
Executable
+245
@@ -0,0 +1,245 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user