14
14
15
15
from apify_client import ApifyClient , ApifyClientAsync
16
16
from 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
18
18
19
19
_MOCKED_API_URL = 'https://example.com'
20
20
_MOCKED_RUN_ID = 'mocked_run_id'
54
54
),
55
55
)
56
56
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
+
57
64
58
65
@pytest .fixture
59
66
def 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 ):
63
70
yield httpx .Response (
64
71
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
+ }
67
96
),
68
97
status_code = 200 ,
69
98
)
70
99
while True :
71
100
yield httpx .Response (
72
101
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
+ }
75
111
),
76
112
status_code = 200 ,
77
113
)
@@ -133,6 +169,15 @@ def propagate_stream_logs() -> None:
133
169
logging .getLogger (f'apify.{ _MOCKED_ACTOR_NAME } -{ _MOCKED_RUN_ID } ' ).setLevel (logging .DEBUG )
134
170
135
171
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
+
136
181
@pytest .mark .parametrize (
137
182
('log_from_start' , 'expected_log_count' ),
138
183
[
@@ -215,6 +260,7 @@ async def test_actor_call_redirect_logs_to_default_logger_async(
215
260
caplog : LogCaptureFixture ,
216
261
mock_api_async : None , # noqa: ARG001, fixture
217
262
propagate_stream_logs : None , # noqa: ARG001, fixture
263
+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
218
264
) -> None :
219
265
"""Test that logs are redirected correctly to the default logger.
220
266
@@ -231,8 +277,8 @@ async def test_actor_call_redirect_logs_to_default_logger_async(
231
277
assert isinstance (logger .handlers [0 ], logging .StreamHandler )
232
278
233
279
# 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 ):
236
282
assert expected_message_and_level [0 ] == record .message
237
283
assert expected_message_and_level [1 ] == record .levelno
238
284
@@ -242,6 +288,7 @@ def test_actor_call_redirect_logs_to_default_logger_sync(
242
288
caplog : LogCaptureFixture ,
243
289
mock_api_sync : None , # noqa: ARG001, fixture
244
290
propagate_stream_logs : None , # noqa: ARG001, fixture
291
+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
245
292
) -> None :
246
293
"""Test that logs are redirected correctly to the default logger.
247
294
@@ -258,8 +305,8 @@ def test_actor_call_redirect_logs_to_default_logger_sync(
258
305
assert isinstance (logger .handlers [0 ], logging .StreamHandler )
259
306
260
307
# 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 ):
263
310
assert expected_message_and_level [0 ] == record .message
264
311
assert expected_message_and_level [1 ] == record .levelno
265
312
@@ -299,6 +346,7 @@ async def test_actor_call_redirect_logs_to_custom_logger_async(
299
346
caplog : LogCaptureFixture ,
300
347
mock_api_async : None , # noqa: ARG001, fixture
301
348
propagate_stream_logs : None , # noqa: ARG001, fixture
349
+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
302
350
) -> None :
303
351
"""Test that logs are redirected correctly to the custom logger."""
304
352
logger_name = 'custom_logger'
@@ -308,8 +356,8 @@ async def test_actor_call_redirect_logs_to_custom_logger_async(
308
356
with caplog .at_level (logging .DEBUG , logger = logger_name ):
309
357
await run_client .call (logger = logger )
310
358
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 ):
313
361
assert expected_message_and_level [0 ] == record .message
314
362
assert expected_message_and_level [1 ] == record .levelno
315
363
@@ -319,6 +367,7 @@ def test_actor_call_redirect_logs_to_custom_logger_sync(
319
367
caplog : LogCaptureFixture ,
320
368
mock_api_sync : None , # noqa: ARG001, fixture
321
369
propagate_stream_logs : None , # noqa: ARG001, fixture
370
+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
322
371
) -> None :
323
372
"""Test that logs are redirected correctly to the custom logger."""
324
373
logger_name = 'custom_logger'
@@ -328,51 +377,56 @@ def test_actor_call_redirect_logs_to_custom_logger_sync(
328
377
with caplog .at_level (logging .DEBUG , logger = logger_name ):
329
378
run_client .call (logger = logger )
330
379
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 ):
333
382
assert expected_message_and_level [0 ] == record .message
334
383
assert expected_message_and_level [1 ] == record .levelno
335
384
385
+
336
386
@respx .mock
337
387
async def test_redirect_status_message_async (
338
388
* ,
339
389
caplog : LogCaptureFixture ,
340
390
mock_api : None , # noqa: ARG001, fixture
341
391
propagate_stream_logs : None , # noqa: ARG001, fixture
392
+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
342
393
) -> None :
343
394
"""Test redirected status and status messages."""
344
395
345
396
run_client = ApifyClientAsync (token = 'mocked_token' , api_url = _MOCKED_API_URL ).run (run_id = _MOCKED_RUN_ID )
346
397
347
398
logger_name = f'apify.{ _MOCKED_ACTOR_NAME } -{ _MOCKED_RUN_ID } '
348
399
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 ))
350
401
with caplog .at_level (logging .DEBUG , logger = logger_name ):
351
402
async with status_message_redirector :
352
403
# 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'
354
409
355
- assert caplog .records [0 ].message == 'Status: RUNNING, Message: Status 1'
356
- assert caplog .records [1 ].message == 'Status: SUCCEEDED, Message: Status 2'
357
410
358
411
@respx .mock
359
412
def test_redirect_status_message_sync (
360
413
* ,
361
414
caplog : LogCaptureFixture ,
362
415
mock_api : None , # noqa: ARG001, fixture
363
416
propagate_stream_logs : None , # noqa: ARG001, fixture
417
+ reduce_final_timeout_for_status_message_redirector : None , # noqa: ARG001, fixture
364
418
) -> None :
365
419
"""Test redirected status and status messages."""
366
420
367
421
run_client = ApifyClient (token = 'mocked_token' , api_url = _MOCKED_API_URL ).run (run_id = _MOCKED_RUN_ID )
368
422
369
423
logger_name = f'apify.{ _MOCKED_ACTOR_NAME } -{ _MOCKED_RUN_ID } '
370
424
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 )
376
429
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