Skip to content

Commit c0db873

Browse files
committed
test: depend on awaitility properly and use it for the new waits
versions.gradle has pinned awaitility 4.2.0 since forever, but tests/build.gradle never declared it — so ApiClientTest was importing org.testcontainers.shaded.org.awaitility.Awaitility, i.e. borrowing the copy shaded inside testcontainers. That is a transitive accident: it breaks silently whenever testcontainers changes its shading or is upgraded, and it is why nobody noticed the real dependency was missing. Declare the dependency, point ApiClientTest at the real package, and express the two new waits (waitUntilSignalable, retryUntil) with awaitility instead of hand-rolled while/sleep loops. failFast() gives the signal wait a proper terminal-state abort rather than polling to the deadline after the workflow has already failed. waitForWorkflowStatus is deliberately left as-is: it is used across the whole suite and rewriting it risks changing behaviour for tests unrelated to this change. Its Thread.sleep is a poll interval, which is fine — the problem was never sleeping, it was sleeping *instead of* checking a condition.
1 parent c98a1ca commit c0db873

3 files changed

Lines changed: 32 additions & 44 deletions

File tree

‎tests/build.gradle‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dependencies {
1616
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${versions.junit}"
1717
testImplementation "ch.qos.logback:logback-classic:1.5.32"
1818
testImplementation "org.mockito:mockito-core:${versions.mockito}"
19+
testImplementation "org.awaitility:awaitility:${versions.awaitility}"
1920
testImplementation "org.testcontainers:localstack:${versions.testContainers}"
2021
testImplementation "org.testcontainers:testcontainers:${versions.testContainers}"
2122
}

‎tests/src/test/java/io/orkes/conductor/client/ApiClientTest.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import static java.util.concurrent.TimeUnit.SECONDS;
3131
import static org.junit.Assert.assertEquals;
3232
import static org.junit.Assert.assertNotNull;
33-
import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
33+
import static org.awaitility.Awaitility.await;
3434

3535
public class ApiClientTest {
3636
private static final String workflowName = "test_api_client_wf";

‎tests/src/test/java/io/orkes/conductor/client/util/TestUtil.java‎

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import java.io.IOException;
1616
import java.io.InputStream;
1717
import java.io.InputStreamReader;
18+
import java.time.Duration;
19+
import java.util.concurrent.Callable;
1820
import java.util.concurrent.TimeoutException;
1921

2022
import com.netflix.conductor.common.config.ObjectMapperProvider;
@@ -25,6 +27,8 @@
2527

2628
import com.fasterxml.jackson.databind.ObjectMapper;
2729

30+
import static org.awaitility.Awaitility.await;
31+
2832
public class TestUtil {
2933
private static int RETRY_ATTEMPT_LIMIT = 4;
3034
protected static ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper();
@@ -134,28 +138,34 @@ public static Workflow waitForWorkflowStatus(OrkesWorkflowClient workflowClient,
134138
public static void waitUntilSignalable(OrkesWorkflowClient workflowClient,
135139
String workflowId,
136140
long maxWaitTimeMs,
137-
long pollIntervalMs) throws Exception {
138-
long endTime = System.currentTimeMillis() + maxWaitTimeMs;
139-
140-
while (System.currentTimeMillis() < endTime) {
141-
Workflow workflow = workflowClient.getWorkflow(workflowId, true);
141+
long pollIntervalMs) {
142+
await().atMost(Duration.ofMillis(maxWaitTimeMs))
143+
.pollInterval(Duration.ofMillis(pollIntervalMs))
144+
.failFast("workflow reached a terminal state before it could be signalled",
145+
() -> isTerminalFailure(workflowClient.getWorkflow(workflowId, true)))
146+
.until(() -> {
147+
Workflow workflow = workflowClient.getWorkflow(workflowId, true);
148+
return workflow.getStatus() == Workflow.WorkflowStatus.RUNNING
149+
&& workflow.getTasks() != null
150+
&& workflow.getTasks().stream().anyMatch(t -> !t.getStatus().isTerminal());
151+
});
152+
}
142153

143-
if (workflow.getStatus() == Workflow.WorkflowStatus.FAILED
144-
|| workflow.getStatus() == Workflow.WorkflowStatus.TERMINATED) {
145-
throw new RuntimeException(describeFailure(workflow));
146-
}
147-
boolean hasPendingTask = workflow.getTasks() != null
148-
&& workflow.getTasks().stream().anyMatch(t -> !t.getStatus().isTerminal());
149-
if (workflow.getStatus() == Workflow.WorkflowStatus.RUNNING && hasPendingTask) {
150-
return;
151-
}
152-
Thread.sleep(pollIntervalMs);
153-
}
154+
/**
155+
* Retries {@code call} until it returns without throwing. For endpoints that are eventually
156+
* consistent or intermittently slow, where a single attempt after a fixed sleep turns ordinary
157+
* latency into a test failure.
158+
*/
159+
public static <T> T retryUntil(Callable<T> call, long maxWaitTimeMs, long pollIntervalMs) {
160+
return await().atMost(Duration.ofMillis(maxWaitTimeMs))
161+
.pollInterval(Duration.ofMillis(pollIntervalMs))
162+
.ignoreExceptions()
163+
.until(call, result -> true);
164+
}
154165

155-
Workflow finalState = workflowClient.getWorkflow(workflowId, true);
156-
throw new TimeoutException(String.format(
157-
"Workflow %s was not signalable within %dms. Status: %s, tasks: %s",
158-
workflowId, maxWaitTimeMs, finalState.getStatus(), taskSummary(finalState)));
166+
private static boolean isTerminalFailure(Workflow workflow) {
167+
return workflow.getStatus() == Workflow.WorkflowStatus.FAILED
168+
|| workflow.getStatus() == Workflow.WorkflowStatus.TERMINATED;
159169
}
160170

161171
/** Includes the reason and task states — without these a failure is undiagnosable from CI logs. */
@@ -177,27 +187,4 @@ private static String taskSummary(Workflow workflow) {
177187
.append(' '));
178188
return sb.append(']').toString();
179189
}
180-
181-
/**
182-
* Retries {@code call} until it returns without throwing. Intended for endpoints that are
183-
* eventually consistent or intermittently slow, where a single attempt after a fixed sleep
184-
* turns ordinary latency into a test failure.
185-
*/
186-
public static <T> T retryUntil(java.util.concurrent.Callable<T> call,
187-
long maxWaitTimeMs,
188-
long pollIntervalMs) throws Exception {
189-
long endTime = System.currentTimeMillis() + maxWaitTimeMs;
190-
Exception last = null;
191-
192-
while (System.currentTimeMillis() < endTime) {
193-
try {
194-
return call.call();
195-
} catch (Exception e) {
196-
last = e;
197-
Thread.sleep(pollIntervalMs);
198-
}
199-
}
200-
throw new TimeoutException("Call did not succeed within " + maxWaitTimeMs + "ms"
201-
+ (last == null ? "" : ", last error: " + last));
202-
}
203190
}

0 commit comments

Comments
 (0)