You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ap...@apache.org on 2026/10/15 06:37:14 UTC

(camel) branch main updated: CAMEL-25667: fix flaky FtpRecursiveDepth*IT tests

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

apupier 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 7e959e4c42be CAMEL-25667: fix flaky FtpRecursiveDepth*IT tests
7e959e4c42be is described below

commit 7e959e4c42be8b18da1922fda373158e40806e28
Author: Aurélien Pupier <ap...@ibm.com>
AuthorDate: Wed Oct 14 21:08:07 2026 +0200

    CAMEL-25667: fix flaky FtpRecursiveDepth*IT tests
    
    The three FtpRecursiveDepth*IT tests uploaded files to the FTP server
    inside the test method while the consumer route was already running.
    This created a race condition: the FTP consumer could start its first
    directory poll before all files had been uploaded, causing it to see
    only a partial file tree. Because the default post-processing strategy
    moves consumed files to a .camel/ subdirectory rather than deleting
    them, a subsequent poll could then pick up previously-consumed files
    again, resulting in duplicate or missing messages in the mock endpoint.
    
    Fix: mark each consumer route autoStartup(false), upload all test
    files synchronously first, then start the route explicitly via
    context.getRouteController().startRoute(...). This ensures the
    consumer's first poll always sees the complete, stable directory tree.
    
    The same pattern is already used by FtpSimpleConsumeAbsoluteIT,
    FtpSimpleConsumeStreamingWithMultipleFilesIT, and others in the same
    test suite. FromFtpRecursiveNoopIT was already safe because it uploads
    in doPostSetup() before routes start.
    
    Co-authored-by: IBM Bob 2.2.1 <no...@ibm.com>
    Signed-off-by: Aurélien Pupier <ap...@ibm.com>
---
 .../file/remote/integration/FtpRecursiveDepth2IT.java   | 17 ++++++++++-------
 .../file/remote/integration/FtpRecursiveDepth3IT.java   | 13 +++++++++----
 .../file/remote/integration/FtpRecursiveDepthIT.java    | 13 +++++++++----
 3 files changed, 28 insertions(+), 15 deletions(-)

diff --git a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepth2IT.java b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepth2IT.java
index 91685807392c..e5ea4b462307 100644
--- a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepth2IT.java
+++ b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepth2IT.java
@@ -16,8 +16,6 @@
  */
 package org.apache.camel.component.file.remote.integration;
 
-import java.util.concurrent.TimeUnit;
-
 import org.apache.camel.Exchange;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
