b16fbb636d
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 14s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 53s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 47s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 32s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 29s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m21s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m45s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 14s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m38s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m41s
AI Code Review / AI Code Review (pull_request) Successful in 5m17s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m6s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 48m5s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 1h10m2s
132 lines
4.2 KiB
Python
132 lines
4.2 KiB
Python
"""
|
||
TTS 配音引擎数据类与纯逻辑测试.
|
||
|
||
覆盖 VoiceoverSegment / VoiceoverResult / TtsEngine 入口判断等纯逻辑.
|
||
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
|
||
|
||
|
||
class TestVoiceoverSegment:
|
||
"""配音片段数据类."""
|
||
|
||
def test_default_values(self):
|
||
seg = VoiceoverSegment(text="hello")
|
||
assert seg.text == "hello"
|
||
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="hello world",
|
||
start_time=1.5,
|
||
end_time=3.0,
|
||
audio_path=Path("/tmp/test.wav"),
|
||
duration=1.5,
|
||
)
|
||
assert seg.text == "hello world"
|
||
assert seg.start_time == 1.5
|
||
assert seg.end_time == 3.0
|
||
assert seg.audio_path == Path("/tmp/test.wav")
|
||
assert seg.duration == 1.5
|
||
|
||
def test_duration_calculation(self):
|
||
seg = VoiceoverSegment(text="test", start_time=0.0, end_time=5.5)
|
||
assert seg.end_time - seg.start_time == 5.5
|
||
|
||
|
||
class TestVoiceoverResult:
|
||
"""配音结果数据类."""
|
||
|
||
def test_default_failure(self):
|
||
result = VoiceoverResult()
|
||
assert result.success is False
|
||
assert result.segments == []
|
||
assert result.total_duration == 0.0
|
||
assert result.error_message == ""
|
||
|
||
def test_success_result(self):
|
||
segs = [
|
||
VoiceoverSegment(text="hello", duration=1.0),
|
||
VoiceoverSegment(text="world", duration=2.0),
|
||
]
|
||
result = VoiceoverResult(
|
||
success=True,
|
||
segments=segs,
|
||
total_duration=3.0,
|
||
)
|
||
assert result.success is True
|
||
assert len(result.segments) == 2
|
||
assert result.total_duration == 3.0
|
||
assert result.error_message == ""
|
||
|
||
def test_failure_with_message(self):
|
||
result = VoiceoverResult(success=False, error_message="TTS服务不可用")
|
||
assert result.success is False
|
||
assert result.error_message == "TTS服务不可用"
|
||
|
||
def test_segments_isolated_list(self):
|
||
"""确保每个实例有独立的segments列表."""
|
||
r1 = VoiceoverResult()
|
||
r2 = VoiceoverResult()
|
||
r1.segments.append(VoiceoverSegment(text="test"))
|
||
assert len(r2.segments) == 0
|
||
|
||
|
||
class TestTtsEngineInit:
|
||
"""TTS 引擎初始化."""
|
||
|
||
def test_init_creates_work_dir(self, tmp_path):
|
||
mock_tts = MagicMock()
|
||
work_dir = tmp_path / "tts_work"
|
||
engine = TtsEngine(mock_tts, work_dir)
|
||
assert work_dir.exists()
|
||
assert work_dir.is_dir()
|
||
|
||
def test_init_with_existing_dir(self, tmp_path):
|
||
mock_tts = MagicMock()
|
||
work_dir = tmp_path / "existing"
|
||
work_dir.mkdir()
|
||
engine = TtsEngine(mock_tts, work_dir)
|
||
assert work_dir.exists()
|
||
|
||
|
||
class TestTtsEngineEntryConditions:
|
||
"""TTS 引擎入口判断逻辑(不调用真实 TTS)."""
|
||
|
||
def test_disabled_returns_failure(self, tmp_path):
|
||
mock_tts = MagicMock()
|
||
engine = TtsEngine(mock_tts, tmp_path)
|
||
config = TtsConfig(enabled=False, text="hello")
|
||
result = engine.generate_full_voiceover(config)
|
||
assert result.success is False
|
||
assert "未启用" in result.error_message or "空" in result.error_message
|
||
mock_tts.synthesize.assert_not_called()
|
||
|
||
def test_empty_text_returns_failure(self, tmp_path):
|
||
mock_tts = MagicMock()
|
||
engine = TtsEngine(mock_tts, tmp_path)
|
||
config = TtsConfig(enabled=True, text="")
|
||
result = engine.generate_full_voiceover(config)
|
||
assert result.success is False
|
||
mock_tts.synthesize.assert_not_called()
|
||
|
||
def test_whitespace_text_returns_failure(self, tmp_path):
|
||
mock_tts = MagicMock()
|
||
engine = TtsEngine(mock_tts, tmp_path)
|
||
config = TtsConfig(enabled=True, text=" ")
|
||
result = engine.generate_full_voiceover(config)
|
||
assert result.success is False
|
||
mock_tts.synthesize.assert_not_called()
|