Files
xiaoxia-saas/tests/unit/test_mock_tts_service.py
T
CI Bot 464f6ea155
CI/CD Pipeline / Frontend Lint (push) Successful in 41s
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 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 / 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 / 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
CI/CD Pipeline / Production Browser E2E (push) Failing after 1286h40m0s
CI/CD Pipeline / Deploy Production (push) Failing after 1286h55m17s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 1287h5m54s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 1287h6m9s
CI/CD Pipeline / Build Production Web Image (push) Failing after 1287h5m56s
CI/CD Pipeline / PR Build Web Image (push) Failing after 1287h6m11s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 1287h7m3s
CI/CD Pipeline / Build Production API Image (push) Failing after 1287h38m28s
CI/CD Pipeline / PR Build API Image (push) Failing after 1287h38m43s
style: auto-format with black + isort + prettier
2026-07-24 16:00:04 +00:00

144 lines
5.4 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""MockTtsService 单测 — Mock TTS服务纯逻辑部分."""
from __future__ import annotations
import pytest
from packages.adapters.tts.mock_tts_service import _CHARS_PER_SECOND, MockTtsService
# ── Fixtures ────────────────────────────────────────────────────────────────
@pytest.fixture
def service():
return MockTtsService()
# ── estimate_duration ──────────────────────────────────────────────────────
class TestEstimateDuration:
"""estimate_duration 时长估算."""
def test_empty_text_returns_zero(self, service):
assert service.estimate_duration("") == 0.0
def test_whitespace_only_returns_zero(self, service):
assert service.estimate_duration(" \n\t ") == 0.0
def test_single_char(self, service):
result = service.estimate_duration("你")
assert abs(result - 1.0 / _CHARS_PER_SECOND) < 0.001
def test_default_speed(self, service):
"""默认 speed=1.0."""
text = "你好世界" # 4个字
result = service.estimate_duration(text)
expected = 4.0 / _CHARS_PER_SECOND
assert abs(result - expected) < 0.001
def test_faster_speed_shortens_duration(self, service):
"""语速越快,时长越短."""
text = "你好世界"
normal = service.estimate_duration(text, speed=1.0)
fast = service.estimate_duration(text, speed=2.0)
assert fast < normal
assert abs(fast - normal / 2) < 0.001
def test_slower_speed_lengthens_duration(self, service):
"""语速越慢,时长越长."""
text = "你好世界"
normal = service.estimate_duration(text, speed=1.0)
slow = service.estimate_duration(text, speed=0.5)
assert slow > normal
assert abs(slow - normal / 0.5) < 0.001
def test_speed_clamped_at_minimum(self, service):
"""speed < 0.1 时被钳制到 0.1,避免除零."""
text = "你好"
# 传一个极小的值,不应该崩溃,且时长不会无限大
result = service.estimate_duration(text, speed=0.001)
assert result > 0
# 应该等同于 speed=0.1
expected = 2.0 / _CHARS_PER_SECOND / 0.1
assert abs(result - expected) < 0.001
def test_chinese_and_english_mixed(self, service):
"""中英文混合时按非空白字符计数."""
text = "Hello 世界" # H-e-l-l-o + 世-界 = 7个非空白字符
result = service.estimate_duration(text)
expected = 7.0 / _CHARS_PER_SECOND
assert abs(result - expected) < 0.001
def test_negative_speed(self, service):
"""负语速按最小处理(取 max(0.1, speed))."""
text = "你好"
result = service.estimate_duration(text, speed=-2.0)
assert result > 0
# 等同于 speed=0.1
expected = 2.0 / _CHARS_PER_SECOND / 0.1
assert abs(result - expected) < 0.001
# ── _extract_freq ──────────────────────────────────────────────────────────
class TestExtractFreq:
"""_extract_freq 基频提取."""
def test_sine_prefix_returns_freq(self, service):
"""sine_ 前缀的voice_id,从下划线后提取频率."""
result = service._extract_freq("sine_440", "female")
assert result == 440.0
def test_sine_with_decimal(self, service):
"""支持小数频率."""
result = service._extract_freq("sine_261.63", "female")
assert abs(result - 261.63) < 0.001
def test_sine_invalid_number_falls_back(self, service):
"""sine_ 后面不是数字时,fallback 到性别默认值."""
result = service._extract_freq("sine_abc", "female")
assert result == 220.0 # female 默认
def test_sine_no_number_falls_back(self, service):
"""sine_ 后面没有内容时,fallback."""
result = service._extract_freq("sine_", "male")
assert result == 120.0 # male 默认
def test_male_default(self, service):
result = service._extract_freq("some_voice", "male")
assert result == 120.0
def test_female_default(self, service):
result = service._extract_freq("some_voice", "female")
assert result == 220.0
def test_child_default(self, service):
result = service._extract_freq("some_voice", "child")
assert result == 350.0
def test_unknown_gender_defaults_to_female(self, service):
"""未知性别 fallback 到 female."""
result = service._extract_freq("some_voice", "alien")
assert result == 220.0
def test_empty_gender_defaults_to_female(self, service):
result = service._extract_freq("some_voice", "")
assert result == 220.0
# ── provider_name / available_voices ───────────────────────────────────────
class TestProviderInfo:
"""provider_name 和 available_voices."""
def test_provider_name(self, service):
assert service.provider_name == "mock"
def test_available_voices_returns_list(self, service):
voices = service.available_voices()
assert isinstance(voices, list)
assert len(voices) > 0