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

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


The following commit(s) were added to refs/heads/main by this push:
     new 0bbd065295 fix move files do nothing, fixes #7858 (#7863)
0bbd065295 is described below

commit 0bbd0652952d3fed61054674248a33b94d0b2517
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Mon Aug 10 14:19:21 2026 +0200

    fix move files do nothing, fixes #7858 (#7863)
---
 .../actions/main-0020-move-files-do-nothing.hwf    | 422 +++++++++++++++++++++
 .../actions/movefiles/ActionMoveFiles.java         |  27 +-
 .../movefiles/messages/messages_en_US.properties   |   2 +
 .../actions/movefiles/ActionMoveFilesTest.java     |  18 +
 .../WorkflowActionMoveFilesLocalTest.java          |  53 +++
 5 files changed, 520 insertions(+), 2 deletions(-)

diff --git a/integration-tests/actions/main-0020-move-files-do-nothing.hwf 
b/integration-tests/actions/main-0020-move-files-do-nothing.hwf
new file mode 100644
index 0000000000..65824b8f1b
--- /dev/null
+++ b/integration-tests/actions/main-0020-move-files-do-nothing.hwf
@@ -0,0 +1,422 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+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.
+
+-->
+<!--
+
+  Guards issue #7858: "Do nothing" on an existing destination file must skip 
that file and
+  let the action succeed, instead of failing the action.
+
+  NOTE: the "move again, option missing from the file" action below 
deliberately has NO
+  <iffileexists> tag. That is what workflows written before the option was 
serialized look
+  like, and it is the exact shape that regressed. Opening this workflow in the 
Hop GUI and
+  saving it writes the tag back and silently removes that half of the test 
coverage.
+
+-->
+<workflow>
+  <name>main-0020-move-files-do-nothing</name>
+  <name_sync_with_filename>Y</name_sync_with_filename>
+  <description>Move files: "do nothing" when the destination file 
exists</description>
+  <extended_description/>
+  <workflow_version/>
+  <created_user>-</created_user>
+  <created_date>2026/08/10 09:00:00.000</created_date>
+  <modified_user>-</modified_user>
+  <modified_date>2026/08/10 09:00:00.000</modified_date>
+  <parameters>
+    </parameters>
+  <actions>
+    <action>
+      <name>Start</name>
+      <description/>
+      <type>SPECIAL</type>
+      <attributes/>
+      <repeat>N</repeat>
+      <schedulerType>0</schedulerType>
+      <intervalSeconds>0</intervalSeconds>
+      <intervalMinutes>60</intervalMinutes>
+      <DayOfMonth>1</DayOfMonth>
+      <weekDay>1</weekDay>
+      <minutes>0</minutes>
+      <hour>12</hour>
+      <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
+      <parallel>N</parallel>
+      <xloc>48</xloc>
+      <yloc>48</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>clean up /tmp/action-0020</name>
+      <description/>
+      <type>DELETE_FOLDERS</type>
+      <attributes/>
+      <arg_from_previous>N</arg_from_previous>
+      <fields>
+        <field>
+          <name>${java.io.tmpdir}/action-0020</name>
+        </field>
+      </fields>
+      <limit_folders>10</limit_folders>
+      <success_condition>success_if_no_errors</success_condition>
+      <parallel>N</parallel>
+      <xloc>208</xloc>
+      <yloc>48</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>source/a.txt</name>
+      <description/>
+      <type>WRITE_TO_FILE</type>
+      <attributes/>
+      <filename>${java.io.tmpdir}/action-0020/source/a.txt</filename>
+      <createParentFolder>Y</createParentFolder>
+      <appendFile>N</appendFile>
+      <content>SOURCE-A</content>
+      <encoding>UTF-8</encoding>
+      <parallel>N</parallel>
+      <xloc>368</xloc>
+      <yloc>48</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>source/b.txt</name>
+      <description/>
+      <type>WRITE_TO_FILE</type>
+      <attributes/>
+      <filename>${java.io.tmpdir}/action-0020/source/b.txt</filename>
+      <createParentFolder>Y</createParentFolder>
+      <appendFile>N</appendFile>
+      <content>SOURCE-B</content>
+      <encoding>UTF-8</encoding>
+      <parallel>N</parallel>
+      <xloc>528</xloc>
+      <yloc>48</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>destination/a.txt</name>
+      <description>Same name as source/a.txt but a different size, so 
overwriting is detectable</description>
+      <type>WRITE_TO_FILE</type>
+      <attributes/>
+      <filename>${java.io.tmpdir}/action-0020/destination/a.txt</filename>
+      <createParentFolder>Y</createParentFolder>
+      <appendFile>N</appendFile>
+      <content>DESTINATION-A-UNTOUCHED</content>
+      <encoding>UTF-8</encoding>
+      <parallel>N</parallel>
+      <xloc>688</xloc>
+      <yloc>48</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>move, do nothing if it exists</name>
+      <description/>
+      <type>MOVE_FILES</type>
+      <attributes/>
+      <move_empty_folders>N</move_empty_folders>
+      <arg_from_previous>N</arg_from_previous>
+      <include_subfolders>N</include_subfolders>
+      <add_result_filesname>N</add_result_filesname>
+      <destination_is_a_file>N</destination_is_a_file>
+      <create_destination_folder>N</create_destination_folder>
+      <add_date>N</add_date>
+      <add_time>N</add_time>
+      <SpecifyFormat>N</SpecifyFormat>
+      <date_time_format/>
+      <nr_errors_less_than>10</nr_errors_less_than>
+      <success_condition>success_if_no_errors</success_condition>
+      <AddDateBeforeExtension>N</AddDateBeforeExtension>
+      <DoNotKeepFolderStructure>N</DoNotKeepFolderStructure>
+      <iffileexists>do_nothing</iffileexists>
+      <destinationFolder/>
+      <ifmovedfileexists>do_nothing</ifmovedfileexists>
+      <moved_date_time_format/>
+      <create_move_to_folder>N</create_move_to_folder>
+      <add_moved_date>N</add_moved_date>
+      <add_moved_time>N</add_moved_time>
+      <SpecifyMoveFormat>N</SpecifyMoveFormat>
+      <AddMovedDateBeforeExtension>N</AddMovedDateBeforeExtension>
+      <simulate>N</simulate>
+      <fields>
+        <field>
+          
<source_filefolder>${java.io.tmpdir}/action-0020/source/</source_filefolder>
+          
<destination_filefolder>${java.io.tmpdir}/action-0020/destination/</destination_filefolder>
+          <wildcard>.*\.txt</wildcard>
+        </field>
+      </fields>
+      <parallel>N</parallel>
+      <xloc>48</xloc>
+      <yloc>160</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>a.txt was left in source</name>
+      <description/>
+      <type>EVAL_FILES_METRICS</type>
+      <attributes/>
+      <scale>bytes</scale>
+      <source_files>files</source_files>
+      <evaluation_type>count</evaluation_type>
+      <comparevalue>1</comparevalue>
+      <successnumbercondition>equal</successnumbercondition>
+      <fields>
+        <field>
+          
<source_filefolder>${java.io.tmpdir}/action-0020/source/</source_filefolder>
+          <wildcard>a\.txt</wildcard>
+        </field>
+      </fields>
+      <parallel>N</parallel>
+      <xloc>208</xloc>
+      <yloc>160</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>b.txt was moved out of source</name>
+      <description/>
+      <type>EVAL_FILES_METRICS</type>
+      <attributes/>
+      <scale>bytes</scale>
+      <source_files>files</source_files>
+      <evaluation_type>count</evaluation_type>
+      <comparevalue>0</comparevalue>
+      <successnumbercondition>equal</successnumbercondition>
+      <fields>
+        <field>
+          
<source_filefolder>${java.io.tmpdir}/action-0020/source/</source_filefolder>
+          <wildcard>b\.txt</wildcard>
+        </field>
+      </fields>
+      <parallel>N</parallel>
+      <xloc>368</xloc>
+      <yloc>160</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>2 files in destination</name>
+      <description/>
+      <type>EVAL_FILES_METRICS</type>
+      <attributes/>
+      <scale>bytes</scale>
+      <source_files>files</source_files>
+      <evaluation_type>count</evaluation_type>
+      <comparevalue>2</comparevalue>
+      <successnumbercondition>equal</successnumbercondition>
+      <fields>
+        <field>
+          
<source_filefolder>${java.io.tmpdir}/action-0020/destination/</source_filefolder>
+          <wildcard>.*\.txt</wildcard>
+        </field>
+      </fields>
+      <parallel>N</parallel>
+      <xloc>528</xloc>
+      <yloc>160</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>destination/a.txt was not overwritten</name>
+      <description>DESTINATION-A-UNTOUCHED is 23 bytes, SOURCE-A is 
8</description>
+      <type>EVAL_FILES_METRICS</type>
+      <attributes/>
+      <scale>bytes</scale>
+      <source_files>files</source_files>
+      <evaluation_type>size</evaluation_type>
+      <comparevalue>23</comparevalue>
+      <successnumbercondition>equal</successnumbercondition>
+      <fields>
+        <field>
+          
<source_filefolder>${java.io.tmpdir}/action-0020/destination/</source_filefolder>
+          <wildcard>a\.txt</wildcard>
+        </field>
+      </fields>
+      <parallel>N</parallel>
+      <xloc>688</xloc>
+      <yloc>160</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>move again, option missing from the file</name>
+      <description>Deliberately has no iffileexists tag - see the comment at 
the top of this file</description>
+      <type>MOVE_FILES</type>
+      <attributes/>
+      <move_empty_folders>N</move_empty_folders>
+      <arg_from_previous>N</arg_from_previous>
+      <include_subfolders>N</include_subfolders>
+      <add_result_filesname>N</add_result_filesname>
+      <destination_is_a_file>N</destination_is_a_file>
+      <create_destination_folder>N</create_destination_folder>
+      <add_date>N</add_date>
+      <add_time>N</add_time>
+      <SpecifyFormat>N</SpecifyFormat>
+      <date_time_format/>
+      <nr_errors_less_than>10</nr_errors_less_than>
+      <success_condition>success_if_no_errors</success_condition>
+      <AddDateBeforeExtension>N</AddDateBeforeExtension>
+      <DoNotKeepFolderStructure>N</DoNotKeepFolderStructure>
+      <destinationFolder/>
+      <moved_date_time_format/>
+      <create_move_to_folder>N</create_move_to_folder>
+      <add_moved_date>N</add_moved_date>
+      <add_moved_time>N</add_moved_time>
+      <SpecifyMoveFormat>N</SpecifyMoveFormat>
+      <AddMovedDateBeforeExtension>N</AddMovedDateBeforeExtension>
+      <simulate>N</simulate>
+      <fields>
+        <field>
+          
<source_filefolder>${java.io.tmpdir}/action-0020/source/</source_filefolder>
+          
<destination_filefolder>${java.io.tmpdir}/action-0020/destination/</destination_filefolder>
+          <wildcard>.*\.txt</wildcard>
+        </field>
+      </fields>
+      <parallel>N</parallel>
+      <xloc>48</xloc>
+      <yloc>272</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>a.txt is still in source</name>
+      <description/>
+      <type>EVAL_FILES_METRICS</type>
+      <attributes/>
+      <scale>bytes</scale>
+      <source_files>files</source_files>
+      <evaluation_type>count</evaluation_type>
+      <comparevalue>1</comparevalue>
+      <successnumbercondition>equal</successnumbercondition>
+      <fields>
+        <field>
+          
<source_filefolder>${java.io.tmpdir}/action-0020/source/</source_filefolder>
+          <wildcard>a\.txt</wildcard>
+        </field>
+      </fields>
+      <parallel>N</parallel>
+      <xloc>208</xloc>
+      <yloc>272</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>destination/a.txt is still not overwritten</name>
+      <description/>
+      <type>EVAL_FILES_METRICS</type>
+      <attributes/>
+      <scale>bytes</scale>
+      <source_files>files</source_files>
+      <evaluation_type>size</evaluation_type>
+      <comparevalue>23</comparevalue>
+      <successnumbercondition>equal</successnumbercondition>
+      <fields>
+        <field>
+          
<source_filefolder>${java.io.tmpdir}/action-0020/destination/</source_filefolder>
+          <wildcard>a\.txt</wildcard>
+        </field>
+      </fields>
+      <parallel>N</parallel>
+      <xloc>368</xloc>
+      <yloc>272</yloc>
+      <attributes_hac/>
+    </action>
+  </actions>
+  <hops>
+    <hop>
+      <from>Start</from>
+      <to>clean up /tmp/action-0020</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>Y</unconditional>
+    </hop>
+    <hop>
+      <from>clean up /tmp/action-0020</from>
+      <to>source/a.txt</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>source/a.txt</from>
+      <to>source/b.txt</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>source/b.txt</from>
+      <to>destination/a.txt</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>destination/a.txt</from>
+      <to>move, do nothing if it exists</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>move, do nothing if it exists</from>
+      <to>a.txt was left in source</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>a.txt was left in source</from>
+      <to>b.txt was moved out of source</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>b.txt was moved out of source</from>
+      <to>2 files in destination</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>2 files in destination</from>
+      <to>destination/a.txt was not overwritten</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>destination/a.txt was not overwritten</from>
+      <to>move again, option missing from the file</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>move again, option missing from the file</from>
+      <to>a.txt is still in source</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+    <hop>
+      <from>a.txt is still in source</from>
+      <to>destination/a.txt is still not overwritten</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+    </hop>
+  </hops>
+  <notepads>
+  </notepads>
+  <attributes/>
+</workflow>
diff --git 
a/plugins/actions/movefiles/src/main/java/org/apache/hop/workflow/actions/movefiles/ActionMoveFiles.java
 
