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,365 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for backend/workers/worker_logging.py.
|
|
3
|
+
|
|
4
|
+
Tests the job logging utilities including JobLogger, JobLogHandler,
|
|
5
|
+
and job_logging_context for concurrent job isolation.
|
|
6
|
+
"""
|
|
7
|
+
import pytest
|
|
8
|
+
import logging
|
|
9
|
+
from unittest.mock import MagicMock, patch
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestJobLoggingContext:
|
|
13
|
+
"""Tests for job_logging_context context manager."""
|
|
14
|
+
|
|
15
|
+
def test_job_logging_context_sets_and_resets_job_id(self):
|
|
16
|
+
"""Test that job_logging_context sets and resets the current job ID."""
|
|
17
|
+
from backend.workers.worker_logging import job_logging_context, _current_job_id
|
|
18
|
+
|
|
19
|
+
# Initially no job
|
|
20
|
+
assert _current_job_id.get() is None
|
|
21
|
+
|
|
22
|
+
# Inside context, job is set
|
|
23
|
+
with job_logging_context("job123"):
|
|
24
|
+
assert _current_job_id.get() == "job123"
|
|
25
|
+
|
|
26
|
+
# After context, job is reset
|
|
27
|
+
assert _current_job_id.get() is None
|
|
28
|
+
|
|
29
|
+
def test_job_logging_context_nested(self):
|
|
30
|
+
"""Test nested job_logging_context calls."""
|
|
31
|
+
from backend.workers.worker_logging import job_logging_context, _current_job_id
|
|
32
|
+
|
|
33
|
+
with job_logging_context("outer_job"):
|
|
34
|
+
assert _current_job_id.get() == "outer_job"
|
|
35
|
+
|
|
36
|
+
with job_logging_context("inner_job"):
|
|
37
|
+
assert _current_job_id.get() == "inner_job"
|
|
38
|
+
|
|
39
|
+
# After inner context, outer job is restored
|
|
40
|
+
assert _current_job_id.get() == "outer_job"
|
|
41
|
+
|
|
42
|
+
assert _current_job_id.get() is None
|
|
43
|
+
|
|
44
|
+
def test_job_logging_context_handles_exception(self):
|
|
45
|
+
"""Test that job_logging_context resets even on exception."""
|
|
46
|
+
from backend.workers.worker_logging import job_logging_context, _current_job_id
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
with job_logging_context("job123"):
|
|
50
|
+
assert _current_job_id.get() == "job123"
|
|
51
|
+
raise ValueError("Test exception")
|
|
52
|
+
except ValueError:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
# Job should still be reset after exception
|
|
56
|
+
assert _current_job_id.get() is None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class TestJobLogHandler:
|
|
60
|
+
"""Tests for JobLogHandler class."""
|
|
61
|
+
|
|
62
|
+
@pytest.fixture
|
|
63
|
+
def mock_job_manager(self):
|
|
64
|
+
"""Create a mock JobManager."""
|
|
65
|
+
manager = MagicMock()
|
|
66
|
+
manager.append_worker_log.return_value = None
|
|
67
|
+
return manager
|
|
68
|
+
|
|
69
|
+
def test_job_log_handler_init(self, mock_job_manager):
|
|
70
|
+
"""Test JobLogHandler initialization."""
|
|
71
|
+
from backend.workers.worker_logging import JobLogHandler
|
|
72
|
+
|
|
73
|
+
handler = JobLogHandler(
|
|
74
|
+
job_id="job123",
|
|
75
|
+
worker_name="audio",
|
|
76
|
+
job_manager=mock_job_manager
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
assert handler.job_id == "job123"
|
|
80
|
+
assert handler.worker_name == "audio"
|
|
81
|
+
assert handler.job_manager == mock_job_manager
|
|
82
|
+
assert handler.level == logging.INFO
|
|
83
|
+
|
|
84
|
+
def test_job_log_handler_custom_level(self, mock_job_manager):
|
|
85
|
+
"""Test JobLogHandler with custom level."""
|
|
86
|
+
from backend.workers.worker_logging import JobLogHandler
|
|
87
|
+
|
|
88
|
+
handler = JobLogHandler(
|
|
89
|
+
job_id="job123",
|
|
90
|
+
worker_name="audio",
|
|
91
|
+
job_manager=mock_job_manager,
|
|
92
|
+
level=logging.DEBUG
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert handler.level == logging.DEBUG
|
|
96
|
+
|
|
97
|
+
def test_job_log_handler_emit_logs_to_firestore(self, mock_job_manager):
|
|
98
|
+
"""Test that emit() calls job_manager.append_worker_log."""
|
|
99
|
+
from backend.workers.worker_logging import JobLogHandler, job_logging_context
|
|
100
|
+
|
|
101
|
+
handler = JobLogHandler(
|
|
102
|
+
job_id="job123",
|
|
103
|
+
worker_name="audio",
|
|
104
|
+
job_manager=mock_job_manager
|
|
105
|
+
)
|
|
106
|
+
handler.setFormatter(logging.Formatter('%(message)s'))
|
|
107
|
+
|
|
108
|
+
# Create a log record
|
|
109
|
+
record = logging.LogRecord(
|
|
110
|
+
name="test",
|
|
111
|
+
level=logging.INFO,
|
|
112
|
+
pathname="",
|
|
113
|
+
lineno=0,
|
|
114
|
+
msg="Test message",
|
|
115
|
+
args=(),
|
|
116
|
+
exc_info=None
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Emit within the job context
|
|
120
|
+
with job_logging_context("job123"):
|
|
121
|
+
handler.emit(record)
|
|
122
|
+
|
|
123
|
+
# Should have called append_worker_log
|
|
124
|
+
mock_job_manager.append_worker_log.assert_called()
|
|
125
|
+
|
|
126
|
+
def test_job_log_handler_filters_other_job_context(self, mock_job_manager):
|
|
127
|
+
"""Test that handler filters logs from other job contexts."""
|
|
128
|
+
from backend.workers.worker_logging import JobLogHandler, job_logging_context
|
|
129
|
+
|
|
130
|
+
handler = JobLogHandler(
|
|
131
|
+
job_id="job123",
|
|
132
|
+
worker_name="audio",
|
|
133
|
+
job_manager=mock_job_manager
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
record = logging.LogRecord(
|
|
137
|
+
name="test",
|
|
138
|
+
level=logging.INFO,
|
|
139
|
+
pathname="",
|
|
140
|
+
lineno=0,
|
|
141
|
+
msg="Test message",
|
|
142
|
+
args=(),
|
|
143
|
+
exc_info=None
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Emit from a different job's context
|
|
147
|
+
with job_logging_context("different_job"):
|
|
148
|
+
handler.emit(record)
|
|
149
|
+
|
|
150
|
+
# Should NOT have called append_worker_log
|
|
151
|
+
mock_job_manager.append_worker_log.assert_not_called()
|
|
152
|
+
|
|
153
|
+
def test_job_log_handler_deduplication(self, mock_job_manager):
|
|
154
|
+
"""Test that handler deduplicates repeated messages."""
|
|
155
|
+
from backend.workers.worker_logging import JobLogHandler, job_logging_context
|
|
156
|
+
|
|
157
|
+
handler = JobLogHandler(
|
|
158
|
+
job_id="job123",
|
|
159
|
+
worker_name="audio",
|
|
160
|
+
job_manager=mock_job_manager
|
|
161
|
+
)
|
|
162
|
+
handler.setFormatter(logging.Formatter('%(message)s'))
|
|
163
|
+
|
|
164
|
+
record = logging.LogRecord(
|
|
165
|
+
name="test",
|
|
166
|
+
level=logging.INFO,
|
|
167
|
+
pathname="",
|
|
168
|
+
lineno=0,
|
|
169
|
+
msg="Duplicate message",
|
|
170
|
+
args=(),
|
|
171
|
+
exc_info=None
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
with job_logging_context("job123"):
|
|
175
|
+
# Emit the same record twice
|
|
176
|
+
handler.emit(record)
|
|
177
|
+
handler.emit(record)
|
|
178
|
+
|
|
179
|
+
# Should only be called once due to deduplication
|
|
180
|
+
assert mock_job_manager.append_worker_log.call_count == 1
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class TestJobLogger:
|
|
184
|
+
"""Tests for JobLogger class."""
|
|
185
|
+
|
|
186
|
+
@pytest.fixture
|
|
187
|
+
def mock_job_manager(self):
|
|
188
|
+
"""Create a mock JobManager."""
|
|
189
|
+
manager = MagicMock()
|
|
190
|
+
manager.append_worker_log.return_value = None
|
|
191
|
+
return manager
|
|
192
|
+
|
|
193
|
+
def test_job_logger_init(self, mock_job_manager):
|
|
194
|
+
"""Test JobLogger initialization."""
|
|
195
|
+
from backend.workers.worker_logging import JobLogger
|
|
196
|
+
|
|
197
|
+
logger = JobLogger(
|
|
198
|
+
job_id="job123",
|
|
199
|
+
worker_name="lyrics",
|
|
200
|
+
job_manager=mock_job_manager
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
assert logger.job_id == "job123"
|
|
204
|
+
assert logger.worker_name == "lyrics"
|
|
205
|
+
assert logger.job_manager == mock_job_manager
|
|
206
|
+
|
|
207
|
+
def test_job_logger_info(self, mock_job_manager):
|
|
208
|
+
"""Test JobLogger.info() method."""
|
|
209
|
+
from backend.workers.worker_logging import JobLogger
|
|
210
|
+
|
|
211
|
+
logger = JobLogger(
|
|
212
|
+
job_id="job123",
|
|
213
|
+
worker_name="lyrics",
|
|
214
|
+
job_manager=mock_job_manager
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
logger.info("Processing started")
|
|
218
|
+
|
|
219
|
+
mock_job_manager.append_worker_log.assert_called_with(
|
|
220
|
+
job_id="job123",
|
|
221
|
+
worker="lyrics",
|
|
222
|
+
level="INFO",
|
|
223
|
+
message="Processing started"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def test_job_logger_warning(self, mock_job_manager):
|
|
227
|
+
"""Test JobLogger.warning() method."""
|
|
228
|
+
from backend.workers.worker_logging import JobLogger
|
|
229
|
+
|
|
230
|
+
logger = JobLogger(
|
|
231
|
+
job_id="job123",
|
|
232
|
+
worker_name="audio",
|
|
233
|
+
job_manager=mock_job_manager
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
logger.warning("Low memory")
|
|
237
|
+
|
|
238
|
+
mock_job_manager.append_worker_log.assert_called_with(
|
|
239
|
+
job_id="job123",
|
|
240
|
+
worker="audio",
|
|
241
|
+
level="WARNING",
|
|
242
|
+
message="Low memory"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def test_job_logger_error(self, mock_job_manager):
|
|
246
|
+
"""Test JobLogger.error() method."""
|
|
247
|
+
from backend.workers.worker_logging import JobLogger
|
|
248
|
+
|
|
249
|
+
logger = JobLogger(
|
|
250
|
+
job_id="job123",
|
|
251
|
+
worker_name="video",
|
|
252
|
+
job_manager=mock_job_manager
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
logger.error("Processing failed")
|
|
256
|
+
|
|
257
|
+
mock_job_manager.append_worker_log.assert_called_with(
|
|
258
|
+
job_id="job123",
|
|
259
|
+
worker="video",
|
|
260
|
+
level="ERROR",
|
|
261
|
+
message="Processing failed"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
def test_job_logger_debug(self, mock_job_manager):
|
|
265
|
+
"""Test JobLogger.debug() method."""
|
|
266
|
+
from backend.workers.worker_logging import JobLogger
|
|
267
|
+
|
|
268
|
+
logger = JobLogger(
|
|
269
|
+
job_id="job123",
|
|
270
|
+
worker_name="screens",
|
|
271
|
+
job_manager=mock_job_manager
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
logger.debug("Debug info")
|
|
275
|
+
|
|
276
|
+
mock_job_manager.append_worker_log.assert_called_with(
|
|
277
|
+
job_id="job123",
|
|
278
|
+
worker="screens",
|
|
279
|
+
level="DEBUG",
|
|
280
|
+
message="Debug info"
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
def test_job_logger_with_format_args(self, mock_job_manager):
|
|
284
|
+
"""Test JobLogger with format arguments."""
|
|
285
|
+
from backend.workers.worker_logging import JobLogger
|
|
286
|
+
|
|
287
|
+
logger = JobLogger(
|
|
288
|
+
job_id="job123",
|
|
289
|
+
worker_name="audio",
|
|
290
|
+
job_manager=mock_job_manager
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
logger.info("Processing %s of %d", "audio", 10)
|
|
294
|
+
|
|
295
|
+
mock_job_manager.append_worker_log.assert_called_with(
|
|
296
|
+
job_id="job123",
|
|
297
|
+
worker="audio",
|
|
298
|
+
level="INFO",
|
|
299
|
+
message="Processing audio of 10"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
def test_job_logger_handles_firestore_error(self, mock_job_manager):
|
|
303
|
+
"""Test that JobLogger handles Firestore errors gracefully."""
|
|
304
|
+
from backend.workers.worker_logging import JobLogger
|
|
305
|
+
|
|
306
|
+
mock_job_manager.append_worker_log.side_effect = Exception("Firestore error")
|
|
307
|
+
|
|
308
|
+
logger = JobLogger(
|
|
309
|
+
job_id="job123",
|
|
310
|
+
worker_name="audio",
|
|
311
|
+
job_manager=mock_job_manager
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Should not raise exception
|
|
315
|
+
logger.info("Test message")
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class TestCreateJobLogger:
|
|
319
|
+
"""Tests for create_job_logger function."""
|
|
320
|
+
|
|
321
|
+
def test_create_job_logger(self):
|
|
322
|
+
"""Test create_job_logger creates a JobLogger."""
|
|
323
|
+
from backend.workers.worker_logging import create_job_logger, JobLogger
|
|
324
|
+
|
|
325
|
+
# Patch at the source module where JobManager is imported
|
|
326
|
+
with patch('backend.services.job_manager.JobManager'):
|
|
327
|
+
logger = create_job_logger("job123", "audio")
|
|
328
|
+
|
|
329
|
+
assert isinstance(logger, JobLogger)
|
|
330
|
+
assert logger.job_id == "job123"
|
|
331
|
+
assert logger.worker_name == "audio"
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class TestSetupJobLogging:
|
|
335
|
+
"""Tests for setup_job_logging function."""
|
|
336
|
+
|
|
337
|
+
def test_setup_job_logging_returns_handler(self):
|
|
338
|
+
"""Test setup_job_logging returns a JobLogHandler."""
|
|
339
|
+
from backend.workers.worker_logging import setup_job_logging, JobLogHandler
|
|
340
|
+
|
|
341
|
+
# Patch at the source module
|
|
342
|
+
with patch('backend.services.job_manager.JobManager'):
|
|
343
|
+
handler = setup_job_logging("job123", "lyrics", "test_logger_wl1")
|
|
344
|
+
|
|
345
|
+
assert isinstance(handler, JobLogHandler)
|
|
346
|
+
assert handler.job_id == "job123"
|
|
347
|
+
assert handler.worker_name == "lyrics"
|
|
348
|
+
|
|
349
|
+
# Clean up - remove the handler
|
|
350
|
+
logging.getLogger("test_logger_wl1").removeHandler(handler)
|
|
351
|
+
|
|
352
|
+
def test_setup_job_logging_adds_handler_to_loggers(self):
|
|
353
|
+
"""Test setup_job_logging adds handler to specified loggers."""
|
|
354
|
+
from backend.workers.worker_logging import setup_job_logging
|
|
355
|
+
|
|
356
|
+
with patch('backend.services.job_manager.JobManager'):
|
|
357
|
+
handler = setup_job_logging("job123", "audio", "test_logger_wl2", "test_logger_wl3")
|
|
358
|
+
|
|
359
|
+
# Check handlers are added
|
|
360
|
+
assert handler in logging.getLogger("test_logger_wl2").handlers
|
|
361
|
+
assert handler in logging.getLogger("test_logger_wl3").handlers
|
|
362
|
+
|
|
363
|
+
# Clean up
|
|
364
|
+
logging.getLogger("test_logger_wl2").removeHandler(handler)
|
|
365
|
+
logging.getLogger("test_logger_wl3").removeHandler(handler)
|