@@ -127,7 +127,7 @@ def make_trigger_manager_mock() -> UiPathResumeTriggerProtocol:
127127 def create_trigger_impl (data : dict [str , Any ]) -> UiPathResumeTrigger :
128128 return UiPathResumeTrigger (
129129 interrupt_id = "" , # Will be set by resumable runtime
130- trigger_type = UiPathResumeTriggerType .API ,
130+ trigger_type = UiPathResumeTriggerType .TASK ,
131131 payload = data ,
132132 )
133133
@@ -453,3 +453,101 @@ async def read_trigger_impl(trigger: UiPathResumeTrigger) -> dict[str, Any]:
453453
454454 # Delegate should have been executed only once)
455455 assert runtime_impl .execution_count == 1
456+
457+ @pytest .mark .asyncio
458+ async def test_resumable_skips_api_triggers_on_auto_resume_check (self ) -> None :
459+ """API triggers should be skipped when checking for auto-resume after suspension."""
460+
461+ runtime_impl = MultiTriggerMockRuntime ()
462+ storage = StatefulStorageMock ()
463+ trigger_manager = make_trigger_manager_mock ()
464+
465+ # Create trigger manager that returns API trigger type
466+ def create_api_trigger (data : dict [str , Any ]) -> UiPathResumeTrigger :
467+ return UiPathResumeTrigger (
468+ interrupt_id = "" , # Will be set by resumable runtime
469+ trigger_type = UiPathResumeTriggerType .API ,
470+ payload = data ,
471+ )
472+
473+ trigger_manager .create_trigger = AsyncMock (side_effect = create_api_trigger ) # type: ignore
474+
475+ async def read_trigger_impl (trigger : UiPathResumeTrigger ) -> dict [str , Any ]:
476+ return {"approved" : True }
477+
478+ trigger_manager .read_trigger = AsyncMock (side_effect = read_trigger_impl ) # type: ignore
479+
480+ resumable = UiPathResumableRuntime (
481+ delegate = runtime_impl ,
482+ storage = storage ,
483+ trigger_manager = trigger_manager ,
484+ runtime_id = "runtime-1" ,
485+ )
486+
487+ # Execute - should suspend and NOT auto-resume because they are API triggers
488+ result = await resumable .execute ({})
489+
490+ assert result .status == UiPathRuntimeStatus .SUSPENDED
491+ assert result .triggers is not None
492+ assert len (result .triggers ) == 2
493+ assert {t .interrupt_id for t in result .triggers } == {"int-1" , "int-2" }
494+
495+ # Verify all triggers are API type
496+ assert all (
497+ t .trigger_type == UiPathResumeTriggerType .API for t in result .triggers
498+ )
499+
500+ # Delegate should have been executed only once (no auto-resume)
501+ assert runtime_impl .execution_count == 1
502+
503+ @pytest .mark .asyncio
504+ async def test_resumable_auto_resumes_task_triggers_but_not_api_triggers (
505+ self ,
506+ ) -> None :
507+ """Mixed triggers: TASK triggers should trigger auto-resume, API triggers should not."""
508+
509+ runtime_impl = MultiTriggerMockRuntime ()
510+ storage = StatefulStorageMock ()
511+ trigger_manager = make_trigger_manager_mock ()
512+
513+ # Create different trigger types: int-1 is TASK, int-2 is API
514+ def create_typed_trigger (data : dict [str , Any ]) -> UiPathResumeTrigger :
515+ # Determine trigger type based on payload action
516+ if "approve_branch_1" in str (data ):
517+ trigger_type = UiPathResumeTriggerType .TASK
518+ else :
519+ trigger_type = UiPathResumeTriggerType .API
520+
521+ return UiPathResumeTrigger (
522+ interrupt_id = "" , # Will be set by resumable runtime
523+ trigger_type = trigger_type ,
524+ payload = data ,
525+ )
526+
527+ trigger_manager .create_trigger = AsyncMock (side_effect = create_typed_trigger ) # type: ignore
528+
529+ # only TASK should trigger auto-resume
530+ async def read_trigger_impl (trigger : UiPathResumeTrigger ) -> dict [str , Any ]:
531+ return {"approved" : True }
532+
533+ trigger_manager .read_trigger = AsyncMock (side_effect = read_trigger_impl ) # type: ignore
534+
535+ resumable = UiPathResumableRuntime (
536+ delegate = runtime_impl ,
537+ storage = storage ,
538+ trigger_manager = trigger_manager ,
539+ runtime_id = "runtime-1" ,
540+ )
541+
542+ # Execute - should auto-resume based on int-1 (TASK) but skip int-2 (API)
543+ result = await resumable .execute ({})
544+
545+ # Should have auto-resumed once (because of TASK trigger)
546+ assert result .status == UiPathRuntimeStatus .SUSPENDED
547+ assert result .triggers is not None
548+
549+ # After auto-resume with int-1, should be at second suspension with int-2 and int-3
550+ assert {t .interrupt_id for t in result .triggers } == {"int-2" , "int-3" }
551+
552+ # Delegate should have been executed twice (initial + auto-resume for TASK trigger)
553+ assert runtime_impl .execution_count == 2
0 commit comments