2f26541188
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m16s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m36s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m26s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m17s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 3m46s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m9s
CI/CD Pipeline / Integration Tests (push) Successful in 1m52s
CI/CD Pipeline / Unit Tests (push) Successful in 8m39s
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 / CI Gate (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 API Image (push) Successful in 12m15s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 31s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 40s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 45s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m1s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
188 lines
6.2 KiB
Python
Executable File
188 lines
6.2 KiB
Python
Executable File
"""预置音色配置单元测试 — 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_v3",
|
|
name="龙小淳",
|
|
description="温柔女声",
|
|
gender="female",
|
|
tags=["温柔", "女声"],
|
|
)
|
|
|
|
result = voice.to_dict()
|
|
|
|
assert result["voice_id"] == "longxiaochun_v3"
|
|
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_v3",
|
|
"longxiaoxia_v3",
|
|
"longsanshu_v3",
|
|
"longyue_v3",
|
|
"longshu_v3",
|
|
"longyingjing_v3",
|
|
"longshuo_v3",
|
|
"longtian_v3",
|
|
}
|
|
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_v3")
|
|
assert voice is not None
|
|
assert voice.name == "龙小淳"
|
|
assert voice.voice_id == "longxiaochun_v3"
|
|
|
|
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_v3") is True
|
|
assert is_preset_voice("longxiaoxia_v3") is True
|
|
assert is_preset_voice("longshuo_v3") 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
|