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);