b/plugins/actions/movefiles/src/main/java/org/apache/hop/workflow/actions/movefiles/ActionMoveFiles.java
index d2701a495c..fe12175f68 100644
--- 
a/plugins/actions/movefiles/src/main/java/org/apache/hop/workflow/actions/movefiles/ActionMoveFiles.java
+++ 
b/plugins/actions/movefiles/src/main/java/org/apache/hop/workflow/actions/movefiles/ActionMoveFiles.java
@@ -170,6 +170,7 @@ public class ActionMoveFiles extends ActionBase implements 
Cloneable, IAction {
   public ActionMoveFiles(String n) {
     super(n, "");
     filesToMove = new ArrayList<>();
+    ifFileExists = CONST_DO_NOTHING;
     ifMovedFileExists = CONST_DO_NOTHING;
     moveEmptyFolders = true;
     nrErrorsLessThan = "10";
@@ -743,7 +744,7 @@ public class ActionMoveFiles extends ActionBase implements 
Cloneable, IAction {
                   PKG, "ActionMoveFiles.Log.FileExists", 
destinationFilename.toString()));
         }
 
-        switch (ifFileExists) {
+        switch (ifExistsOption(ifFileExists)) {
           case "overwrite_file" -> {
             if (!simulate) {
               Long moved = trackBytesMoved(sourceFileFolder, result);
@@ -870,7 +871,7 @@ public class ActionMoveFiles extends ActionBase implements 
Cloneable, IAction {
               }
 
             } else {
-              switch (ifMovedFileExists) {
+              switch (ifExistsOption(ifMovedFileExists)) {
                 case "overwrite_file" -> {
                   if (!simulate) {
                     Long moved = trackBytesMoved(sourceFileFolder, result);
@@ -930,6 +931,13 @@ public class ActionMoveFiles extends ActionBase implements 
Cloneable, IAction {
                 case FAIL ->
                     // Update Errors
                     updateErrors();
+                case CONST_DO_NOTHING -> retVal = true;
+                default ->
+                    logError(
+                        BaseMessages.getString(
+                            PKG,
+                            "ActionMoveFiles.Error.UnknownIfMovedFileExists",
+                            ifMovedFileExists));
               }
             }
           }
@@ -937,6 +945,10 @@ public class ActionMoveFiles extends ActionBase implements 
Cloneable, IAction {
               // Update Errors
               updateErrors();
           case CONST_DO_NOTHING -> retVal = true;
+          default ->
+              logError(
+                  BaseMessages.getString(
+                      PKG, "ActionMoveFiles.Error.UnknownIfFileExists", 
ifFileExists));
         }
       }
     } catch (Exception e) {
@@ -1085,6 +1097,17 @@ public class ActionMoveFiles extends ActionBase 
implements Cloneable, IAction {
     return entryStatus;
   }
 
+  /**
+   * Workflows saved before the option was written to XML (and hand-written 
ones) simply don't have
+   * the tag. In that case we keep the historical default: do nothing.
+   *
+   * @param option the "if (moved) file exists" option as it was deserialized
+   * @return the option to act upon, never null or empty
+   */
+  private String ifExistsOption(String option) {
+    return Utils.isEmpty(option) ? CONST_DO_NOTHING : option;
+  }
+
   private void updateErrors() {
     nrErrors++;
     if (checkIfSuccessConditionBroken()) {
diff --git 
a/plugins/actions/movefiles/src/main/resources/org/apache/hop/workflow/actions/movefiles/messages/messages_en_US.properties
 
b/plugins/actions/movefiles/src/main/resources/org/apache/hop/workflow/actions/movefiles/messages/messages_en_US.properties
index aede83466f..0583f2d674 100644
--- 
a/plugins/actions/movefiles/src/main/resources/org/apache/hop/workflow/actions/movefiles/messages/messages_en_US.properties
+++ 
b/plugins/actions/movefiles/src/main/resources/org/apache/hop/workflow/actions/movefiles/messages/messages_en_US.properties
@@ -52,6 +52,8 @@ ActionMoveFiles.Error.Exception.UnableLoadXML=Unable to load 
action of type ''Mo
 ActionMoveFiles.Error.GettingFilename=Error getting filename for file [{0}]. 
Exception \: {1}
 ActionMoveFiles.Error.SourceFileNotExists=File/folder [{0}] does not exist\!
 ActionMoveFiles.Error.SuccessConditionbroken=Success condition was broken. We 
have {0} error(s)
+ActionMoveFiles.Error.UnknownIfFileExists=Unknown option [{0}] for ''If 
destination file exists'', the file was not moved
+ActionMoveFiles.Error.UnknownIfMovedFileExists=Unknown option [{0}] for ''If 
moved file exists'', the file was not moved
 ActionMoveFiles.Fail_IfFileExists.Label=Fail
 ActionMoveFiles.Fail_IfMovedFileExists.Label=Fail
 ActionMoveFiles.Fields.DestinationFileFolder.Label=File/Folder destination
diff --git 
a/plugins/actions/movefiles/src/test/java/org/apache/hop/workflow/actions/movefiles/ActionMoveFilesTest.java
 
b/plugins/actions/movefiles/src/test/java/org/apache/hop/workflow/actions/movefiles/ActionMoveFilesTest.java
index 85255081b3..fcd906daa9 100644
--- 
a/plugins/actions/movefiles/src/test/java/org/apache/hop/workflow/actions/movefiles/ActionMoveFilesTest.java
+++ 
b/plugins/actions/movefiles/src/test/java/org/apache/hop/workflow/actions/movefiles/ActionMoveFilesTest.java
@@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.apache.hop.core.HopClientEnvironment;
+import org.apache.hop.core.xml.XmlHandler;
 import org.apache.hop.workflow.action.ActionSerializationTestUtil;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -72,4 +73,21 @@ class ActionMoveFilesTest {
     assertEquals("destinationFolder2", f.getDestinationFileFolder());
     assertEquals("wildcard2", f.getWildcard());
   }
+
+  /**
+   * Issue #7858: workflows without an {@code <iffileexists>} tag used to fall 
back to "do nothing".
+   */
+  @Test
+  void testIfFileExistsDefaultsToDoNothing() throws Exception {
+    assertEquals(ActionMoveFiles.CONST_DO_NOTHING, new 
ActionMoveFiles().getIfFileExists());
+
+    String xml =
+        XmlHandler.openTag("action")
+            + XmlHandler.addTagValue("success_condition", 
"success_if_no_errors")
+            + XmlHandler.closeTag("action");
+    ActionMoveFiles action = new ActionMoveFiles();
+    action.loadXml(XmlHandler.getSubNode(XmlHandler.loadXmlString(xml), 
"action"), null, null);
+
+    assertEquals(ActionMoveFiles.CONST_DO_NOTHING, action.getIfFileExists());
+  }
 }
diff --git 
a/plugins/actions/movefiles/src/test/java/org/apache/hop/workflow/actions/movefiles/WorkflowActionMoveFilesLocalTest.java
 
b/plugins/actions/movefiles/src/test/java/org/apache/hop/workflow/actions/movefiles/WorkflowActionMoveFilesLocalTest.java
index a8a7c326e0..24f56a02c1 100644
--- 
a/plugins/actions/movefiles/src/test/java/org/apache/hop/workflow/actions/movefiles/WorkflowActionMoveFilesLocalTest.java
+++ 
b/plugins/actions/movefiles/src/test/java/org/apache/hop/workflow/actions/movefiles/WorkflowActionMoveFilesLocalTest.java
@@ -195,6 +195,59 @@ class WorkflowActionMoveFilesLocalTest {
         "File content should match source");
   }
 
+  @Test
+  void testMoveDoNothingWhenDestinationExists() throws IOException, 
HopException {
+    Path sourceFile = createTestFilePath(sourceFolder, "test.txt");
+    Path destinationFile = destinationFolder.toPath().resolve("test.txt");
+    String originalContent = "original content";
+    Files.writeString(destinationFile, originalContent, 
StandardCharsets.UTF_8);
+
+    ActionMoveFiles.FileToMove fileToMove = new ActionMoveFiles.FileToMove();
+    fileToMove.setSourceFileFolder(sourceFile.toString());
+    fileToMove.setDestinationFileFolder(destinationFile.toString());
+    action.getFilesToMove().add(fileToMove);
+    action.setDestinationIsAFile(true);
+    action.setIfFileExists("do_nothing");
+
+    Result result = action.execute(new Result(), 0);
+    assertTrue(result.isResult(), "Doing nothing on an existing destination is 
not an error");
+    assertEquals(
+        0, result.getNrErrors(), "Doing nothing on an existing destination is 
not an error");
+    assertTrue(Files.exists(sourceFile), "Source file should still exist");
+    assertEquals(
+        originalContent,
+        Files.readString(destinationFile, StandardCharsets.UTF_8),
+        "Destination content should be unchanged");
+  }
+
+  /**
+   * Workflows written before the option was serialized don't have an {@code 
<iffileexists>} tag at
+   * all. They used to fall back to "do nothing", see issue #7858.
+   */
+  @Test
+  void testMoveWithoutIfFileExistsOptionDoesNothing() throws IOException, 
HopException {
+    Path sourceFile = createTestFilePath(sourceFolder, "test.txt");
+    Path destinationFile = destinationFolder.toPath().resolve("test.txt");
+    String originalContent = "original content";
+    Files.writeString(destinationFile, originalContent, 
StandardCharsets.UTF_8);
+
+    ActionMoveFiles.FileToMove fileToMove = new ActionMoveFiles.FileToMove();
+    fileToMove.setSourceFileFolder(sourceFile.toString());
+    fileToMove.setDestinationFileFolder(destinationFile.toString());
+    action.getFilesToMove().add(fileToMove);
+    action.setDestinationIsAFile(true);
+    action.setIfFileExists(null);
+
+    Result result = action.execute(new Result(), 0);
+    assertTrue(result.isResult(), "A missing option should behave like 'do 
nothing'");
+    assertEquals(0, result.getNrErrors(), "A missing option should not report 
errors");
+    assertTrue(Files.exists(sourceFile), "Source file should still exist");
+    assertEquals(
+        originalContent,
+        Files.readString(destinationFile, StandardCharsets.UTF_8),
+        "Destination content should be unchanged");
+  }
+
   private Path createTestFilePath(File folder, String filename) throws 
IOException {
     Path filePath = folder.toPath().resolve(filename);
     Files.writeString(filePath, TEST_FILE_CONTENT, StandardCharsets.UTF_8);

Reply via email to