103 lines
3.2 KiB
Python
103 lines
3.2 KiB
Python
"""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
|