1313import org .junit .jupiter .api .Test ;
1414import software .amazon .awssdk .services .lambda .model .*;
1515
16+ /**
17+ * Unit tests for DurableContext.
18+ *
19+ * <p>Note: Async step execution tests (stepAsync with first execution) are tested in integration tests using
20+ * LocalDurableTestRunner, which properly handles the thread coordination required for async operations. This test class
21+ * focuses on synchronous operations and replay scenarios which can be reliably tested at the unit level.
22+ */
1623class DurableContextTest {
1724
25+ private static final String TEST_EXECUTION_ARN =
26+ "arn:aws:lambda:us-east-1:123456789012:function:test:$LATEST/durable-execution/"
27+ + "349beff4-a89d-4bc8-a56f-af7a8af67a5f/20dae574-53da-37a1-bfd5-b0e2e6ec715d" ;
28+
1829 private DurableContext createTestContext () {
1930 var executionOp = Operation .builder ()
2031 .id ("0" )
@@ -27,12 +38,7 @@ private DurableContext createTestContext() {
2738 private DurableContext createTestContext (List <Operation > initialOperations ) {
2839 var client = TestUtils .createMockClient ();
2940 var initialExecutionState = new InitialExecutionState (initialOperations , null );
30- var executionManager = new ExecutionManager (
31- "arn:aws:lambda:us-east-1:123456789012:function:test:$LATEST/durable-execution/"
32- + "349beff4-a89d-4bc8-a56f-af7a8af67a5f/20dae574-53da-37a1-bfd5-b0e2e6ec715d" ,
33- "test-token" ,
34- initialExecutionState ,
35- client );
41+ var executionManager = new ExecutionManager (TEST_EXECUTION_ARN , "test-token" , initialExecutionState , client );
3642 return new DurableContext (executionManager , DurableConfig .builder ().build (), null );
3743 }
3844
@@ -84,18 +90,8 @@ void testStepReplay() {
8490 }
8591
8692 @ Test
87- void testStepAsync () throws Exception {
88- var context = createTestContext ();
89-
90- var future = context .stepAsync ("async-test" , String .class , () -> "Async Result" );
91-
92- assertNotNull (future );
93- assertEquals ("Async Result" , future .get ());
94- }
95-
96- @ Test
97- void testStepAsyncReplay () throws Exception {
98- // Create context with existing operation
93+ void testStepAsyncReplay () {
94+ // Create context with existing completed operation - tests replay behavior
9995 var existingOp = Operation .builder ()
10096 .id ("1" )
10197 .status (OperationStatus .SUCCEEDED )
@@ -104,7 +100,7 @@ void testStepAsyncReplay() throws Exception {
104100 .build ();
105101 var context = createTestContext (List .of (existingOp ));
106102
107- // This should return cached result immediately
103+ // This should return cached result immediately without blocking
108104 var future = context .stepAsync ("async-test" , String .class , () -> "New Async Result" );
109105 assertEquals ("Cached Async Result" , future .get ());
110106 }
@@ -133,26 +129,8 @@ void testWaitReplay() {
133129 }
134130
135131 @ Test
136- void testCombinedSyncAsyncWait () throws Exception {
137- var context = createTestContext ();
138-
139- // Execute sync step
140- var syncResult = context .step ("sync-step" , String .class , () -> "Sync Done" );
141- assertEquals ("Sync Done" , syncResult );
142-
143- // Execute async step
144- var asyncFuture = context .stepAsync ("async-step" , Integer .class , () -> 42 );
145- assertEquals (42 , asyncFuture .get ());
146-
147- // Wait should suspend (throw exception)
148- assertThrows (SuspendExecutionException .class , () -> {
149- context .wait (Duration .ofSeconds (30 ));
150- });
151- }
152-
153- @ Test
154- void testCombinedReplay () throws Exception {
155- // Create context with all operations completed
132+ void testCombinedReplay () {
133+ // Create context with all operations completed - tests replay behavior
156134 var syncOp = Operation .builder ()
157135 .id ("1" )
158136 .status (OperationStatus .SUCCEEDED )
@@ -250,21 +228,8 @@ void testStepWithTypeTokenAndConfig() {
250228 }
251229
252230 @ Test
253- void testStepAsyncWithTypeToken () throws Exception {
254- var context = createTestContext ();
255-
256- DurableFuture <List <String >> future =
257- context .stepAsync ("async-list" , new TypeToken <List <String >>() {}, () -> List .of ("x" , "y" , "z" ));
258-
259- assertNotNull (future );
260- List <String > result = future .get ();
261- assertEquals (3 , result .size ());
262- assertEquals ("x" , result .get (0 ));
263- }
264-
265- @ Test
266- void testStepAsyncWithTypeTokenReplay () throws Exception {
267- // Create context with existing operation
231+ void testStepAsyncWithTypeTokenReplay () {
232+ // Create context with existing completed operation - tests replay behavior
268233 var existingOp = Operation .builder ()
269234 .id ("1" )
270235 .status (OperationStatus .SUCCEEDED )
@@ -274,7 +239,7 @@ void testStepAsyncWithTypeTokenReplay() throws Exception {
274239 .build ();
275240 var context = createTestContext (List .of (existingOp ));
276241
277- // This should return cached result immediately
242+ // This should return cached result immediately without blocking
278243 DurableFuture <List <String >> future = context .stepAsync (
279244 "async-list" , new TypeToken <List <String >>() {}, () -> List .of ("async-new1" , "async-new2" ));
280245
@@ -283,22 +248,4 @@ void testStepAsyncWithTypeTokenReplay() throws Exception {
283248 assertEquals ("async-cached1" , result .get (0 ));
284249 assertEquals ("async-cached2" , result .get (1 ));
285250 }
286-
287- @ Test
288- void testStepAsyncWithTypeTokenAndConfig () throws Exception {
289- var context = createTestContext ();
290-
291- DurableFuture <List <Integer >> future = context .stepAsync (
292- "async-numbers" ,
293- new TypeToken <List <Integer >>() {},
294- () -> List .of (10 , 20 , 30 ),
295- StepConfig .builder ()
296- .retryStrategy (RetryStrategies .Presets .DEFAULT )
297- .build ());
298-
299- assertNotNull (future );
300- List <Integer > result = future .get ();
301- assertEquals (3 , result .size ());
302- assertEquals (10 , result .get (0 ));
303- }
304251}
0 commit comments