Skip to content

Commit 0b9d817

Browse files
committed
Add tests for Async.await() timed cancellation and condition exceptions
- Add testTimedAsyncAwaitCancellation: verifies cancellation of the timed Async.await(Duration, Supplier) variant via CancellationScope - Add testAsyncAwaitConditionThrows: verifies that exceptions thrown by conditions during subsequent evaluations complete the promise exceptionally
1 parent 12adb29 commit 0b9d817

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

temporal-sdk/src/test/java/io/temporal/workflow/AsyncAwaitTest.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ public void testAsyncAwaitCancellation() {
9292
assertEquals("cancelled", result);
9393
}
9494

95+
@Test
96+
public void testTimedAsyncAwaitCancellation() {
97+
TestWorkflow1 workflow = testWorkflowRule.newWorkflowStubTimeoutOptions(TestWorkflow1.class);
98+
String result = workflow.execute("timed-cancellation");
99+
assertEquals("timed-cancelled", result);
100+
}
101+
102+
@Test
103+
public void testAsyncAwaitConditionThrows() {
104+
TestWorkflow1 workflow = testWorkflowRule.newWorkflowStubTimeoutOptions(TestWorkflow1.class);
105+
String result = workflow.execute("condition-throws");
106+
assertEquals("caught:simulated error", result);
107+
}
108+
95109
/** Combined workflow that handles all test scenarios. */
96110
public static class TestAsyncAwaitWorkflow implements TestWorkflow1 {
97111
private boolean condition1 = false;
@@ -122,6 +136,10 @@ public String execute(String testCase) {
122136
return testChaining();
123137
case "cancellation":
124138
return testCancellation();
139+
case "timed-cancellation":
140+
return testTimedCancellation();
141+
case "condition-throws":
142+
return testConditionThrows();
125143
default:
126144
return "unknown test case";
127145
}
@@ -287,5 +305,59 @@ private String testCancellation() {
287305
return "cancelled";
288306
}
289307
}
308+
309+
private String testTimedCancellation() {
310+
condition1 = false;
311+
final Promise<Boolean>[] promiseHolder = new Promise[1];
312+
313+
CancellationScope scope =
314+
Workflow.newCancellationScope(
315+
() -> {
316+
// Create a timed async await that will never complete on its own
317+
promiseHolder[0] = Async.await(Duration.ofHours(1), () -> condition1);
318+
});
319+
320+
// Run the scope (this is non-blocking since Async.await returns immediately)
321+
scope.run();
322+
323+
// Cancel the scope
324+
scope.cancel();
325+
326+
// The promise should fail with CanceledFailure when we try to get it
327+
try {
328+
promiseHolder[0].get();
329+
return "timed-not-cancelled";
330+
} catch (CanceledFailure e) {
331+
return "timed-cancelled";
332+
}
333+
}
334+
335+
private String testConditionThrows() {
336+
// Start with condition that doesn't throw, but will throw on subsequent evaluation
337+
// Initial check returns false (doesn't throw), then later evaluation throws
338+
counter = 0;
339+
340+
Promise<Void> promise =
341+
Async.await(
342+
() -> {
343+
counter++;
344+
// First evaluation (initial check) returns false
345+
// Second evaluation (in evaluateConditionWatchers) throws
346+
if (counter > 1) {
347+
throw new RuntimeException("simulated error");
348+
}
349+
return false;
350+
});
351+
352+
// Trigger re-evaluation by sleeping (causes event loop iteration)
353+
Workflow.sleep(Duration.ofMillis(1));
354+
355+
try {
356+
promise.get();
357+
return "no-exception";
358+
} catch (RuntimeException e) {
359+
return "caught:" + e.getMessage();
360+
}
361+
}
290362
}
291363
}

0 commit comments

Comments
 (0)