33import datetime
44import json
55import time
6+ from decimal import Decimal
67from typing import Any
78from unittest .mock import Mock , patch
89
10+
11+ from aws_durable_execution_sdk_python import (
12+ DurableContext ,
13+ ExtendedTypeSerDes ,
14+ JsonSerDes ,
15+ durable_execution ,
16+ )
17+ from aws_durable_execution_sdk_python .execution import (
18+ DurableExecutionInvocationInputWithClient ,
19+ InitialExecutionState ,
20+ )
21+ from aws_durable_execution_sdk_python .lambda_service import (
22+ CheckpointOutput ,
23+ CheckpointUpdatedExecutionState ,
24+ DurableServiceClient ,
25+ ExecutionDetails ,
26+ Operation ,
27+ OperationStatus ,
28+ OperationType ,
29+ )
30+
31+
932import pytest
1033
1134from aws_durable_execution_sdk_python .config import StepConfig , StepSemantics
@@ -946,7 +969,7 @@ def test_handler(event: Any, context: DurableContext) -> dict:
946969 lambda_context .invoked_function_arn = None
947970 lambda_context .tenant_id = None
948971
949- with pytest .raises (json . JSONDecodeError ):
972+ with pytest .raises (ExecutionError ):
950973 test_handler (invocation_input , lambda_context )
951974
952975
@@ -2047,7 +2070,6 @@ def test_handler(event: Any, context: DurableContext) -> dict:
20472070
20482071def test_durable_execution_with_non_dict_event_raises_error ():
20492072 """Test that invoking a durable function with a non-dict event raises a helpful error."""
2050-
20512073 # GIVEN a durable function
20522074 @durable_execution
20532075 def test_handler (event : Any , context : DurableContext ) -> dict :
@@ -2071,3 +2093,125 @@ def test_handler(event: Any, context: DurableContext) -> dict:
20712093 match = "The payload is not the correct Durable Function input" ,
20722094 ):
20732095 test_handler (non_dict_event , lambda_context )
2096+
2097+
2098+ # region SERDES
2099+
2100+ @pytest .mark .parametrize (
2101+ "input_serdes,output_serdes,input_data,expected_output" ,
2102+ [
2103+ # Both ExtendedTypeSerDes
2104+ (
2105+ ExtendedTypeSerDes (),
2106+ ExtendedTypeSerDes (),
2107+ {
2108+ "amount" : Decimal ("123.45" ),
2109+ "timestamp" : datetime .datetime (2025 , 11 , 20 , 18 , 0 , 0 ),
2110+ },
2111+ {
2112+ "result_amount" : Decimal ("678.90" ),
2113+ "processed_at" : datetime .datetime (2025 , 11 , 20 , 19 , 0 , 0 ),
2114+ },
2115+ ),
2116+ # Both JsonSerDes
2117+ (
2118+ JsonSerDes (),
2119+ JsonSerDes (),
2120+ {"name" : "test" , "value" : 42 },
2121+ {"result" : "success" , "count" : 100 },
2122+ ),
2123+ # Input ExtendedTypeSerDes, Output JsonSerDes
2124+ (
2125+ ExtendedTypeSerDes (),
2126+ JsonSerDes (),
2127+ {"amount" : Decimal ("123.45" )},
2128+ {"result" : "success" },
2129+ ),
2130+ # Input JsonSerDes, Output ExtendedTypeSerDes
2131+ (
2132+ JsonSerDes (),
2133+ ExtendedTypeSerDes (),
2134+ {"name" : "test" },
2135+ {"timestamp" : datetime .datetime (2025 , 11 , 20 , 19 , 0 , 0 )},
2136+ ),
2137+ ],
2138+ )
2139+ def test_durable_execution_with_serdes (input_serdes , output_serdes , input_data , expected_output ):
2140+ """Test that input_serdes and output_serdes are invoked correctly."""
2141+ serialized_input = input_serdes .serialize (input_data , None )
2142+
2143+ mock_client = Mock (spec = DurableServiceClient )
2144+ mock_client .checkpoint .return_value = CheckpointOutput (
2145+ checkpoint_token = "new-token" ,
2146+ new_execution_state = CheckpointUpdatedExecutionState (operations = [], next_marker = None ),
2147+ )
2148+
2149+ execution_op = Operation (
2150+ operation_id = "EXECUTION" ,
2151+ operation_type = OperationType .EXECUTION ,
2152+ status = OperationStatus .STARTED ,
2153+ execution_details = ExecutionDetails (input_payload = serialized_input ),
2154+ )
2155+
2156+ invocation_input = DurableExecutionInvocationInputWithClient (
2157+ durable_execution_arn = "arn:aws:lambda:us-east-1:123456789012:function:test" ,
2158+ checkpoint_token = "initial-token" ,
2159+ initial_execution_state = InitialExecutionState (
2160+ operations = [execution_op ],
2161+ next_marker = "" ,
2162+ ),
2163+ is_local_runner = False ,
2164+ service_client = mock_client ,
2165+ )
2166+
2167+ with patch .object (input_serdes , "deserialize" , wraps = input_serdes .deserialize ) as mock_input_deser , patch .object (
2168+ output_serdes , "serialize" , wraps = output_serdes .serialize
2169+ ) as mock_output_ser :
2170+
2171+ @durable_execution (input_serdes = input_serdes , output_serdes = output_serdes )
2172+ def handler (event , context : DurableContext ):
2173+ return expected_output
2174+
2175+ result = handler (invocation_input , Mock ())
2176+
2177+ mock_input_deser .assert_called_once ()
2178+ mock_output_ser .assert_called_once_with (expected_output , mock_output_ser .call_args [0 ][1 ])
2179+
2180+ assert result ["Status" ] == "SUCCEEDED"
2181+ result_data = output_serdes .deserialize (result ["Result" ], None )
2182+ assert result_data == expected_output
2183+
2184+
2185+ def test_durable_execution_with_none_input ():
2186+ """Test that None input is handled correctly with serdes."""
2187+ mock_client = Mock (spec = DurableServiceClient )
2188+ mock_client .checkpoint .return_value = CheckpointOutput (
2189+ checkpoint_token = "new-token" ,
2190+ new_execution_state = CheckpointUpdatedExecutionState (operations = [], next_marker = None ),
2191+ )
2192+
2193+ execution_op = Operation (
2194+ operation_id = "EXECUTION" ,
2195+ operation_type = OperationType .EXECUTION ,
2196+ status = OperationStatus .STARTED ,
2197+ execution_details = ExecutionDetails (input_payload = None ),
2198+ )
2199+
2200+ invocation_input = DurableExecutionInvocationInputWithClient (
2201+ durable_execution_arn = "arn:aws:lambda:us-east-1:123456789012:function:test" ,
2202+ checkpoint_token = "initial-token" ,
2203+ initial_execution_state = InitialExecutionState (
2204+ operations = [execution_op ],
2205+ next_marker = "" ,
2206+ ),
2207+ is_local_runner = False ,
2208+ service_client = mock_client ,
2209+ )
2210+
2211+ @durable_execution (input_serdes = ExtendedTypeSerDes ())
2212+ def handler (event , context : DurableContext ):
2213+ assert event is None
2214+ return {"result" : "success" }
2215+
2216+ result = handler (invocation_input , Mock ())
2217+ assert result ["Status" ] == "SUCCEEDED"
0 commit comments