diff --git a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/checkpoint/effects.py b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/checkpoint/effects.py index 9472028c..69d642e5 100644 --- a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/checkpoint/effects.py +++ b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/checkpoint/effects.py @@ -34,7 +34,7 @@ class Failed: """The execution failed with ``error``.""" execution_arn: str - error: ErrorObject + error: ErrorObject | None @dataclass(frozen=True) diff --git a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/checkpoint/processors/execution.py b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/checkpoint/processors/execution.py index f8b29321..c235a465 100644 --- a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/checkpoint/processors/execution.py +++ b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/checkpoint/processors/execution.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING from aws_durable_execution_sdk_python.lambda_service import ( - ErrorObject, Operation, OperationAction, OperationUpdate, @@ -41,15 +40,8 @@ def process( ) case _: # intentional. actual service will fail any EXECUTION update that is not SUCCEED. - error = ( - update.error - if update.error - else ErrorObject.from_message( - "There is no error details but EXECUTION checkpoint action is not SUCCEED." - ) - ) # All EXECUTION failures go through normal fail path # Timeout/Stop status is set by executor based on the operation that caused it - notifier.notify_failed(execution_arn=execution_arn, error=error) + notifier.notify_failed(execution_arn=execution_arn, error=update.error) # TODO: Svc doesn't actually create checkpoint for EXECUTION. might have to for localrunner though. return None diff --git a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/execution.py b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/execution.py index a4f54b53..e894caf1 100644 --- a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/execution.py +++ b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/execution.py @@ -406,7 +406,9 @@ def complete_success(self, result: str | None, now: datetime | None = None) -> N self.close_status = ExecutionStatus.SUCCEEDED self._end_execution(OperationStatus.SUCCEEDED, now) - def complete_fail(self, error: ErrorObject, now: datetime | None = None) -> None: + def complete_fail( + self, error: ErrorObject | None, now: datetime | None = None + ) -> None: """Complete execution with failure (DecisionType.FAIL_WORKFLOW_EXECUTION).""" self.result = DurableExecutionInvocationOutput( status=InvocationStatus.FAILED, error=error diff --git a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/executor.py b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/executor.py index f11e39f6..3ace22b7 100644 --- a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/executor.py +++ b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/executor.py @@ -1285,9 +1285,7 @@ def _validate_invocation_response_and_store( ) raise InvalidParameterValueException(msg_failed_result) logger.info("[%s] Execution failed", execution_arn) - self._complete_workflow( - execution_arn, result=None, error=response.error - ) + self._fail_workflow(execution_arn, response.error) case InvocationStatus.SUCCEEDED: if response.error is not None: @@ -1591,7 +1589,7 @@ def _complete_workflow( else: self.complete_execution(execution_arn, result) - def _fail_workflow(self, execution_arn: str, error: ErrorObject): + def _fail_workflow(self, execution_arn: str, error: ErrorObject | None): """Fail workflow with terminal state validation.""" execution = self._store.load(execution_arn) @@ -1671,8 +1669,8 @@ def complete_execution(self, execution_arn: str, result: str | None = None) -> N raise IllegalStateException(msg) self._complete_events(execution_arn=execution_arn) - def fail_execution(self, execution_arn: str, error: ErrorObject) -> None: - """Fail execution with error (FAIL_WORKFLOW_EXECUTION decision).""" + def fail_execution(self, execution_arn: str, error: ErrorObject | None) -> None: + """Fail execution with optional error (FAIL_WORKFLOW_EXECUTION decision).""" logger.error("[%s] Completing execution with error: %s", execution_arn, error) execution: Execution = self._store.load(execution_arn=execution_arn) execution.complete_fail(error=error, now=self._clock.now()) @@ -1688,7 +1686,7 @@ def on_completed(self, execution_arn: str, result: str | None = None) -> None: """Complete execution successfully. Observer method triggered by notifier.""" self.complete_execution(execution_arn, result) - def on_failed(self, execution_arn: str, error: ErrorObject) -> None: + def on_failed(self, execution_arn: str, error: ErrorObject | None) -> None: """Fail execution. Observer method triggered by notifier.""" self.fail_execution(execution_arn, error) diff --git a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/observer.py b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/observer.py index e1ea7435..86ef8263 100644 --- a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/observer.py +++ b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/observer.py @@ -41,7 +41,7 @@ def on_completed(self, execution_arn: str, result: str | None = None) -> None: """Called when execution completes successfully.""" @abstractmethod - def on_failed(self, execution_arn: str, error: ErrorObject) -> None: + def on_failed(self, execution_arn: str, error: ErrorObject | None) -> None: """Called when execution fails.""" @abstractmethod @@ -77,7 +77,7 @@ def notify_completed(self, execution_arn: str, result: str | None = None) -> Non """Record that the execution completed successfully.""" self.effects.append(Completed(execution_arn=execution_arn, result=result)) - def notify_failed(self, execution_arn: str, error: ErrorObject) -> None: + def notify_failed(self, execution_arn: str, error: ErrorObject | None) -> None: """Record that the execution failed.""" self.effects.append(Failed(execution_arn=execution_arn, error=error)) diff --git a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/runner.py b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/runner.py index 13c4d17b..28f719a0 100644 --- a/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/runner.py +++ b/packages/aws-durable-execution-sdk-python-testing/src/aws_durable_execution_sdk_python_testing/runner.py @@ -45,6 +45,7 @@ ResourceNotFoundException, ) from aws_durable_execution_sdk_python_testing.executor import Executor +from aws_durable_execution_sdk_python_testing.execution import ExecutionStatus from aws_durable_execution_sdk_python_testing.invoker import ( InProcessInvoker, LambdaInvoker, @@ -498,6 +499,7 @@ class DurableFunctionTestResult: operations: list[Operation] result: OperationPayload | None = None error: ErrorObject | None = None + execution_status: ExecutionStatus | None = None @classmethod def create(cls, execution: Execution) -> DurableFunctionTestResult: @@ -513,12 +515,16 @@ def create(cls, execution: Execution) -> DurableFunctionTestResult: if execution.result is None: msg: str = "Execution result must exist to create test result." raise DurableFunctionsTestError(msg) + if execution.close_status is None: + msg_status: str = "Execution close status must exist to create test result." + raise DurableFunctionsTestError(msg_status) return cls( status=execution.result.status, operations=operations, result=execution.result.result, error=execution.result.error, + execution_status=execution.close_status, ) @classmethod @@ -541,6 +547,16 @@ def from_execution_history( ) status = InvocationStatus.FAILED + # Map overall execution status string separately from invocation status. + try: + execution_status = ExecutionStatus[execution_response.status] + except KeyError: + logger.warning( + "Unknown execution status: %s, defaulting to FAILED", + execution_response.status, + ) + execution_status = ExecutionStatus.FAILED + # Convert Events to Operations - group by operation_id and merge try: svc_operations = events_to_operations(history_response.events) @@ -561,6 +577,7 @@ def from_execution_history( operations=operations, result=execution_response.result, error=execution_response.error, + execution_status=execution_status, ) def get_operation_by_name(self, name: str) -> Operation: @@ -1185,7 +1202,7 @@ def _wait_for_completion( if execution.status == "FAILED": logger.warning("Execution failed") return execution - if execution.status in ["TIMED_OUT", "ABORTED"]: + if execution.status in ["TIMED_OUT", "STOPPED"]: logger.warning("Execution terminated: %s", execution.status) return execution diff --git a/packages/aws-durable-execution-sdk-python-testing/tests/checkpoint/processors/execution_processor_test.py b/packages/aws-durable-execution-sdk-python-testing/tests/checkpoint/processors/execution_processor_test.py index 1ca805d3..178ae447 100644 --- a/packages/aws-durable-execution-sdk-python-testing/tests/checkpoint/processors/execution_processor_test.py +++ b/packages/aws-durable-execution-sdk-python-testing/tests/checkpoint/processors/execution_processor_test.py @@ -136,13 +136,7 @@ def test_process_fail_action_without_error(): assert result is None assert len(notifier.failed_calls) == 1 - execution_arn_arg, error_arg = notifier.failed_calls[0] - assert execution_arn_arg == execution_arn - assert isinstance(error_arg, ErrorObject) - assert ( - "There is no error details but EXECUTION checkpoint action is not SUCCEED" - in str(error_arg) - ) + assert notifier.failed_calls[0] == (execution_arn, None) def test_process_start_action(): @@ -160,9 +154,7 @@ def test_process_start_action(): assert result is None assert len(notifier.failed_calls) == 1 - execution_arn_arg, error_arg = notifier.failed_calls[0] - assert execution_arn_arg == execution_arn - assert isinstance(error_arg, ErrorObject) + assert notifier.failed_calls[0] == (execution_arn, None) def test_process_retry_action(): @@ -180,9 +172,7 @@ def test_process_retry_action(): assert result is None assert len(notifier.failed_calls) == 1 - execution_arn_arg, error_arg = notifier.failed_calls[0] - assert execution_arn_arg == execution_arn - assert isinstance(error_arg, ErrorObject) + assert notifier.failed_calls[0] == (execution_arn, None) def test_process_cancel_action(): @@ -200,9 +190,7 @@ def test_process_cancel_action(): assert result is None assert len(notifier.failed_calls) == 1 - execution_arn_arg, error_arg = notifier.failed_calls[0] - assert execution_arn_arg == execution_arn - assert isinstance(error_arg, ErrorObject) + assert notifier.failed_calls[0] == (execution_arn, None) def test_process_with_current_operation_and_error(): diff --git a/packages/aws-durable-execution-sdk-python-testing/tests/e2e/child_context_failure_test.py b/packages/aws-durable-execution-sdk-python-testing/tests/e2e/child_context_failure_test.py new file mode 100644 index 00000000..a7c447b8 --- /dev/null +++ b/packages/aws-durable-execution-sdk-python-testing/tests/e2e/child_context_failure_test.py @@ -0,0 +1,63 @@ +"""End-to-end child context failure handling through the test runner.""" + +import json +from typing import Any + +from aws_durable_execution_sdk_python.config import StepConfig +from aws_durable_execution_sdk_python.context import ( + DurableContext, + durable_step, + durable_with_child_context, +) +from aws_durable_execution_sdk_python.execution import durable_execution +from aws_durable_execution_sdk_python.lambda_service import ( + InvocationStatus, + OperationStatus, +) +from aws_durable_execution_sdk_python.retries import RetryPresets +from aws_durable_execution_sdk_python.types import StepContext + +from aws_durable_execution_sdk_python_testing.runner import ( + ContextOperation, + DurableFunctionTestResult, + DurableFunctionTestRunner, +) + + +def test_caught_child_context_failure_does_not_fail_root_execution() -> None: + @durable_step + def failing_step(step_context: StepContext) -> str: # noqa: ARG001 + msg = "Child step failed" + raise RuntimeError(msg) + + @durable_with_child_context + def failing_child(ctx: DurableContext) -> str: + return ctx.step( + failing_step(), + config=StepConfig(retry_strategy=RetryPresets.none()), + ) + + @durable_step + def recovery_step(step_context: StepContext, value: str) -> str: # noqa: ARG001 + return value + + @durable_execution + def handler(event: Any, context: DurableContext) -> str: # noqa: ARG001 + try: + context.run_in_child_context(failing_child(), name="failing-child") + except Exception: + pass + + return context.step(recovery_step("handled")) + + with DurableFunctionTestRunner(handler=handler, execution_timeout=10) as runner: + result: DurableFunctionTestResult = runner.run(input="input str") + + assert result.status is InvocationStatus.SUCCEEDED + assert result.result == json.dumps("handled") + + child_op: ContextOperation = result.get_context("failing-child") + assert child_op.status is OperationStatus.FAILED + assert child_op.error is not None + assert child_op.error.message is not None + assert "Child step failed" in child_op.error.message diff --git a/packages/aws-durable-execution-sdk-python-testing/tests/e2e/failed_without_error_test.py b/packages/aws-durable-execution-sdk-python-testing/tests/e2e/failed_without_error_test.py new file mode 100644 index 00000000..8a290c64 --- /dev/null +++ b/packages/aws-durable-execution-sdk-python-testing/tests/e2e/failed_without_error_test.py @@ -0,0 +1,26 @@ +"""End-to-end failed invocation handling through the test runner.""" + +from typing import Any + +from aws_durable_execution_sdk_python.execution import InvocationStatus + +from aws_durable_execution_sdk_python_testing.execution import ExecutionStatus +from aws_durable_execution_sdk_python_testing.runner import ( + DurableFunctionTestResult, + DurableFunctionTestRunner, +) + + +def test_failed_invocation_without_error_sets_execution_status() -> None: + def handler(event: Any, context: Any) -> dict[str, str]: # noqa: ARG001 + return {"Status": "FAILED"} + + with DurableFunctionTestRunner(handler=handler, execution_timeout=10) as runner: + execution_arn = runner.run_async(input="input str") + result: DurableFunctionTestResult = runner.wait_for_result( + execution_arn, timeout=10 + ) + + assert result.status is InvocationStatus.FAILED + assert result.error is None + assert result.execution_status is ExecutionStatus.FAILED diff --git a/packages/aws-durable-execution-sdk-python-testing/tests/event_factory_test.py b/packages/aws-durable-execution-sdk-python-testing/tests/event_factory_test.py index 1f4238e3..6418304d 100644 --- a/packages/aws-durable-execution-sdk-python-testing/tests/event_factory_test.py +++ b/packages/aws-durable-execution-sdk-python-testing/tests/event_factory_test.py @@ -158,6 +158,41 @@ def test_create_execution_failed(): assert event.execution_failed_details.error.payload.message == "Execution failed" +def test_create_execution_failed_without_error_payload(): + from aws_durable_execution_sdk_python.execution import ( + DurableExecutionInvocationOutput, + InvocationStatus, + ) + + operation = create_mock_operation("op-1", status=OperationStatus.FAILED) + operation.end_timestamp = datetime.now(UTC) + + error_result = DurableExecutionInvocationOutput( + status=InvocationStatus.FAILED, + error=None, + ) + context = EventCreationContext.create( + operation=operation, + event_id=3, + durable_execution_arn="arn:test", + start_input=StartDurableExecutionInput( + account_id="123", + function_name="test", + function_qualifier="$LATEST", + execution_name="test", + execution_timeout_seconds=300, + execution_retention_period_days=7, + ), + result=error_result, + include_execution_data=True, + ) + event = Event.create_execution_event(context) + + assert event.event_type == "ExecutionFailed" + assert event.execution_failed_details.error is not None + assert event.execution_failed_details.error.payload is None + + def test_create_execution_timed_out(): from aws_durable_execution_sdk_python.execution import ( DurableExecutionInvocationOutput, diff --git a/packages/aws-durable-execution-sdk-python-testing/tests/execution_test.py b/packages/aws-durable-execution-sdk-python-testing/tests/execution_test.py index 7002d799..7ea66fe3 100644 --- a/packages/aws-durable-execution-sdk-python-testing/tests/execution_test.py +++ b/packages/aws-durable-execution-sdk-python-testing/tests/execution_test.py @@ -507,6 +507,26 @@ def test_complete_fail(): assert execution.result.error == error +def test_complete_fail_without_error(): + """Test complete_fail preserves a missing error payload.""" + start_input = StartDurableExecutionInput( + account_id="123456789012", + function_name="test-function", + function_qualifier="$LATEST", + execution_name="test-execution", + execution_timeout_seconds=300, + execution_retention_period_days=7, + invocation_id="test-invocation-id", + ) + execution = Execution("test-arn", start_input, [Mock()]) + + execution.complete_fail(None) + + assert execution.is_complete is True + assert execution.result.status is InvocationStatus.FAILED + assert execution.result.error is None + + def test_find_operation_exists(): """Test find_operation method when operation exists.""" start_input = StartDurableExecutionInput( diff --git a/packages/aws-durable-execution-sdk-python-testing/tests/executor_test.py b/packages/aws-durable-execution-sdk-python-testing/tests/executor_test.py index 36ec26bb..b7292124 100644 --- a/packages/aws-durable-execution-sdk-python-testing/tests/executor_test.py +++ b/packages/aws-durable-execution-sdk-python-testing/tests/executor_test.py @@ -68,7 +68,7 @@ def on_completed(self, execution_arn: str, result: str | None = None) -> None: """Capture completion events.""" self.completed_executions[execution_arn] = result - def on_failed(self, execution_arn: str, error: ErrorObject) -> None: + def on_failed(self, execution_arn: str, error: ErrorObject | None) -> None: """Capture failure events.""" self.failed_executions[execution_arn] = error @@ -310,6 +310,42 @@ def test_should_complete_workflow_with_error_when_invocation_fails( mock_fail.assert_called_once_with("test-arn", failed_response.error) +def test_validate_invocation_response_failed_without_error_still_fails(): + """FAILED without an error must fail (not succeed), preserving the null error.""" + + store = InMemoryExecutionStore() + executor = Executor(store, Mock(), Mock(), Mock()) + + start_input = StartDurableExecutionInput( + account_id="123456789012", + function_name="test-function", + function_qualifier="$LATEST", + execution_name="test-execution", + execution_timeout_seconds=300, + execution_retention_period_days=7, + invocation_id="test-invocation-id", + ) + execution = Execution.new(start_input) + execution.start() + store.save(execution) + + response = DurableExecutionInvocationOutput( + status=InvocationStatus.FAILED, error=None + ) + + executor._validate_invocation_response_and_store( # noqa: SLF001 + execution.durable_execution_arn, response, execution + ) + + stored = store.load(execution.durable_execution_arn) + assert stored.is_complete is True + assert stored.close_status is not None + assert stored.close_status is ExecutionStatus.FAILED + assert stored.result is not None + assert stored.result.status is InvocationStatus.FAILED + assert stored.result.error is None + + def test_should_complete_workflow_with_result_when_invocation_succeeds( executor, mock_store, mock_scheduler, mock_invoker, start_input ): diff --git a/packages/aws-durable-execution-sdk-python-testing/tests/observer_test.py b/packages/aws-durable-execution-sdk-python-testing/tests/observer_test.py index 6c95de24..c5be5c4d 100644 --- a/packages/aws-durable-execution-sdk-python-testing/tests/observer_test.py +++ b/packages/aws-durable-execution-sdk-python-testing/tests/observer_test.py @@ -31,7 +31,7 @@ def __init__(self): def on_completed(self, execution_arn: str, result: str | None = None) -> None: self.on_completed_calls.append((execution_arn, result)) - def on_failed(self, execution_arn: str, error: ErrorObject) -> None: + def on_failed(self, execution_arn: str, error: ErrorObject | None) -> None: self.on_failed_calls.append((execution_arn, error)) def on_timed_out(self, execution_arn: str, error: ErrorObject) -> None: @@ -80,6 +80,12 @@ def test_notify_failed_records_failed_effect(): assert notifier.effects == [Failed(execution_arn="test-arn", error=error)] +def test_notify_failed_records_none_error_effect(): + notifier = ExecutionNotifier() + notifier.notify_failed("test-arn", None) + assert notifier.effects == [Failed(execution_arn="test-arn", error=None)] + + def test_notify_callback_created_records_callback_effect(): notifier = ExecutionNotifier() token = CallbackToken(execution_arn="test-arn", operation_id="op-1") @@ -129,6 +135,12 @@ def test_apply_effects_dispatches_failed(): assert observer.on_failed_calls == [("arn", error)] +def test_apply_effects_dispatches_failed_with_none_error(): + observer = MockExecutionObserver() + apply_effects([Failed(execution_arn="arn", error=None)], observer) + assert observer.on_failed_calls == [("arn", None)] + + def test_apply_effects_dispatches_callback_created(): observer = MockExecutionObserver() token = CallbackToken(execution_arn="arn", operation_id="op-1") diff --git a/packages/aws-durable-execution-sdk-python-testing/tests/runner_test.py b/packages/aws-durable-execution-sdk-python-testing/tests/runner_test.py index 206de3d3..c6ae8fbe 100644 --- a/packages/aws-durable-execution-sdk-python-testing/tests/runner_test.py +++ b/packages/aws-durable-execution-sdk-python-testing/tests/runner_test.py @@ -23,11 +23,14 @@ InvalidParameterValueException, ResourceNotFoundException, ) -from aws_durable_execution_sdk_python_testing.execution import Execution +from aws_durable_execution_sdk_python_testing.execution import ( + Execution, + ExecutionStatus, +) from aws_durable_execution_sdk_python_testing.model import ( + GetDurableExecutionHistoryResponse, StartDurableExecutionInput, StartDurableExecutionOutput, - GetDurableExecutionHistoryResponse, ) from aws_durable_execution_sdk_python_testing.runner import ( OPERATION_FACTORIES, @@ -541,15 +544,42 @@ def test_durable_function_test_result_create(): execution.result.status = InvocationStatus.SUCCEEDED execution.result.result = json.dumps("test-result") execution.result.error = None + execution.close_status = ExecutionStatus.SUCCEEDED result = DurableFunctionTestResult.create(execution) assert result.status is InvocationStatus.SUCCEEDED assert result.result == json.dumps("test-result") assert result.error is None + assert result.execution_status is ExecutionStatus.SUCCEEDED assert len(result.operations) == 1 # EXECUTION operation filtered out +def test_durable_function_test_result_direct_construction_defaults_execution_status(): + result = DurableFunctionTestResult( + status=InvocationStatus.SUCCEEDED, + operations=[], + ) + + assert result.execution_status is None + + +def test_durable_function_test_result_create_requires_close_status(): + execution = Mock(spec=Execution) + execution.operations = [] + execution.result = Mock() + execution.result.status = InvocationStatus.SUCCEEDED + execution.result.result = json.dumps("test-result") + execution.result.error = None + execution.close_status = None + + with pytest.raises( + DurableFunctionsTestError, + match="Execution close status must exist to create test result.", + ): + DurableFunctionTestResult.create(execution) + + def test_durable_function_test_result_get_operation_by_name(): """Test DurableFunctionTestResult get_operation_by_name method.""" step_op = StepOperation( @@ -784,6 +814,7 @@ def test_durable_function_test_runner_run(mock_store_class, mock_executor_class) mock_execution.result.status = InvocationStatus.SUCCEEDED mock_execution.result.result = json.dumps("test-result") mock_execution.result.error = None + mock_execution.close_status = ExecutionStatus.SUCCEEDED mock_store.load.return_value = mock_execution runner = DurableFunctionTestRunner(handler) @@ -807,6 +838,7 @@ def test_durable_function_test_runner_run(mock_store_class, mock_executor_class) # Verify result assert isinstance(result, DurableFunctionTestResult) assert result.status is InvocationStatus.SUCCEEDED + assert result.execution_status is ExecutionStatus.SUCCEEDED @patch("aws_durable_execution_sdk_python_testing.runner.Executor") @@ -835,6 +867,7 @@ def test_durable_function_test_runner_run_with_custom_params( mock_execution.result.status = InvocationStatus.SUCCEEDED mock_execution.result.result = json.dumps("test-result") mock_execution.result.error = None + mock_execution.close_status = ExecutionStatus.SUCCEEDED mock_store.load.return_value = mock_execution runner = DurableFunctionTestRunner(handler) @@ -858,6 +891,7 @@ def test_durable_function_test_runner_run_with_custom_params( mock_executor.wait_until_complete.assert_called_once_with("test-arn", 1800) assert result.status is InvocationStatus.SUCCEEDED + assert result.execution_status is ExecutionStatus.SUCCEEDED @patch("aws_durable_execution_sdk_python_testing.runner.Executor") @@ -960,6 +994,7 @@ def test_durable_function_test_result_create_with_parent_operations(): execution.result.status = InvocationStatus.SUCCEEDED execution.result.result = json.dumps("test-result") execution.result.error = None + execution.close_status = ExecutionStatus.SUCCEEDED result = DurableFunctionTestResult.create(execution) @@ -1135,6 +1170,7 @@ def test_durable_function_test_result_from_execution_history(): ) assert result.status == InvocationStatus.SUCCEEDED + assert result.execution_status is ExecutionStatus.SUCCEEDED assert result.result == "test-result" assert result.error is None assert len(result.operations) == 1 @@ -1552,6 +1588,7 @@ def test_durable_function_test_result_from_execution_history_unknown_status(): ) assert result.status == InvocationStatus.FAILED + assert result.execution_status is ExecutionStatus.FAILED def test_durable_function_test_result_from_execution_history_with_parent_operations(): @@ -1639,6 +1676,7 @@ def test_durable_function_test_result_from_execution_history_failed(): ) assert result.status == InvocationStatus.FAILED + assert result.execution_status is ExecutionStatus.FAILED assert result.error.message == "execution failed" @@ -1668,8 +1706,8 @@ def test_cloud_runner_wait_for_completion_timed_out_status(mock_boto3): @patch("aws_durable_execution_sdk_python_testing.runner.boto3") -def test_cloud_runner_wait_for_completion_aborted_status(mock_boto3): - """Test DurableFunctionCloudTestRunner._wait_for_completion with ABORTED status.""" +def test_cloud_runner_wait_for_completion_stopped_status(mock_boto3): + """Test DurableFunctionCloudTestRunner._wait_for_completion with STOPPED status.""" from aws_durable_execution_sdk_python_testing.runner import ( DurableFunctionCloudTestRunner, ) @@ -1681,7 +1719,7 @@ def test_cloud_runner_wait_for_completion_aborted_status(mock_boto3): "DurableExecutionArn": "arn:aws:lambda:us-east-1:123456789012:function:test:execution:exec-1", "DurableExecutionName": "test-execution", "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:test", - "Status": "ABORTED", + "Status": "STOPPED", "StartTimestamp": "2023-01-01T00:00:00Z", "EndTimestamp": "2023-01-01T00:01:00Z", } @@ -1689,7 +1727,7 @@ def test_cloud_runner_wait_for_completion_aborted_status(mock_boto3): runner = DurableFunctionCloudTestRunner(function_name="test-function") result = runner._wait_for_completion("test-arn", timeout=10) - assert result.status == "ABORTED" + assert result.status == "STOPPED" @patch("aws_durable_execution_sdk_python_testing.runner.boto3")