"""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