karaoke-gen 0.99.3__py3-none-any.whl → 0.103.1__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.
Files changed (55) hide show
  1. backend/api/routes/admin.py +512 -1
  2. backend/api/routes/audio_search.py +17 -34
  3. backend/api/routes/file_upload.py +60 -84
  4. backend/api/routes/internal.py +6 -0
  5. backend/api/routes/jobs.py +11 -3
  6. backend/api/routes/rate_limits.py +428 -0
  7. backend/api/routes/review.py +13 -6
  8. backend/api/routes/tenant.py +120 -0
  9. backend/api/routes/users.py +229 -247
  10. backend/config.py +16 -0
  11. backend/exceptions.py +66 -0
  12. backend/main.py +30 -1
  13. backend/middleware/__init__.py +7 -1
  14. backend/middleware/tenant.py +192 -0
  15. backend/models/job.py +19 -3
  16. backend/models/tenant.py +208 -0
  17. backend/models/user.py +18 -0
  18. backend/services/email_service.py +253 -6
  19. backend/services/email_validation_service.py +646 -0
  20. backend/services/firestore_service.py +27 -0
  21. backend/services/job_defaults_service.py +113 -0
  22. backend/services/job_manager.py +73 -3
  23. backend/services/rate_limit_service.py +641 -0
  24. backend/services/stripe_service.py +61 -35
  25. backend/services/tenant_service.py +285 -0
  26. backend/services/user_service.py +85 -7
  27. backend/tests/conftest.py +7 -1
  28. backend/tests/emulator/test_made_for_you_integration.py +167 -0
  29. backend/tests/test_admin_job_files.py +337 -0
  30. backend/tests/test_admin_job_reset.py +384 -0
  31. backend/tests/test_admin_job_update.py +326 -0
  32. backend/tests/test_audio_search.py +12 -8
  33. backend/tests/test_email_service.py +233 -0
  34. backend/tests/test_email_validation_service.py +298 -0
  35. backend/tests/test_file_upload.py +8 -6
  36. backend/tests/test_impersonation.py +223 -0
  37. backend/tests/test_job_creation_regression.py +4 -0
  38. backend/tests/test_job_manager.py +146 -1
  39. backend/tests/test_made_for_you.py +2088 -0
  40. backend/tests/test_models.py +139 -0
  41. backend/tests/test_rate_limit_service.py +396 -0
  42. backend/tests/test_rate_limits_api.py +392 -0
  43. backend/tests/test_tenant_api.py +350 -0
  44. backend/tests/test_tenant_middleware.py +345 -0
  45. backend/tests/test_tenant_models.py +406 -0
  46. backend/tests/test_tenant_service.py +418 -0
  47. backend/workers/video_worker.py +8 -3
  48. backend/workers/video_worker_orchestrator.py +26 -0
  49. {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.103.1.dist-info}/METADATA +1 -1
  50. {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.103.1.dist-info}/RECORD +55 -33
  51. lyrics_transcriber/frontend/src/api.ts +13 -5
  52. lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +90 -57
  53. {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.103.1.dist-info}/WHEEL +0 -0
  54. {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.103.1.dist-info}/entry_points.txt +0 -0
  55. {karaoke_gen-0.99.3.dist-info → karaoke_gen-0.103.1.dist-info}/licenses/LICENSE +0 -0
@@ -313,9 +313,154 @@ class TestJobFailure:
313
313
  assert call_args[1]['error_message'] == error_message
314
314
 
315
315
 
