diff --git a/backend/app/celery_signals.py b/backend/app/celery_signals.py index 222a565..a6e007c 100644 --- a/backend/app/celery_signals.py +++ b/backend/app/celery_signals.py @@ -69,7 +69,15 @@ def _queue_for(task) -> str: return "ml" if name.startswith("backend.app.tasks.thumbnail."): return "thumbnail" - if name.startswith("backend.app.tasks.download."): + if name.startswith(( + "backend.app.tasks.download.", + # External file-host fetches share the download lane (celery_app + # routes external.* → download). Mirror it here or TaskRun.queue + # lies 'default' for them, so per-queue dashboard filters and the + # per-queue threshold override miss them — the same gap the + # 2026-06-02 audit fixed for backup/admin/library_audit. + "backend.app.tasks.external.", + )): return "download" if name.startswith("backend.app.tasks.scan."): return "scan" diff --git a/tests/test_celery_routing.py b/tests/test_celery_routing.py index cbba0b7..8a7cb67 100644 --- a/tests/test_celery_routing.py +++ b/tests/test_celery_routing.py @@ -18,3 +18,21 @@ def test_long_one_shots_route_to_maintenance_long(): def test_quick_maintenance_stays_on_maintenance(): routes = celery.conf.task_routes assert routes["backend.app.tasks.maintenance.*"]["queue"] == "maintenance" + + +def test_queue_for_mirrors_external_to_download(): + """celery_signals._queue_for is a hand-maintained mirror of task_routes + that stamps TaskRun.queue. external.* routes to the download lane, so the + mirror must agree — else TaskRun.queue lies 'default' for external fetches + and per-queue dashboard filters / threshold overrides miss them + (operator-flagged 2026-06-17).""" + from backend.app.celery_signals import _queue_for + + class _T: + name = "backend.app.tasks.external.fetch_external_link" + + assert _queue_for(_T()) == "download" + assert ( + celery.conf.task_routes["backend.app.tasks.external.*"]["queue"] + == "download" + )