Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb8ed8df0c | |||
| fa991831b1 |
@@ -1,280 +0,0 @@
|
||||
"""VerificationCode 单元测试."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from domain.verification_code import VerificationCode
|
||||
|
||||
|
||||
class TestVerificationCodeCreate:
|
||||
"""create() 工厂方法测试."""
|
||||
|
||||
def test_create_basic(self):
|
||||
vc = VerificationCode.create("test@example.com", "email_login")
|
||||
assert vc.id is not None
|
||||
assert len(vc.id) == 32
|
||||
assert vc.recipient == "test@example.com"
|
||||
assert vc.code_type == "email_login"
|
||||
assert len(vc.code) == 6
|
||||
assert vc.code.isdigit()
|
||||
assert vc.used_at is None
|
||||
assert vc.attempts == 0
|
||||
assert vc.created_at is not None
|
||||
assert vc.expires_at > vc.created_at
|
||||
|
||||
def test_create_recipient_stripped(self):
|
||||
vc = VerificationCode.create(" test@example.com ", "email_login")
|
||||
assert vc.recipient == "test@example.com"
|
||||
|
||||
def test_create_custom_code(self):
|
||||
vc = VerificationCode.create("test@example.com", "email_login", custom_code="123456")
|
||||
assert vc.code == "123456"
|
||||
|
||||
def test_create_custom_ttl(self):
|
||||
fixed_now = datetime(2026, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
|
||||
with patch("domain.verification_code.datetime") as mock_dt:
|
||||
mock_dt.now.return_value = fixed_now
|
||||
mock_dt.side_effect = lambda *a, **kw: datetime(*a, **kw)
|
||||
vc = VerificationCode.create("test@example.com", "email_login", ttl_seconds=60)
|
||||
assert vc.expires_at == fixed_now + timedelta(seconds=60)
|
||||
|
||||
def test_create_default_ttl_300(self):
|
||||
fixed_now = datetime(2026, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
|
||||
with patch("domain.verification_code.datetime") as mock_dt:
|
||||
mock_dt.now.return_value = fixed_now
|
||||
mock_dt.side_effect = lambda *a, **kw: datetime(*a, **kw)
|
||||
vc = VerificationCode.create("test@example.com", "email_login")
|
||||
assert vc.expires_at == fixed_now + timedelta(seconds=300)
|
||||
|
||||
def test_create_unique_ids(self):
|
||||
vc1 = VerificationCode.create("a@b.com", "email_login")
|
||||
vc2 = VerificationCode.create("a@b.com", "email_login")
|
||||
assert vc1.id != vc2.id
|
||||
|
||||
def test_create_unique_codes(self):
|
||||
codes = set()
|
||||
for _ in range(20):
|
||||
vc = VerificationCode.create("a@b.com", "email_login")
|
||||
codes.add(vc.code)
|
||||
# 20个随机6位码几乎肯定不都一样
|
||||
assert len(codes) > 1
|
||||
|
||||
def test_create_phone_recipient(self):
|
||||
vc = VerificationCode.create("13800138000", "phone_login")
|
||||
assert vc.recipient == "13800138000"
|
||||
assert vc.code_type == "phone_login"
|
||||
|
||||
def test_create_all_code_types(self):
|
||||
for ct in ["email_bind", "phone_bind", "email_login", "phone_login", "reset_password"]:
|
||||
vc = VerificationCode.create("test@example.com", ct)
|
||||
assert vc.code_type == ct
|
||||
|
||||
|
||||
class TestVerificationCodeIsExpired:
|
||||
"""is_expired 属性测试."""
|
||||
|
||||
def test_not_expired_future(self):
|
||||
future = datetime.now(timezone.utc) + timedelta(hours=1)
|
||||
vc = VerificationCode(
|
||||
id="1",
|
||||
recipient="a@b.com",
|
||||
code="123456",
|
||||
code_type="email_login",
|
||||
expires_at=future,
|
||||
)
|
||||
assert vc.is_expired is False
|
||||
|
||||
def test_expired_past(self):
|
||||
past = datetime.now(timezone.utc) - timedelta(hours=1)
|
||||
vc = VerificationCode(
|
||||
id="1",
|
||||
recipient="a@b.com",
|
||||
code="123456",
|
||||
code_type="email_login",
|
||||
expires_at=past,
|
||||
)
|
||||
assert vc.is_expired is True
|
||||
|
||||
def test_expired_boundary_exact(self):
|
||||
# 用mock固定时间,expires_at等于当前时间不算过期
|
||||
fixed_now = datetime(2026, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
|
||||
with patch("domain.verification_code.datetime") as mock_dt:
|
||||
mock_dt.now.return_value = fixed_now
|
||||
mock_dt.side_effect = lambda *a, **kw: datetime(*a, **kw)
|
||||
vc = VerificationCode(
|
||||
id="1",
|
||||
recipient="a@b.com",
|
||||
code="123456",
|
||||
code_type="email_login",
|
||||
expires_at=fixed_now,
|
||||
)
|
||||
assert vc.is_expired is False
|
||||
|
||||
|
||||
class TestVerificationCodeIsUsed:
|
||||
"""is_used 属性测试."""
|
||||
|
||||
def test_not_used_default(self):
|
||||
vc = VerificationCode.create("a@b.com", "email_login")
|
||||
assert vc.is_used is False
|
||||
|
||||
def test_is_used_after_mark(self):
|
||||
vc = VerificationCode.create("a@b.com", "email_login")
|
||||
vc.mark_used()
|
||||
assert vc.is_used is True
|
||||
|
||||
|
||||
class TestVerificationCodeIsValid:
|
||||
"""is_valid 属性测试."""
|
||||
|
||||
def test_valid_fresh(self):
|
||||
future = datetime.now(timezone.utc) + timedelta(hours=1)
|
||||
vc = VerificationCode(
|
||||
id="1",
|
||||
recipient="a@b.com",
|
||||
code="123456",
|
||||
code_type="email_login",
|
||||
expires_at=future,
|
||||
)
|
||||
assert vc.is_valid is True
|
||||
|
||||
def test_invalid_expired(self):
|
||||
past = datetime.now(timezone.utc) - timedelta(hours=1)
|
||||
vc = VerificationCode(
|
||||
id="1",
|
||||
recipient="a@b.com",
|
||||
code="123456",
|
||||
code_type="email_login",
|
||||
expires_at=past,
|
||||
)
|
||||
assert vc.is_valid is False
|
||||
|
||||
def test_invalid_used(self):
|
||||
future = datetime.now(timezone.utc) + timedelta(hours=1)
|
||||
vc = VerificationCode(
|
||||
id="1",
|
||||
recipient="a@b.com",
|
||||
code="123456",
|
||||
code_type="email_login",
|
||||
expires_at=future,
|
||||
)
|
||||
vc.mark_used()
|
||||
assert vc.is_valid is False
|
||||
|
||||
def test_invalid_expired_and_used(self):
|
||||
past = datetime.now(timezone.utc) - timedelta(hours=1)
|
||||
vc = VerificationCode(
|
||||
id="1",
|
||||
recipient="a@b.com",
|
||||
code="123456",
|
||||
code_type="email_login",
|
||||
expires_at=past,
|
||||
)
|
||||
vc.mark_used()
|
||||
assert vc.is_valid is False
|
||||
|
||||
|
||||
class TestVerificationCodeMarkUsed:
|
||||
"""mark_used 方法测试."""
|
||||
|
||||
def test_mark_used_sets_timestamp(self):
|
||||
vc = VerificationCode.create("a@b.com", "email_login")
|
||||
assert vc.used_at is None
|
||||
before = datetime.now(timezone.utc)
|
||||
vc.mark_used()
|
||||
after = datetime.now(timezone.utc)
|
||||
assert vc.used_at is not None
|
||||
assert before <= vc.used_at <= after
|
||||
|
||||
def test_mark_used_twice_overwrites(self):
|
||||
vc = VerificationCode.create("a@b.com", "email_login")
|
||||
vc.mark_used()
|
||||
first = vc.used_at
|
||||
# 时间足够短,一般不会不同,但确保可以重复调用
|
||||
vc.mark_used()
|
||||
assert vc.used_at is not None
|
||||
|
||||
|
||||
class TestVerificationCodeIncrementAttempts:
|
||||
"""increment_attempts 方法测试."""
|
||||
|
||||
def test_default_zero(self):
|
||||
vc = VerificationCode.create("a@b.com", "email_login")
|
||||
assert vc.attempts == 0
|
||||
|
||||
def test_increment_once(self):
|
||||
vc = VerificationCode.create("a@b.com", "email_login")
|
||||
vc.increment_attempts()
|
||||
assert vc.attempts == 1
|
||||
|
||||
def test_increment_multiple(self):
|
||||
vc = VerificationCode.create("a@b.com", "email_login")
|
||||
for _i in range(5):
|
||||
vc.increment_attempts()
|
||||
assert vc.attempts == 5
|
||||
|
||||
|
||||
class TestVerificationCodeBasics:
|
||||
"""基础构造和 slots 测试."""
|
||||
|
||||
def test_direct_construction(self):
|
||||
now = datetime.now(timezone.utc)
|
||||
vc = VerificationCode(
|
||||
id="abc123",
|
||||
recipient="test@test.com",
|
||||
code="000000",
|
||||
code_type="email_bind",
|
||||
expires_at=now + timedelta(minutes=5),
|
||||
used_at=None,
|
||||
attempts=0,
|
||||
created_at=now,
|
||||
)
|
||||
assert vc.id == "abc123"
|
||||
assert vc.recipient == "test@test.com"
|
||||
assert vc.code == "000000"
|
||||
|
||||
def test_slots_no_extra_attrs(self):
|
||||
vc = VerificationCode.create("a@b.com", "email_login")
|
||||
with pytest.raises((AttributeError, TypeError)):
|
||||
vc.new_field = "value" # type: ignore[attr-defined]
|
||||
|
||||
def test_equality_same_id(self):
|
||||
now = datetime(2026, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
|
||||
vc1 = VerificationCode(
|
||||
id="same",
|
||||
recipient="a@b.com",
|
||||
code="111",
|
||||
code_type="email_login",
|
||||
expires_at=now,
|
||||
created_at=now,
|
||||
)
|
||||
vc2 = VerificationCode(
|
||||
id="same",
|
||||
recipient="a@b.com",
|
||||
code="111",
|
||||
code_type="email_login",
|
||||
expires_at=now,
|
||||
created_at=now,
|
||||
)
|
||||
assert vc1 == vc2
|
||||
|
||||
def test_equality_different_id(self):
|
||||
now = datetime.now(timezone.utc)
|
||||
vc1 = VerificationCode(
|
||||
id="id1",
|
||||
recipient="a@b.com",
|
||||
code="111",
|
||||
code_type="email_login",
|
||||
expires_at=now,
|
||||
)
|
||||
vc2 = VerificationCode(
|
||||
id="id2",
|
||||
recipient="a@b.com",
|
||||
code="111",
|
||||
code_type="email_login",
|
||||
expires_at=now,
|
||||
)
|
||||
assert vc1 != vc2
|
||||
Executable
+368
@@ -0,0 +1,368 @@
|
||||
"""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_enum_values(self):
|
||||
assert VoiceGender.MALE.value == "male"
|
||||
assert VoiceGender.FEMALE.value == "female"
|
||||
assert VoiceGender.CHILD.value == "child"
|
||||
|
||||
def test_is_str(self):
|
||||
assert isinstance(VoiceGender.MALE, str)
|
||||
assert VoiceGender.FEMALE == "female"
|
||||
|
||||
def test_from_string(self):
|
||||
assert VoiceGender("male") == VoiceGender.MALE
|
||||
assert VoiceGender("child") == VoiceGender.CHILD
|
||||
|
||||
def test_invalid_raises(self):
|
||||
with pytest.raises(ValueError):
|
||||
VoiceGender("unknown")
|
||||
|
||||
|
||||
class TestVoiceStyle:
|
||||
"""VoiceStyle 音色风格枚举"""
|
||||
|
||||
def test_enum_values(self):
|
||||
assert VoiceStyle.STABLE.value == "stable"
|
||||
assert VoiceStyle.LIVELY.value == "lively"
|
||||
assert VoiceStyle.CUSTOMER_SERVICE.value == "customer_service"
|
||||
assert VoiceStyle.NARRATION.value == "narration"
|
||||
assert VoiceStyle.NEWS.value == "news"
|
||||
assert VoiceStyle.STORY.value == "story"
|
||||
|
||||
def test_is_str(self):
|
||||
assert isinstance(VoiceStyle.NARRATION, str)
|
||||
assert VoiceStyle.STORY == "story"
|
||||
|
||||
def test_from_string(self):
|
||||
assert VoiceStyle("news") == VoiceStyle.NEWS
|
||||
|
||||
def test_invalid_raises(self):
|
||||
with pytest.raises(ValueError):
|
||||
VoiceStyle("rock")
|
||||
|
||||
|
||||
# ── VoicePreset dataclass ─────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestVoicePreset:
|
||||
"""VoicePreset 音色预设 dataclass"""
|
||||
|
||||
def test_minimal_creation(self):
|
||||
v = VoicePreset(voice_id="test_voice", name="测试音色")
|
||||
assert v.voice_id == "test_voice"
|
||||
assert v.name == "测试音色"
|
||||
# 默认值
|
||||
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="male_deep",
|
||||
name="深沉男声",
|
||||
gender=VoiceGender.MALE,
|
||||
style=VoiceStyle.STABLE,
|
||||
description="非常深沉的男声",
|
||||
provider="aliyun",
|
||||
provider_voice_id="zhiyuan",
|
||||
default_speed=0.8,
|
||||
default_pitch=-1.0,
|
||||
sample_rate=16000,
|
||||
language="zh-CN",
|
||||
)
|
||||
assert v.voice_id == "male_deep"
|
||||
assert v.gender == VoiceGender.MALE
|
||||
assert v.style == VoiceStyle.STABLE
|
||||
assert v.provider == "aliyun"
|
||||
assert v.default_speed == 0.8
|
||||
assert v.sample_rate == 16000
|
||||
|
||||
def test_str_gender_creation(self):
|
||||
# 用字符串值创建也可以(因为是 StrEnum)
|
||||
v = VoicePreset(voice_id="v1", name="V1", gender="male")
|
||||
assert v.gender == VoiceGender.MALE
|
||||
|
||||
def test_str_style_creation(self):
|
||||
v = VoicePreset(voice_id="v1", name="V1", style="news")
|
||||
assert v.style == VoiceStyle.NEWS
|
||||
|
||||
def test_equality(self):
|
||||
v1 = VoicePreset(voice_id="same", name="同名")
|
||||
v2 = VoicePreset(voice_id="same", name="同名")
|
||||
assert v1 == v2
|
||||
|
||||
def test_inequality(self):
|
||||
v1 = VoicePreset(voice_id="a", name="A")
|
||||
v2 = VoicePreset(voice_id="b", name="B")
|
||||
assert v1 != v2
|
||||
|
||||
def test_slots_no_extra_attrs(self):
|
||||
v = VoicePreset(voice_id="test", name="Test")
|
||||
with pytest.raises(AttributeError):
|
||||
v.nonexistent_field = "value"
|
||||
|
||||
|
||||
# ── MOCK_VOICES 列表 ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestMockVoices:
|
||||
"""Mock 音色预设列表"""
|
||||
|
||||
def test_not_empty(self):
|
||||
assert len(MOCK_VOICES) > 0
|
||||
|
||||
def test_count(self):
|
||||
assert len(MOCK_VOICES) == 8
|
||||
|
||||
def test_all_are_voice_preset(self):
|
||||
for v in MOCK_VOICES:
|
||||
assert isinstance(v, VoicePreset)
|
||||
|
||||
def test_unique_voice_ids(self):
|
||||
ids = [v.voice_id for v in MOCK_VOICES]
|
||||
assert len(ids) == len(set(ids))
|
||||
|
||||
def test_female_warm_preset(self):
|
||||
v = next(v for v in MOCK_VOICES if v.voice_id == "female_warm")
|
||||
assert v.name == "温暖女声"
|
||||
assert v.gender == VoiceGender.FEMALE
|
||||
assert v.style == VoiceStyle.NARRATION
|
||||
assert v.default_speed == 1.0
|
||||
assert "温柔" in v.description
|
||||
|
||||
def test_male_stable_preset(self):
|
||||
v = next(v for v in MOCK_VOICES if v.voice_id == "male_stable")
|
||||
assert v.name == "沉稳男声"
|
||||
assert v.gender == VoiceGender.MALE
|
||||
assert v.style == VoiceStyle.STABLE
|
||||
assert v.default_speed == 0.9
|
||||
|
||||
def test_female_lively_preset(self):
|
||||
v = next(v for v in MOCK_VOICES if v.voice_id == "female_lively")
|
||||
assert v.gender == VoiceGender.FEMALE
|
||||
assert v.style == VoiceStyle.LIVELY
|
||||
assert v.default_speed == 1.2
|
||||
assert v.default_pitch == 2.0
|
||||
|
||||
def test_child_cute_preset(self):
|
||||
v = next(v for v in MOCK_VOICES if v.voice_id == "child_cute")
|
||||
assert v.gender == VoiceGender.CHILD
|
||||
assert v.style == VoiceStyle.STORY
|
||||
assert v.default_pitch == 4.0
|
||||
|
||||
def test_all_mock_provider(self):
|
||||
for v in MOCK_VOICES:
|
||||
assert v.provider == "mock"
|
||||
|
||||
def test_all_have_provider_voice_id(self):
|
||||
for v in MOCK_VOICES:
|
||||
assert v.provider_voice_id != ""
|
||||
|
||||
def test_all_chinese(self):
|
||||
for v in MOCK_VOICES:
|
||||
assert v.language == "zh-CN"
|
||||
|
||||
|
||||
# ── 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
|
||||
|
||||
def test_get_child_cute(self):
|
||||
v = get_voice("child_cute")
|
||||
assert v is not None
|
||||
assert v.gender == VoiceGender.CHILD
|
||||
|
||||
def test_get_nonexistent_returns_none(self):
|
||||
v = get_voice("nonexistent_voice")
|
||||
assert v is None
|
||||
|
||||
def test_get_empty_string_returns_none(self):
|
||||
v = get_voice("")
|
||||
assert v is None
|
||||
|
||||
def test_non_mock_provider_returns_none(self):
|
||||
v = get_voice("female_warm", provider="aliyun")
|
||||
assert v is None
|
||||
|
||||
def test_non_mock_provider_nonexistent(self):
|
||||
v = get_voice("whatever", provider="xunfei")
|
||||
assert v is None
|
||||
|
||||
def test_mock_provider_explicit(self):
|
||||
v = get_voice("female_warm", provider="mock")
|
||||
assert v is not None
|
||||
assert v.voice_id == "female_warm"
|
||||
|
||||
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_filters_returns_all(self):
|
||||
result = list_voices()
|
||||
assert len(result) == len(MOCK_VOICES)
|
||||
assert len(result) == 8
|
||||
|
||||
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_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_child(self):
|
||||
result = list_voices(gender="child")
|
||||
assert len(result) == 1
|
||||
assert result[0].voice_id == "child_cute"
|
||||
|
||||
def test_filter_by_gender_invalid_returns_empty(self):
|
||||
result = list_voices(gender="alien")
|
||||
assert len(result) == 0
|
||||
|
||||
def test_filter_by_style_stable(self):
|
||||
result = list_voices(style="stable")
|
||||
assert len(result) > 0
|
||||
for v in result:
|
||||
assert v.style == VoiceStyle.STABLE
|
||||
|
||||
def test_filter_by_style_lively(self):
|
||||
result = list_voices(style="lively")
|
||||
assert len(result) == 1
|
||||
assert result[0].voice_id == "female_lively"
|
||||
|
||||
def test_filter_by_style_story(self):
|
||||
result = list_voices(style="story")
|
||||
assert len(result) >= 2
|
||||
for v in result:
|
||||
assert v.style == VoiceStyle.STORY
|
||||
|
||||
def test_filter_by_style_invalid_returns_empty(self):
|
||||
result = list_voices(style="punk")
|
||||
assert len(result) == 0
|
||||
|
||||
def test_filter_by_provider_mock(self):
|
||||
result = list_voices(provider="mock")
|
||||
assert len(result) == len(MOCK_VOICES)
|
||||
|
||||
def test_filter_by_provider_other_returns_empty(self):
|
||||
result = list_voices(provider="aliyun")
|
||||
assert len(result) == 0
|
||||
|
||||
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 or "女声" in v.voice_id
|
||||
|
||||
def test_filter_by_keyword_description(self):
|
||||
result = list_voices(keyword="商务")
|
||||
assert len(result) > 0
|
||||
# 沉稳男声描述里有"商务"
|
||||
|
||||
def test_filter_by_keyword_voice_id(self):
|
||||
result = list_voices(keyword="male_stable")
|
||||
assert len(result) == 1
|
||||
assert result[0].voice_id == "male_stable"
|
||||
|
||||
def test_filter_by_keyword_case_insensitive(self):
|
||||
result1 = list_voices(keyword="Female")
|
||||
result2 = list_voices(keyword="female")
|
||||
assert len(result1) == len(result2)
|
||||
|
||||
def test_filter_by_keyword_nonexistent(self):
|
||||
result = list_voices(keyword="不存在的关键词999")
|
||||
assert len(result) == 0
|
||||
|
||||
def test_combined_gender_and_style(self):
|
||||
result = list_voices(gender="female", style="lively")
|
||||
assert len(result) == 1
|
||||
assert result[0].voice_id == "female_lively"
|
||||
|
||||
def test_combined_gender_style_keyword(self):
|
||||
result = list_voices(gender="male", style="story", keyword="磁性")
|
||||
assert len(result) == 1
|
||||
assert result[0].voice_id == "male_magnetic"
|
||||
|
||||
def test_combined_no_match(self):
|
||||
result = list_voices(gender="child", style="news")
|
||||
assert len(result) == 0
|
||||
|
||||
def test_returns_new_list(self):
|
||||
# 修改返回值不应影响原始列表
|
||||
result = list_voices()
|
||||
result.clear()
|
||||
assert len(MOCK_VOICES) == 8
|
||||
|
||||
|
||||
# ── get_default_voice ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestGetDefaultVoice:
|
||||
"""get_default_voice 函数"""
|
||||
|
||||
def test_returns_voice_preset(self):
|
||||
v = get_default_voice()
|
||||
assert isinstance(v, VoicePreset)
|
||||
|
||||
def test_returns_first_mock_voice(self):
|
||||
v = get_default_voice()
|
||||
assert v == MOCK_VOICES[0]
|
||||
|
||||
def test_default_is_female_warm(self):
|
||||
v = get_default_voice()
|
||||
assert v.voice_id == "female_warm"
|
||||
assert v.gender == VoiceGender.FEMALE
|
||||
|
||||
def test_multiple_calls_same(self):
|
||||
v1 = get_default_voice()
|
||||
v2 = get_default_voice()
|
||||
assert v1 is v2
|
||||
Reference in New Issue
Block a user