"""Tests für Queue-Worker mit fakeredis.""" from __future__ import annotations from collections.abc import Generator from datetime import datetime from unittest.mock import patch import fakeredis import pytest from src.models.submission import Submission from src.queue.handler import QueueHandler from src.queue.worker import verarbeite_submission, worker_loop class BreakLoopError(Exception): """Exception zum Durchbrechen der worker_loop-Endlosschleife in Tests.""" @pytest.fixture def handler() -> Generator[QueueHandler]: """QueueHandler mit fakeredis-Backend.""" fake_redis = fakeredis.FakeRedis(decode_responses=True) qh = QueueHandler.__new__(QueueHandler) qh.redis = fake_redis qh.stream_key = "bsn:submissions" qh.processed_set = "bsn:processed" qh.stream_ids_hash = "bsn:stream_ids" qh.group_name = "bsn-workers" qh._ensure_consumer_group() yield qh fake_redis.flushall() def _make_submission(message_id: str = "msg-001") -> Submission: """Erzeugt eine Test-Submission.""" return Submission( message_id=message_id, plattform="whatsapp", absender="+491****7890", text="Testnachricht für Queue-Worker.", zeitstempel=datetime(2025, 1, 1, 12, 0, 0), ) def test_verarbeite_submission_setzt_status_auf_erledigt() -> None: """verarbeite_submission setzt den Status auf 'erledigt'.""" sub = _make_submission("msg-001") assert sub.status == "neu" result = verarbeite_submission(sub) assert result is True assert sub.status == "erledigt" def test_worker_loop_verarbeitet_alle_submissions(handler: QueueHandler) -> None: """worker_loop verarbeitet alle Submissions in der Queue.""" # Mehrere Submissions in die Queue einreihen subs = [_make_submission(f"msg-w{i}") for i in range(3)] for sub in subs: handler.enqueue(sub) # Verifiziere: Queue enthält 3 Einträge assert handler.queue_size() == 3 # time.sleep so mocken, dass die Schleife nach einer Leer-Poll-Iteration abbricht def _raise_on_sleep(seconds: int) -> None: # noqa: ARG001 raise BreakLoopError with patch("src.queue.worker.time.sleep", side_effect=_raise_on_sleep): try: worker_loop(handler, poll_interval=1, batch_size=5) except BreakLoopError: pass # Nach Verarbeitung: dequeue liefert keine neuen Messages mehr # (alle wurden per XACK bestätigt und sind nicht mehr "new") remaining = handler.dequeue(count=10) assert len(remaining) == 0, ( f"Erwartet 0 verbleibende Submissions, aber {len(remaining)} gefunden" ) def test_worker_loop_stoppt_bei_leerer_queue(handler: QueueHandler) -> None: """worker_loop pollt leere Queue ohne Fehler (bricht nach erster sleep-Iteration ab).""" call_count = 0 def _raise_after_first_sleep(seconds: int) -> None: # noqa: ARG001 nonlocal call_count call_count += 1 raise BreakLoopError with patch("src.queue.worker.time.sleep", side_effect=_raise_after_first_sleep): try: worker_loop(handler, poll_interval=1, batch_size=5) except BreakLoopError: pass # Sicherstellen, dass sleep genau einmal aufgerufen wurde (leere Queue → sleep) assert call_count == 1