88import pytest
99
1010from apify_client ._models import Run
11+ from crawlee import service_locator
1112from crawlee .events import Event , EventAbortingData
1213
13- from apify import Actor
14+ from apify import Actor , Configuration
1415from apify ._actor import _ActorType
1516from apify ._child_runs import CHILD_RUNS_KEY , ChildRunRegistry
17+ from apify .events import ApifyEventManager
1618
1719if TYPE_CHECKING :
1820 from ..conftest import ApifyClientAsyncPatcher
@@ -39,6 +41,14 @@ def make_run(run_id: str, status: str) -> Run:
3941 )
4042
4143
44+ @pytest .fixture
45+ def apify_event_manager () -> ApifyEventManager :
46+ """Make the Actor use `ApifyEventManager`, which delivers `ABORTING` on the platform, without a websocket."""
47+ event_manager = ApifyEventManager (Configuration .get_global_configuration ())
48+ service_locator .set_event_manager (event_manager )
49+ return event_manager
50+
51+
4252async def record_child_run (name : str , run_id : str , * , actor_id : str = 'some-actor' ) -> None :
4353 """Seed the registry the way an earlier attempt of this Actor run would have left it."""
4454 kvs = await Actor .open_key_value_store ()
@@ -452,7 +462,7 @@ async def test_reattach_replaces_recorded_abort_with_parent(
452462
453463
454464async def test_aborting_event_aborts_marked_active_child_runs (
455- apify_client_async_patcher : ApifyClientAsyncPatcher ,
465+ apify_client_async_patcher : ApifyClientAsyncPatcher , apify_event_manager : ApifyEventManager
456466) -> None :
457467 """On `ABORTING`, only child runs marked `abort_with_parent` that are still active are gracefully aborted."""
458468 runs = {
@@ -482,16 +492,18 @@ async def get_run(run_client: Any, *_args: Any, **_kwargs: Any) -> Run | None:
482492 ]
483493 },
484494 )
485- Actor . event_manager .emit (event = Event .ABORTING , event_data = EventAbortingData ())
486- await Actor . event_manager .wait_for_all_listeners_to_complete ()
495+ apify_event_manager .emit (event = Event .ABORTING , event_data = EventAbortingData ())
496+ await apify_event_manager .wait_for_all_listeners_to_complete ()
487497
488498 aborts = apify_client_async_patcher .calls ['run' ]['abort' ]
489499 assert sorted (args [0 ].resource_id for args , _ in aborts ) == ['ready-run' , 'running-run' ]
490500 assert all (kwargs == {'gracefully' : True } for _ , kwargs in aborts )
491501
492502
493503async def test_failed_child_run_abort_does_not_stop_others (
494- apify_client_async_patcher : ApifyClientAsyncPatcher , caplog : pytest .LogCaptureFixture
504+ apify_client_async_patcher : ApifyClientAsyncPatcher ,
505+ caplog : pytest .LogCaptureFixture ,
506+ apify_event_manager : ApifyEventManager ,
495507) -> None :
496508 """A child run that fails to abort is logged, and the other marked child runs are still aborted."""
497509
@@ -513,8 +525,8 @@ async def abort_run(run_client: Any, *_args: Any, **_kwargs: Any) -> None:
513525 for name in ['broken' , 'healthy' ]
514526 },
515527 )
516- Actor . event_manager .emit (event = Event .ABORTING , event_data = EventAbortingData ())
517- await Actor . event_manager .wait_for_all_listeners_to_complete ()
528+ apify_event_manager .emit (event = Event .ABORTING , event_data = EventAbortingData ())
529+ await apify_event_manager .wait_for_all_listeners_to_complete ()
518530
519531 aborts = apify_client_async_patcher .calls ['run' ]['abort' ]
520532 assert sorted (args [0 ].resource_id for args , _ in aborts ) == ['broken-run' , 'healthy-run' ]
@@ -608,16 +620,35 @@ async def start_run() -> Run:
608620 client .run .return_value .abort .assert_awaited_once_with (gracefully = True )
609621
610622
611- async def test_exit_removes_the_aborting_listener (apify_client_async_patcher : ApifyClientAsyncPatcher ) -> None :
623+ async def test_exit_removes_the_aborting_listener (
624+ apify_client_async_patcher : ApifyClientAsyncPatcher , apify_event_manager : ApifyEventManager
625+ ) -> None :
612626 """After the Actor exits, an `ABORTING` event on a still-active event manager aborts no child run."""
613627 apify_client_async_patcher .patch ('actor' , 'start' , return_value = make_run ('new-run' , 'READY' ))
614628 apify_client_async_patcher .patch ('run' , 'get' , return_value = make_run ('new-run' , 'RUNNING' ))
615629 apify_client_async_patcher .patch ('run' , 'abort' , return_value = None )
616630
617- async with Actor . event_manager :
631+ async with apify_event_manager :
618632 async with Actor :
619633 await Actor .start ('some-actor' , name = 'scrape-eu' , abort_with_parent = True )
620- Actor . event_manager .emit (event = Event .ABORTING , event_data = EventAbortingData ())
621- await Actor . event_manager .wait_for_all_listeners_to_complete ()
634+ apify_event_manager .emit (event = Event .ABORTING , event_data = EventAbortingData ())
635+ await apify_event_manager .wait_for_all_listeners_to_complete ()
622636
623637 assert apify_client_async_patcher .calls ['run' ]['abort' ] == []
638+
639+
640+ async def test_removing_all_aborting_listeners_keeps_aborting_child_runs (
641+ apify_client_async_patcher : ApifyClientAsyncPatcher , apify_event_manager : ApifyEventManager
642+ ) -> None :
643+ """Removing all `ABORTING` listeners from the event manager still aborts child runs marked `abort_with_parent`."""
644+ apify_client_async_patcher .patch ('actor' , 'start' , return_value = make_run ('new-run' , 'READY' ))
645+ apify_client_async_patcher .patch ('run' , 'get' , return_value = make_run ('new-run' , 'RUNNING' ))
646+ apify_client_async_patcher .patch ('run' , 'abort' , return_value = None )
647+
648+ async with Actor :
649+ await Actor .start ('some-actor' , name = 'scrape-eu' , abort_with_parent = True )
650+ apify_event_manager .off (event = Event .ABORTING )
651+ apify_event_manager .emit (event = Event .ABORTING , event_data = EventAbortingData ())
652+ await apify_event_manager .wait_for_all_listeners_to_complete ()
653+
654+ assert len (apify_client_async_patcher .calls ['run' ]['abort' ]) == 1
0 commit comments