Files
xiaoxia-saas/tests/unit/test_tts_service_factory.py
CI Bot 464f6ea155
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 41s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m2s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m5s
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 / Build Staging Web Image (push) Successful in 1m25s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m7s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m16s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m4s
CI/CD Pipeline / Build Staging API Image (push) Successful in 14m15s
CI/CD Pipeline / Unit Tests (push) Failing after 4m43s
CI/CD Pipeline / Integration Tests (push) Successful in 2m37s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 2m13s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 27s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m29s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m31s
style: auto-format with black + isort + prettier
2026-07-24 16:00:04 +00:00

139 lines
4.6 KiB
Python
Executable File

"""TTS Service Factory 单测 — TTS服务工厂."""
from __future__ import annotations
import os
from unittest.mock import patch
import pytest
# 注意:工厂模块有全局状态(_PROVIDERS),每个测试前重置
from services.tts_service_factory import (
_PROVIDERS,
available_providers,
get_tts_service,
register_provider,
)
# ── Fixtures ────────────────────────────────────────────────────────────────
@pytest.fixture(autouse=True)
def reset_providers():
"""每个测试前后重置 provider 注册表."""
# 保存原始状态
original = dict(_PROVIDERS)
yield
# 恢复
_PROVIDERS.clear()
_PROVIDERS.update(original)
# ── register_provider ──────────────────────────────────────────────────────
class TestRegisterProvider:
"""register_provider 注册供应商."""
def test_register_new_provider(self):
class DummyService:
pass
register_provider("dummy", DummyService)
assert "dummy" in _PROVIDERS
assert _PROVIDERS["dummy"] is DummyService
def test_register_overwrites_existing(self):
class ServiceV1:
pass
class ServiceV2:
pass
register_provider("test", ServiceV1)
register_provider("test", ServiceV2)
assert _PROVIDERS["test"] is ServiceV2
# ── get_tts_service ───────────────────────────────────────────────────────
class TestGetTtsService:
"""get_tts_service 获取TTS服务."""
def test_get_mock_provider(self):
"""mock 供应商可用."""
service = get_tts_service("mock")
assert service is not None
assert service.provider_name == "mock"
def test_get_cosyvoice_provider(self):
"""cosyvoice 别名映射到 CosyVoiceTtsService."""
# 不传 api_key 也能实例化(默认空字符串)
service = get_tts_service("cosyvoice")
assert service is not None
assert service.provider_name == "cosyvoice"
def test_aliyun_alias_maps_to_cosyvoice(self):
"""aliyun 是 cosyvoice 的别名."""
service = get_tts_service("aliyun")
assert service.provider_name == "cosyvoice"
def test_dashscope_alias_maps_to_cosyvoice(self):
"""dashscope 是 cosyvoice 的别名."""
service = get_tts_service("dashscope")
assert service.provider_name == "cosyvoice"
def test_unknown_provider_falls_back_to_mock(self):
"""未知供应商回退到 mock."""
service = get_tts_service("unknown_provider_xyz")
assert service.provider_name == "mock"
def test_provider_name_case_insensitive(self):
"""供应商名称不区分大小写."""
service = get_tts_service("MOCK")
assert service.provider_name == "mock"
def test_passes_kwargs_to_constructor(self):
"""kwargs 传递给服务构造函数."""
# MockTtsService 接受 ffmpeg_bin 参数
service = get_tts_service("mock", ffmpeg_bin="/custom/ffmpeg")
assert service is not None
def test_none_provider_reads_env_var(self):
"""provider=None 时从 TTS_PROVIDER 环境变量读取."""
with patch.dict(os.environ, {"TTS_PROVIDER": "mock"}):
service = get_tts_service(None)
assert service.provider_name == "mock"
def test_empty_env_falls_back_to_auto_detect(self):
"""环境变量为空时自动检测."""
with patch.dict(os.environ, {"TTS_PROVIDER": ""}):
# 没有 cosyvoice_api_key 时应该用 mock
service = get_tts_service(None)
assert service.provider_name == "mock"
# ── available_providers ────────────────────────────────────────────────────
class TestAvailableProviders:
"""available_providers 可用供应商列表."""
def test_returns_list(self):
result = available_providers()
assert isinstance(result, list)
assert len(result) >= 1 # 至少有 mock
def test_mock_is_always_available(self):
result = available_providers()
assert "mock" in result
def test_after_register_appears_in_list(self):
class Dummy:
pass
register_provider("dummy_test", Dummy)
result = available_providers()
assert "dummy_test" in result