Skip to content

Commit 518cba1

Browse files
authored
test: fix NPE in child context completion tests (#729)
1 parent 7c08710 commit 518cba1

1 file changed

Lines changed: 21 additions & 6 deletions

File tree

‎sdk/src/test/java/software/amazon/lambda/durable/operation/ChildContextOperationTest.java‎

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.List;
99
import java.util.concurrent.Executors;
10+
import java.util.concurrent.TimeUnit;
1011
import java.util.concurrent.atomic.AtomicBoolean;
1112
import java.util.function.Function;
1213
import org.junit.jupiter.api.BeforeEach;
@@ -20,6 +21,7 @@
2021
import software.amazon.lambda.durable.DurableConfig;
2122
import software.amazon.lambda.durable.DurableContext;
2223
import software.amazon.lambda.durable.TypeToken;
24+
import software.amazon.lambda.durable.config.ParallelConfig;
2325
import software.amazon.lambda.durable.config.RunInChildContextConfig;
2426
import software.amazon.lambda.durable.context.DurableContextImpl;
2527
import software.amazon.lambda.durable.exception.ChildContextFailedException;
@@ -126,6 +128,17 @@ private ChildContextOperation<String> createOperationWithParent(
126128
parent);
127129
}
128130

131+
private ParallelOperation createCompletedParentOperation() {
132+
// Child completion synchronizes on the parent's future, which a constructor-free mock leaves null.
133+
var parent = new ParallelOperation(
134+
OperationIdentifier.of("parent", "parent-parallel", OperationSubType.PARALLEL),
135+
SERDES,
136+
durableContext,
137+
ParallelConfig.builder().build());
138+
parent.markAlreadyCompleted();
139+
return parent;
140+
}
141+
129142
// ===== SUCCEEDED replay =====
130143

131144
/** SUCCEEDED replay returns cached result without re-executing the function. */
@@ -348,12 +361,13 @@ void replayWithNameMismatchTerminatesExecution() {
348361
void childSkipsSuccessCheckpointWhenParentAlreadyCompleted() throws Exception {
349362
when(executionManager.getOperationAndUpdateReplayState("1")).thenReturn(null);
350363

351-
var parent = mock(ConcurrencyOperation.class);
352-
when(parent.isOperationCompleted()).thenReturn(true);
364+
var parent = createCompletedParentOperation();
353365

354366
var operation = createOperationWithParent(ctx -> "result", parent);
355367
operation.execute();
356-
Thread.sleep(200);
368+
operation.getRunningUserHandler().get(5, TimeUnit.SECONDS);
369+
370+
assertTrue(operation.isOperationCompleted(), "Child should complete even when its parent has completed");
357371

358372
// sendOperationUpdate should only be called once for START, not for SUCCEED
359373
verify(executionManager, never())
@@ -399,16 +413,17 @@ void virtualChildSucceedsWhenResultValidationDisabled() throws Exception {
399413
void childSkipsFailureCheckpointWhenParentAlreadyCompleted() throws Exception {
400414
when(executionManager.getOperationAndUpdateReplayState("1")).thenReturn(null);
401415

402-
var parent = mock(ConcurrencyOperation.class);
403-
when(parent.isOperationCompleted()).thenReturn(true);
416+
var parent = createCompletedParentOperation();
404417

405418
var operation = createOperationWithParent(
406419
ctx -> {
407420
throw new RuntimeException("branch failed");
408421
},
409422
parent);
410423
operation.execute();
411-
Thread.sleep(200);
424+
operation.getRunningUserHandler().get(5, TimeUnit.SECONDS);
425+
426+
assertTrue(operation.isOperationCompleted(), "Failed child should complete even when its parent has completed");
412427

413428
// sendOperationUpdate should not be called with FAIL action
414429
verify(executionManager, never())

0 commit comments

Comments
 (0)