Files
xiaoxia-saas/tests/unit/test_cosyvoice_tts_adapter.py
xiaoxia ef8b747c7b
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 35s
CI/CD Pipeline / Frontend Lint (push) Successful in 1m11s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 3m26s
CI/CD Pipeline / Unit Tests (push) Successful in 4m25s
CI/CD Pipeline / Integration Tests (push) Successful in 1m32s
CI Build & Deploy Pipeline / Build Staging API Image (push) Waiting to run
CI Build & Deploy Pipeline / Build Staging Web Image (push) Waiting to run
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Waiting to run
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI Build & Deploy Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI Build & Deploy Pipeline / Build Production API Image (push) Waiting to run
CI Build & Deploy Pipeline / Build Production Web Image (push) Waiting to run
CI Build & Deploy Pipeline / Build Production Worker Image (push) Waiting to run
CI Build & Deploy Pipeline / Deploy Production (push) Blocked by required conditions
CI Build & Deploy Pipeline / Production Browser E2E (push) Blocked by required conditions
fix(P1): 修复预设配音没有真实声音 - 接入CosyVoice TTS适配器 + legacy引擎配音补齐 (#559)
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-19 07:28:56 +08:00

175 lines
6.1 KiB
Python
Executable File

"""CosyVoice TTS 适配器单元测试."""
from __future__ import annotations
from unittest.mock import MagicMock, patch
import pytest
from packages.domain.tts_config import TtsConfig
class TestTtsConfig:
"""TTS 配置解析测试."""
def test_parse_full_config(self):
"""完整配置解析."""
cfg = TtsConfig.parse(
{
"enabled": True,
"voice_id": "longxiaochun_v3",
"text": "你好世界",
"speed": 1.2,
"pitch": 2.0,
"volume": 0.7,
"align_mode": "full",
"overlap_mode": "mix",
}
)
assert cfg.enabled is True
assert cfg.voice_id == "longxiaochun_v3"
assert cfg.text == "你好世界"
assert cfg.speed == 1.2
assert cfg.pitch == 2.0
assert cfg.volume == 0.7
assert cfg.align_mode == "full"
assert cfg.overlap_mode == "mix"
def test_parse_disabled(self):
"""禁用状态."""
cfg = TtsConfig.parse({"enabled": False})
assert cfg.enabled is False
def test_parse_none(self):
"""空配置."""
cfg = TtsConfig.parse(None)
assert cfg.enabled is False
def test_parse_empty_text_still_enabled(self):
"""有 enabled 但无 text,配置仍然有效(调用方判断是否有文本)."""
cfg = TtsConfig.parse({"enabled": True, "voice_id": "test"})
assert cfg.enabled is True
assert cfg.text == ""
def test_speed_clamp(self):
"""语速边界钳制."""
cfg = TtsConfig.parse({"enabled": True, "speed": 3.0})
assert cfg.speed == 2.0
cfg2 = TtsConfig.parse({"enabled": True, "speed": 0.1})
assert cfg2.speed == 0.5
def test_volume_clamp(self):
"""音量边界钳制."""
cfg = TtsConfig.parse({"enabled": True, "volume": 2.0})
assert cfg.volume == 1.0
def test_invalid_align_mode(self):
"""无效对齐模式回退到默认."""
cfg = TtsConfig.parse({"enabled": True, "align_mode": "invalid"})
assert cfg.align_mode == "full"
class TestCosyVoiceTtsAdapter:
"""CosyVoice TTS 适配器测试."""
def test_import_ok(self):
"""适配器能正常导入."""
from packages.adapters.tts.cosyvoice_tts_service import CosyVoiceTtsService
assert CosyVoiceTtsService is not None
def test_provider_name(self):
"""provider_name 属性."""
from packages.adapters.tts.cosyvoice_tts_service import CosyVoiceTtsService
# 用 mock 替换底层 CosyVoiceService
with patch("packages.application.cosyvoice_service.CosyVoiceService"):
svc = CosyVoiceTtsService()
assert svc.provider_name == "cosyvoice"
def test_available_voices(self):
"""可用音色列表来自预设音色."""
from packages.adapters.tts.cosyvoice_tts_service import CosyVoiceTtsService
with patch("packages.application.cosyvoice_service.CosyVoiceService"):
svc = CosyVoiceTtsService()
voices = svc.available_voices()
assert len(voices) > 0
assert "longxiaochun_v3" in voices
assert "longxiaoxia_v3" in voices
def test_estimate_duration(self):
"""时长估算."""
from packages.adapters.tts.cosyvoice_tts_service import CosyVoiceTtsService
with patch("packages.application.cosyvoice_service.CosyVoiceService"):
svc = CosyVoiceTtsService()
d = svc.estimate_duration("你好世界", speed=1.0)
assert d > 0
# 4 个字,4 字/秒 = 1 秒
assert abs(d - 1.0) < 0.1
def test_estimate_duration_speed(self):
"""语速影响时长估算."""
from packages.adapters.tts.cosyvoice_tts_service import CosyVoiceTtsService
with patch("packages.application.cosyvoice_service.CosyVoiceService"):
svc = CosyVoiceTtsService()
d_normal = svc.estimate_duration("你好世界", speed=1.0)
d_fast = svc.estimate_duration("你好世界", speed=2.0)
assert d_fast < d_normal
assert abs(d_fast - d_normal / 2.0) < 0.01
def test_synthesize_empty_text_raises(self):
"""空文本抛出异常."""
from packages.adapters.tts.cosyvoice_tts_service import CosyVoiceTtsService
from packages.ports.tts_service import TtsError
with patch("packages.application.cosyvoice_service.CosyVoiceService"):
svc = CosyVoiceTtsService()
with pytest.raises(TtsError, match="文本不能为空"):
svc.synthesize(" ")
class TestTtsServiceFactory:
"""TTS 服务工厂测试."""
def test_mock_provider(self):
"""mock provider 正常."""
from apps.worker.services.tts_service_factory import get_tts_service
svc = get_tts_service("mock")
assert svc.provider_name == "mock"
def test_cosyvoice_provider(self):
"""cosyvoice provider 注册正常."""
from apps.worker.services.tts_service_factory import get_tts_service
with patch("packages.application.cosyvoice_service.CosyVoiceService"):
svc = get_tts_service("cosyvoice")
assert svc.provider_name == "cosyvoice"
def test_aliyun_alias(self):
"""aliyun 别名映射到 cosyvoice."""
from apps.worker.services.tts_service_factory import get_tts_service
with patch("packages.application.cosyvoice_service.CosyVoiceService"):
svc = get_tts_service("aliyun")
assert svc.provider_name == "cosyvoice"
def test_dashscope_alias(self):
"""dashscope 别名映射到 cosyvoice."""
from apps.worker.services.tts_service_factory import get_tts_service
with patch("packages.application.cosyvoice_service.CosyVoiceService"):
svc = get_tts_service("dashscope")
assert svc.provider_name == "cosyvoice"
def test_unknown_fallback_to_mock(self):
"""未知 provider 回退到 mock."""
from apps.worker.services.tts_service_factory import get_tts_service
svc = get_tts_service("unknown_provider")
assert svc.provider_name == "mock"