This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new ee335421b7d0 CAMEL-23902: Fix flaky UndertowWsConsumerRouteTest 
(#24421)
ee335421b7d0 is described below

commit ee335421b7d0faf049b09361fc089b08daf3a9d3
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Jul 6 11:26:47 2026 +0200

    CAMEL-23902: Fix flaky UndertowWsConsumerRouteTest (#24421)
    
    * CAMEL-23902: Fix flaky UndertowWsConsumerRouteTest
    
    The WebSocket tests in UndertowWsConsumerRouteTest fail intermittently on
    JDK 17 because WebSocket.sendText() returns a CompletableFuture that must
    complete before the next send. When two messages are sent in rapid
    succession, the second send can fail silently with IllegalStateException.
    
    Replace CountDownLatch-based assertTrue(await(10)) with Awaitility
    polling in echo(), echoMulti(), sendToAll() and connectionKeyList().
    For tests that send multiple messages on the same connection (echo,
    connectionKeyList), wait for the echo of the first message before
    sending the next to avoid the JDK WebSocket pending-send conflict.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    * CAMEL-23902: Avoid ConcurrentModificationException during Awaitility 
polling
    
    Address Copilot review: getReceived(String.class) iterates over
    WebsocketTestClient's non-thread-safe ArrayList via for-each, risking
    ConcurrentModificationException while the listener thread adds messages.
    
    Switch Awaitility polling to use getReceived().size() (index-based, no
    iterator). Content assertions via getReceived(String.class) are only
    called after the size check confirms all expected messages arrived.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
---
 components/camel-undertow/pom.xml                  |  6 +++
 .../undertow/ws/UndertowWsConsumerRouteTest.java   | 45 ++++++++++++++--------
 2 files changed, 36 insertions(+), 15 deletions(-)

diff --git a/components/camel-undertow/pom.xml 
b/components/camel-undertow/pom.xml
index 5da11c33f5ef..5ed99a6b734e 100644
--- a/components/camel-undertow/pom.xml
+++ b/components/camel-undertow/pom.xml
@@ -127,6 +127,12 @@
             <artifactId>junit-jupiter</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <version>${awaitility-version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.eclipse.jetty.http2</groupId>
             <artifactId>jetty-http2-client</artifactId>
diff --git 
a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/ws/UndertowWsConsumerRouteTest.java
 
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/ws/UndertowWsConsumerRouteTest.java
index 84752761c891..4fd6fc7a8e26 100644
--- 
a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/ws/UndertowWsConsumerRouteTest.java
+++ 
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/ws/UndertowWsConsumerRouteTest.java
@@ -46,6 +46,7 @@ import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.awaitility.Awaitility.await;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -168,9 +169,15 @@ public class UndertowWsConsumerRouteTest extends 
BaseUndertowTest {
         wsclient1.connect();
 
         wsclient1.sendTextMessage("Test1");
-        wsclient1.sendTextMessage("Test2");
+        // Wait for the first echo before sending the next message to avoid
+        // IllegalStateException from JDK WebSocket when a send is still 
pending.
+        // Poll using size() to avoid iterating the non-thread-safe received 
list.
+        await().atMost(10, TimeUnit.SECONDS)
+                .until(() -> wsclient1.getReceived().size() >= 1);
 
-        assertTrue(wsclient1.await(10));
+        wsclient1.sendTextMessage("Test2");
+        await().atMost(10, TimeUnit.SECONDS)
+                .until(() -> wsclient1.getReceived().size() >= 2);
 
         assertEquals(Arrays.asList("Test1", "Test2"), 
wsclient1.getReceived(String.class));
 
@@ -187,8 +194,9 @@ public class UndertowWsConsumerRouteTest extends 
BaseUndertowTest {
         wsclient1.sendTextMessage("Gambas");
         wsclient2.sendTextMessage("Calamares");
 
-        assertTrue(wsclient1.await(10));
-        assertTrue(wsclient2.await(10));
+        // Poll using size() to avoid iterating the non-thread-safe received 
list
+        await().atMost(10, TimeUnit.SECONDS)
+                .until(() -> wsclient1.getReceived().size() >= 1 && 
wsclient2.getReceived().size() >= 1);
 
         assertEquals(List.of("Gambas"), wsclient1.getReceived(String.class));
         assertEquals(List.of("Calamares"), 
wsclient2.getReceived(String.class));
@@ -207,12 +215,12 @@ public class UndertowWsConsumerRouteTest extends 
BaseUndertowTest {
         wsclient1.sendTextMessage("Gambas");
         wsclient2.sendTextMessage("Calamares");
 
-        assertTrue(wsclient1.await(10));
-        assertTrue(wsclient2.await(10));
+        // Poll using size() to avoid iterating the non-thread-safe received 
list
+        await().atMost(10, TimeUnit.SECONDS)
+                .until(() -> wsclient1.getReceived().size() >= 2 && 
wsclient2.getReceived().size() >= 2);
 
         List<String> received1 = wsclient1.getReceived(String.class);
         assertEquals(2, received1.size());
-
         assertTrue(received1.contains("Gambas"));
         assertTrue(received1.contains("Calamares"));
 
@@ -292,12 +300,16 @@ public class UndertowWsConsumerRouteTest extends 
BaseUndertowTest {
         wsclient2.connect();
         wsclient3.connect();
 
-        wsclient1.await(10);
+        // Poll using size() to avoid iterating the non-thread-safe received 
list
+        await().atMost(10, TimeUnit.SECONDS)
+                .until(() -> wsclient1.getReceived().size() >= 1);
         final String connectionKey1 = assertConnected(wsclient1);
         assertNotNull(connectionKey1);
-        wsclient2.await(10);
+        await().atMost(10, TimeUnit.SECONDS)
+                .until(() -> wsclient2.getReceived().size() >= 1);
         final String connectionKey2 = assertConnected(wsclient2);
-        wsclient3.await(10);
+        await().atMost(10, TimeUnit.SECONDS)
+                .until(() -> wsclient3.getReceived().size() >= 1);
         final String connectionKey3 = assertConnected(wsclient3);
 
         wsclient1.reset(1);
@@ -305,13 +317,16 @@ public class UndertowWsConsumerRouteTest extends 
BaseUndertowTest {
         wsclient3.reset(1);
         final String broadcastMsg = BROADCAST_MESSAGE_PREFIX + connectionKey2 
+ " " + connectionKey3;
         wsclient1.sendTextMessage(broadcastMsg); // this one should go to 
wsclient2 and wsclient3
-        wsclient1.sendTextMessage("private"); // this one should go to 
wsclient1 only
-
-        wsclient2.await(10);
+        // Wait for broadcast delivery before sending the next message to avoid
+        // IllegalStateException from JDK WebSocket when a send is still 
pending
+        await().atMost(10, TimeUnit.SECONDS)
+                .until(() -> wsclient2.getReceived().size() >= 1 && 
wsclient3.getReceived().size() >= 1);
         assertEquals(broadcastMsg, wsclient2.getReceived(String.class).get(0));
-        wsclient3.await(10);
         assertEquals(broadcastMsg, wsclient3.getReceived(String.class).get(0));
-        wsclient1.await(10);
+
+        wsclient1.sendTextMessage("private"); // this one should go to 
wsclient1 only
+        await().atMost(10, TimeUnit.SECONDS)
+                .until(() -> wsclient1.getReceived().size() >= 1);
         assertEquals("private", wsclient1.getReceived(String.class).get(0));
 
         wsclient1.close();

Reply via email to