Skip to content

Commit 66da59b

Browse files
committed
added tests
1 parent 7eb2567 commit 66da59b

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

endtoendtests/src/main/java/com/functions/RewindTest.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public class RewindTest {
2626
// Flag to control whether the activity should fail (first call fails, subsequent calls succeed)
2727
private static final AtomicBoolean shouldFail = new AtomicBoolean(true);
2828

29+
// Separate flag for sub-orchestration rewind test
30+
private static final AtomicBoolean shouldSubFail = new AtomicBoolean(true);
31+
2932
/**
3033
* HTTP trigger to start the rewindable orchestration.
3134
*/
@@ -86,4 +89,74 @@ public HttpResponseMessage resetRewindFailureFlag(
8689
.body("Failure flag reset to true")
8790
.build();
8891
}
92+
93+
// --- Sub-orchestration rewind test functions ---
94+
95+
/**
96+
* HTTP trigger to start the parent orchestration for sub-orchestration rewind test.
97+
*/
98+
@FunctionName("StartRewindableSubOrchestration")
99+
public HttpResponseMessage startRewindableSubOrchestration(
100+
@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
101+
@DurableClientInput(name = "durableContext") DurableClientContext durableContext,
102+
final ExecutionContext context) {
103+
context.getLogger().info("Starting rewindable sub-orchestration test.");
104+
105+
// Reset the sub failure flag so the first activity call will fail
106+
shouldSubFail.set(true);
107+
108+
DurableTaskClient client = durableContext.getClient();
109+
String instanceId = client.scheduleNewOrchestrationInstance("RewindableParentOrchestration");
110+
context.getLogger().info("Created parent orchestration with instance ID = " + instanceId);
111+
return durableContext.createCheckStatusResponse(request, instanceId);
112+
}
113+
114+
/**
115+
* Parent orchestration that calls a sub-orchestration which may fail.
116+
*/
117+
@FunctionName("RewindableParentOrchestration")
118+
public String rewindableParentOrchestration(
119+
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
120+
String result = ctx.callSubOrchestrator("RewindableChildOrchestration", "SubRewindTest", String.class).await();
121+
return "Parent:" + result;
122+
}
123+
124+
/**
125+
* Sub-orchestration that calls an activity which will fail on the first attempt.
126+
*/
127+
@FunctionName("RewindableChildOrchestration")
128+
public String rewindableChildOrchestration(
129+
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
130+
String result = ctx.callActivity("FailOnceSubActivity", "SubRewindTest", String.class).await();
131+
return result;
132+
}
133+
134+
/**
135+
* Activity for sub-orchestration test that fails on the first call but succeeds on subsequent calls.
136+
*/
137+
@FunctionName("FailOnceSubActivity")
138+
public String failOnceSubActivity(
139+
@DurableActivityTrigger(name = "input") String input,
140+
final ExecutionContext context) {
141+
if (shouldSubFail.compareAndSet(true, false)) {
142+
context.getLogger().warning("FailOnceSubActivity: Simulating failure for input: " + input);
143+
throw new RuntimeException("Simulated sub-orchestration transient failure - rewind to retry");
144+
}
145+
context.getLogger().info("FailOnceSubActivity: Success for input: " + input);
146+
return input + "-sub-rewound-success";
147+
}
148+
149+
/**
150+
* HTTP trigger to reset the sub-orchestration failure flag.
151+
*/
152+
@FunctionName("ResetSubRewindFailureFlag")
153+
public HttpResponseMessage resetSubRewindFailureFlag(
154+
@HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
155+
final ExecutionContext context) {
156+
shouldSubFail.set(true);
157+
context.getLogger().info("Reset sub failure flag to true.");
158+
return request.createResponseBuilder(com.microsoft.azure.functions.HttpStatus.OK)
159+
.body("Sub failure flag reset to true")
160+
.build();
161+
}
89162
}

endtoendtests/src/test/java/com/functions/EndToEndTests.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,64 @@ public void rewindFailedOrchestration() throws InterruptedException {
265265
assertTrue(output.contains("rewound-success"), "Output should indicate successful rewind: " + output);
266266
}
267267

268+
@Test
269+
public void rewindNonFailedOrchestration() throws InterruptedException {
270+
// Start a normal orchestration and wait for it to complete
271+
String startOrchestrationPath = "/api/StartOrchestration";
272+
Response response = post(startOrchestrationPath);
273+
JsonPath jsonPath = response.jsonPath();
274+
String statusQueryGetUri = jsonPath.get("statusQueryGetUri");
275+
276+
boolean completed = pollingCheck(statusQueryGetUri, "Completed", null, Duration.ofSeconds(15));
277+
assertTrue(completed, "Orchestration should complete before rewind attempt");
278+
279+
// Attempt to rewind the completed (non-failed) orchestration
280+
String rewindPostUri = jsonPath.get("rewindPostUri");
281+
rewindPostUri = rewindPostUri.replace("{text}", "Testing rewind on non-failed orchestration");
282+
post(rewindPostUri);
283+
284+
// Wait a few seconds to allow any potential state change
285+
Thread.sleep(5000);
286+
287+
// Verify the orchestration remains in Completed state (rewind should have no effect)
288+
Response statusResponse = get(statusQueryGetUri);
289+
String status = statusResponse.jsonPath().get("runtimeStatus");
290+
assertEquals("Completed", status,
291+
"Orchestration should remain Completed after rewind attempt on a non-failed instance");
292+
}
293+
294+
@Test
295+
public void rewindSubOrchestrationFailure() throws InterruptedException {
296+
// Reset the sub-orchestration failure flag before starting
297+
post("/api/ResetSubRewindFailureFlag");
298+
299+
// Start the parent orchestration - the sub-orchestration's activity will fail
300+
String startOrchestrationPath = "/api/StartRewindableSubOrchestration";
301+
Response response = post(startOrchestrationPath);
302+
JsonPath jsonPath = response.jsonPath();
303+
String statusQueryGetUri = jsonPath.get("statusQueryGetUri");
304+
305+
// Wait for the parent orchestration to fail (due to sub-orchestration failure)
306+
boolean failed = pollingCheck(statusQueryGetUri, "Failed", null, Duration.ofSeconds(15));
307+
assertTrue(failed, "Parent orchestration should have failed due to sub-orchestration failure");
308+
309+
// Rewind the parent orchestration
310+
String rewindPostUri = jsonPath.get("rewindPostUri");
311+
rewindPostUri = rewindPostUri.replace("{text}", "Testing rewind with sub-orchestration failure");
312+
Response rewindResponse = post(rewindPostUri);
313+
assertEquals(202, rewindResponse.getStatusCode(), "Rewind should return 202 Accepted");
314+
315+
// Wait for the parent orchestration to complete after rewind
316+
boolean completed = pollingCheck(statusQueryGetUri, "Completed", null, Duration.ofSeconds(30));
317+
assertTrue(completed, "Parent orchestration should complete after rewind");
318+
319+
// Verify the output contains the expected result from the sub-orchestration
320+
Response statusResponse = get(statusQueryGetUri);
321+
String output = statusResponse.jsonPath().get("output");
322+
assertTrue(output.contains("sub-rewound-success"),
323+
"Output should indicate successful sub-orchestration rewind: " + output);
324+
}
325+
268326
@Test
269327
public void externalEventDeserializeFail() throws InterruptedException {
270328
String startOrchestrationPath = "api/ExternalEventHttp";

0 commit comments

Comments
 (0)