316
+ class TestMadeForYouFieldMapping:
317
+ """
318
+ CRITICAL: Test that made-for-you fields are properly copied from JobCreate to Job.
319
+
320
+ These tests verify that JobManager.create_job() properly maps all fields from
321
+ JobCreate to the Job object that gets persisted to Firestore.
322
+
323
+ Bug context (2026-01-09): Production made-for-you orders were created with
324
+ made_for_you=False, customer_email=None, customer_notes=None because
325
+ JobManager.create_job() wasn't copying these fields from JobCreate to Job.
326
+ """
327
+
328
+ def test_create_job_copies_made_for_you_flag(self, job_manager, mock_firestore_service):
329
+ """
330
+ CRITICAL: made_for_you flag must be copied from JobCreate to Job.
331
+
332
+ This flag is essential for:
333
+ - Ownership transfer on job completion
334
+ - Email suppression for intermediate states
335
+ - Identifying made-for-you jobs in admin UI
336
+ """
337
+ job_create = JobCreate(
338
+ artist="Test Artist",
339
+ title="Test Song",
340
+ theme_id="nomad",
341
+ made_for_you=True, # This MUST be copied to the Job
342
+ )
343
+
344
+ job = job_manager.create_job(job_create)
345
+
346
+ # CRITICAL ASSERTION: made_for_you must be True on the created Job
347
+ assert job.made_for_you is True, \
348
+ "made_for_you=True on JobCreate must be copied to Job"
349
+
350
+ # Also verify what was saved to Firestore
351
+ mock_firestore_service.create_job.assert_called_once()
352
+ saved_job = mock_firestore_service.create_job.call_args[0][0]
353
+ assert saved_job.made_for_you is True, \
354
+ "made_for_you=True must be persisted to Firestore"
355
+
356
+ def test_create_job_copies_customer_email(self, job_manager, mock_firestore_service):
357
+ """
358
+ CRITICAL: customer_email must be copied from JobCreate to Job.
359
+
360
+ This field is essential for:
361
+ - Ownership transfer on job completion (transferring to this email)
362
+ - Sending completion email with download links to customer
363
+ """
364
+ job_create = JobCreate(
365
+ artist="Test Artist",
366
+ title="Test Song",
367
+ theme_id="nomad",
368
+ made_for_you=True,
369
+ customer_email="customer@example.com", # This MUST be copied to the Job
370
+ )
371
+
372
+ job = job_manager.create_job(job_create)
373
+
374
+ # CRITICAL ASSERTION: customer_email must be copied
375
+ assert job.customer_email == "customer@example.com", \
376
+ "customer_email on JobCreate must be copied to Job"
377
+
378
+ # Also verify what was saved to Firestore
379
+ saved_job = mock_firestore_service.create_job.call_args[0][0]
380
+ assert saved_job.customer_email == "customer@example.com", \
381
+ "customer_email must be persisted to Firestore"
382
+
383
+ def test_create_job_copies_customer_notes(self, job_manager, mock_firestore_service):
384
+ """
385
+ customer_notes must be copied from JobCreate to Job.
386
+
387
+ Customer notes contain special requests that admin needs to see.
388
+ """
389
+ job_create = JobCreate(
390
+ artist="Test Artist",
391
+ title="Test Song",
392
+ theme_id="nomad",
393
+ made_for_you=True,
394
+ customer_notes="Please make this perfect for my wedding!",
395
+ )
396
+
397
+ job = job_manager.create_job(job_create)
398
+
399
+ assert job.customer_notes == "Please make this perfect for my wedding!", \
400
+ "customer_notes on JobCreate must be copied to Job"
401
+
402
+ saved_job = mock_firestore_service.create_job.call_args[0][0]
403
+ assert saved_job.customer_notes == "Please make this perfect for my wedding!", \
404
+ "customer_notes must be persisted to Firestore"
405
+
406
+ def test_create_job_full_made_for_you_config(self, job_manager, mock_firestore_service):
407
+ """
408
+ Test complete made-for-you job creation with all fields.
409
+
410
+ This is the realistic scenario: a made-for-you order creates a job with:
411
+ - made_for_you=True
412
+ - user_email=admin (owner during processing)
413
+ - customer_email=customer (for final delivery)
414
+ - customer_notes=notes (customer's special requests)
415
+ """
416
+ job_create = JobCreate(
417
+ artist="Avril Lavigne",
418
+ title="Complicated",
419
+ theme_id="nomad",
420
+ made_for_you=True,
421
+ user_email="admin@nomadkaraoke.com",
422
+ customer_email="customer@example.com",
423
+ customer_notes="Anniversary gift!",
424
+ # Distribution settings that should also be applied
425
+ enable_youtube_upload=True,
426
+ dropbox_path="/Production/Ready",
427
+ brand_prefix="NOMAD",
428
+ )
429
+
430
+ job = job_manager.create_job(job_create)
431
+
432
+ # Verify all made-for-you fields
433
+ assert job.made_for_you is True
434
+ assert job.user_email == "admin@nomadkaraoke.com"
435
+ assert job.customer_email == "customer@example.com"
436
+ assert job.customer_notes == "Anniversary gift!"
437
+
438
+ # Verify distribution settings too
439
+ assert job.enable_youtube_upload is True
440
+ assert job.dropbox_path == "/Production/Ready"
441
+ assert job.brand_prefix == "NOMAD"
442
+
443
+ def test_create_job_made_for_you_false_by_default(self, job_manager, mock_firestore_service):
444
+ """
445
+ Regular jobs should have made_for_you=False by default.
446
+ """
447
+ job_create = JobCreate(
448
+ artist="Test Artist",
449
+ title="Test Song",
450
+ theme_id="nomad",
451
+ # No made_for_you specified - should default to False
452
+ )
453
+
454
+ job = job_manager.create_job(job_create)
455
+
456
+ assert job.made_for_you is False
457
+ assert job.customer_email is None
458
+ assert job.customer_notes is None
459
+
460
+
316
461
  class TestJobDeletion:
317
462
  """Test job deletion logic."""
318
-
463
+
319
464
  def test_delete_job(self, job_manager, mock_firestore_service):
320
465
  """Test deleting a job."""
321
466
  existing_job = Job(