"""Тесты постановки задач backend-продюсерами `Celery.send_task`. `pipeline_producer`/`invitations_producer` создают собственный (голый) Celery-клиент без `task_routes` из `workers/celery_app.py` (backend не импортирует пакет `workers`, см. докстрины модулей) — очередь этим клиентам нужно передавать явным параметром `queue=` при каждой отправке, иначе задача уйдёт в дефолтную очередь `celery`, а не в изолированную очередь семьи задач. """ import uuid from unittest.mock import MagicMock import pytest from kombu.exceptions import OperationalError from services import invitations_producer, pipeline_producer @pytest.fixture(autouse=True) def _fake_celery_client(monkeypatch: pytest.MonkeyPatch) -> MagicMock: """Подменить `celery.Celery` фейком, чтобы не создавать реальное broker-подключение.""" instance = MagicMock() factory = MagicMock(return_value=instance) monkeypatch.setattr(pipeline_producer, "Celery", factory) monkeypatch.setattr(invitations_producer, "Celery", factory) return instance def test_enqueue_pipeline_uses_transcription_queue(_fake_celery_client: MagicMock) -> None: session_id = uuid.uuid4() pipeline_producer.enqueue_pipeline(session_id) _fake_celery_client.send_task.assert_called_once_with( pipeline_producer.RUN_PIPELINE_TASK_NAME, args=[str(session_id)], queue="transcription", ) def test_enqueue_invitations_uses_notify_queue(_fake_celery_client: MagicMock) -> None: conference_id = uuid.uuid4() invitations_producer.enqueue_invitations(conference_id, emails=["a@example.com"]) _fake_celery_client.send_task.assert_called_once_with( invitations_producer.SEND_INVITATIONS_TASK_NAME, args=[str(conference_id), ["a@example.com"]], queue="notify", ) def test_transcription_queue_served_true_when_worker_reports_queue( _fake_celery_client: MagicMock, ) -> None: """`active_queues()` вернул хотя бы одного воркера с очередью `transcription` среди прочих его очередей.""" _fake_celery_client.control.inspect.return_value.active_queues.return_value = { "celery@worker1": [{"name": "notify"}, {"name": "transcription"}], } assert pipeline_producer.transcription_queue_served() is True _fake_celery_client.control.inspect.assert_called_once_with(timeout=1.0) def test_transcription_queue_served_false_when_no_worker_replies( _fake_celery_client: MagicMock, ) -> None: """`active_queues()` вернул `None` (нет воркеров либо брокер недоступен) → `False`.""" _fake_celery_client.control.inspect.return_value.active_queues.return_value = None assert pipeline_producer.transcription_queue_served() is False def test_transcription_queue_served_false_when_worker_active_but_other_queue( _fake_celery_client: MagicMock, ) -> None: """Воркер(ы) ответили, но никто не слушает именно `transcription` → `False`.""" _fake_celery_client.control.inspect.return_value.active_queues.return_value = { "celery@worker1": [{"name": "notify"}], } assert pipeline_producer.transcription_queue_served() is False def test_transcription_queue_served_false_when_broker_unavailable( _fake_celery_client: MagicMock, ) -> None: """Недоступный брокер (Redis лежит) кидает `OperationalError`, а не возвращает `None`, — по контракту («нет воркеров ИЛИ брокер недоступен → `False`») исключение не должно пробрасываться наружу (500 в `GET`/`PUT /admin/settings`).""" _fake_celery_client.control.inspect.return_value.active_queues.side_effect = OperationalError( "Error 61 connecting to localhost:6379. Connection refused." ) assert pipeline_producer.transcription_queue_served() is False