1+ import asyncio
12import logging
3+ from concurrent .futures import Executor
24
35import pytest
46
57from taskiq import InMemoryBroker , TaskiqRouter
8+ from taskiq .acks import AcknowledgeType
69from taskiq .cli .worker .args import WorkerArgs
7- from taskiq .cli .worker .run import get_broker_selection , shutdown_broker
10+ from taskiq .cli .worker .run import get_broker_selection , shutdown_broker , start_listen
11+ from taskiq .receiver import Receiver
12+ from taskiq .receiver .runtime import BrokerSelection , WorkerRuntimeOptions
813from taskiq .warnings import TaskiqDeprecationWarning
914
1015
@@ -17,6 +22,103 @@ def test_get_broker_selection_preserves_direct_single_broker() -> None:
1722 assert not selection .explicit_group
1823
1924
25+ def test_start_listen_builds_runtime_from_worker_arguments (
26+ monkeypatch : pytest .MonkeyPatch ,
27+ ) -> None :
28+ broker = InMemoryBroker ()
29+ args = WorkerArgs (
30+ broker = broker ,
31+ modules = ["application.tasks" ],
32+ tasks_pattern = ("**/jobs.py" ,),
33+ fs_discover = True ,
34+ configure_logging = False ,
35+ max_threadpool_threads = 2 ,
36+ no_parse = True ,
37+ shutdown_timeout = 0.7 ,
38+ max_async_tasks = 7 ,
39+ max_async_tasks_jitter = 2 ,
40+ receiver_arg = [("dependency" , "value" )],
41+ max_prefetch = 3 ,
42+ no_propagate_errors = True ,
43+ ack_type = AcknowledgeType .WHEN_RECEIVED ,
44+ max_tasks_per_child = 11 ,
45+ wait_tasks_timeout = 0.5 ,
46+ )
47+ loop = asyncio .new_event_loop ()
48+ captured_selection : BrokerSelection | None = None
49+ captured_receiver_type : type [Receiver ] | None = None
50+ captured_executor : Executor | None = None
51+ captured_options : WorkerRuntimeOptions | None = None
52+ runtime_ran = False
53+ import_calls : list [tuple [list [str ], tuple [str , ...], bool ]] = []
54+
55+ class RecordingRuntime :
56+ def __init__ (
57+ self ,
58+ * ,
59+ selection : BrokerSelection ,
60+ receiver_type : type [Receiver ],
61+ executor : Executor ,
62+ options : WorkerRuntimeOptions ,
63+ ) -> None :
64+ nonlocal captured_selection
65+ nonlocal captured_receiver_type
66+ nonlocal captured_executor
67+ nonlocal captured_options
68+ captured_selection = selection
69+ captured_receiver_type = receiver_type
70+ captured_executor = executor
71+ captured_options = options
72+
73+ async def run (self , finish_event : asyncio .Event ) -> None :
74+ nonlocal runtime_ran
75+ runtime_ran = True
76+ finish_event .set ()
77+
78+ def record_import_tasks (
79+ modules : list [str ],
80+ task_pattern : tuple [str , ...],
81+ fs_discover : bool ,
82+ ) -> None :
83+ import_calls .append ((modules , task_pattern , fs_discover ))
84+
85+ monkeypatch .setattr ("taskiq.cli.worker.run.uvloop" , None )
86+ monkeypatch .setattr ("taskiq.cli.worker.run.signal.signal" , lambda * args : None )
87+ monkeypatch .setattr ("taskiq.cli.worker.run.asyncio.new_event_loop" , lambda : loop )
88+ monkeypatch .setattr ("taskiq.cli.worker.run.import_tasks" , record_import_tasks )
89+ monkeypatch .setattr (
90+ "taskiq.cli.worker.run.get_receiver_type" ,
91+ lambda args : Receiver ,
92+ )
93+ monkeypatch .setattr ("taskiq.cli.worker.run.WorkerRuntime" , RecordingRuntime )
94+
95+ try :
96+ start_listen (args )
97+ finally :
98+ asyncio .set_event_loop (None )
99+ loop .close ()
100+
101+ assert captured_selection is not None
102+ assert captured_selection .listeners == (broker ,)
103+ assert broker .is_worker_process
104+ assert captured_receiver_type is Receiver
105+ assert captured_executor is not None
106+ assert captured_options == WorkerRuntimeOptions (
107+ validate_params = False ,
108+ max_async_tasks = 7 ,
109+ max_async_tasks_jitter = 2 ,
110+ max_prefetch = 3 ,
111+ propagate_exceptions = False ,
112+ ack_type = AcknowledgeType .WHEN_RECEIVED ,
113+ max_tasks_to_execute = 11 ,
114+ wait_tasks_timeout = 0.5 ,
115+ shutdown_timeout = 0.7 ,
116+ receiver_kwargs = {"dependency" : "value" },
117+ )
118+ assert import_calls == [(["application.tasks" ], ("**/jobs.py" ,), True )]
119+ assert runtime_ran
120+
121+
20122@pytest .mark .parametrize ("use_factory" , [False , True ])
21123def test_get_broker_selection_preserves_imported_single_broker (
22124 monkeypatch : pytest .MonkeyPatch ,
@@ -67,6 +169,25 @@ async def shutdown(self) -> None:
67169 assert diagnostic not in caplog .text
68170
69171
172+ async def test_legacy_shutdown_broker_helper_preserves_cancellation () -> None :
173+ shutdown_error = asyncio .CancelledError ("adapter shutdown cancelled" )
174+
175+ class CancelledShutdownBroker (InMemoryBroker ):
176+ async def shutdown (self ) -> None :
177+ raise shutdown_error
178+
179+ broker = CancelledShutdownBroker ()
180+
181+ with (
182+ pytest .warns (
183+ TaskiqDeprecationWarning ,
184+ match = "WorkerRuntime" ,
185+ ),
186+ pytest .raises (asyncio .CancelledError ),
187+ ):
188+ await shutdown_broker (broker , timeout = 1 )
189+
190+
70191@pytest .mark .parametrize ("use_factory" , [False , True ])
71192def test_get_broker_selection_imports_explicit_listener_sequence (
72193 monkeypatch : pytest .MonkeyPatch ,
0 commit comments