9554373f24
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Staging Web Image (push) Successful in 37s
CI/CD Pipeline / Build Staging API Image (push) Successful in 1m27s
CI/CD Pipeline / Frontend Lint (push) Successful in 1m31s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m54s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 2m32s
CI/CD Pipeline / Unit Tests (push) Successful in 2m37s
CI/CD Pipeline / Integration Tests (push) Successful in 1m3s
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
新增7个领域模块单元测试:classification、edit_plan_clip、filter_presets、generated_video、preset_bgm、template_clip_config、transition_presets,共231个用例
232 lines
7.1 KiB
Python
Executable File
232 lines
7.1 KiB
Python
Executable File
"""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)
|