1414
1515from apify_client import ApifyClient , ApifyClientAsync
1616from apify_client ._logging import RedirectLogFormatter
17- from apify_client .clients .resource_clients .log import StreamedLog , StatusMessageRedirector
17+ from apify_client .clients .resource_clients .log import StatusMessageRedirector , StreamedLog
1818
1919_MOCKED_API_URL = 'https://example.com'
2020_MOCKED_RUN_ID = 'mocked_run_id'
5454 ),
5555)
5656
57+ _EXPECTED_MESSAGES_AND_LEVELS_WITH_STATUS_MESSAGES = (
58+ ('Status: RUNNING, Message: Initial message' , logging .INFO ),
59+ * _EXPECTED_MESSAGES_AND_LEVELS ,
60+ ('Status: RUNNING, Message: Another message' , logging .INFO ),
61+ ('Status: SUCCEEDED, Message: Final message' , logging .INFO ),
62+ )
63+
5764
5865@pytest .fixture
5966def mock_api () -> None :
60-
61- def create_status_responses_generator ():
62- for i in range (10 ):
67+ def create_status_responses_generator () -> Iterator [ httpx . Response ]:
68+ """Simulate actor run that changes status 3 times."""
69+ for _ in range (5 ):
6370 yield httpx .Response (
6471 content = json .dumps (
65- {'data' : {'id' : _MOCKED_RUN_ID , 'actId' : _MOCKED_ACTOR_ID , 'status' : ActorJobStatus .RUNNING ,
66- 'statusMessage' : f'Status { i } ' , 'isStatusMessageTerminal' : False }}
72+ {
73+ 'data' : {
74+ 'id' : _MOCKED_RUN_ID ,
75+ 'actId' : _MOCKED_ACTOR_ID ,
76+ 'status' : ActorJobStatus .RUNNING ,
77+ 'statusMessage' : 'Initial message' ,
78+ 'isStatusMessageTerminal' : False ,
79+ }
80+ }
81+ ),
82+ status_code = 200 ,
83+ )
84+ for _ in range (5 ):
85+ yield httpx .Response (
86+ content = json .dumps (
87+ {
88+ 'data' : {
89+ 'id' : _MOCKED_RUN_ID ,
90+ 'actId' : _MOCKED_ACTOR_ID ,
91+ 'status' : ActorJobStatus .RUNNING ,
92+ 'statusMessage' : 'Another message' ,
93+ 'isStatusMessageTerminal' : False ,
94+ }
95+ }
6796 ),
6897 status_code = 200 ,
6998 )
7099 while True :
71100 yield httpx .Response (
72101 content = json .dumps (
73- {'data' : {'id' : _MOCKED_RUN_ID , 'actId' : _MOCKED_ACTOR_ID , 'status' : ActorJobStatus .SUCCEEDED ,
74- 'statusMessage' : f'Status 101' , 'isStatusMessageTerminal' : True }}
102+ {
103+ 'data' : {
104+ 'id' : _MOCKED_RUN_ID ,
105+ 'actId' : _MOCKED_ACTOR_ID ,
106+ 'status' : ActorJobStatus .SUCCEEDED ,
107+ 'statusMessage' : 'Final message' ,
108+ 'isStatusMessageTerminal' : True ,
109+ }
110+ }
75111 ),
76112 status_code = 200 ,
77113 )
@@ -133,6 +169,15 @@ def propagate_stream_logs() -> None:
133169 logging .getLogger (f'apify.{ _MOCKED_ACTOR_NAME } -{ _MOCKED_RUN_ID } ' ).setLevel (logging .DEBUG )
134170
135171
172+ @pytest .fixture
173+ def reduce_final_timeout_for_status_message_redirector () -> None :
174+ """Reduce timeout used by the `StatusMessageRedirector`
175+
176+ This timeout makes sense on the platform, but in tests it is better to reduce it to speed up the tests.
177+ """
178+ StatusMessageRedirector ._final_sleep_time_s = 2
179+
180+
136181@pytest .mark .parametrize (
137182 ('log_from_start' , 'expected_log_count' ),
138183 [
@@ -215,6 +260,7 @@ async def test_actor_call_redirect_logs_to_default_logger_async(
215260 caplog : LogCaptureFixture ,
216261 mock_api_async : None , # noqa: ARG001, fixture
217262 propagate_stream_logs : None , # noqa: ARG001, fixture
263+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
218264) -> None :
219265 """Test that logs are redirected correctly to the default logger.
220266
@@ -231,8 +277,8 @@ async def test_actor_call_redirect_logs_to_default_logger_async(
231277 assert isinstance (logger .handlers [0 ], logging .StreamHandler )
232278
233279 # Ensure logs are propagated
234- assert len (caplog .records ) == len (_EXPECTED_MESSAGES_AND_LEVELS )
235- for expected_message_and_level , record in zip (_EXPECTED_MESSAGES_AND_LEVELS , caplog .records ):
280+ assert len (caplog .records ) == len (_EXPECTED_MESSAGES_AND_LEVELS_WITH_STATUS_MESSAGES )
281+ for expected_message_and_level , record in zip (_EXPECTED_MESSAGES_AND_LEVELS_WITH_STATUS_MESSAGES , caplog .records ):
236282 assert expected_message_and_level [0 ] == record .message
237283 assert expected_message_and_level [1 ] == record .levelno
238284
@@ -242,6 +288,7 @@ def test_actor_call_redirect_logs_to_default_logger_sync(
242288 caplog : LogCaptureFixture ,
243289 mock_api_sync : None , # noqa: ARG001, fixture
244290 propagate_stream_logs : None , # noqa: ARG001, fixture
291+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
245292) -> None :
246293 """Test that logs are redirected correctly to the default logger.
247294
@@ -258,8 +305,8 @@ def test_actor_call_redirect_logs_to_default_logger_sync(
258305 assert isinstance (logger .handlers [0 ], logging .StreamHandler )
259306
260307 # Ensure logs are propagated
261- assert len (caplog .records ) == len (_EXPECTED_MESSAGES_AND_LEVELS )
262- for expected_message_and_level , record in zip (_EXPECTED_MESSAGES_AND_LEVELS , caplog .records ):
308+ assert len (caplog .records ) == len (_EXPECTED_MESSAGES_AND_LEVELS_WITH_STATUS_MESSAGES )
309+ for expected_message_and_level , record in zip (_EXPECTED_MESSAGES_AND_LEVELS_WITH_STATUS_MESSAGES , caplog .records ):
263310 assert expected_message_and_level [0 ] == record .message
264311 assert expected_message_and_level [1 ] == record .levelno
265312
@@ -299,6 +346,7 @@ async def test_actor_call_redirect_logs_to_custom_logger_async(
299346 caplog : LogCaptureFixture ,
300347 mock_api_async : None , # noqa: ARG001, fixture
301348 propagate_stream_logs : None , # noqa: ARG001, fixture
349+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
302350) -> None :
303351 """Test that logs are redirected correctly to the custom logger."""
304352 logger_name = 'custom_logger'
@@ -308,8 +356,8 @@ async def test_actor_call_redirect_logs_to_custom_logger_async(
308356 with caplog .at_level (logging .DEBUG , logger = logger_name ):
309357 await run_client .call (logger = logger )
310358
311- assert len (caplog .records ) == len (_EXPECTED_MESSAGES_AND_LEVELS )
312- for expected_message_and_level , record in zip (_EXPECTED_MESSAGES_AND_LEVELS , caplog .records ):
359+ assert len (caplog .records ) == len (_EXPECTED_MESSAGES_AND_LEVELS_WITH_STATUS_MESSAGES )
360+ for expected_message_and_level , record in zip (_EXPECTED_MESSAGES_AND_LEVELS_WITH_STATUS_MESSAGES , caplog .records ):
313361 assert expected_message_and_level [0 ] == record .message
314362 assert expected_message_and_level [1 ] == record .levelno
315363
@@ -319,6 +367,7 @@ def test_actor_call_redirect_logs_to_custom_logger_sync(
319367 caplog : LogCaptureFixture ,
320368 mock_api_sync : None , # noqa: ARG001, fixture
321369 propagate_stream_logs : None , # noqa: ARG001, fixture
370+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
322371) -> None :
323372 """Test that logs are redirected correctly to the custom logger."""
324373 logger_name = 'custom_logger'
@@ -328,51 +377,56 @@ def test_actor_call_redirect_logs_to_custom_logger_sync(
328377 with caplog .at_level (logging .DEBUG , logger = logger_name ):
329378 run_client .call (logger = logger )
330379
331- assert len (caplog .records ) == len (_EXPECTED_MESSAGES_AND_LEVELS )
332- for expected_message_and_level , record in zip (_EXPECTED_MESSAGES_AND_LEVELS , caplog .records ):
380+ assert len (caplog .records ) == len (_EXPECTED_MESSAGES_AND_LEVELS_WITH_STATUS_MESSAGES )
381+ for expected_message_and_level , record in zip (_EXPECTED_MESSAGES_AND_LEVELS_WITH_STATUS_MESSAGES , caplog .records ):
333382 assert expected_message_and_level [0 ] == record .message
334383 assert expected_message_and_level [1 ] == record .levelno
335384
385+
336386@respx .mock
337387async def test_redirect_status_message_async (
338388 * ,
339389 caplog : LogCaptureFixture ,
340390 mock_api : None , # noqa: ARG001, fixture
341391 propagate_stream_logs : None , # noqa: ARG001, fixture
392+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
342393) -> None :
343394 """Test redirected status and status messages."""
344395
345396 run_client = ApifyClientAsync (token = 'mocked_token' , api_url = _MOCKED_API_URL ).run (run_id = _MOCKED_RUN_ID )
346397
347398 logger_name = f'apify.{ _MOCKED_ACTOR_NAME } -{ _MOCKED_RUN_ID } '
348399
349- status_message_redirector = await run_client .get_status_message_redirector (check_period = timedelta (seconds = 0.1 ))
400+ status_message_redirector = await run_client .get_status_message_redirector (check_period = timedelta (seconds = 0 ))
350401 with caplog .at_level (logging .DEBUG , logger = logger_name ):
351402 async with status_message_redirector :
352403 # Do stuff while the status from the other actor is being redirected to the logs.
353- await asyncio .sleep (2 )
404+ await asyncio .sleep (3 )
405+
406+ assert caplog .records [0 ].message == 'Status: RUNNING, Message: Initial message'
407+ assert caplog .records [1 ].message == 'Status: RUNNING, Message: Another message'
408+ assert caplog .records [2 ].message == 'Status: SUCCEEDED, Message: Final message'
354409
355- assert caplog .records [0 ].message == 'Status: RUNNING, Message: Status 1'
356- assert caplog .records [1 ].message == 'Status: SUCCEEDED, Message: Status 2'
357410
358411@respx .mock
359412def test_redirect_status_message_sync (
360413 * ,
361414 caplog : LogCaptureFixture ,
362415 mock_api : None , # noqa: ARG001, fixture
363416 propagate_stream_logs : None , # noqa: ARG001, fixture
417+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
364418) -> None :
365419 """Test redirected status and status messages."""
366420
367421 run_client = ApifyClient (token = 'mocked_token' , api_url = _MOCKED_API_URL ).run (run_id = _MOCKED_RUN_ID )
368422
369423 logger_name = f'apify.{ _MOCKED_ACTOR_NAME } -{ _MOCKED_RUN_ID } '
370424
371- status_message_redirector = run_client .get_status_message_redirector (check_period = timedelta (seconds = 0.1 ))
372- with caplog .at_level (logging .DEBUG , logger = logger_name ):
373- with status_message_redirector :
374- # Do stuff while the status from the other actor is being redirected to the logs.
375- time .sleep (2 )
425+ status_message_redirector = run_client .get_status_message_redirector (check_period = timedelta (seconds = 0 ))
426+ with caplog .at_level (logging .DEBUG , logger = logger_name ), status_message_redirector :
427+ # Do stuff while the status from the other actor is being redirected to the logs.
428+ time .sleep (3 )
376429
377- assert caplog .records [0 ].message == 'Status: RUNNING, Message: Status 1'
378- assert caplog .records [1 ].message == 'Status: SUCCEEDED, Message: Status 2'
430+ assert caplog .records [0 ].message == 'Status: RUNNING, Message: Initial message'
431+ assert caplog .records [1 ].message == 'Status: RUNNING, Message: Another message'
432+ assert caplog .records [2 ].message == 'Status: SUCCEEDED, Message: Final message'
0 commit comments