test: P3-1 第47波单元测试(tts_workflow补充,+17)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 31s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 39s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m24s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m34s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 16s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 27s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m0s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 29s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m3s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m1s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m18s
AI Code Review / AI Code Review (pull_request) Successful in 4m38s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m30s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 4m47s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 42s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1296h25m57s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1296h25m59s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1296h26m1s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1296h27m0s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1296h27m2s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1296h27m6s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1296h27m8s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1296h28m19s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1296h28m20s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1296h28m21s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1296h58m29s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1296h58m27s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1296h59m32s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 31s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 39s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m24s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m34s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 16s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 27s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m0s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 29s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m3s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m1s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m18s
AI Code Review / AI Code Review (pull_request) Successful in 4m38s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m30s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 4m47s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 42s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1296h25m57s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1296h25m59s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1296h26m1s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1296h27m0s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1296h27m2s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1296h27m6s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1296h27m8s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1296h28m19s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1296h28m20s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1296h28m21s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1296h58m29s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1296h58m27s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1296h59m32s
补充 TTSWorkflowService 边界和细节测试: TestTransferAudioToOSS (4个): - mp3/wav/未知格式 content-type 正确映射 - storage_key 路径格式正确 TestResynthesizeParams (5个): - speed/volume 从metadata读取 - 无metadata时默认值(1.0/50) - 重新合成无audio_url时标记失败 TestPollSegmentTasks (3个): - 所有分段缺audio_url时全部重合成 - 已有audio_url的分段不重合成 - 部分缺audio_url的分段只重合成缺失段 TestSegmentSyncDetails (3个): - 分段数量写入metadata - 分段时长累加 - 某段无audio_url抛出TTSWorkflowError TestUploadMergedToOSS (2个): - 上传成功返回URL和storage_key - 上传失败返回空字符串 合计 +17 个测试
This commit is contained in:
@@ -547,3 +547,428 @@ class TestErrorClasses:
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=MagicMock(), cosyvoice_service=MagicMock(), storage_service=storage)
|
||||
assert svc._storage is storage
|
||||
|
||||
|
||||
# ── Additional edge case tests ──────────────────────────
|
||||
|
||||
|
||||
class TestTransferAudioToOSS:
|
||||
"""_transfer_audio_to_oss 细节测试."""
|
||||
|
||||
def test_mp3_content_type(self):
|
||||
"""MP3格式使用audio/mpeg content-type."""
|
||||
job = make_job(format="mp3")
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService(
|
||||
submit_result={
|
||||
"audio_url": "https://temp.example.com/a.mp3",
|
||||
"request_id": "r",
|
||||
"task_id": "",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with patch("packages.application.tts_job.workflow.safe_download_bytes", return_value=b"audio"):
|
||||
svc.start_synthesis("job-123")
|
||||
|
||||
assert storage.uploads[0]["content_type"] == "audio/mpeg"
|
||||
|
||||
def test_wav_content_type(self):
|
||||
"""WAV格式使用audio/wav content-type."""
|
||||
job = make_job(format="wav")
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService(
|
||||
submit_result={
|
||||
"audio_url": "https://temp.example.com/a.wav",
|
||||
"request_id": "r",
|
||||
"task_id": "",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with patch("packages.application.tts_job.workflow.safe_download_bytes", return_value=b"audio"):
|
||||
svc.start_synthesis("job-123")
|
||||
|
||||
assert storage.uploads[0]["content_type"] == "audio/wav"
|
||||
|
||||
def test_unknown_format_default_content_type(self):
|
||||
"""未知格式使用application/octet-stream."""
|
||||
job = make_job(format="flac")
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService(
|
||||
submit_result={
|
||||
"audio_url": "https://temp.example.com/a.flac",
|
||||
"request_id": "r",
|
||||
"task_id": "",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with patch("packages.application.tts_job.workflow.safe_download_bytes", return_value=b"audio"):
|
||||
svc.start_synthesis("job-123")
|
||||
|
||||
assert storage.uploads[0]["content_type"] == "application/octet-stream"
|
||||
|
||||
def test_storage_key_format(self):
|
||||
"""storage_key格式正确:tts-outputs/{user_id}/{job_id}.{format}."""
|
||||
job = make_job(id="custom-job", user_id="user-999", format="wav")
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService(
|
||||
submit_result={
|
||||
"audio_url": "https://temp.example.com/a.wav",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with patch("packages.application.tts_job.workflow.safe_download_bytes", return_value=b"audio"):
|
||||
svc.start_synthesis("custom-job")
|
||||
|
||||
assert storage.uploads[0]["storage_key"] == "tts-outputs/user-999/custom-job.wav"
|
||||
|
||||
|
||||
class TestResynthesizeParams:
|
||||
"""重新合成时参数从metadata读取测试."""
|
||||
|
||||
def test_speed_from_metadata(self):
|
||||
"""重新合成时speed从metadata读取."""
|
||||
job = make_job(input_text="test")
|
||||
job.mark_processing()
|
||||
job.metadata = {"speed": 1.5}
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService(
|
||||
submit_result={
|
||||
"audio_url": "https://temp.example.com/r.mp3",
|
||||
"duration": 2.0,
|
||||
"file_size": 500,
|
||||
}
|
||||
)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with patch("packages.application.tts_job.workflow.safe_download_bytes", return_value=b"audio"):
|
||||
svc.poll_and_process_synthesis("job-123")
|
||||
|
||||
assert cosy.submit_calls[0]["speed"] == 1.5
|
||||
|
||||
def test_volume_from_metadata(self):
|
||||
"""重新合成时volume从metadata读取."""
|
||||
job = make_job(input_text="test")
|
||||
job.mark_processing()
|
||||
job.metadata = {"volume": 80}
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService(
|
||||
submit_result={
|
||||
"audio_url": "https://temp.example.com/r.mp3",
|
||||
"duration": 2.0,
|
||||
"file_size": 500,
|
||||
}
|
||||
)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with patch("packages.application.tts_job.workflow.safe_download_bytes", return_value=b"audio"):
|
||||
svc.poll_and_process_synthesis("job-123")
|
||||
|
||||
assert cosy.submit_calls[0]["volume"] == 80
|
||||
|
||||
def test_default_speed_when_no_metadata(self):
|
||||
"""无metadata时speed默认1.0."""
|
||||
job = make_job(input_text="test")
|
||||
job.mark_processing()
|
||||
job.metadata = {}
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService(
|
||||
submit_result={
|
||||
"audio_url": "https://temp.example.com/r.mp3",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with patch("packages.application.tts_job.workflow.safe_download_bytes", return_value=b"audio"):
|
||||
svc.poll_and_process_synthesis("job-123")
|
||||
|
||||
assert cosy.submit_calls[0]["speed"] == 1.0
|
||||
|
||||
def test_default_volume_when_no_metadata(self):
|
||||
"""无metadata时volume默认50."""
|
||||
job = make_job(input_text="test")
|
||||
job.mark_processing()
|
||||
job.metadata = {}
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService(
|
||||
submit_result={
|
||||
"audio_url": "https://temp.example.com/r.mp3",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with patch("packages.application.tts_job.workflow.safe_download_bytes", return_value=b"audio"):
|
||||
svc.poll_and_process_synthesis("job-123")
|
||||
|
||||
assert cosy.submit_calls[0]["volume"] == 50
|
||||
|
||||
def test_resynthesize_no_audio_url_marks_failed(self):
|
||||
"""重新合成未返回audio_url时标记失败."""
|
||||
job = make_job(input_text="test")
|
||||
job.mark_processing()
|
||||
job.metadata = {}
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService(
|
||||
submit_result={"audio_url": "", "task_id": "", "request_id": "r"}
|
||||
)
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy)
|
||||
|
||||
result = svc.poll_and_process_synthesis("job-123")
|
||||
|
||||
assert result.status == TTSJobStatus.FAILED.value
|
||||
assert "重新合成" in result.error_message
|
||||
|
||||
|
||||
class TestPollSegmentTasks:
|
||||
"""分段任务轮询测试."""
|
||||
|
||||
def test_poll_segment_with_no_audio_urls_triggers_resynth(self):
|
||||
"""所有分段都缺audio_url时全部重新合成."""
|
||||
long_text = "x" * 600
|
||||
job = make_job(input_text=long_text, format="mp3")
|
||||
job.mark_processing()
|
||||
job.metadata = {
|
||||
"segment_task_ids": ["task1", "task2"],
|
||||
"segment_audio_urls": ["", ""],
|
||||
"segment_count": 2,
|
||||
}
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
|
||||
def mock_submit(**kwargs):
|
||||
return {
|
||||
"audio_url": "https://resynth.example.com/r.mp3",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
|
||||
cosy = FakeCosyVoiceService()
|
||||
cosy.submit_synthesize_task = MagicMock(side_effect=mock_submit)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with (
|
||||
patch("packages.application.tts_job.workflow.safe_download_file"),
|
||||
patch("packages.application.tts_job.workflow.AudioMerger") as mock_merger_class,
|
||||
):
|
||||
mock_merger = MagicMock()
|
||||
mock_merger.merge.return_value = b"merged"
|
||||
mock_merger_class.return_value = mock_merger
|
||||
result = svc.poll_and_process_synthesis("job-123")
|
||||
|
||||
# 2个分段都需要重新合成
|
||||
assert cosy.submit_synthesize_task.call_count == 2
|
||||
assert result.status == TTSJobStatus.COMPLETED.value
|
||||
|
||||
def test_existing_audio_urls_used_directly(self):
|
||||
"""已有segment_audio_urls的分段直接使用,不重新合成."""
|
||||
long_text = "x" * 600
|
||||
job = make_job(input_text=long_text, format="mp3")
|
||||
job.mark_processing()
|
||||
job.metadata = {
|
||||
"segment_task_ids": ["task1", "task2"],
|
||||
"segment_audio_urls": ["https://seg1.mp3", "https://seg2.mp3"],
|
||||
"segment_count": 2,
|
||||
}
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
cosy = FakeCosyVoiceService()
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with (
|
||||
patch("packages.application.tts_job.workflow.safe_download_file"),
|
||||
patch("packages.application.tts_job.workflow.AudioMerger") as mock_merger_class,
|
||||
):
|
||||
mock_merger = MagicMock()
|
||||
mock_merger.merge.return_value = b"merged"
|
||||
mock_merger_class.return_value = mock_merger
|
||||
result = svc.poll_and_process_synthesis("job-123")
|
||||
|
||||
# 所有分段都有audio_url,不需要重新合成
|
||||
assert len(cosy.submit_calls) == 0
|
||||
assert result.status == TTSJobStatus.COMPLETED.value
|
||||
|
||||
def test_missing_audio_url_resynthesized(self):
|
||||
"""缺少audio_url的分段会重新合成."""
|
||||
long_text = "x" * 600
|
||||
job = make_job(input_text=long_text, format="mp3")
|
||||
job.mark_processing()
|
||||
job.metadata = {
|
||||
"segment_task_ids": ["task1", "task2"],
|
||||
"segment_audio_urls": ["https://seg1.mp3", ""],
|
||||
"segment_count": 2,
|
||||
}
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
|
||||
def mock_submit(**kwargs):
|
||||
return {
|
||||
"audio_url": "https://resynth.mp3",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
|
||||
cosy = FakeCosyVoiceService()
|
||||
cosy.submit_synthesize_task = MagicMock(side_effect=mock_submit)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with (
|
||||
patch("packages.application.tts_job.workflow.safe_download_file"),
|
||||
patch("packages.application.tts_job.workflow.AudioMerger") as mock_merger_class,
|
||||
):
|
||||
mock_merger = MagicMock()
|
||||
mock_merger.merge.return_value = b"merged"
|
||||
mock_merger_class.return_value = mock_merger
|
||||
result = svc.poll_and_process_synthesis("job-123")
|
||||
|
||||
# 只有1个分段需要重新合成
|
||||
assert cosy.submit_synthesize_task.call_count == 1
|
||||
assert result.status == TTSJobStatus.COMPLETED.value
|
||||
|
||||
|
||||
class TestSegmentSyncDetails:
|
||||
"""分段同步路径细节测试."""
|
||||
|
||||
def test_segment_count_in_metadata(self):
|
||||
"""分段合成时segment_count写入metadata."""
|
||||
long_text = "x" * 1200
|
||||
job = make_job(input_text=long_text)
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
|
||||
def mock_submit(**kwargs):
|
||||
return {
|
||||
"audio_url": "https://seg.mp3",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
|
||||
cosy = FakeCosyVoiceService()
|
||||
cosy.submit_synthesize_task = MagicMock(side_effect=mock_submit)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with (
|
||||
patch("packages.application.tts_job.workflow.safe_download_file"),
|
||||
patch("packages.application.tts_job.workflow.AudioMerger") as mock_merger_class,
|
||||
):
|
||||
mock_merger = MagicMock()
|
||||
mock_merger.merge.return_value = b"merged audio"
|
||||
mock_merger_class.return_value = mock_merger
|
||||
result = svc.start_synthesis("job-123")
|
||||
|
||||
# 检查完成状态和文件大小
|
||||
assert result.status == TTSJobStatus.COMPLETED.value
|
||||
assert result.file_size == len(b"merged audio")
|
||||
|
||||
def test_segment_sync_duration_accumulated(self):
|
||||
"""分段同步路径时长累加."""
|
||||
long_text = "x" * 600
|
||||
job = make_job(input_text=long_text)
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
|
||||
call_idx = {"n": 0}
|
||||
|
||||
def mock_submit(**kwargs):
|
||||
call_idx["n"] += 1
|
||||
return {
|
||||
"audio_url": f"https://seg{call_idx['n']}.mp3",
|
||||
"duration": 2.5 * call_idx["n"], # 2.5 + 5.0 = 7.5
|
||||
"file_size": 100,
|
||||
}
|
||||
|
||||
cosy = FakeCosyVoiceService()
|
||||
cosy.submit_synthesize_task = MagicMock(side_effect=mock_submit)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with (
|
||||
patch("packages.application.tts_job.workflow.safe_download_file"),
|
||||
patch("packages.application.tts_job.workflow.AudioMerger") as mock_merger_class,
|
||||
):
|
||||
mock_merger = MagicMock()
|
||||
mock_merger.merge.return_value = b"merged"
|
||||
mock_merger_class.return_value = mock_merger
|
||||
result = svc.start_synthesis("job-123")
|
||||
|
||||
assert result.duration > 0
|
||||
assert result.status == TTSJobStatus.COMPLETED.value
|
||||
|
||||
def test_segment_missing_audio_url_raises(self):
|
||||
"""分段同步路径中某段无audio_url抛出TTSWorkflowError."""
|
||||
long_text = "x" * 600
|
||||
job = make_job(input_text=long_text)
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
|
||||
call_idx = {"n": 0}
|
||||
|
||||
def mock_submit(**kwargs):
|
||||
call_idx["n"] += 1
|
||||
if call_idx["n"] == 2:
|
||||
return {"audio_url": "", "duration": 0, "file_size": 0}
|
||||
return {
|
||||
"audio_url": "https://example.com/seg1.mp3",
|
||||
"duration": 1.0,
|
||||
"file_size": 100,
|
||||
}
|
||||
|
||||
cosy = FakeCosyVoiceService()
|
||||
cosy.submit_synthesize_task = MagicMock(side_effect=mock_submit)
|
||||
storage = FakeStorageService()
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=cosy, storage_service=storage)
|
||||
|
||||
with (
|
||||
patch("packages.application.tts_job.workflow.safe_download_file"),
|
||||
pytest.raises(TTSWorkflowError, match="没有返回 audio_url"),
|
||||
):
|
||||
svc.start_synthesis("job-123")
|
||||
|
||||
|
||||
class TestUploadMergedToOSS:
|
||||
"""_upload_merged_to_oss 测试."""
|
||||
|
||||
def test_upload_success_returns_url_and_key(self):
|
||||
"""上传成功返回永久URL和storage_key."""
|
||||
job = make_job(id="job-merge", user_id="u1", format="mp3")
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
storage = FakeStorageService(upload_url="https://oss.example.com/merged.mp3")
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=MagicMock(), storage_service=storage)
|
||||
|
||||
url, key = svc._upload_merged_to_oss(b"merged data", "u1", "job-merge", "mp3")
|
||||
|
||||
assert url == "https://oss.example.com/merged.mp3"
|
||||
assert key == "tts-outputs/u1/job-merge.mp3"
|
||||
assert len(storage.uploads) == 1
|
||||
|
||||
def test_upload_failure_returns_empty(self):
|
||||
"""上传失败返回空字符串."""
|
||||
job = make_job()
|
||||
repo = FakeTTSJobRepository(job=job)
|
||||
storage = FakeStorageService(upload_error=RuntimeError("upload failed"))
|
||||
svc = TTSWorkflowService(repository=repo, cosyvoice_service=MagicMock(), storage_service=storage)
|
||||
|
||||
url, key = svc._upload_merged_to_oss(b"data", "user", "job", "wav")
|
||||
|
||||
assert url == ""
|
||||
assert key == ""
|
||||
|
||||
Reference in New Issue
Block a user