"""Настоящий воркер dramatiq на брокере в памяти: actor'ы, event loop AsyncIO, пул окон и heartbeat.""" import asyncio import dramatiq import fakeredis import pytest from api import actions from tasks import store as task_store from tasks.broker import COMMON_QUEUE, personal_queue, task_message from worker import windows from .conftest import FakeSession, fake_execute @pytest.fixture def worker(monkeypatch, tmp_path): server = fakeredis.FakeServer() monkeypatch.setattr(task_store, "connect", lambda url=None: fakeredis.aioredis.FakeRedis(server=server, decode_responses=True)) monkeypatch.setattr(windows, "AsyncStealthySession", FakeSession) monkeypatch.setattr(windows, "USER_DATA_ROOT", tmp_path) monkeypatch.setattr(actions, "execute", fake_execute()) FakeSession.instances, FakeSession.error = [], None from db.engine import connect as connect_db from worker import actors monkeypatch.setattr(actors, "connect_db", lambda: connect_db(f"sqlite+aiosqlite:///{tmp_path / 'robotex.db'}")) w = dramatiq.Worker(actors.broker, worker_timeout=100, worker_threads=2) w.start() try: yield actors, server finally: w.stop() # при остановке воркер закрывает окна и снимает heartbeat assert all(s.closed for s in FakeSession.instances) assert not fakeredis.FakeRedis(server=server).keys("robotex:worker:*") SID = "11111111-1111-1111-1111-111111111111" def submit(actors, server, queue, idle=0): """То же, что делает api: задача в Redis, сообщение в очередь; ждём, пока воркер её выполнит.""" store = task_store.TaskStore(fakeredis.aioredis.FakeRedis(server=server, decode_responses=True)) asyncio.run(store.create("t1", SID, "https://example.com")) actors.broker.enqueue(task_message("t1", SID, {"url": "https://example.com", "actions": []}, idle, queue)) actors.broker.join(queue) return fakeredis.FakeRedis(server=server, decode_responses=True) @pytest.mark.parametrize("queue", ["common", "personal"]) def test_task_executed_from_queue(worker, queue): actors, server = worker # имя воркера — первое свободное в Redis (#110), персональная очередь объявлена до запуска потоков assert actors._pool.worker_id == "worker-1" redis = submit(actors, server, COMMON_QUEUE if queue == "common" else personal_queue("worker-1")) task = redis.hgetall("robotex:task:t1") assert task["status"] == "done", task assert '"status": "ok"' in task["result"] assert redis.exists("robotex:worker:worker-1") def test_window_held_until_shutdown(worker): actors, server = worker redis = submit(actors, server, COMMON_QUEUE, idle=5) assert redis.hgetall(f"robotex:session:{SID}")["worker_id"] == "worker-1" assert not FakeSession.instances[0].closed