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,356 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integration tests using local GCP emulators.
|
|
3
|
+
|
|
4
|
+
These tests use real Firestore and GCS emulators running locally,
|
|
5
|
+
providing true integration testing without cloud resources or costs.
|
|
6
|
+
|
|
7
|
+
Run with: ./scripts/run-emulator-tests.sh
|
|
8
|
+
|
|
9
|
+
NOTE: These tests mock background workers to avoid race conditions.
|
|
10
|
+
The fixtures are defined in conftest.py in this directory.
|
|
11
|
+
"""
|
|
12
|
+
import pytest
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
from .conftest import emulators_running
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Skip all tests in this module if emulators aren't running
|
|
19
|
+
pytestmark = pytest.mark.skipif(
|
|
20
|
+
not emulators_running(),
|
|
21
|
+
reason="GCP emulators not running. Start with: scripts/start-emulators.sh"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Fixtures are loaded from conftest.py in this directory
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TestEmulatorBasics:
|
|
28
|
+
"""Basic emulator connectivity tests."""
|
|
29
|
+
|
|
30
|
+
def test_health_endpoint(self, client, auth_headers):
|
|
31
|
+
"""Test health endpoint works."""
|
|
32
|
+
response = client.get("/api/health", )
|
|
33
|
+
assert response.status_code == 200
|
|
34
|
+
assert response.json()["status"] == "healthy"
|
|
35
|
+
|
|
36
|
+
def test_root_endpoint(self, client, auth_headers):
|
|
37
|
+
"""Test root endpoint works."""
|
|
38
|
+
response = client.get("/", )
|
|
39
|
+
assert response.status_code == 200
|
|
40
|
+
assert response.json()["service"] == "karaoke-gen-backend"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TestJobCreation:
|
|
44
|
+
"""Test job creation with Firestore emulator."""
|
|
45
|
+
|
|
46
|
+
def test_create_job_simple(self, client, auth_headers):
|
|
47
|
+
"""Test creating a simple job."""
|
|
48
|
+
response = client.post(
|
|
49
|
+
"/api/jobs",
|
|
50
|
+
headers=auth_headers,
|
|
51
|
+
json={"url": "https://youtube.com/watch?v=test123"}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
assert response.status_code == 200
|
|
55
|
+
data = response.json()
|
|
56
|
+
assert data["status"] == "success"
|
|
57
|
+
assert "job_id" in data
|
|
58
|
+
|
|
59
|
+
def test_create_job_with_metadata(self, client, auth_headers):
|
|
60
|
+
"""Test creating a job with artist/title."""
|
|
61
|
+
response = client.post(
|
|
62
|
+
"/api/jobs",
|
|
63
|
+
headers=auth_headers,
|
|
64
|
+
json={
|
|
65
|
+
"url": "https://youtube.com/watch?v=test",
|
|
66
|
+
"artist": "Test Artist",
|
|
67
|
+
"title": "Test Song"
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
assert response.status_code == 200
|
|
72
|
+
data = response.json()
|
|
73
|
+
assert "job_id" in data
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TestJobRetrieval:
|
|
77
|
+
"""Test job retrieval from Firestore emulator."""
|
|
78
|
+
|
|
79
|
+
def test_create_and_get_job(self, client, auth_headers):
|
|
80
|
+
"""Test creating and then retrieving a job."""
|
|
81
|
+
# Create
|
|
82
|
+
create_resp = client.post(
|
|
83
|
+
"/api/jobs",
|
|
84
|
+
headers=auth_headers,
|
|
85
|
+
json={
|
|
86
|
+
"url": "https://youtube.com/watch?v=abc123",
|
|
87
|
+
"artist": "Test Artist",
|
|
88
|
+
"title": "Test Song"
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
assert create_resp.status_code == 200
|
|
92
|
+
job_id = create_resp.json()["job_id"]
|
|
93
|
+
|
|
94
|
+
# Small delay for emulator consistency
|
|
95
|
+
time.sleep(0.2)
|
|
96
|
+
|
|
97
|
+
# Retrieve
|
|
98
|
+
get_resp = client.get(f"/api/jobs/{job_id}", headers=auth_headers)
|
|
99
|
+
|
|
100
|
+
if get_resp.status_code != 200:
|
|
101
|
+
print(f"GET failed: {get_resp.status_code} - {get_resp.text}")
|
|
102
|
+
|
|
103
|
+
assert get_resp.status_code == 200
|
|
104
|
+
|
|
105
|
+
job = get_resp.json()
|
|
106
|
+
assert job["job_id"] == job_id
|
|
107
|
+
assert job["status"] == "pending"
|
|
108
|
+
assert job["artist"] == "Test Artist"
|
|
109
|
+
assert job["title"] == "Test Song"
|
|
110
|
+
|
|
111
|
+
def test_get_nonexistent_job(self, client, auth_headers):
|
|
112
|
+
"""Test fetching a job that doesn't exist."""
|
|
113
|
+
response = client.get("/api/jobs/nonexistent-id", headers=auth_headers)
|
|
114
|
+
assert response.status_code == 404
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class TestJobList:
|
|
118
|
+
"""Test listing jobs from Firestore."""
|
|
119
|
+
|
|
120
|
+
def test_list_jobs(self, client, auth_headers):
|
|
121
|
+
"""Test listing jobs."""
|
|
122
|
+
# Create a few jobs
|
|
123
|
+
for i in range(3):
|
|
124
|
+
client.post(
|
|
125
|
+
"/api/jobs",
|
|
126
|
+
headers=auth_headers,
|
|
127
|
+
json={"url": f"https://youtube.com/watch?v=list{i}"}
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
time.sleep(0.2)
|
|
131
|
+
|
|
132
|
+
# List
|
|
133
|
+
response = client.get("/api/jobs", headers=auth_headers)
|
|
134
|
+
assert response.status_code == 200
|
|
135
|
+
|
|
136
|
+
jobs = response.json()
|
|
137
|
+
assert isinstance(jobs, list)
|
|
138
|
+
# We should have at least the 3 we just created
|
|
139
|
+
assert len(jobs) >= 3
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class TestJobDeletion:
|
|
143
|
+
"""Test job deletion."""
|
|
144
|
+
|
|
145
|
+
def test_delete_job(self, client, auth_headers):
|
|
146
|
+
"""Test deleting a job."""
|
|
147
|
+
# Create
|
|
148
|
+
create_resp = client.post(
|
|
149
|
+
"/api/jobs",
|
|
150
|
+
headers=auth_headers,
|
|
151
|
+
json={"url": "https://youtube.com/watch?v=delete-me"}
|
|
152
|
+
)
|
|
153
|
+
job_id = create_resp.json()["job_id"]
|
|
154
|
+
|
|
155
|
+
time.sleep(0.2)
|
|
156
|
+
|
|
157
|
+
# Delete
|
|
158
|
+
del_resp = client.delete(f"/api/jobs/{job_id}", headers=auth_headers)
|
|
159
|
+
assert del_resp.status_code == 200
|
|
160
|
+
|
|
161
|
+
# Verify deleted
|
|
162
|
+
get_resp = client.get(f"/api/jobs/{job_id}", headers=auth_headers)
|
|
163
|
+
assert get_resp.status_code == 404
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class TestJobUpdates:
|
|
167
|
+
"""Test job status updates."""
|
|
168
|
+
|
|
169
|
+
def test_cancel_job(self, client, auth_headers):
|
|
170
|
+
"""Test cancelling a job."""
|
|
171
|
+
# Create
|
|
172
|
+
create_resp = client.post(
|
|
173
|
+
"/api/jobs",
|
|
174
|
+
headers=auth_headers,
|
|
175
|
+
json={"url": "https://youtube.com/watch?v=cancel-me"}
|
|
176
|
+
)
|
|
177
|
+
job_id = create_resp.json()["job_id"]
|
|
178
|
+
|
|
179
|
+
time.sleep(0.2)
|
|
180
|
+
|
|
181
|
+
# Cancel
|
|
182
|
+
cancel_resp = client.post(
|
|
183
|
+
f"/api/jobs/{job_id}/cancel",
|
|
184
|
+
headers=auth_headers,
|
|
185
|
+
json={"reason": "test cancellation"}
|
|
186
|
+
)
|
|
187
|
+
assert cancel_resp.status_code == 200
|
|
188
|
+
|
|
189
|
+
# Verify cancelled
|
|
190
|
+
get_resp = client.get(f"/api/jobs/{job_id}", headers=auth_headers)
|
|
191
|
+
assert get_resp.status_code == 200
|
|
192
|
+
job = get_resp.json()
|
|
193
|
+
assert job["status"] == "cancelled"
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class TestFileUpload:
|
|
197
|
+
"""Test file upload with GCS emulator."""
|
|
198
|
+
|
|
199
|
+
def test_upload_file(self, client, auth_headers):
|
|
200
|
+
"""Test uploading a file."""
|
|
201
|
+
test_file_content = b"fake audio data for testing"
|
|
202
|
+
|
|
203
|
+
response = client.post(
|
|
204
|
+
"/api/jobs/upload",
|
|
205
|
+
headers={"Authorization": auth_headers["Authorization"]},
|
|
206
|
+
files={"file": ("test.flac", test_file_content, "audio/flac")},
|
|
207
|
+
data={"artist": "Upload Artist", "title": "Upload Song"}
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
assert response.status_code == 200
|
|
211
|
+
data = response.json()
|
|
212
|
+
assert data["status"] == "success"
|
|
213
|
+
assert "job_id" in data
|
|
214
|
+
|
|
215
|
+
job_id = data["job_id"]
|
|
216
|
+
|
|
217
|
+
time.sleep(0.2)
|
|
218
|
+
|
|
219
|
+
# Verify job created with upload data
|
|
220
|
+
get_resp = client.get(f"/api/jobs/{job_id}", headers=auth_headers)
|
|
221
|
+
assert get_resp.status_code == 200
|
|
222
|
+
job = get_resp.json()
|
|
223
|
+
assert job["artist"] == "Upload Artist"
|
|
224
|
+
assert job["title"] == "Upload Song"
|
|
225
|
+
assert "input_media_gcs_path" in job
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class TestInternalEndpoints:
|
|
229
|
+
"""Test internal worker endpoints."""
|
|
230
|
+
|
|
231
|
+
def test_internal_workers_exist(self, client, auth_headers):
|
|
232
|
+
"""Test that internal worker endpoints exist and respond."""
|
|
233
|
+
# Create a job first
|
|
234
|
+
create_resp = client.post(
|
|
235
|
+
"/api/jobs",
|
|
236
|
+
headers=auth_headers,
|
|
237
|
+
json={"url": "https://youtube.com/watch?v=worker-test"}
|
|
238
|
+
)
|
|
239
|
+
job_id = create_resp.json()["job_id"]
|
|
240
|
+
|
|
241
|
+
time.sleep(0.2)
|
|
242
|
+
|
|
243
|
+
# Test audio worker endpoint exists
|
|
244
|
+
response = client.post(
|
|
245
|
+
"/api/internal/workers/audio",
|
|
246
|
+
headers=auth_headers,
|
|
247
|
+
json={"job_id": job_id}
|
|
248
|
+
)
|
|
249
|
+
assert response.status_code == 200
|
|
250
|
+
assert response.json()["status"] == "started"
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class TestReviewEndpoints:
|
|
254
|
+
"""Test lyrics review API endpoints."""
|
|
255
|
+
|
|
256
|
+
def test_review_ping(self, client, auth_headers):
|
|
257
|
+
"""Test review ping endpoint."""
|
|
258
|
+
# Create a job first
|
|
259
|
+
create_resp = client.post(
|
|
260
|
+
"/api/jobs",
|
|
261
|
+
headers=auth_headers,
|
|
262
|
+
json={"url": "https://youtube.com/watch?v=review-test"}
|
|
263
|
+
)
|
|
264
|
+
job_id = create_resp.json()["job_id"]
|
|
265
|
+
|
|
266
|
+
# Ping should work for any job
|
|
267
|
+
response = client.get(f"/api/review/{job_id}/ping")
|
|
268
|
+
assert response.status_code == 200
|
|
269
|
+
assert response.json()["status"] == "ok"
|
|
270
|
+
|
|
271
|
+
def test_review_correction_data_wrong_status(self, client, auth_headers):
|
|
272
|
+
"""Test that correction-data returns error for non-review jobs."""
|
|
273
|
+
# Create a job (starts in pending status)
|
|
274
|
+
create_resp = client.post(
|
|
275
|
+
"/api/jobs",
|
|
276
|
+
headers=auth_headers,
|
|
277
|
+
json={"url": "https://youtube.com/watch?v=review-status-test"}
|
|
278
|
+
)
|
|
279
|
+
job_id = create_resp.json()["job_id"]
|
|
280
|
+
|
|
281
|
+
time.sleep(0.2)
|
|
282
|
+
|
|
283
|
+
# Should fail - job not in AWAITING_REVIEW status
|
|
284
|
+
response = client.get(f"/api/review/{job_id}/correction-data", headers=auth_headers)
|
|
285
|
+
assert response.status_code == 400
|
|
286
|
+
assert "not ready for review" in response.json()["detail"].lower()
|
|
287
|
+
|
|
288
|
+
def test_review_audio_no_job(self, client, auth_headers):
|
|
289
|
+
"""Test audio endpoint returns 404 for nonexistent job."""
|
|
290
|
+
response = client.get("/api/review/nonexistent-job/audio/", headers=auth_headers)
|
|
291
|
+
assert response.status_code == 404
|
|
292
|
+
|
|
293
|
+
def test_review_preview_video_stub(self, client, auth_headers):
|
|
294
|
+
"""Test preview video endpoint exists."""
|
|
295
|
+
# Create a job
|
|
296
|
+
create_resp = client.post(
|
|
297
|
+
"/api/jobs",
|
|
298
|
+
headers=auth_headers,
|
|
299
|
+
json={"url": "https://youtube.com/watch?v=preview-test"}
|
|
300
|
+
)
|
|
301
|
+
job_id = create_resp.json()["job_id"]
|
|
302
|
+
|
|
303
|
+
# Preview video should return error since job not ready
|
|
304
|
+
response = client.post(
|
|
305
|
+
f"/api/review/{job_id}/preview-video",
|
|
306
|
+
headers=auth_headers,
|
|
307
|
+
json={"corrections": [], "corrected_segments": []}
|
|
308
|
+
)
|
|
309
|
+
# Should return 400 (job not in AWAITING_REVIEW state)
|
|
310
|
+
assert response.status_code == 400
|
|
311
|
+
|
|
312
|
+
def test_review_annotations_stub(self, client, auth_headers):
|
|
313
|
+
"""Test annotations endpoint (stub)."""
|
|
314
|
+
create_resp = client.post(
|
|
315
|
+
"/api/jobs",
|
|
316
|
+
headers=auth_headers,
|
|
317
|
+
json={"url": "https://youtube.com/watch?v=annotation-test"}
|
|
318
|
+
)
|
|
319
|
+
job_id = create_resp.json()["job_id"]
|
|
320
|
+
|
|
321
|
+
# Annotations endpoint should accept data (even if it just logs it)
|
|
322
|
+
response = client.post(
|
|
323
|
+
f"/api/review/{job_id}/v1/annotations",
|
|
324
|
+
headers=auth_headers,
|
|
325
|
+
json={"type": "test", "data": "test annotation"}
|
|
326
|
+
)
|
|
327
|
+
assert response.status_code == 200
|
|
328
|
+
assert response.json()["status"] == "success"
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class TestRenderVideoWorker:
|
|
332
|
+
"""Test render video worker endpoint."""
|
|
333
|
+
|
|
334
|
+
def test_render_video_worker_endpoint_exists(self, client, auth_headers):
|
|
335
|
+
"""Test that render-video worker endpoint exists."""
|
|
336
|
+
# Create a job
|
|
337
|
+
create_resp = client.post(
|
|
338
|
+
"/api/jobs",
|
|
339
|
+
headers=auth_headers,
|
|
340
|
+
json={"url": "https://youtube.com/watch?v=render-test"}
|
|
341
|
+
)
|
|
342
|
+
job_id = create_resp.json()["job_id"]
|
|
343
|
+
|
|
344
|
+
time.sleep(0.2)
|
|
345
|
+
|
|
346
|
+
# Test render-video worker endpoint exists
|
|
347
|
+
response = client.post(
|
|
348
|
+
"/api/internal/workers/render-video",
|
|
349
|
+
headers=auth_headers,
|
|
350
|
+
json={"job_id": job_id}
|
|
351
|
+
)
|
|
352
|
+
# Should return 200 (endpoint exists and starts)
|
|
353
|
+
assert response.status_code == 200
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
print("✅ Emulator integration tests ready to run")
|