Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/aws_durable_execution_sdk_python_testing/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,14 +323,14 @@ def stop_execution(

Raises:
ResourceNotFoundException: If execution does not exist
ExecutionAlreadyStartedException: If execution is already completed
"""
execution = self.get_execution(execution_arn)

if execution.is_complete:
# Context-aware mapping: execution already completed maps to ExecutionAlreadyStartedException
msg: str = f"Execution {execution_arn} is already completed"
raise ExecutionAlreadyStartedException(msg, execution_arn)
# Idempotent: return the existing stop timestamp
execution_op = execution.get_operation_execution_started()
stop_timestamp = execution_op.end_timestamp or datetime.now(UTC)
return StopDurableExecutionResponse(stop_timestamp=stop_timestamp)

# Use provided error or create a default one
stop_error = error or ErrorObject.from_message(
Expand Down
16 changes: 13 additions & 3 deletions tests/executor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
SendDurableExecutionCallbackHeartbeatResponse,
SendDurableExecutionCallbackSuccessResponse,
StartDurableExecutionInput,
StopDurableExecutionResponse,
)
from aws_durable_execution_sdk_python_testing.observer import (
ExecutionNotifier,
Expand Down Expand Up @@ -2056,13 +2057,22 @@ def test_stop_execution(executor, mock_store):


def test_stop_execution_already_complete(executor, mock_store):
"""Test stop_execution with already completed execution."""
"""Test stop_execution with already completed execution returns idempotent response."""
mock_execution = Mock()
mock_execution.is_complete = True
mock_execution.durable_execution_arn = "test-arn"

# Mock the execution operation with end_timestamp
mock_execution_op = Mock()
mock_execution_op.end_timestamp = datetime(2023, 1, 1, 0, 1, 0, tzinfo=UTC)
mock_execution.get_operation_execution_started.return_value = mock_execution_op

mock_store.load.return_value = mock_execution

with pytest.raises(ExecutionAlreadyStartedException, match="already completed"):
executor.stop_execution("test-arn")
result = executor.stop_execution("test-arn")

assert isinstance(result, StopDurableExecutionResponse)
assert result.stop_timestamp == datetime(2023, 1, 1, 0, 1, 0, tzinfo=UTC)


def test_stop_execution_with_custom_error(executor, mock_store):
Expand Down
16 changes: 8 additions & 8 deletions tests/web/handlers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -911,14 +911,15 @@ def test_stop_durable_execution_handler_success():


def test_stop_durable_execution_handler_execution_already_stopped():
"""Test StopDurableExecutionHandler with execution already stopped error."""
"""Test StopDurableExecutionHandler with execution already stopped returns idempotent response."""

executor = Mock()
handler = StopDurableExecutionHandler(executor)

# Mock executor to raise IllegalStateException
executor.stop_execution.side_effect = IllegalStateException(
"Execution test-arn is already completed"
# Mock executor to return stop response with timestamp
stop_timestamp = "2023-01-01T00:01:00Z"
executor.stop_execution.return_value = StopDurableExecutionResponse(
stop_timestamp=stop_timestamp
)

request_body = {
Expand All @@ -941,10 +942,9 @@ def test_stop_durable_execution_handler_execution_already_stopped():

response = handler.handle(typed_route, request)

# Verify IllegalStateException maps to ServiceException in AWS-compliant format
assert response.status_code == 500
assert response.body["Type"] == "ServiceException"
assert response.body["Message"] == "Execution test-arn is already completed"
# Verify idempotent response with stop timestamp
assert response.status_code == 200
assert response.body["StopTimestamp"] == stop_timestamp


def test_stop_durable_execution_handler_resource_not_found():
Expand Down
Loading