karaoke-gen 0.90.1__py3-none-any.whl → 0.96.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- backend/.coveragerc +20 -0
- backend/.gitignore +37 -0
- backend/Dockerfile +43 -0
- backend/Dockerfile.base +74 -0
- backend/README.md +242 -0
- backend/__init__.py +0 -0
- backend/api/__init__.py +0 -0
- backend/api/dependencies.py +457 -0
- backend/api/routes/__init__.py +0 -0
- backend/api/routes/admin.py +742 -0
- backend/api/routes/audio_search.py +903 -0
- backend/api/routes/auth.py +348 -0
- backend/api/routes/file_upload.py +2076 -0
- backend/api/routes/health.py +344 -0
- backend/api/routes/internal.py +435 -0
- backend/api/routes/jobs.py +1610 -0
- backend/api/routes/review.py +652 -0
- backend/api/routes/themes.py +162 -0
- backend/api/routes/users.py +1014 -0
- backend/config.py +172 -0
- backend/main.py +133 -0
- backend/middleware/__init__.py +5 -0
- backend/middleware/audit_logging.py +124 -0
- backend/models/__init__.py +0 -0
- backend/models/job.py +519 -0
- backend/models/requests.py +123 -0
- backend/models/theme.py +153 -0
- backend/models/user.py +254 -0
- backend/models/worker_log.py +164 -0
- backend/pyproject.toml +29 -0
- backend/quick-check.sh +93 -0
- backend/requirements.txt +29 -0
- backend/run_tests.sh +60 -0
- backend/services/__init__.py +0 -0
- backend/services/audio_analysis_service.py +243 -0
- backend/services/audio_editing_service.py +278 -0
- backend/services/audio_search_service.py +702 -0
- backend/services/auth_service.py +630 -0
- backend/services/credential_manager.py +792 -0
- backend/services/discord_service.py +172 -0
- backend/services/dropbox_service.py +301 -0
- backend/services/email_service.py +1093 -0
- backend/services/encoding_interface.py +454 -0
- backend/services/encoding_service.py +405 -0
- backend/services/firestore_service.py +512 -0
- backend/services/flacfetch_client.py +573 -0
- backend/services/gce_encoding/README.md +72 -0
- backend/services/gce_encoding/__init__.py +22 -0
- backend/services/gce_encoding/main.py +589 -0
- backend/services/gce_encoding/requirements.txt +16 -0
- backend/services/gdrive_service.py +356 -0
- backend/services/job_logging.py +258 -0
- backend/services/job_manager.py +842 -0
- backend/services/job_notification_service.py +271 -0
- backend/services/local_encoding_service.py +590 -0
- backend/services/local_preview_encoding_service.py +407 -0
- backend/services/lyrics_cache_service.py +216 -0
- backend/services/metrics.py +413 -0
- backend/services/packaging_service.py +287 -0
- backend/services/rclone_service.py +106 -0
- backend/services/storage_service.py +209 -0
- backend/services/stripe_service.py +275 -0
- backend/services/structured_logging.py +254 -0
- backend/services/template_service.py +330 -0
- backend/services/theme_service.py +469 -0
- backend/services/tracing.py +543 -0
- backend/services/user_service.py +721 -0
- backend/services/worker_service.py +558 -0
- backend/services/youtube_service.py +112 -0
- backend/services/youtube_upload_service.py +445 -0
- backend/tests/__init__.py +4 -0
- backend/tests/conftest.py +224 -0
- backend/tests/emulator/__init__.py +7 -0
- backend/tests/emulator/conftest.py +88 -0
- backend/tests/emulator/test_e2e_cli_backend.py +1053 -0
- backend/tests/emulator/test_emulator_integration.py +356 -0
- backend/tests/emulator/test_style_loading_direct.py +436 -0
- backend/tests/emulator/test_worker_logs_direct.py +229 -0
- backend/tests/emulator/test_worker_logs_subcollection.py +443 -0
- backend/tests/requirements-test.txt +10 -0
- backend/tests/requirements.txt +6 -0
- backend/tests/test_admin_email_endpoints.py +411 -0
- backend/tests/test_api_integration.py +460 -0
- backend/tests/test_api_routes.py +93 -0
- backend/tests/test_audio_analysis_service.py +294 -0
- backend/tests/test_audio_editing_service.py +386 -0
- backend/tests/test_audio_search.py +1398 -0
- backend/tests/test_audio_services.py +378 -0
- backend/tests/test_auth_firestore.py +231 -0
- backend/tests/test_config_extended.py +68 -0
- backend/tests/test_credential_manager.py +377 -0
- backend/tests/test_dependencies.py +54 -0
- backend/tests/test_discord_service.py +244 -0
- backend/tests/test_distribution_services.py +820 -0
- backend/tests/test_dropbox_service.py +472 -0
- backend/tests/test_email_service.py +492 -0
- backend/tests/test_emulator_integration.py +322 -0
- backend/tests/test_encoding_interface.py +412 -0
- backend/tests/test_file_upload.py +1739 -0
- backend/tests/test_flacfetch_client.py +632 -0
- backend/tests/test_gdrive_service.py +524 -0
- backend/tests/test_instrumental_api.py +431 -0
- backend/tests/test_internal_api.py +343 -0
- backend/tests/test_job_creation_regression.py +583 -0
- backend/tests/test_job_manager.py +339 -0
- backend/tests/test_job_manager_notifications.py +329 -0
- backend/tests/test_job_notification_service.py +443 -0
- backend/tests/test_jobs_api.py +273 -0
- backend/tests/test_local_encoding_service.py +423 -0
- backend/tests/test_local_preview_encoding_service.py +567 -0
- backend/tests/test_main.py +87 -0
- backend/tests/test_models.py +918 -0
- backend/tests/test_packaging_service.py +382 -0
- backend/tests/test_requests.py +201 -0
- backend/tests/test_routes_jobs.py +282 -0
- backend/tests/test_routes_review.py +337 -0
- backend/tests/test_services.py +556 -0
- backend/tests/test_services_extended.py +112 -0
- backend/tests/test_storage_service.py +448 -0
- backend/tests/test_style_upload.py +261 -0
- backend/tests/test_template_service.py +295 -0
- backend/tests/test_theme_service.py +516 -0
- backend/tests/test_unicode_sanitization.py +522 -0
- backend/tests/test_upload_api.py +256 -0
- backend/tests/test_validate.py +156 -0
- backend/tests/test_video_worker_orchestrator.py +847 -0
- backend/tests/test_worker_log_subcollection.py +509 -0
- backend/tests/test_worker_logging.py +365 -0
- backend/tests/test_workers.py +1116 -0
- backend/tests/test_workers_extended.py +178 -0
- backend/tests/test_youtube_service.py +247 -0
- backend/tests/test_youtube_upload_service.py +568 -0
- backend/validate.py +173 -0
- backend/version.py +27 -0
- backend/workers/README.md +597 -0
- backend/workers/__init__.py +11 -0
- backend/workers/audio_worker.py +618 -0
- backend/workers/lyrics_worker.py +683 -0
- backend/workers/render_video_worker.py +483 -0
- backend/workers/screens_worker.py +525 -0
- backend/workers/style_helper.py +198 -0
- backend/workers/video_worker.py +1277 -0
- backend/workers/video_worker_orchestrator.py +701 -0
- backend/workers/worker_logging.py +278 -0
- karaoke_gen/instrumental_review/static/index.html +7 -4
- karaoke_gen/karaoke_finalise/karaoke_finalise.py +6 -1
- karaoke_gen/utils/__init__.py +163 -8
- karaoke_gen/video_background_processor.py +9 -4
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.96.0.dist-info}/METADATA +1 -1
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.96.0.dist-info}/RECORD +186 -41
- lyrics_transcriber/correction/agentic/providers/config.py +9 -5
- lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +1 -51
- lyrics_transcriber/correction/corrector.py +192 -130
- lyrics_transcriber/correction/operations.py +24 -9
- lyrics_transcriber/frontend/package-lock.json +2 -2
- lyrics_transcriber/frontend/package.json +1 -1
- lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +1 -1
- lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +11 -7
- lyrics_transcriber/frontend/src/components/EditActionBar.tsx +31 -5
- lyrics_transcriber/frontend/src/components/EditModal.tsx +28 -10
- lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +123 -27
- lyrics_transcriber/frontend/src/components/EditWordList.tsx +112 -60
- lyrics_transcriber/frontend/src/components/Header.tsx +90 -76
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +53 -31
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +44 -13
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +66 -50
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +124 -30
- lyrics_transcriber/frontend/src/components/ReferenceView.tsx +1 -1
- lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +12 -5
- lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +3 -3
- lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +1 -1
- lyrics_transcriber/frontend/src/components/WordDivider.tsx +11 -7
- lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +4 -2
- lyrics_transcriber/frontend/src/hooks/useManualSync.ts +103 -1
- lyrics_transcriber/frontend/src/theme.ts +42 -15
- lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
- lyrics_transcriber/frontend/vite.config.js +5 -0
- lyrics_transcriber/frontend/web_assets/assets/{index-BECn1o8Q.js → index-BSMgOq4Z.js} +6959 -5782
- lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js.map +1 -0
- lyrics_transcriber/frontend/web_assets/index.html +6 -2
- lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.svg +5 -0
- lyrics_transcriber/output/generator.py +17 -3
- lyrics_transcriber/output/video.py +60 -95
- lyrics_transcriber/frontend/web_assets/assets/index-BECn1o8Q.js.map +0 -1
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.96.0.dist-info}/WHEEL +0 -0
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.96.0.dist-info}/entry_points.txt +0 -0
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.96.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for internal.py API routes using FastAPI TestClient.
|
|
3
|
+
|
|
4
|
+
Internal routes are used for worker communication within Cloud Run.
|
|
5
|
+
These endpoints require admin authentication.
|
|
6
|
+
|
|
7
|
+
With Cloud Tasks integration, these endpoints include idempotency checks
|
|
8
|
+
to prevent duplicate processing on retries.
|
|
9
|
+
"""
|
|
10
|
+
import os
|
|
11
|
+
import pytest
|
|
12
|
+
from datetime import datetime, UTC
|
|
13
|
+
from unittest.mock import MagicMock, AsyncMock, patch
|
|
14
|
+
from fastapi.testclient import TestClient
|
|
15
|
+
|
|
16
|
+
from backend.models.job import Job, JobStatus
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Set test admin token for auth
|
|
20
|
+
os.environ.setdefault('ADMIN_TOKENS', 'test-admin-token')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _create_mock_job(job_id: str = "test123") -> Job:
|
|
24
|
+
"""Create a mock Job object for testing."""
|
|
25
|
+
return Job(
|
|
26
|
+
job_id=job_id,
|
|
27
|
+
status=JobStatus.PROCESSING,
|
|
28
|
+
created_at=datetime.now(UTC),
|
|
29
|
+
updated_at=datetime.now(UTC),
|
|
30
|
+
state_data={}, # Empty state_data - no worker progress yet
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def client():
|
|
36
|
+
"""Create TestClient with mocked workers and job manager."""
|
|
37
|
+
mock_creds = MagicMock()
|
|
38
|
+
mock_creds.universe_domain = 'googleapis.com'
|
|
39
|
+
|
|
40
|
+
# Mock the worker functions at the internal routes module level
|
|
41
|
+
mock_audio = AsyncMock()
|
|
42
|
+
mock_lyrics = AsyncMock()
|
|
43
|
+
mock_screens = AsyncMock()
|
|
44
|
+
mock_video = AsyncMock()
|
|
45
|
+
mock_render = AsyncMock()
|
|
46
|
+
|
|
47
|
+
# Mock JobManager for idempotency checks
|
|
48
|
+
mock_job_manager = MagicMock()
|
|
49
|
+
mock_job_manager.get_job.return_value = _create_mock_job()
|
|
50
|
+
mock_job_manager.update_state_data.return_value = None
|
|
51
|
+
|
|
52
|
+
with patch('backend.api.routes.internal.process_audio_separation', mock_audio), \
|
|
53
|
+
patch('backend.api.routes.internal.process_lyrics_transcription', mock_lyrics), \
|
|
54
|
+
patch('backend.api.routes.internal.generate_screens', mock_screens), \
|
|
55
|
+
patch('backend.api.routes.internal.generate_video', mock_video), \
|
|
56
|
+
patch('backend.api.routes.internal.process_render_video', mock_render), \
|
|
57
|
+
patch('backend.api.routes.internal.JobManager', return_value=mock_job_manager), \
|
|
58
|
+
patch('backend.services.firestore_service.firestore'), \
|
|
59
|
+
patch('backend.services.storage_service.storage'), \
|
|
60
|
+
patch('google.auth.default', return_value=(mock_creds, 'test-project')):
|
|
61
|
+
from backend.main import app
|
|
62
|
+
yield TestClient(app)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@pytest.fixture
|
|
66
|
+
def auth_headers():
|
|
67
|
+
"""Auth headers for internal API testing."""
|
|
68
|
+
return {"Authorization": "Bearer test-admin-token"}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class TestAudioWorkerEndpoint:
|
|
72
|
+
"""Tests for POST /api/internal/workers/audio."""
|
|
73
|
+
|
|
74
|
+
def test_audio_worker_returns_200(self, client, auth_headers):
|
|
75
|
+
"""Test audio worker endpoint returns 200 with auth."""
|
|
76
|
+
response = client.post(
|
|
77
|
+
"/api/internal/workers/audio",
|
|
78
|
+
headers=auth_headers,
|
|
79
|
+
json={"job_id": "test123"}
|
|
80
|
+
)
|
|
81
|
+
assert response.status_code == 200
|
|
82
|
+
|
|
83
|
+
def test_audio_worker_accepts_job_id(self, client, auth_headers):
|
|
84
|
+
"""Test audio worker accepts job_id in body."""
|
|
85
|
+
response = client.post(
|
|
86
|
+
"/api/internal/workers/audio",
|
|
87
|
+
headers=auth_headers,
|
|
88
|
+
json={"job_id": "any-job-id"}
|
|
89
|
+
)
|
|
90
|
+
assert response.status_code == 200
|
|
91
|
+
|
|
92
|
+
def test_audio_worker_requires_job_id(self, client, auth_headers):
|
|
93
|
+
"""Test audio worker requires job_id."""
|
|
94
|
+
response = client.post(
|
|
95
|
+
"/api/internal/workers/audio",
|
|
96
|
+
headers=auth_headers,
|
|
97
|
+
json={}
|
|
98
|
+
)
|
|
99
|
+
assert response.status_code == 422
|
|
100
|
+
|
|
101
|
+
def test_audio_worker_requires_auth(self, client, auth_headers):
|
|
102
|
+
"""Test audio worker requires authentication."""
|
|
103
|
+
response = client.post(
|
|
104
|
+
"/api/internal/workers/audio",
|
|
105
|
+
json={"job_id": "test123"}
|
|
106
|
+
)
|
|
107
|
+
assert response.status_code == 401
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class TestLyricsWorkerEndpoint:
|
|
111
|
+
"""Tests for POST /api/internal/workers/lyrics."""
|
|
112
|
+
|
|
113
|
+
def test_lyrics_worker_returns_200(self, client, auth_headers):
|
|
114
|
+
"""Test lyrics worker endpoint returns 200 with auth."""
|
|
115
|
+
response = client.post(
|
|
116
|
+
"/api/internal/workers/lyrics",
|
|
117
|
+
headers=auth_headers,
|
|
118
|
+
json={"job_id": "test123"}
|
|
119
|
+
)
|
|
120
|
+
assert response.status_code == 200
|
|
121
|
+
|
|
122
|
+
def test_lyrics_worker_requires_job_id(self, client, auth_headers):
|
|
123
|
+
"""Test lyrics worker requires job_id."""
|
|
124
|
+
response = client.post(
|
|
125
|
+
"/api/internal/workers/lyrics",
|
|
126
|
+
headers=auth_headers,
|
|
127
|
+
json={}
|
|
128
|
+
)
|
|
129
|
+
assert response.status_code == 422
|
|
130
|
+
|
|
131
|
+
def test_lyrics_worker_requires_auth(self, client, auth_headers):
|
|
132
|
+
"""Test lyrics worker requires authentication."""
|
|
133
|
+
response = client.post(
|
|
134
|
+
"/api/internal/workers/lyrics",
|
|
135
|
+
json={"job_id": "test123"}
|
|
136
|
+
)
|
|
137
|
+
assert response.status_code == 401
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class TestScreensWorkerEndpoint:
|
|
141
|
+
"""Tests for POST /api/internal/workers/screens."""
|
|
142
|
+
|
|
143
|
+
def test_screens_worker_returns_200(self, client, auth_headers):
|
|
144
|
+
"""Test screens worker endpoint returns 200 with auth."""
|
|
145
|
+
response = client.post(
|
|
146
|
+
"/api/internal/workers/screens",
|
|
147
|
+
headers=auth_headers,
|
|
148
|
+
json={"job_id": "test123"}
|
|
149
|
+
)
|
|
150
|
+
assert response.status_code == 200
|
|
151
|
+
|
|
152
|
+
def test_screens_worker_requires_job_id(self, client, auth_headers):
|
|
153
|
+
"""Test screens worker requires job_id."""
|
|
154
|
+
response = client.post(
|
|
155
|
+
"/api/internal/workers/screens",
|
|
156
|
+
headers=auth_headers,
|
|
157
|
+
json={}
|
|
158
|
+
)
|
|
159
|
+
assert response.status_code == 422
|
|
160
|
+
|
|
161
|
+
def test_screens_worker_requires_auth(self, client, auth_headers):
|
|
162
|
+
"""Test screens worker requires authentication."""
|
|
163
|
+
response = client.post(
|
|
164
|
+
"/api/internal/workers/screens",
|
|
165
|
+
json={"job_id": "test123"}
|
|
166
|
+
)
|
|
167
|
+
assert response.status_code == 401
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class TestVideoWorkerEndpoint:
|
|
171
|
+
"""Tests for POST /api/internal/workers/video."""
|
|
172
|
+
|
|
173
|
+
def test_video_worker_returns_200(self, client, auth_headers):
|
|
174
|
+
"""Test video worker endpoint returns 200 with auth."""
|
|
175
|
+
response = client.post(
|
|
176
|
+
"/api/internal/workers/video",
|
|
177
|
+
headers=auth_headers,
|
|
178
|
+
json={"job_id": "test123"}
|
|
179
|
+
)
|
|
180
|
+
assert response.status_code == 200
|
|
181
|
+
|
|
182
|
+
def test_video_worker_requires_job_id(self, client, auth_headers):
|
|
183
|
+
"""Test video worker requires job_id."""
|
|
184
|
+
response = client.post(
|
|
185
|
+
"/api/internal/workers/video",
|
|
186
|
+
headers=auth_headers,
|
|
187
|
+
json={}
|
|
188
|
+
)
|
|
189
|
+
assert response.status_code == 422
|
|
190
|
+
|
|
191
|
+
def test_video_worker_requires_auth(self, client, auth_headers):
|
|
192
|
+
"""Test video worker requires authentication."""
|
|
193
|
+
response = client.post(
|
|
194
|
+
"/api/internal/workers/video",
|
|
195
|
+
json={"job_id": "test123"}
|
|
196
|
+
)
|
|
197
|
+
assert response.status_code == 401
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class TestRenderVideoWorkerEndpoint:
|
|
201
|
+
"""Tests for POST /api/internal/workers/render-video."""
|
|
202
|
+
|
|
203
|
+
def test_render_video_worker_returns_200(self, client, auth_headers):
|
|
204
|
+
"""Test render-video worker endpoint returns 200 with auth."""
|
|
205
|
+
response = client.post(
|
|
206
|
+
"/api/internal/workers/render-video",
|
|
207
|
+
headers=auth_headers,
|
|
208
|
+
json={"job_id": "test123"}
|
|
209
|
+
)
|
|
210
|
+
assert response.status_code == 200
|
|
211
|
+
|
|
212
|
+
def test_render_video_worker_requires_job_id(self, client, auth_headers):
|
|
213
|
+
"""Test render-video worker requires job_id."""
|
|
214
|
+
response = client.post(
|
|
215
|
+
"/api/internal/workers/render-video",
|
|
216
|
+
headers=auth_headers,
|
|
217
|
+
json={}
|
|
218
|
+
)
|
|
219
|
+
assert response.status_code == 422
|
|
220
|
+
|
|
221
|
+
def test_render_video_worker_requires_auth(self, client, auth_headers):
|
|
222
|
+
"""Test render-video worker requires authentication."""
|
|
223
|
+
response = client.post(
|
|
224
|
+
"/api/internal/workers/render-video",
|
|
225
|
+
json={"job_id": "test123"}
|
|
226
|
+
)
|
|
227
|
+
assert response.status_code == 401
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class TestWorkerResponseFormat:
|
|
231
|
+
"""Tests for worker response format."""
|
|
232
|
+
|
|
233
|
+
def test_audio_worker_response_contains_status(self, client, auth_headers):
|
|
234
|
+
"""Test audio worker response contains status."""
|
|
235
|
+
response = client.post(
|
|
236
|
+
"/api/internal/workers/audio",
|
|
237
|
+
headers=auth_headers,
|
|
238
|
+
json={"job_id": "test123"}
|
|
239
|
+
)
|
|
240
|
+
data = response.json()
|
|
241
|
+
assert "status" in data or "message" in data
|
|
242
|
+
|
|
243
|
+
def test_lyrics_worker_response_contains_status(self, client, auth_headers):
|
|
244
|
+
"""Test lyrics worker response contains status."""
|
|
245
|
+
response = client.post(
|
|
246
|
+
"/api/internal/workers/lyrics",
|
|
247
|
+
headers=auth_headers,
|
|
248
|
+
json={"job_id": "test123"}
|
|
249
|
+
)
|
|
250
|
+
data = response.json()
|
|
251
|
+
assert "status" in data or "message" in data
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class TestWorkerIdempotency:
|
|
255
|
+
"""Tests for worker idempotency checks (Cloud Tasks retry support)."""
|
|
256
|
+
|
|
257
|
+
def test_audio_worker_returns_already_running_when_in_progress(self, auth_headers):
|
|
258
|
+
"""Test audio worker returns already_running if worker is already running."""
|
|
259
|
+
mock_creds = MagicMock()
|
|
260
|
+
mock_creds.universe_domain = 'googleapis.com'
|
|
261
|
+
|
|
262
|
+
# Create job with audio already running
|
|
263
|
+
mock_job = _create_mock_job()
|
|
264
|
+
mock_job.state_data = {'audio_progress': {'stage': 'running'}}
|
|
265
|
+
|
|
266
|
+
mock_job_manager = MagicMock()
|
|
267
|
+
mock_job_manager.get_job.return_value = mock_job
|
|
268
|
+
|
|
269
|
+
with patch('backend.api.routes.internal.process_audio_separation', AsyncMock()), \
|
|
270
|
+
patch('backend.api.routes.internal.JobManager', return_value=mock_job_manager), \
|
|
271
|
+
patch('backend.services.firestore_service.firestore'), \
|
|
272
|
+
patch('backend.services.storage_service.storage'), \
|
|
273
|
+
patch('google.auth.default', return_value=(mock_creds, 'test-project')):
|
|
274
|
+
from backend.main import app
|
|
275
|
+
client = TestClient(app)
|
|
276
|
+
|
|
277
|
+
response = client.post(
|
|
278
|
+
"/api/internal/workers/audio",
|
|
279
|
+
headers=auth_headers,
|
|
280
|
+
json={"job_id": "test123"}
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
assert response.status_code == 200
|
|
284
|
+
data = response.json()
|
|
285
|
+
assert data["status"] == "already_running"
|
|
286
|
+
|
|
287
|
+
def test_audio_worker_returns_already_complete_when_finished(self, auth_headers):
|
|
288
|
+
"""Test audio worker returns already_complete if worker already finished."""
|
|
289
|
+
mock_creds = MagicMock()
|
|
290
|
+
mock_creds.universe_domain = 'googleapis.com'
|
|
291
|
+
|
|
292
|
+
# Create job with audio already complete
|
|
293
|
+
mock_job = _create_mock_job()
|
|
294
|
+
mock_job.state_data = {'audio_progress': {'stage': 'complete'}}
|
|
295
|
+
|
|
296
|
+
mock_job_manager = MagicMock()
|
|
297
|
+
mock_job_manager.get_job.return_value = mock_job
|
|
298
|
+
|
|
299
|
+
with patch('backend.api.routes.internal.process_audio_separation', AsyncMock()), \
|
|
300
|
+
patch('backend.api.routes.internal.JobManager', return_value=mock_job_manager), \
|
|
301
|
+
patch('backend.services.firestore_service.firestore'), \
|
|
302
|
+
patch('backend.services.storage_service.storage'), \
|
|
303
|
+
patch('google.auth.default', return_value=(mock_creds, 'test-project')):
|
|
304
|
+
from backend.main import app
|
|
305
|
+
client = TestClient(app)
|
|
306
|
+
|
|
307
|
+
response = client.post(
|
|
308
|
+
"/api/internal/workers/audio",
|
|
309
|
+
headers=auth_headers,
|
|
310
|
+
json={"job_id": "test123"}
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
assert response.status_code == 200
|
|
314
|
+
data = response.json()
|
|
315
|
+
assert data["status"] == "already_complete"
|
|
316
|
+
|
|
317
|
+
def test_audio_worker_returns_not_found_when_job_missing(self, auth_headers):
|
|
318
|
+
"""Test audio worker returns not_found if job doesn't exist."""
|
|
319
|
+
mock_creds = MagicMock()
|
|
320
|
+
mock_creds.universe_domain = 'googleapis.com'
|
|
321
|
+
|
|
322
|
+
# Return None for job not found
|
|
323
|
+
mock_job_manager = MagicMock()
|
|
324
|
+
mock_job_manager.get_job.return_value = None
|
|
325
|
+
|
|
326
|
+
with patch('backend.api.routes.internal.process_audio_separation', AsyncMock()), \
|
|
327
|
+
patch('backend.api.routes.internal.JobManager', return_value=mock_job_manager), \
|
|
328
|
+
patch('backend.services.firestore_service.firestore'), \
|
|
329
|
+
patch('backend.services.storage_service.storage'), \
|
|
330
|
+
patch('google.auth.default', return_value=(mock_creds, 'test-project')):
|
|
331
|
+
from backend.main import app
|
|
332
|
+
client = TestClient(app)
|
|
333
|
+
|
|
334
|
+
response = client.post(
|
|
335
|
+
"/api/internal/workers/audio",
|
|
336
|
+
headers=auth_headers,
|
|
337
|
+
json={"job_id": "nonexistent"}
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
assert response.status_code == 200
|
|
341
|
+
data = response.json()
|
|
342
|
+
assert data["status"] == "not_found"
|
|
343
|
+
|