Files
xiaoxia-saas/tests/unit/test_mock_tts_service.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

144 lines
5.4 KiB
Python
Executable File
Raw Permalink 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