Files
xiaoxia-saas/tests/unit/test_tts_engine_pure.py
T
xiaoxia dc62884c5f
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
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 / Frontend Lint (push) Successful in 57s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m30s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m31s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m38s
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 / Validate - Code Quality (push) Failing after 2m59s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m46s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m49s
CI/CD Pipeline / Unit Tests (push) Failing after 4m29s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 2m4s
CI/CD Pipeline / Build Staging API Image (push) Successful in 13m7s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m16s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 15s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m13s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 5m38s
test(unit): 第70波 - 去重算法 + 字幕渲染 + TTS引擎纯逻辑 (+73) (#868)
2026-07-25 14:18:22 +08:00

185 lines
6.6 KiB
Python
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.
"""TTS配音引擎纯逻辑测试 — 数据结构 + 边界情况(mock TTS服务)."""
from __future__ import annotations
from pathlib import Path
from unittest.mock import MagicMock
import pytest
from video_processing.tts_engine import (
TtsEngine,
VoiceoverResult,
VoiceoverSegment,
)
from packages.domain.tts_config import TtsConfig
from packages.ports.tts_service import TtsError
class TestVoiceoverSegment:
"""VoiceoverSegment 数据结构测试."""
def test_default_values(self):
"""默认值正确."""
seg = VoiceoverSegment(text="你好")
assert seg.text == "你好"
assert seg.start_time == 0.0
assert seg.end_time == 0.0
assert seg.audio_path is None
assert seg.duration == 0.0
def test_full_values(self):
"""完整字段."""
seg = VoiceoverSegment(
text="测试",
start_time=1.5,
end_time=3.5,
audio_path=Path("/tmp/test.wav"),
duration=2.0,
)
assert seg.text == "测试"
assert seg.start_time == 1.5
assert seg.end_time == 3.5
assert seg.audio_path == Path("/tmp/test.wav")
assert seg.duration == 2.0
class TestVoiceoverResult:
"""VoiceoverResult 数据结构测试."""
def test_failure_default(self):
"""失败结果默认值."""
result = VoiceoverResult(success=False)
assert result.success is False
assert result.segments == []
assert result.total_duration == 0.0
assert result.error_message == ""
def test_success_with_segments(self):
"""成功结果带片段."""
seg = VoiceoverSegment(text="hi", duration=5.0)
result = VoiceoverResult(
success=True,
segments=[seg],
total_duration=5.0,
)
assert result.success is True
assert len(result.segments) == 1
assert result.total_duration == 5.0
def test_failure_with_message(self):
"""失败带错误信息."""
result = VoiceoverResult(success=False, error_message="TTS出错")
assert result.success is False
assert result.error_message == "TTS出错"
class TestTtsEngineFullVoiceover:
"""TtsEngine.generate_full_voiceover 整段配音测试(mock TTS."""
def _make_engine(self, tmp_path: Path, tts: MagicMock | None = None) -> TtsEngine:
"""创建测试用TtsEngine."""
if tts is None:
tts = MagicMock()
tts.synthesize.return_value = str(tmp_path / "out.wav")
return TtsEngine(tts_service=tts, work_dir=tmp_path)
def test_disabled_returns_failure(self, tmp_path):
"""配音未启用→失败."""
engine = self._make_engine(tmp_path)
config = TtsConfig(enabled=False, text="测试")
result = engine.generate_full_voiceover(config)
assert result.success is False
assert "未启用" in result.error_message
assert len(result.segments) == 0
def test_empty_text_returns_failure(self, tmp_path):
"""文本为空→失败."""
engine = self._make_engine(tmp_path)
config = TtsConfig(enabled=True, text=" ")
result = engine.generate_full_voiceover(config)
assert result.success is False
assert "文本为空" in result.error_message
def test_success_creates_segment(self, tmp_path):
"""成功合成返回正确结构."""
mock_tts = MagicMock()
output_file = tmp_path / "voiceover_full.wav"
output_file.write_bytes(b"fake audio")
mock_tts.synthesize.return_value = str(output_file)
engine = self._make_engine(tmp_path, mock_tts)
config = TtsConfig(enabled=True, text="测试文本", voice_id="female_warm", speed=1.0)
result = engine.generate_full_voiceover(config)
assert result.success is True
assert len(result.segments) == 1
assert result.segments[0].text == "测试文本"
assert result.segments[0].start_time == 0.0
assert result.total_duration > 0
mock_tts.synthesize.assert_called_once()
def test_tts_error_returns_failure_gracefully(self, tmp_path):
"""TTS抛错→优雅降级返回失败."""
mock_tts = MagicMock()
mock_tts.synthesize.side_effect = TtsError("合成失败")
engine = self._make_engine(tmp_path, mock_tts)
config = TtsConfig(enabled=True, text="测试")
result = engine.generate_full_voiceover(config)
assert result.success is False
assert "合成失败" in result.error_message
def test_generic_exception_returns_failure(self, tmp_path):
"""其他异常也降级返回失败."""
mock_tts = MagicMock()
mock_tts.synthesize.side_effect = RuntimeError("未知错误")
engine = self._make_engine(tmp_path, mock_tts)
config = TtsConfig(enabled=True, text="测试")
result = engine.generate_full_voiceover(config)
assert result.success is False
assert "未知错误" in result.error_message
def test_work_dir_created(self, tmp_path):
"""工作目录自动创建."""
new_dir = tmp_path / "nested" / "tts"
mock_tts = MagicMock()
TtsEngine(tts_service=mock_tts, work_dir=new_dir)
assert new_dir.exists()
class TestTtsEngineSubtitleVoiceover:
"""TtsEngine.generate_subtitle_voiceover 字幕配音测试(mock TTS."""
def _make_engine(self, tmp_path: Path, tts: MagicMock | None = None) -> TtsEngine:
if tts is None:
tts = MagicMock()
return TtsEngine(tts_service=tts, work_dir=tmp_path)
def test_disabled_returns_failure(self, tmp_path):
"""配音未启用→失败."""
engine = self._make_engine(tmp_path)
config = TtsConfig(enabled=False, text="")
result = engine.generate_subtitle_voiceover(config, [{"text": "hi", "start_time": 0, "end_time": 1}])
assert result.success is False
assert "未启用" in result.error_message
def test_empty_subtitles_returns_failure(self, tmp_path):
"""字幕列表为空→失败."""
engine = self._make_engine(tmp_path)
config = TtsConfig(enabled=True, text="")
result = engine.generate_subtitle_voiceover(config, [])
assert result.success is False
assert "字幕为空" in result.error_message
def test_none_subtitles_returns_failure(self, tmp_path):
"""None字幕也失败."""
engine = self._make_engine(tmp_path)
config = TtsConfig(enabled=True, text="")
result = engine.generate_subtitle_voiceover(config, None) # type: ignore
assert result.success is False