You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/04/20 07:50:09 UTC

[camel-spring-boot] branch main updated: CAMEL-17959 Add tests in camel-file-starter (#527)

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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git


The following commit(s) were added to refs/heads/main by this push:
     new 2f1d2f03384 CAMEL-17959 Add tests in camel-file-starter (#527)
2f1d2f03384 is described below

commit 2f1d2f03384f98f5a91d428690e9e177378d3dbb
Author: JiriOndrusek <on...@gmail.com>
AuthorDate: Wed Apr 20 09:50:04 2022 +0200

    CAMEL-17959 Add tests in camel-file-starter (#527)
---
 components-starter/camel-file-starter/pom.xml      |   5 +
 .../file/AntPathMatcherGenericFileFilterTest.java  |  86 ++++++++++
 .../org/apache/camel/component/file/BaseFile.java  | 174 +++++++++++++++++++++
 .../file/FileBeginFailureOneTimeTest.java          | 138 ++++++++++++++++
 .../component/file/FileBridgeErrorHandlerTest.java |  78 +++++++++
 .../component/file/FileBrowsableEndpointTest.java  |  71 +++++++++
 .../FileChangedReadLockMarkerFileFalseTest.java    | 121 ++++++++++++++
 .../FileConsumerAbsolutePathDefaultMoveTest.java   |  74 +++++++++
 .../component/file/FileConsumerBatchTest.java      |  86 ++++++++++
 .../file/FileConsumerFileExpressionTest.java       | 121 ++++++++++++++
 .../component/file/FileConsumerFileFilterTest.java | 118 ++++++++++++++
 .../component/file/FileConsumerIdempotentTest.java | 101 ++++++++++++
 .../file/FileConsumerMoveAndMoveFailureTest.java   |  84 ++++++++++
 .../component/file/FileConsumerPreMoveTest.java    | 113 +++++++++++++
 ...ileIdempotentReadLockWithFileIdempRepoTest.java | 125 +++++++++++++++
 .../file/FileIdempotentReadSameFileAgainTest.java  |  81 ++++++++++
 .../component/file/FileProduceTempPrefixTest.java  |  73 +++++++++
 .../file/FileProducerCharsetUTFtoISOTest.java      |  83 ++++++++++
 .../component/file/FileProducerExpressionTest.java |  51 ++++++
 .../file/FileProducerFileExistOverrideTest.java    |  77 +++++++++
 .../FileProducerOverruleToDifferentFolderTest.java |  74 +++++++++
 ...eProducerRecursivelayToDifferentFolderTest.java |  78 +++++++++
 .../component/file/FileProducerStreamTest.java     |  75 +++++++++
 .../camel/component/file/FileSorterRefTest.java    | 102 ++++++++++++
 .../component/file/FileToFileWithFlattenTest.java  | 111 +++++++++++++
 .../file/FilerConsumerDoneFileNamePrefixTest.java  |  90 +++++++++++
 .../file/FilerProducerDoneFileNameRouteTest.java   |  97 ++++++++++++
 .../file/FromFileMoveFileIfProcessFailsTest.java   |  80 ++++++++++
 28 files changed, 2567 insertions(+)

diff --git a/components-starter/camel-file-starter/pom.xml b/components-starter/camel-file-starter/pom.xml
index fb77d9ddb58..030307e85e9 100644
--- a/components-starter/camel-file-starter/pom.xml
+++ b/components-starter/camel-file-starter/pom.xml
@@ -39,6 +39,11 @@
       <artifactId>camel-file</artifactId>
       <version>${camel-version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.awaitility</groupId>
+      <artifactId>awaitility</artifactId>
+      <scope>test</scope>
+    </dependency>
     <!--START OF GENERATED CODE-->
     <dependency>
       <groupId>org.apache.camel.springboot</groupId>
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/AntPathMatcherGenericFileFilterTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/AntPathMatcherGenericFileFilterTest.java
new file mode 100644
index 00000000000..623e02617e5
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/AntPathMatcherGenericFileFilterTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.io.File;
+
+/**
+ * Unit tests for {@link AntPathMatcherGenericFileFilter}.
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                AntPathMatcherGenericFileFilterTest.class,
+                AntPathMatcherGenericFileFilterTest.TestConfiguration.class
+        }
+)
+public class AntPathMatcherGenericFileFilterTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Test
+    public void testInclude() throws Exception {
+        resultEndpoint.expectedBodiesReceivedInAnyOrder("Hello World");
+
+        String endpointUri = fileUri("files/ant-path/x/y/z");
+        template.sendBodyAndHeader(endpointUri, "Hello World", Exchange.FILE_NAME, "report.txt");
+        template.sendBodyAndHeader(endpointUri, "Hello World 2", Exchange.FILE_NAME, "b.TXT");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean(value = "filter")
+        public GenericFileFilter filter() {
+            return new AntPathMatcherGenericFileFilter<File>("**/c*");
+        }
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri(
+                            "files/ant-path?initialDelay=0&delay=10&recursive=true&antInclude=**/*.txt&antFilterCaseSensitive=true"))
+                            .convertBodyTo(String.class)
+                            .to("mock:result");
+                }
+            };
+        }
+
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/BaseFile.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/BaseFile.java
new file mode 100644
index 00000000000..30bb4227a57
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/BaseFile.java
@@ -0,0 +1,174 @@
+package org.apache.camel.component.file;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.RouteConfigurationsBuilder;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.seda.SedaComponent;
+import org.apache.camel.model.ModelCamelContext;
+import org.junit.jupiter.api.BeforeEach;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BaseFile {
+
+    @Autowired
+    protected CamelContext context;
+
+    @Autowired
+    protected ProducerTemplate template;
+
+    NotifyBuilder oneExchangeDone;
+
+    protected void assertMockEndpointsSatisfied() throws InterruptedException {
+        MockEndpoint.assertIsSatisfied(this.context);
+    }
+
+    @BeforeEach
+    public void setUp() {
+        oneExchangeDone = new NotifyBuilder(context).whenDone(1).create();
+    }
+
+    //-------------------- assertions from testSupport ------------------------------
+
+    /**
+     * To be used to check is a file is found in the file system
+     */
+    public static void  assertFileExists(Path file, String content) throws IOException {
+        assertTrue(Files.exists(file), "File " + file + " should exist");
+        assertTrue(Files.isRegularFile(file), "File " + file + " should be a file");
+        assertEquals(content, new String(Files.readAllBytes(file)), "File " + file + " has unexpected content");
+    }
+
+    /**
+     * To be used to check is a file is found in the file system
+     */
+    public static void assertFileExists(Path file) {
+        assertTrue(Files.exists(file), "File " + file + " should exist");
+        assertTrue(Files.exists(file), "File " + file + " should be a file");
+    }
+
+    /**
+     * To be used to check is a file is <b>not</b> found in the file system
+     */
+    public static void assertFileNotExists(Path file) {
+        assertFalse(Files.exists(file), "File " + file + " should not exist");
+    }
+
+    /**
+     * To be used for folder/directory comparison that works across different platforms such as Window, Mac and Linux.
+     */
+    public static void assertDirectoryEquals(String expected, String actual) {
+        assertDirectoryEquals(null, expected, actual);
+    }
+
+    /**
+     * To be used for folder/directory comparison that works across different platforms such as Window, Mac and Linux.
+     */
+    public static void assertDirectoryEquals(String message, String expected, String actual) {
+        // must use single / as path separators
+        String expectedPath = expected.replace('\\', '/');
+        String actualPath = actual.replace('\\', '/');
+
+        if (message != null) {
+            assertEquals(expectedPath, actualPath, message);
+        } else {
+            assertEquals(expectedPath, actualPath);
+        }
+    }
+
+
+    //--------------------- from TestSupport ------------------------------------
+
+    protected static final String LS = System.lineSeparator();
+    protected boolean testDirectoryCleaned;
+
+    public void deleteTestDirectory() {
+        if (!testDirectoryCleaned) {
+            deleteDirectory(testDirectory().toFile());
+            testDirectoryCleaned = true;
+        }
+    }
+
+    protected Path testDirectory() {
+        return testDirectory(false);
+    }
+
+    protected Path testDirectory(boolean create) {
+        Class<?> testClass = getClass();
+        if (create) {
+            deleteTestDirectory();
+        }
+        return testDirectory(testClass, create);
+    }
+
+    public static Path testDirectory(Class<?> testClass, boolean create) {
+        Path dir = Paths.get("target", "data", testClass.getSimpleName());
+        if (create) {
+            try {
+                Files.createDirectories(dir);
+            } catch (IOException e) {
+                throw new IllegalStateException("Unable to create test directory: " + dir, e);
+            }
+        }
+        return dir;
+    }
+
+    protected Path testFile(String dir) {
+        return testDirectory().resolve(dir);
+    }
+
+
+    protected Path testDirectory(String dir) {
+        return testDirectory(dir, false);
+    }
+
+    protected Path testDirectory(String dir, boolean create) {
+        Path f = testDirectory().resolve(dir);
+        if (create) {
+            try {
+                Files.createDirectories(f);
+            } catch (IOException e) {
+                throw new IllegalStateException("Unable to create test directory: " + dir, e);
+            }
+        }
+        return f;
+    }
+
+    /**
+     * Recursively delete a directory, useful to zapping test data
+     *
+     * @param file the directory to be deleted
+     */
+    public static void deleteDirectory(File file) {
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+            if (files != null) {
+                for (File child : files) {
+                    deleteDirectory(child);
+                }
+            }
+        }
+
+        file.delete();
+    }
+
+    protected String fileUri() {
+        return "file:" + testDirectory();
+    }
+
+    protected String fileUri(String query) {
+        return "file:" + testDirectory() + (query.startsWith("?") ? "" : "/") + query;
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileBeginFailureOneTimeTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileBeginFailureOneTimeTest.java
new file mode 100644
index 00000000000..93cc4c26ebc
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileBeginFailureOneTimeTest.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.io.File;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileBeginFailureOneTimeTest.class,
+                FileBeginFailureOneTimeTest.TestConfiguration.class
+        }
+)
+public class FileBeginFailureOneTimeTest extends BaseFile {
+
+    private static MyStrategy myStrategy = new MyStrategy();
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Test
+    public void testBeginFailureOneTime() throws Exception {
+        resultEndpoint.expectedMessageCount(1);
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "hello.txt");
+
+        assertMockEndpointsSatisfied();
+
+        assertEquals(2, myStrategy.getInvoked(), "Begin should have been invoked 2 times");
+    }
+
+    private static class MyStrategy implements GenericFileProcessStrategy<File> {
+
+        private volatile int invoked;
+
+        @Override
+        public void prepareOnStartup(
+                GenericFileOperations<File> fileGenericFileOperations, GenericFileEndpoint<File> fileGenericFileEndpoint)
+                throws Exception {
+        }
+
+        @Override
+        public boolean begin(
+                GenericFileOperations<File> fileGenericFileOperations, GenericFileEndpoint<File> fileGenericFileEndpoint,
+                Exchange exchange,
+                GenericFile<File> fileGenericFile)
+                throws Exception {
+            invoked++;
+            if (invoked <= 1) {
+                throw new IllegalArgumentException("Damn I cannot do this");
+            }
+            return true;
+        }
+
+        @Override
+        public void abort(
+                GenericFileOperations<File> fileGenericFileOperations, GenericFileEndpoint<File> fileGenericFileEndpoint,
+                Exchange exchange,
+                GenericFile<File> fileGenericFile)
+                throws Exception {
+            // noop
+        }
+
+        @Override
+        public void commit(
+                GenericFileOperations<File> fileGenericFileOperations, GenericFileEndpoint<File> fileGenericFileEndpoint,
+                Exchange exchange,
+                GenericFile<File> fileGenericFile)
+                throws Exception {
+        }
+
+        @Override
+        public void rollback(
+                GenericFileOperations<File> fileGenericFileOperations, GenericFileEndpoint<File> fileGenericFileEndpoint,
+                Exchange exchange,
+                GenericFile<File> fileGenericFile)
+                throws Exception {
+        }
+
+        public int getInvoked() {
+            return invoked;
+        }
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+
+        @Bean(value = "myStrategy")
+        public GenericFileProcessStrategy myStrategy() {
+            return myStrategy;
+        }
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?initialDelay=0&delay=10&processStrategy=#myStrategy")).convertBodyTo(String.class)
+                            .to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileBridgeErrorHandlerTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileBridgeErrorHandlerTest.java
new file mode 100644
index 00000000000..b096be2cf67
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileBridgeErrorHandlerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileBridgeErrorHandlerTest.class,
+                FileBridgeErrorHandlerTest.TestConfiguration.class
+        }
+)
+public class FileBridgeErrorHandlerTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @EndpointInject("mock:error")
+    private MockEndpoint errorEndpoint;
+
+    @Test
+    public void testBridgeErrorHandler() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+        errorEndpoint.expectedMinimumMessageCount(1);
+        errorEndpoint.allMessages().exchangeProperty(Exchange.EXCEPTION_CAUGHT).isNotNull();
+
+        assertMockEndpointsSatisfied();
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    onException(Exception.class).handled(true).to("mock:error");
+
+                    from("file://target/dummy?bridgeErrorHandler=true&autoCreate=false&directoryMustExist=true")
+                            .log("${body}")
+                            .to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileBrowsableEndpointTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileBrowsableEndpointTest.java
new file mode 100644
index 00000000000..a5d4ef38ee3
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileBrowsableEndpointTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.nio.file.Files;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileBrowsableEndpointTest.class
+        }
+)
+public class FileBrowsableEndpointTest extends BaseFile {
+
+    @Test
+    public void testBrowsableTwoFiles() throws Exception {
+        template.sendBodyAndHeader(fileUri(), "B", Exchange.FILE_NAME, "b.txt");
+        template.sendBodyAndHeader(fileUri(), "A", Exchange.FILE_NAME, "a.txt");
+
+        FileEndpoint endpoint
+                = context.getEndpoint(fileUri("?initialDelay=0&delay=10&sortBy=file:name"), FileEndpoint.class);
+        assertNotNull(endpoint);
+
+        MemoryIdempotentRepository repo = (MemoryIdempotentRepository) endpoint.getInProgressRepository();
+        assertEquals(0, repo.getCacheSize());
+
+        List<Exchange> list = endpoint.getExchanges();
+        assertNotNull(list);
+        assertEquals(2, list.size());
+
+        assertEquals("a.txt", list.get(0).getIn().getHeader(Exchange.FILE_NAME));
+        assertEquals("b.txt", list.get(1).getIn().getHeader(Exchange.FILE_NAME));
+
+        // the in progress repo should not leak
+        assertEquals(0, repo.getCacheSize());
+
+        // and the files is still there
+        assertTrue(Files.exists(testFile("a.txt")), "File should exist a.txt");
+        assertTrue(Files.exists(testFile("b.txt")), "File should exist b.txt");
+    }
+
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileChangedReadLockMarkerFileFalseTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileChangedReadLockMarkerFileFalseTest.java
new file mode 100644
index 00000000000..1e835bde363
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileChangedReadLockMarkerFileFalseTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.Isolated;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.io.OutputStream;
+import java.nio.file.Files;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@Isolated("Does not play well with parallel unit test execution")
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileChangedReadLockMarkerFileFalseTest.class,
+                FileChangedReadLockMarkerFileFalseTest.TestConfiguration.class
+        }
+)
+public class FileChangedReadLockMarkerFileFalseTest extends BaseFile {
+
+    private static final Logger LOG = LoggerFactory.getLogger(FileChangedReadLockMarkerFileFalseTest.class);
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Override
+    @BeforeEach
+    public void setUp() {
+        super.setUp();
+        testDirectory("in", true);
+    }
+
+    @Test
+    public void testChangedReadLock() throws Exception {
+        resultEndpoint.expectedMessageCount(1);
+        resultEndpoint.expectedFileExists(testFile("out/slowfile.dat"));
+        resultEndpoint.expectedHeaderReceived(Exchange.FILE_LENGTH, expectedFileLength());
+
+        writeSlowFile();
+
+        assertMockEndpointsSatisfied();
+
+        String content = new String(Files.readAllBytes(testFile("out/slowfile.dat")));
+        String[] lines = content.split(LS);
+        assertEquals(20, lines.length, "There should be 20 lines in the file");
+        for (int i = 0; i < 20; i++) {
+            assertEquals("Line " + i, lines[i]);
+        }
+    }
+
+    private void writeSlowFile() throws Exception {
+        LOG.debug("Writing slow file...");
+        try (OutputStream fos = Files.newOutputStream(testFile("in/slowfile.dat"))) {
+            for (int i = 0; i < 20; i++) {
+                fos.write(("Line " + i + LS).getBytes());
+                LOG.debug("Writing line " + i);
+                Thread.sleep(50);
+            }
+            fos.flush();
+        }
+        LOG.debug("Writing slow file DONE...");
+    }
+
+    long expectedFileLength() {
+        long length = 0;
+        for (int i = 0; i < 20; i++) {
+            length += ("Line " + i + LS).getBytes().length;
+        }
+        return length;
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("in?initialDelay=0&delay=10&readLock=changed&readLockCheckInterval=100&readLockMarkerFile=false"))
+                            .to(fileUri("out"),
+                                    "mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerAbsolutePathDefaultMoveTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerAbsolutePathDefaultMoveTest.java
new file mode 100644
index 00000000000..fbf278b3020
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerAbsolutePathDefaultMoveTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+/**
+ * Unit test for consuming from an absolute path
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileConsumerAbsolutePathDefaultMoveTest.class,
+                FileConsumerAbsolutePathDefaultMoveTest.TestConfiguration.class
+        }
+)
+public class FileConsumerAbsolutePathDefaultMoveTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+
+    @Test
+    public void testConsumeFromAbsolutePath() throws Exception {
+        resultEndpoint.expectedBodiesReceived("Hello Paris");
+        resultEndpoint.expectedFileExists(testFile(".camel/paris.txt"));
+
+        template.sendBodyAndHeader(fileUri(), "Hello Paris", Exchange.FILE_NAME, "paris.txt");
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?initialDelay=0&delay=10")).convertBodyTo(String.class).to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerBatchTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerBatchTest.java
new file mode 100644
index 00000000000..b4ba576d869
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerBatchTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+/**
+ * Unit test for consuming a batch of files (multiple files in one consume)
+ */
+//Based on FileConsumerBatchTest
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileConsumerBatchTest.class,
+                FileConsumerBatchTest.TestConfiguration.class
+        }
+)
+public class FileConsumerBatchTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Test
+    public void testConsumeBatch() throws Exception {
+        resultEndpoint.expectedBodiesReceivedInAnyOrder("Hello World", "Bye World");
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader(fileUri(), "Bye World", Exchange.FILE_NAME, "bye.txt");
+
+        // test header keys
+        resultEndpoint.message(0).exchangeProperty(Exchange.BATCH_SIZE).isEqualTo(2);
+        resultEndpoint.message(0).exchangeProperty(Exchange.BATCH_INDEX).isEqualTo(0);
+        resultEndpoint.message(0).exchangeProperty(Exchange.BATCH_COMPLETE).isEqualTo(false);
+        resultEndpoint.message(1).exchangeProperty(Exchange.BATCH_INDEX).isEqualTo(1);
+        resultEndpoint.message(1).exchangeProperty(Exchange.BATCH_COMPLETE).isEqualTo(true);
+
+        // start routes
+        context.getRouteController().startAllRoutes();
+
+        assertMockEndpointsSatisfied();
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?initialDelay=0&delay=10")).noAutoStartup().convertBodyTo(String.class)
+                            .to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerFileExpressionTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerFileExpressionTest.java
new file mode 100644
index 00000000000..698892336eb
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerFileExpressionTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+/**
+ * Unit test for expression option for file consumer.
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileConsumerFileExpressionTest.class,
+                FileConsumerFileExpressionTest.TestConfiguration.class
+        }
+)
+public class FileConsumerFileExpressionTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @AfterEach
+    public void reset() {
+        resultEndpoint.reset();
+    }
+
+    @Test
+    public void testConsumeFileBasedOnBeanName() throws Exception {
+        template.sendBodyAndHeader(fileUri("bean"), "Hello World", Exchange.FILE_NAME, "122.txt");
+        template.sendBodyAndHeader(fileUri("bean"), "Goodday World", Exchange.FILE_NAME, "123.txt");
+        template.sendBodyAndHeader(fileUri("bean"), "Bye World", Exchange.FILE_NAME, "124.txt");
+
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from(fileUri("bean"
+                             + "?initialDelay=0&delay=10&fileName=${bean:counter.next}.txt&delete=true")).to("mock:result");
+            }
+        });
+
+        // we should only get one as we only poll a single file using the file
+        // expression
+        resultEndpoint.expectedBodiesReceived("Goodday World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testConsumeFileBasedOnDatePattern() throws Exception {
+        template.sendBodyAndHeader(fileUri("date"), "Bye World", Exchange.FILE_NAME,
+                "myfile-20081128.txt");
+        template.sendBodyAndHeader(fileUri("date"), "Hello World", Exchange.FILE_NAME,
+                "myfile-20081129.txt");
+        template.sendBodyAndHeader(fileUri("date"), "Goodday World", Exchange.FILE_NAME,
+                context.resolveLanguage("simple").createExpression("myfile-${date:now:yyyyMMdd}.txt"));
+
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // START SNIPPET: e1
+                from(fileUri("date"
+                             + "?initialDelay=0&delay=10&fileName=myfile-${date:now:yyyyMMdd}.txt")).convertBodyTo(String.class)
+                                     .to("mock:result");
+                // END SNIPPET: e1
+            }
+        });
+
+        // we should only get one as we only poll a single file using the file
+        // expression
+        resultEndpoint.expectedBodiesReceived("Goodday World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public class MyGuidGenerator {
+        public String next() {
+            return "123";
+        }
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean(value = "counter")
+        public MyGuidGenerator myGuidGenerator() {
+            return new MyGuidGenerator();
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerFileFilterTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerFileFilterTest.java
new file mode 100644
index 00000000000..7d0f07d253b
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerFileFilterTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+/**
+ * Unit test for the file filter option
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileConsumerFileFilterTest.class,
+                FileConsumerFileFilterTest.TestConfiguration.class
+        }
+)
+public class FileConsumerFileFilterTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    private String fileUrl = fileUri("?initialDelay=0&delay=10&filter=#myFilter");
+
+    @AfterEach
+    public void reset() {
+        resultEndpoint.reset();
+    }
+
+    @Test
+    public void testFilterFiles() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+
+        template.sendBodyAndHeader(fileUri(), "This is a file to be filtered",
+                Exchange.FILE_NAME,
+                "skipme.txt");
+
+        resultEndpoint.setResultWaitTime(100);
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testFilterFilesWithARegularFile() throws Exception {
+        resultEndpoint.expectedBodiesReceived("Hello World");
+
+        template.sendBodyAndHeader(fileUri(), "This is a file to be filtered",
+                Exchange.FILE_NAME,
+                "skipme.txt");
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME,
+                "hello.txt");
+
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    // START SNIPPET: e1
+    public class MyFileFilter<T> implements GenericFileFilter<T> {
+        @Override
+        public boolean accept(GenericFile<T> file) {
+            // we want all directories
+            if (file.isDirectory()) {
+                return true;
+            }
+            // we dont accept any files starting with skip in the name
+            return !file.getFileName().startsWith("skip");
+        }
+    }
+    // END SNIPPET: e1
+
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean(value = "myFilter")
+        public GenericFileFilter myFilter() {
+            return new MyFileFilter();
+        }
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUrl).convertBodyTo(String.class).to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerIdempotentTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerIdempotentTest.java
new file mode 100644
index 00000000000..6cfaeada941
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerIdempotentTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.nio.file.Files;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Unit test for the idempotent=true option.
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileConsumerIdempotentTest.class,
+                FileConsumerIdempotentTest.TestConfiguration.class
+        }
+)
+public class FileConsumerIdempotentTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Test
+    public void testIdempotent() throws Exception {
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "report.txt");
+
+        // consume the file the first time
+        resultEndpoint.expectedBodiesReceived("Hello World");
+
+        assertMockEndpointsSatisfied();
+
+        oneExchangeDone.matchesWaitTime();
+
+        // reset mock and set new expectations
+        resultEndpoint.reset();
+        resultEndpoint.expectedMessageCount(0);
+
+        // move file back
+        Files.move(testFile("done/report.txt"), testFile("report.txt"));
+
+        // should NOT consume the file again, let a bit time pass to let the
+        // consumer try to consume it but it should not
+        Thread.sleep(100);
+        assertMockEndpointsSatisfied();
+
+        FileEndpoint fe = context.getEndpoint(fileUri(), FileEndpoint.class);
+        assertNotNull(fe);
+
+        MemoryIdempotentRepository repo = (MemoryIdempotentRepository) fe.getInProgressRepository();
+        assertEquals(0, repo.getCacheSize(), "Should be no in-progress files");
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?idempotent=true&move=done/${file:name}&initialDelay=0&delay=10"))
+                            .convertBodyTo(String.class).to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerMoveAndMoveFailureTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerMoveAndMoveFailureTest.java
new file mode 100644
index 00000000000..56c6f7c3baf
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerMoveAndMoveFailureTest.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+//Based on FileConsumerMoveAndMoveFailureTest
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileConsumerMoveAndMoveFailureTest.class,
+                FileConsumerMoveAndMoveFailureTest.TestConfiguration.class
+        }
+)
+public class FileConsumerMoveAndMoveFailureTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Test
+    public void testMoveAndMoveFailed() throws Exception {
+        resultEndpoint.expectedBodiesReceived("Hello World");
+
+        resultEndpoint.expectedFileExists(testFile("moved/hello.txt"), "Hello World");
+        resultEndpoint.expectedFileExists(testFile("error/bye-error.txt"), "Kabom");
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader(fileUri(), "Kabom", Exchange.FILE_NAME, "bye.txt");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?move=moved&initialDelay=0&delay=10&moveFailed=error/${file:name.noext}-error.txt"))
+                            .process(new Processor() {
+                                public void process(Exchange exchange) throws Exception {
+                                    String body = exchange.getIn().getBody(String.class);
+                                    if ("Kabom".equals(body)) {
+                                        throw new IllegalArgumentException("Forced");
+                                    }
+                                }
+                            }).convertBodyTo(String.class).to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerPreMoveTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerPreMoveTest.java
new file mode 100644
index 00000000000..50bb603fcb3
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileConsumerPreMoveTest.java
@@ -0,0 +1,113 @@
+package org.apache.camel.component.file;/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileConsumerPreMoveTest.class,
+                FileConsumerPreMoveTest.TestConfiguration.class
+        }
+)
+public class FileConsumerPreMoveTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @AfterEach
+    public void reset() {
+        resultEndpoint.reset();
+    }
+
+    @Test
+    public void testPreMove() throws Exception {
+        resultEndpoint.expectedMessageCount(1);
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "hello.txt");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testPreMoveSameFileTwice() throws Exception {
+        resultEndpoint.expectedBodiesReceived("Hello World");
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "hello.txt");
+
+        assertMockEndpointsSatisfied();
+        oneExchangeDone.matchesWaitTime();
+
+        // reset and drop the same file again
+        resultEndpoint.reset();
+        resultEndpoint.expectedBodiesReceived("Hello Again World");
+
+        template.sendBodyAndHeader(fileUri(), "Hello Again World", Exchange.FILE_NAME, "hello.txt");
+        assertMockEndpointsSatisfied();
+    }
+
+    public static class MyPreMoveCheckerProcessor implements Processor {
+
+        @Override
+        public void process(Exchange exchange) throws Exception {
+            Class<?> cl = getClass();
+            while (cl.getEnclosingClass() != null) {
+                cl = cl.getEnclosingClass();
+            }
+            Path file = testDirectory(cl, false).resolve("work/work-hello.txt");
+            assertTrue(Files.exists(file), "Pre move file should exist");
+        }
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?preMove=work/work-${file:name}&initialDelay=0&delay=10"))
+                            .process(new MyPreMoveCheckerProcessor()).to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileIdempotentReadLockWithFileIdempRepoTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileIdempotentReadLockWithFileIdempRepoTest.java
new file mode 100644
index 00000000000..e8f0d1e216d
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileIdempotentReadLockWithFileIdempRepoTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spi.IdempotentRepository;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.support.processor.idempotent.FileIdempotentRepository;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileIdempotentReadLockWithFileIdempRepoTest.class,
+                FileIdempotentReadLockWithFileIdempRepoTest.TestConfiguration.class
+        }
+)
+//Based on FileIdempotentReadLockTest
+public class FileIdempotentReadLockWithFileIdempRepoTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    static File repoFile;
+    static FileIdempotentRepository myRepo;
+
+    static {
+        File f;
+        myRepo = null;
+        try {
+            f = File.createTempFile(FileIdempotentReadLockWithFileIdempRepoTest.class.getName(), "-repo");
+
+            myRepo = new FileIdempotentRepository(f, new HashMap<>());
+        } catch (IOException e) {
+            //asserted before the test
+        }
+    }
+
+    @Test
+    public void testIdempotentReadLock() throws Exception {
+        assertNotNull(myRepo);
+        assertEquals(0, myRepo.getCacheSize());
+
+        NotifyBuilder notify = new NotifyBuilder(context).whenDone(2).create();
+
+        resultEndpoint.expectedMessageCount(2);
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader(fileUri(), "Bye World", Exchange.FILE_NAME, "bye.txt");
+
+        assertTrue(notify.matches(5, TimeUnit.SECONDS));
+
+        assertMockEndpointsSatisfied();
+
+        // the files are kept on commit
+        // if you want to remove them then the idempotent repo need some way to
+        // evict idle keys
+        assertEquals(2, myRepo.getCacheSize());
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean(value = "myRepo")
+        public IdempotentRepository myRepo() {
+            return myRepo;
+        }
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?initialDelay=0&delay=10&readLock=idempotent&idempotentRepository=#myRepo"))
+                            .process(new Processor() {
+                                @Override
+                                public void process(Exchange exchange) throws Exception {
+                                    // we are in progress
+                                    int size = myRepo.getCacheSize();
+                                    assertTrue(size == 1 || size == 2);
+                                }
+                            }).to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileIdempotentReadSameFileAgainTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileIdempotentReadSameFileAgainTest.java
new file mode 100644
index 00000000000..0146d7c4129
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileIdempotentReadSameFileAgainTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileIdempotentReadSameFileAgainTest.class,
+                FileIdempotentReadSameFileAgainTest.TestConfiguration.class
+        }
+)
+public class FileIdempotentReadSameFileAgainTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+    private String uri = fileUri("?idempotent=false&move=../done&moveFailed=../error"
+                                 + "&preMove=working/${date:now:yyyyMMddHHmmssSSS}-${file:name}&readLock=none&initialDelay=0&delay=10");
+
+    @Test
+    public void testConsumeSameFileAgain() throws Exception {
+        // some file systems may read files in different order
+        resultEndpoint.expectedBodiesReceivedInAnyOrder("Hello World", "Foo");
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "foo.txt");
+        template.sendBodyAndHeader(fileUri(), "Foo", Exchange.FILE_NAME, "bar.txt");
+
+        assertMockEndpointsSatisfied();
+
+        resultEndpoint.reset();
+        resultEndpoint.expectedBodiesReceived("Bye World");
+
+        template.sendBodyAndHeader(fileUri(), "Bye World", Exchange.FILE_NAME, "foo.txt");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(uri).convertBodyTo(String.class).to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProduceTempPrefixTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProduceTempPrefixTest.java
new file mode 100644
index 00000000000..759939c2e3a
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProduceTempPrefixTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+/**
+ * Unit test for file producer option tempPrefix
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileProduceTempPrefixTest.class,
+                FileProduceTempPrefixTest.TestConfiguration.class
+        }
+)
+public class FileProduceTempPrefixTest extends BaseFile {
+
+    private String fileUrl = fileUri("?tempPrefix=inprogress.");
+
+    @Test
+    public void testCreateTempFileName() throws Exception {
+        Endpoint endpoint = context.getEndpoint(fileUrl);
+        GenericFileProducer<?> producer = (GenericFileProducer<?>) endpoint.createProducer();
+        Exchange exchange = endpoint.createExchange();
+        exchange.getIn().setHeader(Exchange.FILE_NAME, "claus.txt");
+
+        String tempFileName = producer.createTempFileName(exchange, testFile("claus.txt").toString());
+        assertDirectoryEquals(testFile("inprogress.claus.txt").toString(), tempFileName);
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from("direct:a").to(fileUrl);
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerCharsetUTFtoISOTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerCharsetUTFtoISOTest.java
new file mode 100644
index 00000000000..69a513439b6
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerCharsetUTFtoISOTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+/**
+ *
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileProducerCharsetUTFtoISOTest.class,
+                FileProducerCharsetUTFtoISOTest.TestConfiguration.class
+        }
+)
+//Based on FileProducerCharsetUTFtoISOTest
+public class FileProducerCharsetUTFtoISOTest extends BaseFile {
+
+    private static final String DATA = "ABC\u00e6";
+
+    @Test
+    public void testFileProducerCharsetUTFtoISO() throws Exception {
+        try (OutputStream fos = Files.newOutputStream(testFile("input.txt"))) {
+            fos.write(DATA.getBytes(StandardCharsets.UTF_8));
+        }
+        oneExchangeDone.matchesWaitTime();
+        assertFileExists(testFile("output.txt"));
+
+        byte[] data = Files.readAllBytes(testFile("output.txt"));
+
+        assertEquals(DATA, new String(data, StandardCharsets.ISO_8859_1));
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?initialDelay=0&delay=10&noop=true"))
+                            .to(fileUri("?fileName=output.txt&charset=iso-8859-1"));
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerExpressionTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerExpressionTest.java
new file mode 100644
index 00000000000..0256e6b11cb
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerExpressionTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Unit test for expression option for file producer.
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileProducerExpressionTest.class
+        }
+)
+public class FileProducerExpressionTest extends BaseFile {
+
+    @Test
+    public void testProducerDateByHeader() throws Exception {
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME,
+                context.resolveLanguage("simple").createExpression("myfile-${date:now:yyyyMMdd}.txt"));
+
+        String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
+        assertFileExists(testFile("myfile-" + date + ".txt"));
+    }
+
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerFileExistOverrideTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerFileExistOverrideTest.java
new file mode 100644
index 00000000000..342f2cf5c85
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerFileExistOverrideTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileProducerFileExistOverrideTest.class,
+                FileProducerFileExistOverrideTest.TestConfiguration.class
+        }
+)
+public class FileProducerFileExistOverrideTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+
+    @Test
+    public void testOverride() throws Exception {
+        resultEndpoint.expectedBodiesReceived("Bye World");
+        resultEndpoint.expectedFileExists(testFile("hello.txt"), "Bye World");
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "hello.txt");
+        template.sendBodyAndHeader(fileUri("?fileExist=Override"), "Bye World", Exchange.FILE_NAME, "hello.txt");
+
+        context.getRouteController().startAllRoutes();
+
+        assertMockEndpointsSatisfied();
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?noop=true&initialDelay=0&delay=10")).noAutoStartup().convertBodyTo(String.class)
+                            .to("mock:result");
+                }
+            };
+        }
+    }
+
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerOverruleToDifferentFolderTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerOverruleToDifferentFolderTest.java
new file mode 100644
index 00000000000..b12dafaaf53
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerOverruleToDifferentFolderTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.nio.file.Files;
+import java.util.concurrent.TimeUnit;
+
+import static org.awaitility.Awaitility.await;
+
+
+/**
+ *
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileProducerOverruleToDifferentFolderTest.class,
+                FileProducerOverruleToDifferentFolderTest.TestConfiguration.class
+        }
+)
+//Based on FileProducerCharsetUTFtoISOTest
+public class FileProducerOverruleToDifferentFolderTest extends BaseFile {
+
+    @Test
+    public void testFileProducerCharsetUTFtoISO() throws Exception {
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "in/hello.txt");
+        template.sendBodyAndHeader(fileUri(), "Bye World", Exchange.FILE_NAME, "in/bye.txt");
+
+        await().atMost(10, TimeUnit.SECONDS).until(() -> Files.exists(testFile("out/copy-of-hello.txt")) && Files.exists(testFile("out/copy-of-bye.txt")));
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("in/?delete=true")).to(fileUri("out?fileName=copy-of-${file:name}"));
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerRecursivelayToDifferentFolderTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerRecursivelayToDifferentFolderTest.java
new file mode 100644
index 00000000000..807c4584483
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerRecursivelayToDifferentFolderTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.concurrent.TimeUnit;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+/**
+ *
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileProducerRecursivelayToDifferentFolderTest.class,
+                FileProducerRecursivelayToDifferentFolderTest.TestConfiguration.class
+        }
+)
+//Based on FileProducerCharsetUTFtoISOTest
+public class FileProducerRecursivelayToDifferentFolderTest extends BaseFile {
+
+    @Test
+    public void testFileProducerCharsetUTFtoISO() throws Exception {
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "in/hello.txt");
+        template.sendBodyAndHeader(fileUri(), "Hello World in subdir", Exchange.FILE_NAME, "in/sub/bye.txt");
+
+        await().atMost(10, TimeUnit.SECONDS).until(() -> Files.exists(testFile("out/hello.txt")) && Files.exists(testFile("out/sub/bye.txt")));
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("in?recursive=true&delete=true")).to(fileUri("out"));
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerStreamTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerStreamTest.java
new file mode 100644
index 00000000000..8692cce387a
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileProducerStreamTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.util.stream.Stream;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileProducerStreamTest.class,
+                FileProducerStreamTest.TestConfiguration.class
+        }
+)
+public class FileProducerStreamTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Test
+    public void testStream() throws Exception {
+        resultEndpoint.expectedMessageCount(1);
+
+        Object body = Stream.of("ABC", "DEF", "1234567890");
+        template.sendBodyAndHeader("direct:start", body, Exchange.FILE_NAME, "report.txt");
+
+        assertMockEndpointsSatisfied();
+
+        assertFileExists(testFile("report.txt"), "ABCDEF1234567890");
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from("direct:start").to(fileUri()).to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileSorterRefTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileSorterRefTest.java
new file mode 100644
index 00000000000..d20f6860246
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileSorterRefTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.util.Comparator;
+
+/**
+ * Unit test for the file sorter ref option
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileSorterRefTest.class,
+                FileSorterRefTest.TestConfiguration.class
+        }
+)
+public class FileSorterRefTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Test
+    public void testSortFiles() throws Exception {
+        template.sendBodyAndHeader(fileUri(), "Hello Paris", Exchange.FILE_NAME, "paris.txt");
+
+        template.sendBodyAndHeader(fileUri(), "Hello London", Exchange.FILE_NAME, "london.txt");
+
+        template.sendBodyAndHeader(fileUri(), "Hello Copenhagen", Exchange.FILE_NAME, "copenhagen.txt");
+
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from(fileUri("?initialDelay=0&delay=10&sorter=#mySorter")).convertBodyTo(String.class).to("mock:result");
+            }
+        });
+
+        resultEndpoint.expectedBodiesReceived("Hello Copenhagen", "Hello London", "Hello Paris");
+        assertMockEndpointsSatisfied();
+    }
+
+    // START SNIPPET: e1
+    public class MyFileSorter<T> implements Comparator<GenericFile<T>> {
+        @Override
+        public int compare(GenericFile<T> o1, GenericFile<T> o2) {
+            return o1.getFileName().compareToIgnoreCase(o2.getFileName());
+        }
+    }
+    // END SNIPPET: e1
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+
+        @Bean(value = "mySorter")
+        public Comparator mySorter() {
+            return new MyFileSorter();
+        }
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("in?initialDelay=0&delay=10&readLock=changed&readLockCheckInterval=100&readLockMarkerFile=false"))
+                            .to(fileUri("out"),
+                                    "mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileToFileWithFlattenTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileToFileWithFlattenTest.java
new file mode 100644
index 00000000000..700fd18a104
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FileToFileWithFlattenTest.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FileToFileWithFlattenTest.class
+        }
+)
+public class FileToFileWithFlattenTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Override
+    @BeforeEach
+    public void setUp() {
+        super.setUp();
+
+        template.sendBodyAndHeader(fileUri("flatten-in"), "Bye World", Exchange.FILE_NAME, "bye.txt");
+        template.sendBodyAndHeader(fileUri("flatten-in"), "Hello World", Exchange.FILE_NAME, "sub/hello.txt");
+        template.sendBodyAndHeader(fileUri("flatten-in"), "Goodday World", Exchange.FILE_NAME, "sub/sub2/goodday.txt");
+    }
+
+    @AfterEach
+    public void reset() {
+        resultEndpoint.reset();
+    }
+
+    @Test
+    public void testFlatternConsumer() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from(fileUri("flatten-in?initialDelay=0&delay=10&recursive=true&flatten=true"))
+                        .to(fileUri("flatten-out"), "mock:result");
+            }
+        });
+        context.start();
+
+        resultEndpoint.expectedMessageCount(3);
+
+        // flatten files
+        resultEndpoint.expectedFileExists(testFile("flatten-out/bye.txt"));
+        resultEndpoint.expectedFileExists(testFile("flatten-out/hello.txt"));
+        resultEndpoint.expectedFileExists(testFile("flatten-out/goodday.txt"));
+
+        // default move files
+        resultEndpoint.expectedFileExists(testFile("flatten-in/.camel/bye.txt"));
+        resultEndpoint.expectedFileExists(testFile("flatten-in/sub/.camel/hello.txt"));
+        resultEndpoint.expectedFileExists(testFile("flatten-in/sub/sub2/.camel/goodday.txt"));
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testFlatternProducer() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from(fileUri("flatten-in?initialDelay=0&delay=10&recursive=true"))
+                        .to(fileUri("flatten-out?flatten=true"), "mock:result");
+            }
+        });
+        context.start();
+
+        resultEndpoint.expectedMessageCount(3);
+
+        // flatten files
+        resultEndpoint.expectedFileExists(testFile("flatten-out/bye.txt"));
+        resultEndpoint.expectedFileExists(testFile("flatten-out/hello.txt"));
+        resultEndpoint.expectedFileExists(testFile("flatten-out/goodday.txt"));
+
+        // default move files
+        resultEndpoint.expectedFileExists(testFile("flatten-in/.camel/bye.txt"));
+        resultEndpoint.expectedFileExists(testFile("flatten-in/sub/.camel/hello.txt"));
+        resultEndpoint.expectedFileExists(testFile("flatten-in/sub/sub2/.camel/goodday.txt"));
+
+        assertMockEndpointsSatisfied();
+    }
+
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FilerConsumerDoneFileNamePrefixTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FilerConsumerDoneFileNamePrefixTest.java
new file mode 100644
index 00000000000..1337bfa0753
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FilerConsumerDoneFileNamePrefixTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+/**
+ * Unit test for writing done files
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FilerConsumerDoneFileNamePrefixTest.class,
+                FilerConsumerDoneFileNamePrefixTest.TestConfiguration.class
+        }
+)
+public class FilerConsumerDoneFileNamePrefixTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    @Test
+    public void testDoneFile() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "hello.txt");
+
+        // wait a bit and it should not pickup the written file as there are no
+        // done file
+        Thread.sleep(250);
+
+        assertMockEndpointsSatisfied();
+        oneExchangeDone.reset();
+
+        resultEndpoint.expectedBodiesReceived("Hello World");
+
+        // write the done file
+        template.sendBodyAndHeader(fileUri(), "", Exchange.FILE_NAME, "done-hello.txt");
+
+        assertMockEndpointsSatisfied();
+        oneExchangeDone.matchesWaitTime();
+
+        // done file should be deleted now
+        assertFileNotExists(testFile("done-hello.txt"));
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?doneFileName=done-${file:name}&initialDelay=0&delay=10")).to("mock:result");
+                }
+            };
+        }
+    }
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FilerProducerDoneFileNameRouteTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FilerProducerDoneFileNameRouteTest.java
new file mode 100644
index 00000000000..ba62f1b7501
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FilerProducerDoneFileNameRouteTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Unit test for writing done files
+ */
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FilerProducerDoneFileNameRouteTest.class,
+                FilerProducerDoneFileNameRouteTest.TestConfiguration.class
+        }
+)
+public class FilerProducerDoneFileNameRouteTest extends BaseFile {
+
+    @EndpointInject("mock:result")
+    private MockEndpoint resultEndpoint;
+
+    private Properties myProp = new Properties();
+
+    @Test
+    public void testProducerPlaceholderPrefixDoneFileName() throws Exception {
+        resultEndpoint.expectedMessageCount(1);
+
+        template.sendBodyAndHeader("direct:start", "Hello World", Exchange.FILE_NAME, "hello.txt");
+
+        assertMockEndpointsSatisfied();
+
+        assertTrue(oneExchangeDone.matches(5, TimeUnit.SECONDS));
+
+        assertFileExists(testFile("hello.txt"));
+        assertFileExists(testFile("done-hello.txt"));
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+
+        @Bean(value = "myProp")
+        public Properties myProp() throws Exception {
+            return myProp;
+        }
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    myProp.put("myDir", testDirectory().toString());
+
+                    context.getPropertiesComponent().setLocation("ref:myProp");
+
+                    from("direct:start").to("file:{{myDir}}?doneFileName=done-${file:name}").to("mock:result");
+                }
+            };
+        }
+    }
+
+}
diff --git a/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FromFileMoveFileIfProcessFailsTest.java b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FromFileMoveFileIfProcessFailsTest.java
new file mode 100644
index 00000000000..212b56d9b18
--- /dev/null
+++ b/components-starter/camel-file-starter/src/test/java/org/apache/camel/component/file/FromFileMoveFileIfProcessFailsTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.util.Properties;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+@CamelSpringBootTest
+@SpringBootTest(
+        classes = {
+                CamelAutoConfiguration.class,
+                FromFileMoveFileIfProcessFailsTest.class,
+                FromFileMoveFileIfProcessFailsTest.TestConfiguration.class
+        }
+)
+public class FromFileMoveFileIfProcessFailsTest extends BaseFile {
+
+    @EndpointInject("mock:foo")
+    private MockEndpoint fooEndpoint;
+
+    @Test
+    public void testPollFileAndShouldNotBeMoved() throws Exception {
+        template.sendBodyAndHeader(fileUri(), "Hello World", Exchange.FILE_NAME, "hello.txt");
+
+        fooEndpoint.expectedBodiesReceived("Hello World");
+        fooEndpoint.expectedFileExists(testFile("error/hello.txt"), "Hello World");
+
+        fooEndpoint.assertIsSatisfied();
+    }
+
+    // *************************************
+    // Config
+    // *************************************
+
+    @Configuration
+    public class TestConfiguration {
+
+        @Bean
+        public RouteBuilder routeBuilder() {
+            return new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(fileUri("?initialDelay=0&delay=10&moveFailed=error")).convertBodyTo(String.class)
+                            .to("mock:foo").process(new Processor() {
+                                public void process(Exchange exchange) throws Exception {
+                                    throw new IllegalArgumentException("Forced by unittest");
+                                }
+                            });
+                }
+            };
+        }
+    }
+}