@@ -31,9 +29,8 @@ public class FtpRecursiveDepth2IT extends FtpServerTestSupport {
 
     @Test
     public void testDepthMin2Max99() throws Exception {
-        MockEndpoint mock = getMockEndpoint("mock:result");
-        mock.expectedBodiesReceivedInAnyOrder("a2", "b2", "a3", "b3");
-
+        // Upload all files before starting the consumer to avoid a race where
+        // the route picks up files mid-upload and produces inconsistent results.
         template.sendBodyAndHeader("ftp://admin@localhost:{{ftp.server.port}}/depth2?password=admin", "a", Exchange.FILE_NAME,
                 "a.txt");
         template.sendBodyAndHeader("ftp://admin@localhost:{{ftp.server.port}}/depth2?password=admin", "b", Exchange.FILE_NAME,
@@ -47,7 +44,12 @@ public class FtpRecursiveDepth2IT extends FtpServerTestSupport {
         template.sendBodyAndHeader("ftp://admin@localhost:{{ftp.server.port}}/depth2/bar/foo?password=admin", "b3",
                 Exchange.FILE_NAME, "b3.txt");
 
-        MockEndpoint.assertIsSatisfied(context, 20, TimeUnit.SECONDS);
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceivedInAnyOrder("a2", "b2", "a3", "b3");
+
+        context.getRouteController().startRoute("ftp-recursive-depth2");
+
+        MockEndpoint.assertIsSatisfied(context);
     }
 
     @Override
@@ -55,7 +57,8 @@ public class FtpRecursiveDepth2IT extends FtpServerTestSupport {
         return new RouteBuilder() {
             @Override
             public void configure() {
-                from(getFtpUrl() + "&minDepth=2&maxDepth=99").convertBodyTo(String.class).to("mock:result");
+                from(getFtpUrl() + "&minDepth=2&maxDepth=99").routeId("ftp-recursive-depth2").autoStartup(false)
+                        .convertBodyTo(String.class).to("mock:result");
             }
         };
     }
diff --git a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepth3IT.java b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepth3IT.java
index 9a1f22ecfefe..89a0fd1c51b0 100644
--- a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepth3IT.java
+++ b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepth3IT.java
@@ -29,9 +29,8 @@ public class FtpRecursiveDepth3IT extends FtpServerTestSupport {
 
     @Test
     public void testDepthMin1Max1() throws Exception {
-        MockEndpoint mock = getMockEndpoint("mock:result");
-        mock.expectedBodiesReceivedInAnyOrder("a", "b");
-
+        // Upload all files before starting the consumer to avoid a race where
+        // the route picks up files mid-upload and produces inconsistent results.
         template.sendBodyAndHeader("ftp://admin@localhost:{{ftp.server.port}}/depth3?password=admin", "a", Exchange.FILE_NAME,
                 "a.txt");
         template.sendBodyAndHeader("ftp://admin@localhost:{{ftp.server.port}}/depth3?password=admin", "b", Exchange.FILE_NAME,
@@ -45,6 +44,11 @@ public class FtpRecursiveDepth3IT extends FtpServerTestSupport {
         template.sendBodyAndHeader("ftp://admin@localhost:{{ftp.server.port}}/depth3/bar/foo?password=admin", "b3",
                 Exchange.FILE_NAME, "b.txt");
 
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceivedInAnyOrder("a", "b");
+
+        context.getRouteController().startRoute("ftp-recursive-depth3");
+
         MockEndpoint.assertIsSatisfied(context);
     }
 
@@ -53,7 +57,8 @@ public class FtpRecursiveDepth3IT extends FtpServerTestSupport {
         return new RouteBuilder() {
             @Override
             public void configure() {
-                from(getFtpUrl() + "&minDepth=1&maxDepth=1").convertBodyTo(String.class).to("mock:result");
+                from(getFtpUrl() + "&minDepth=1&maxDepth=1").routeId("ftp-recursive-depth3").autoStartup(false)
+                        .convertBodyTo(String.class).to("mock:result");
             }
         };
     }
diff --git a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepthIT.java b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepthIT.java
index 0e71eb9ca7b6..abd4af82d460 100644
--- a/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepthIT.java
+++ b/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/integration/FtpRecursiveDepthIT.java
@@ -29,9 +29,8 @@ public class FtpRecursiveDepthIT extends FtpServerTestSupport {
 
     @Test
     public void testDepth() throws Exception {
-        MockEndpoint mock = getMockEndpoint("mock:result");
-        mock.expectedBodiesReceivedInAnyOrder("a2", "b2");
-
+        // Upload all files before starting the consumer to avoid a race where
+        // the route picks up files mid-upload and produces inconsistent results.
         template.sendBodyAndHeader("ftp://admin@localhost:{{ftp.server.port}}/depth?password=admin", "a", Exchange.FILE_NAME,
                 "a.txt");
         template.sendBodyAndHeader("ftp://admin@localhost:{{ftp.server.port}}/depth?password=admin", "b", Exchange.FILE_NAME,
@@ -47,6 +46,11 @@ public class FtpRecursiveDepthIT extends FtpServerTestSupport {
         template.sendBodyAndHeader("ftp://admin@localhost:{{ftp.server.port}}/depth/bar/foo?password=admin", "b3",
                 Exchange.FILE_NAME, "b.txt");
 
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceivedInAnyOrder("a2", "b2");
+
+        context.getRouteController().startRoute("ftp-recursive-depth");
+
         // only expect 2 of the 6 sent, those at depth 2
         MockEndpoint.assertIsSatisfied(context);
     }
@@ -56,7 +60,8 @@ public class FtpRecursiveDepthIT extends FtpServerTestSupport {
         return new RouteBuilder() {
             @Override
             public void configure() {
-                from(getFtpUrl() + "&minDepth=2&maxDepth=2").convertBodyTo(String.class).to("mock:result");
+                from(getFtpUrl() + "&minDepth=2&maxDepth=2").routeId("ftp-recursive-depth").autoStartup(false)
+                        .convertBodyTo(String.class).to("mock:result");
             }
         };
     }