MINIFICPP-2305 Fix various C2 integration test issues

- Use separate temporary files for each integration test for flow config files, because they are being written after parsed and can cause flaky test failures due to concurrent writes
- Only copy test resources once
- Delete all remaining temp dirs after test runs

Closes #2022

Signed-off-by: Marton Szasz <szaszm@apache.org>
diff --git a/controller/tests/CMakeLists.txt b/controller/tests/CMakeLists.txt
index c5fca51..2b079f2 100644
--- a/controller/tests/CMakeLists.txt
+++ b/controller/tests/CMakeLists.txt
@@ -40,15 +40,17 @@
     add_test(NAME ${testfilename} COMMAND ${testfilename} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
 
     math(EXPR CONTROLLER_TEST_COUNT "${CONTROLLER_TEST_COUNT}+1")
-
-    # Copy test resources
-    add_custom_command(
-        TARGET "${testfilename}"
-        POST_BUILD
-        COMMAND ${CMAKE_COMMAND} -E copy_directory
-                "${CMAKE_SOURCE_DIR}/controller/tests/resources"
-                "$<TARGET_FILE_DIR:${testfilename}>/resources"
-        )
 endforeach()
 
+list(GET CONTROLLER_TESTS 0 firsttestfile)
+get_filename_component(firsttestfilename "${firsttestfile}" NAME_WE)
+# Copy test resources
+add_custom_command(
+    TARGET "${firsttestfilename}"
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy_directory
+            "${CMAKE_SOURCE_DIR}/controller/tests/resources"
+            "$<TARGET_FILE_DIR:${firsttestfilename}>/resources"
+    )
+
 message("-- Finished building ${CONTROLLER_TEST_COUNT} controller test file(s)...")
diff --git a/extensions/civetweb/tests/C2VerifyServeResults.cpp b/extensions/civetweb/tests/C2VerifyServeResults.cpp
index 3a355ee..9e3de47 100644
--- a/extensions/civetweb/tests/C2VerifyServeResults.cpp
+++ b/extensions/civetweb/tests/C2VerifyServeResults.cpp
@@ -33,7 +33,7 @@
 
 class VerifyC2Server : public HTTPIntegrationBase {
  public:
-  VerifyC2Server() {
+  explicit VerifyC2Server(const std::filesystem::path& test_file_location) : HTTPIntegrationBase(test_file_location) {
     dir_ = testController.createTempDirectory();
   }
 
@@ -83,10 +83,9 @@
 };
 
 TEST_CASE("C2VerifyServeResults", "[c2test]") {
-  VerifyC2Server harness;
+  VerifyC2Server harness(std::filesystem::path(TEST_RESOURCES) / "C2VerifyServeResults.yml");
   harness.setKeyDir(TEST_RESOURCES);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "C2VerifyServeResults.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/extensions/civetweb/tests/CMakeLists.txt b/extensions/civetweb/tests/CMakeLists.txt
index c404136..dc92800 100644
--- a/extensions/civetweb/tests/CMakeLists.txt
+++ b/extensions/civetweb/tests/CMakeLists.txt
@@ -33,15 +33,18 @@
     target_link_libraries(${testfilename} Catch2WithMain)
     MATH(EXPR CIVETWEB-EXTENSIONS_TEST_COUNT "${CIVETWEB-EXTENSIONS_TEST_COUNT}+1")
     add_test(NAME "${testfilename}" COMMAND "${testfilename}" WORKING_DIRECTORY ${TEST_DIR})
-    # Copy test resources
-    add_custom_command(
-        TARGET "${testfilename}"
-        POST_BUILD
-        COMMAND ${CMAKE_COMMAND} -E copy_directory
-                "${CMAKE_SOURCE_DIR}/extensions/civetweb/tests/resources"
-                "$<TARGET_FILE_DIR:${testfilename}>/resources"
-        )
     set_tests_properties("${testfilename}" PROPERTIES LABELS "civetweb")
 ENDFOREACH()
 
+list(GET CIVETWEB_INTEGRATION_TESTS 0 firsttestfile)
+get_filename_component(firsttestfilename "${firsttestfile}" NAME_WE)
+# Copy test resources
+add_custom_command(
+    TARGET "${firsttestfilename}"
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy_directory
+            "${CMAKE_SOURCE_DIR}/extensions/civetweb/tests/resources"
+            "$<TARGET_FILE_DIR:${firsttestfilename}>/resources"
+    )
+
 message("-- Finished building ${CIVETWEB-EXTENSIONS_TEST_COUNT} civetweb related test file(s)...")
diff --git a/extensions/civetweb/tests/HttpPostIntegrationTest.cpp b/extensions/civetweb/tests/HttpPostIntegrationTest.cpp
index 601b19d..8262fce 100644
--- a/extensions/civetweb/tests/HttpPostIntegrationTest.cpp
+++ b/extensions/civetweb/tests/HttpPostIntegrationTest.cpp
@@ -35,7 +35,7 @@
 
 class HttpTestHarness : public HTTPIntegrationBase {
  public:
-  HttpTestHarness() {
+  explicit HttpTestHarness(const std::filesystem::path& test_file_location) : HTTPIntegrationBase(test_file_location) {
     dir_ = test_controller_.createTempDirectory();
   }
 
@@ -79,18 +79,18 @@
 };
 
 TEST_CASE("Test HTTP client POST request", "[httptest]") {
-  HttpTestHarness harness;
-  harness.setKeyDir(TEST_RESOURCES);
+  std::filesystem::path test_file_path;
   SECTION("Without chunked encoding") {
-    const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPPost.yml";
-    harness.run(test_file_path);
+    test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPPost.yml";
   }
 #ifndef __APPLE__
   SECTION("With chunked encoding") {
-    const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPPostChunkedEncoding.yml";
-    harness.run(test_file_path);
+    test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPPostChunkedEncoding.yml";
   }
 #endif
+  HttpTestHarness harness(test_file_path);
+  harness.setKeyDir(TEST_RESOURCES);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/extensions/expression-language/tests/integration/UpdateAttributeIntegrationTest.cpp b/extensions/expression-language/tests/integration/UpdateAttributeIntegrationTest.cpp
index ead1f8f..dc4ce3e 100644
--- a/extensions/expression-language/tests/integration/UpdateAttributeIntegrationTest.cpp
+++ b/extensions/expression-language/tests/integration/UpdateAttributeIntegrationTest.cpp
@@ -30,6 +30,8 @@
 
 class TestHarness : public IntegrationBase {
  public:
+  using IntegrationBase::IntegrationBase;
+
   void testSetup() override {
     LogTestController::getInstance().setTrace<minifi::FlowController>();
     LogTestController::getInstance().setTrace<core::ProcessSession>();
@@ -51,9 +53,8 @@
 };
 
 TEST_CASE("UpdateAttributeIntegrationTest", "[updateattribute]") {
-  TestHarness harness;
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestUpdateAttribute.yml";
-  harness.run(test_file_path);
+  TestHarness harness(std::filesystem::path(TEST_RESOURCES) / "TestUpdateAttribute.yml");
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/extensions/standard-processors/tests/integration/ProcessGroupTest.cpp b/extensions/standard-processors/tests/integration/ProcessGroupTest.cpp
index 6eb6b2a..f647c47 100644
--- a/extensions/standard-processors/tests/integration/ProcessGroupTest.cpp
+++ b/extensions/standard-processors/tests/integration/ProcessGroupTest.cpp
@@ -33,7 +33,7 @@
 
 class ProcessGroupTestHarness : public IntegrationBase {
  public:
-  ProcessGroupTestHarness() : IntegrationBase(2s) {
+  explicit ProcessGroupTestHarness(const std::filesystem::path& test_file_location) : IntegrationBase(test_file_location, {}, 2s) {
   }
 
   void testSetup() override {
@@ -50,9 +50,8 @@
 };
 
 TEST_CASE("ProcessGroupTest", "[ProcessGroupTest]") {
-  ProcessGroupTestHarness harness;
-  auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestProcessGroup.yml";
-  harness.run(test_file_path);
+  ProcessGroupTestHarness harness(std::filesystem::path(TEST_RESOURCES) / "TestProcessGroup.yml");
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/extensions/standard-processors/tests/integration/TailFileIntegrationTest.cpp b/extensions/standard-processors/tests/integration/TailFileIntegrationTest.cpp
index 3e25032..fbf241c 100644
--- a/extensions/standard-processors/tests/integration/TailFileIntegrationTest.cpp
+++ b/extensions/standard-processors/tests/integration/TailFileIntegrationTest.cpp
@@ -36,7 +36,8 @@
 
 class TailFileTestHarness : public IntegrationBase {
  public:
-  TailFileTestHarness() {
+  explicit TailFileTestHarness(const std::filesystem::path& test_file_location)
+      : IntegrationBase(test_file_location) {
     dir = testController.createTempDirectory();
 
     statefile = dir / "statefile";
@@ -85,7 +86,6 @@
 };
 
 TEST_CASE("TailFile integration test", "[tailfile]") {
-  TailFileTestHarness harness;
   std::filesystem::path test_file_path;
   SECTION("Timer driven") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestTailFile.yml";
@@ -93,7 +93,8 @@
   SECTION("Cron driven") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestTailFileCron.yml";
   }
-  harness.run(test_file_path);
+  TailFileTestHarness harness(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/extensions/standard-processors/tests/integration/VerifyInvokeHTTP.h b/extensions/standard-processors/tests/integration/VerifyInvokeHTTP.h
index e30af2c..8b6a428 100644
--- a/extensions/standard-processors/tests/integration/VerifyInvokeHTTP.h
+++ b/extensions/standard-processors/tests/integration/VerifyInvokeHTTP.h
@@ -40,8 +40,8 @@
 
 class VerifyInvokeHTTP : public HTTPIntegrationBase {
  public:
-  VerifyInvokeHTTP()
-      : HTTPIntegrationBase(6s) {
+  explicit VerifyInvokeHTTP(const std::filesystem::path& test_file_location)
+      : HTTPIntegrationBase(test_file_location, {}, 6s) {
   }
 
   void testSetup() override {
@@ -82,14 +82,14 @@
     REQUIRE(executed);
   }
 
-  virtual void setupFlow(const std::optional<std::filesystem::path>& flow_yml_path) {
+  virtual void setupFlow() {
     testSetup();
 
     std::shared_ptr<core::Repository> test_repo = std::make_shared<TestThreadedRepository>();
     std::shared_ptr<core::Repository> test_flow_repo = std::make_shared<TestFlowRepository>();
 
-    if (flow_yml_path) {
-      configuration->set(minifi::Configure::nifi_flow_configuration_file, flow_yml_path->string());
+    if (flow_config_path_.config_path) {
+      configuration->set(minifi::Configure::nifi_flow_configuration_file, flow_config_path_.config_path->string());
     }
     configuration->set(minifi::Configure::nifi_c2_agent_heartbeat_period, "200");
     std::shared_ptr<core::ContentRepository> content_repo = std::make_shared<core::repository::VolatileContentRepository>();
@@ -99,7 +99,7 @@
         .flow_file_repo = test_repo,
         .content_repo = content_repo,
         .configuration = configuration,
-        .path = flow_yml_path,
+        .path = flow_config_path_.config_path,
         .filesystem = std::make_shared<minifi::utils::file::FileSystem>(),
         .sensitive_values_encryptor = minifi::utils::crypto::EncryptionProvider{minifi::utils::crypto::XSalsa20Cipher{encryption_key}}
     };
@@ -111,8 +111,8 @@
     setProperty(minifi::processors::InvokeHTTP::URL, url);
   }
 
-  void run(const std::optional<std::filesystem::path>& flow_yml_path = {}, const std::optional<std::filesystem::path>& = {}) override {
-    setupFlow(flow_yml_path);
+  void run() override {
+    setupFlow();
     startFlowController();
 
     runAssertions();
@@ -122,12 +122,11 @@
   }
 
   void run(const std::string& url,
-           const std::string& test_file_location,
            const std::string& key_dir,
            ServerAwareHandler* handler) {
     setKeyDir(key_dir);
     setUrl(url, handler);
-    run(test_file_location);
+    run();
   }
 
   void startFlowController() {
diff --git a/extensions/standard-processors/tests/integration/VerifyInvokeHTTPGetTest.cpp b/extensions/standard-processors/tests/integration/VerifyInvokeHTTPGetTest.cpp
index 1fb361a..b4bdc58 100644
--- a/extensions/standard-processors/tests/integration/VerifyInvokeHTTPGetTest.cpp
+++ b/extensions/standard-processors/tests/integration/VerifyInvokeHTTPGetTest.cpp
@@ -25,6 +25,7 @@
 
 class VerifyHTTPGet : public VerifyInvokeHTTP {
  public:
+  using VerifyInvokeHTTP::VerifyInvokeHTTP;
   void runAssertions() override {
     REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(
         std::chrono::seconds(10),
@@ -35,6 +36,7 @@
 
 class VerifyRetryHTTPGet : public VerifyInvokeHTTP {
  public:
+  using VerifyInvokeHTTP::VerifyInvokeHTTP;
   void runAssertions() override {
     REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(
         std::chrono::seconds(10),
@@ -47,7 +49,6 @@
 
 TEST_CASE("Verify InvokeHTTP GET request", "[invokehttp]") {
   HttpGetResponder http_handler;
-  VerifyHTTPGet harness;
   std::filesystem::path test_file_path;
   std::string key_dir;
   SECTION("Insecure") {
@@ -57,12 +58,12 @@
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPGetSecure.yml";
     key_dir = TEST_RESOURCES;
   }
-  harness.run("http://localhost:0/", test_file_path.string(), key_dir, &http_handler);
+  VerifyHTTPGet harness(test_file_path);
+  harness.run("http://localhost:0/", key_dir, &http_handler);
 }
 
 TEST_CASE("Verify InvokeHTTP GET request with retry", "[invokehttp]") {
   RetryHttpGetResponder http_handler;
-  VerifyRetryHTTPGet harness;
   std::filesystem::path test_file_path;
   std::string key_dir;
   SECTION("Insecure") {
@@ -72,7 +73,8 @@
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPGetSecure.yml";
     key_dir = TEST_RESOURCES;
   }
-  harness.run("http://localhost:0/", test_file_path.string(), key_dir, &http_handler);
+  VerifyRetryHTTPGet harness(test_file_path);
+  harness.run("http://localhost:0/", key_dir, &http_handler);
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/extensions/standard-processors/tests/integration/VerifyInvokeHTTPPostTest.cpp b/extensions/standard-processors/tests/integration/VerifyInvokeHTTPPostTest.cpp
index 4cedc2d..3590fc4 100644
--- a/extensions/standard-processors/tests/integration/VerifyInvokeHTTPPostTest.cpp
+++ b/extensions/standard-processors/tests/integration/VerifyInvokeHTTPPostTest.cpp
@@ -28,6 +28,7 @@
 
 class VerifyInvokeHTTPOKResponse : public VerifyInvokeHTTP {
  public:
+  using VerifyInvokeHTTP::VerifyInvokeHTTP;
   void runAssertions() override {
     REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::seconds(6),
         "key:invokehttp.status.code value:201",
@@ -37,6 +38,7 @@
 
 class VerifyInvokeHTTPOK200Response : public VerifyInvokeHTTP {
  public:
+  using VerifyInvokeHTTP::VerifyInvokeHTTP;
   void runAssertions() override {
     REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::seconds(6),
         "key:invokehttp.status.code value:200",
@@ -46,8 +48,9 @@
 
 class VerifyInvokeHTTPRedirectResponse : public VerifyInvokeHTTP {
  public:
-  void setupFlow(const std::optional<std::filesystem::path>& flow_yml_path) override {
-    VerifyInvokeHTTP::setupFlow(flow_yml_path);
+  using VerifyInvokeHTTP::VerifyInvokeHTTP;
+  void setupFlow() override {
+    VerifyInvokeHTTP::setupFlow();
     setProperty(minifi::processors::InvokeHTTP::FollowRedirects, "false");
   }
 
@@ -60,6 +63,7 @@
 
 class VerifyCouldNotConnectInvokeHTTP : public VerifyInvokeHTTP {
  public:
+  using VerifyInvokeHTTP::VerifyInvokeHTTP;
   void runAssertions() override {
     REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::seconds(6), "key:invoke_http value:failure"));
   }
@@ -67,6 +71,7 @@
 
 class VerifyNoRetryInvokeHTTP : public VerifyInvokeHTTP {
  public:
+  using VerifyInvokeHTTP::VerifyInvokeHTTP;
   void runAssertions() override {
     REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::seconds(6),
         "key:invokehttp.status.message value:HTTP/1.1 404 Not Found",
@@ -76,6 +81,7 @@
 
 class VerifyRetryInvokeHTTP : public VerifyInvokeHTTP {
  public:
+  using VerifyInvokeHTTP::VerifyInvokeHTTP;
   void runAssertions() override {
     REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::seconds(6),
         "key:invokehttp.status.message value:HTTP/1.1 501 Not Implemented",
@@ -85,6 +91,7 @@
 
 class VerifyRWTimeoutInvokeHTTP : public VerifyInvokeHTTP {
  public:
+  using VerifyInvokeHTTP::VerifyInvokeHTTP;
   void runAssertions() override {
     REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(std::chrono::seconds(6),
         "key:invoke_http value:failure",
@@ -96,18 +103,21 @@
   // Stop civet server to simulate
   // unreachable remote end point
   InvokeHTTPCouldNotConnectHandler handler;
-  VerifyCouldNotConnectInvokeHTTP harness;
   std::filesystem::path test_file_path;
   std::string key_dir;
   SECTION("Secure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPostSecure.yml";
-    harness.setKeyDir(TEST_RESOURCES);
+    key_dir = TEST_RESOURCES;
   }
   SECTION("Insecure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPost.yml";
   }
+  VerifyCouldNotConnectInvokeHTTP harness(test_file_path);
+  if (!key_dir.empty()) {
+    harness.setKeyDir(key_dir);
+  }
   harness.setUrl("http://localhost:0/", &handler);
-  harness.setupFlow(test_file_path);
+  harness.setupFlow();
   harness.shutdownBeforeFlowController();
   harness.startFlowController();
   harness.runAssertions();
@@ -116,92 +126,110 @@
 
 TEST_CASE("Verify InvokeHTTP POST request with 201 OK response", "[invokehttp]") {
   InvokeHTTPResponseOKHandler handler;
-  VerifyInvokeHTTPOKResponse harness;
   std::filesystem::path test_file_path;
   std::string key_dir;
   SECTION("Secure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPostSecure.yml";
-    harness.setKeyDir(TEST_RESOURCES);
+    key_dir = TEST_RESOURCES;
   }
   SECTION("Insecure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPost.yml";
   }
-  harness.run("http://localhost:0/", test_file_path.string(), key_dir, &handler);
+  VerifyInvokeHTTPOKResponse harness(test_file_path);
+  if (!key_dir.empty()) {
+    harness.setKeyDir(key_dir);
+  }
+  harness.run("http://localhost:0/", key_dir, &handler);
 }
 
 TEST_CASE("Verify InvokeHTTP POST request with 200 OK response", "[invokehttp]") {
   InvokeHTTPRedirectHandler handler;
-  VerifyInvokeHTTPOK200Response harness;
   std::filesystem::path test_file_path;
   std::string key_dir;
   SECTION("Secure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPostSecure.yml";
-    harness.setKeyDir(TEST_RESOURCES);
+    key_dir = TEST_RESOURCES;
   }
   SECTION("Insecure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPost.yml";
   }
-  harness.run("http://localhost:0/", test_file_path.string(), key_dir, &handler);
+  VerifyInvokeHTTPOK200Response harness(test_file_path);
+  if (!key_dir.empty()) {
+    harness.setKeyDir(key_dir);
+  }
+  harness.run("http://localhost:0/", key_dir, &handler);
 }
 
 TEST_CASE("Verify InvokeHTTP POST request with 301 redirect response", "[invokehttp]") {
   InvokeHTTPRedirectHandler handler;
-  VerifyInvokeHTTPRedirectResponse harness;
   std::filesystem::path test_file_path;
   std::string key_dir;
   SECTION("Secure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPostSecure.yml";
-    harness.setKeyDir(TEST_RESOURCES);
+    key_dir = TEST_RESOURCES;
   }
   SECTION("Insecure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPost.yml";
   }
-  harness.run("http://localhost:0/", test_file_path.string(), key_dir, &handler);
+  VerifyInvokeHTTPRedirectResponse harness(test_file_path);
+  if (!key_dir.empty()) {
+    harness.setKeyDir(key_dir);
+  }
+  harness.run("http://localhost:0/", key_dir, &handler);
 }
 
 TEST_CASE("Verify InvokeHTTP POST request with 404 not found response", "[invokehttp]") {
   InvokeHTTPResponse404Handler handler;
-  VerifyNoRetryInvokeHTTP harness;
   std::filesystem::path test_file_path;
   std::string key_dir;
   SECTION("Secure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPostSecure.yml";
-    harness.setKeyDir(TEST_RESOURCES);
+    key_dir = TEST_RESOURCES;
   }
   SECTION("Insecure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPost.yml";
   }
-  harness.run("http://localhost:0/", test_file_path.string(), key_dir, &handler);
+  VerifyNoRetryInvokeHTTP harness(test_file_path);
+  if (!key_dir.empty()) {
+    harness.setKeyDir(key_dir);
+  }
+  harness.run("http://localhost:0/", key_dir, &handler);
 }
 
 TEST_CASE("Verify InvokeHTTP POST request with 501 not implemented response", "[invokehttp]") {
   InvokeHTTPResponse501Handler handler;
-  VerifyRetryInvokeHTTP harness;
   std::filesystem::path test_file_path;
   std::string key_dir;
   SECTION("Secure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPostSecure.yml";
-    harness.setKeyDir(TEST_RESOURCES);
+    key_dir = TEST_RESOURCES;
   }
   SECTION("Insecure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPost.yml";
   }
-  harness.run("http://localhost:0/", test_file_path.string(), key_dir, &handler);
+  VerifyRetryInvokeHTTP harness(test_file_path);
+  if (!key_dir.empty()) {
+    harness.setKeyDir(key_dir);
+  }
+  harness.run("http://localhost:0/", key_dir, &handler);
 }
 
 TEST_CASE("Verify InvokeHTTP POST request with timeout failure", "[invokehttp]") {
   TimeoutingHTTPHandler handler({std::chrono::seconds(2)});
-  VerifyRWTimeoutInvokeHTTP harness;
   std::filesystem::path test_file_path;
   std::string key_dir;
   SECTION("Secure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPostSecure.yml";
-    harness.setKeyDir(TEST_RESOURCES);
+    key_dir = TEST_RESOURCES;
   }
   SECTION("Insecure") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestInvokeHTTPPost.yml";
   }
-  harness.run("http://localhost:0/", test_file_path.string(), key_dir, &handler);
+  VerifyRWTimeoutInvokeHTTP harness(test_file_path);
+  if (!key_dir.empty()) {
+    harness.setKeyDir(key_dir);
+  }
+  harness.run("http://localhost:0/", key_dir, &handler);
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/AlertTests.cpp b/libminifi/test/integration/AlertTests.cpp
index b972d4b..73fa202 100644
--- a/libminifi/test/integration/AlertTests.cpp
+++ b/libminifi/test/integration/AlertTests.cpp
@@ -58,6 +58,7 @@
 
 class VerifyAlerts : public HTTPIntegrationBase {
  public:
+  using HTTPIntegrationBase::HTTPIntegrationBase;
   void testSetup() override {}
 
   void runAssertions() override {
@@ -76,7 +77,7 @@
   std::ofstream(flow_config_file) << empty_flow;
 
   std::string agent_id = "test-agent-1";
-  VerifyAlerts harness;
+  VerifyAlerts harness(flow_config_file.string(), dir.getPath());
   AlertHandler handler(agent_id);
   harness.setUrl("http://localhost:0/api/alerts", &handler);
   harness.getConfiguration()->set(minifi::Configuration::nifi_c2_agent_identifier, agent_id);
@@ -140,7 +141,7 @@
     return true;
   };
 
-  harness.run(flow_config_file.string(), dir.getPath());
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2AssetSyncTest.cpp b/libminifi/test/integration/C2AssetSyncTest.cpp
index d9c2195..7c238bc 100644
--- a/libminifi/test/integration/C2AssetSyncTest.cpp
+++ b/libminifi/test/integration/C2AssetSyncTest.cpp
@@ -147,6 +147,7 @@
 
 class VerifyC2AssetSync : public VerifyC2Base {
  public:
+  using VerifyC2Base::VerifyC2Base;
   void configureC2() override {
     configuration->set("nifi.c2.agent.protocol.class", "RESTSender");
     configuration->set("nifi.c2.enable", "true");
diff --git a/libminifi/test/integration/C2ClearCoreComponentStateTest.cpp b/libminifi/test/integration/C2ClearCoreComponentStateTest.cpp
index fae3808..0b40500 100644
--- a/libminifi/test/integration/C2ClearCoreComponentStateTest.cpp
+++ b/libminifi/test/integration/C2ClearCoreComponentStateTest.cpp
@@ -32,7 +32,9 @@
 
 class VerifyC2ClearCoreComponentState : public VerifyC2Base {
  public:
-  explicit VerifyC2ClearCoreComponentState(const std::atomic_bool& component_cleared_successfully) : component_cleared_successfully_(component_cleared_successfully) {
+  explicit VerifyC2ClearCoreComponentState(const std::filesystem::path& test_file_location, const std::atomic_bool& component_cleared_successfully)
+      : VerifyC2Base(test_file_location),
+        component_cleared_successfully_(component_cleared_successfully) {
     auto temp_dir = testController.createTempDirectory();
     test_file_1_ = utils::putFileToDir(temp_dir, "test1.txt", "foo\n");
     test_file_2_ = utils::putFileToDir(temp_dir, "test2.txt", "foobar\n");
@@ -194,11 +196,10 @@
 
 TEST_CASE("C2ClearCoreComponentState", "[c2test]") {
   std::atomic_bool component_cleared_successfully{false};
-  VerifyC2ClearCoreComponentState harness(component_cleared_successfully);
+  VerifyC2ClearCoreComponentState harness(std::filesystem::path(TEST_RESOURCES) / "TestC2DescribeCoreComponentState.yml", component_cleared_successfully);
   ClearCoreComponentStateHandler handler(component_cleared_successfully, harness.getConfiguration(), harness.getFile1Location());
   harness.setUrl("http://localhost:0/api/heartbeat", &handler);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestC2DescribeCoreComponentState.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2CompressTest.cpp b/libminifi/test/integration/C2CompressTest.cpp
index 2cf809e..5b7d1ac 100644
--- a/libminifi/test/integration/C2CompressTest.cpp
+++ b/libminifi/test/integration/C2CompressTest.cpp
@@ -65,6 +65,7 @@
 
 class VerifyCompressedHeartbeat : public VerifyC2Base {
  public:
+  using VerifyC2Base::VerifyC2Base;
   void testSetup() override {
     LogTestController::getInstance().setTrace<minifi::c2::C2Agent>();
     LogTestController::getInstance().setDebug<minifi::c2::RESTSender>();
diff --git a/libminifi/test/integration/C2ConfigEncryption.cpp b/libminifi/test/integration/C2ConfigEncryption.cpp
index 25edaea..6a4554a 100644
--- a/libminifi/test/integration/C2ConfigEncryption.cpp
+++ b/libminifi/test/integration/C2ConfigEncryption.cpp
@@ -35,26 +35,21 @@
   SECTION("Json config") {
     test_file_path = std::filesystem::path(TEST_RESOURCES) / "decrypted.config.json";
   }
-  TestController controller;
-  // copy config file to temporary location as it will get overridden
-  auto home_path = controller.createTempDirectory();
-  auto live_config_file = home_path / "config.yml";
-  minifi::utils::file::copy_file(test_file_path, live_config_file);
-  // the C2 server will update the flow with the contents of test_file_path
-  // which will be encrypted and persisted to the temporary live_config_file
+
   C2UpdateHandler handler(test_file_path.string());
-  VerifyC2Update harness(10s);
+  VerifyC2Update harness(test_file_path, TEST_RESOURCES, 10s);
   harness.getConfiguration()->set(minifi::Configure::nifi_flow_configuration_encrypt, "true");
   harness.setKeyDir(TEST_RESOURCES);
   harness.setUrl("https://localhost:0/update", &handler);
   handler.setC2RestResponse(harness.getC2RestUrl(), "configuration", "true");
 
-  harness.run(live_config_file, TEST_RESOURCES);
+  harness.run();
+  auto live_config_file = harness.getFlowConfigPath();
 
   auto encryptor = minifi::utils::crypto::EncryptionProvider::create(TEST_RESOURCES);
   REQUIRE(encryptor);
 
-  std::ifstream encrypted_file{live_config_file};
+  std::ifstream encrypted_file{*live_config_file};
   std::string decrypted_config = encryptor->decrypt(std::string(std::istreambuf_iterator<char>(encrypted_file), {}));
 
   std::ifstream expected_file{test_file_path.string() + ".reformatted"};
diff --git a/libminifi/test/integration/C2ControllerEnableFailureTest.cpp b/libminifi/test/integration/C2ControllerEnableFailureTest.cpp
index 0fc6ab3..8a04120 100644
--- a/libminifi/test/integration/C2ControllerEnableFailureTest.cpp
+++ b/libminifi/test/integration/C2ControllerEnableFailureTest.cpp
@@ -105,7 +105,9 @@
 
 class VerifyC2ControllerUpdate : public VerifyC2Base {
  public:
-  explicit VerifyC2ControllerUpdate(const std::atomic_bool& flow_updated_successfully) : flow_updated_successfully_(flow_updated_successfully) {
+  explicit VerifyC2ControllerUpdate(const std::filesystem::path& test_file_location, const std::atomic_bool& flow_updated_successfully)
+      : VerifyC2Base(test_file_location),
+        flow_updated_successfully_(flow_updated_successfully) {
   }
 
   void testSetup() override {
@@ -177,13 +179,13 @@
 
 TEST_CASE("C2ControllerEnableFailureTest", "[c2test]") {
   std::atomic_bool flow_updated_successfully{false};
-  VerifyC2ControllerUpdate harness(flow_updated_successfully);
   const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestC2InvalidController.yml";
+  VerifyC2ControllerUpdate harness(test_file_path, flow_updated_successfully);
   auto replacement_path = test_file_path.string();
   minifi::utils::string::replaceAll(replacement_path, "TestC2InvalidController", "TestC2ValidController");
   ControllerUpdateHandler handler(flow_updated_successfully, harness.getConfiguration(), replacement_path);
   harness.setUrl("https://localhost:0/api/heartbeat", &handler);
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2DebugBundleTest.cpp b/libminifi/test/integration/C2DebugBundleTest.cpp
index 3e9f1a6..e30c77a 100644
--- a/libminifi/test/integration/C2DebugBundleTest.cpp
+++ b/libminifi/test/integration/C2DebugBundleTest.cpp
@@ -33,7 +33,8 @@
 
 class VerifyDebugInfo : public VerifyC2Base {
  public:
-  explicit VerifyDebugInfo(std::function<bool()> verify): verify_(std::move(verify)) {}
+  explicit VerifyDebugInfo(const std::filesystem::path& test_file_location, std::function<bool()> verify)
+    : VerifyC2Base(test_file_location), verify_(std::move(verify)) {}
 
   void testSetup() override {
     LogTestController::getInstance().setTrace<minifi::c2::C2Agent>();
@@ -151,7 +152,7 @@
   std::ofstream{home_dir / "conf/minifi.properties", std::ios::binary} << properties_file;
   std::ofstream{home_dir / "conf/config.yml", std::ios::binary} << flow_config_file;
 
-  VerifyDebugInfo harness([&]() -> bool {
+  VerifyDebugInfo harness(home_dir / "conf/config.yml", [&]() -> bool {
     if (!ack_handler.isAcknowledged("79")) {
       return false;
     }
@@ -199,7 +200,7 @@
 
   logging::LoggerFactory<C2HeartbeatHandler>::getLogger()->log_error("Tis but a scratch");
 
-  harness.run((home_dir / "conf/config.yml").string());
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2DescribeCoreComponentStateTest.cpp b/libminifi/test/integration/C2DescribeCoreComponentStateTest.cpp
index 3a2a850..1d0e791 100644
--- a/libminifi/test/integration/C2DescribeCoreComponentStateTest.cpp
+++ b/libminifi/test/integration/C2DescribeCoreComponentStateTest.cpp
@@ -28,8 +28,8 @@
 
 class VerifyC2DescribeCoreComponentState : public VerifyC2Describe {
  public:
-  explicit VerifyC2DescribeCoreComponentState(std::atomic_bool& verified)
-    : VerifyC2Describe(verified) {
+  explicit VerifyC2DescribeCoreComponentState(const std::filesystem::path& test_file_path, std::atomic_bool& verified)
+    : VerifyC2Describe(test_file_path, verified) {
     temp_dir_ = testController.createTempDirectory();
 
     test_file_1_ = temp_dir_ / "test1.txt";
@@ -97,12 +97,11 @@
 
 TEST_CASE("C2DescribeCoreComponentStateTest", "[c2test]") {
   std::atomic_bool verified{false};
-  VerifyC2DescribeCoreComponentState harness(verified);
+  VerifyC2DescribeCoreComponentState harness(std::filesystem::path(TEST_RESOURCES) / "TestC2DescribeCoreComponentState.yml", verified);
   harness.setKeyDir(TEST_RESOURCES);
   DescribeCoreComponentStateHandler handler(harness.getConfiguration(), verified);
   harness.setUrl("https://localhost:0/api/heartbeat", &handler);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestC2DescribeCoreComponentState.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2DescribeManifestTest.cpp b/libminifi/test/integration/C2DescribeManifestTest.cpp
index 14a02ab..66b3004 100644
--- a/libminifi/test/integration/C2DescribeManifestTest.cpp
+++ b/libminifi/test/integration/C2DescribeManifestTest.cpp
@@ -50,7 +50,7 @@
 
 TEST_CASE("C2DescribeManifestTest", "[c2test]") {
   std::atomic_bool verified{false};
-  VerifyC2Describe harness(verified);
+  VerifyC2Describe harness(std::filesystem::path(TEST_RESOURCES) / "TestHTTPGet.yml", verified);
   minifi::utils::crypto::Bytes encryption_key = minifi::utils::string::from_hex("4024b327fdc987ce3eb43dd1f690b9987e4072e0020e3edf4349ce1ad91a4e38");
   minifi::Decryptor decryptor{minifi::utils::crypto::EncryptionProvider{encryption_key}};
   std::string encrypted_value = "l3WY1V27knTiPa6jVX0jrq4qjmKsySOu||ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo=";
@@ -68,8 +68,7 @@
   harness.getConfiguration()->set(minifi::Configuration::nifi_log_appender_rolling_directory, "/var/log/minifi");
 
   harness.setUrl("https://localhost:0/api/heartbeat", &responder);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPGet.yml";
-  harness.run(test_file_path.string());
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2DescribeMetricsTest.cpp b/libminifi/test/integration/C2DescribeMetricsTest.cpp
index 9e5bcbc..3d59306 100644
--- a/libminifi/test/integration/C2DescribeMetricsTest.cpp
+++ b/libminifi/test/integration/C2DescribeMetricsTest.cpp
@@ -35,7 +35,7 @@
 
 class VerifyEmptyC2Metric : public VerifyC2Base {
  public:
-  explicit VerifyEmptyC2Metric(const std::atomic_bool& metrics_found) : metrics_found_(metrics_found) {
+  explicit VerifyEmptyC2Metric(const std::filesystem::path& test_file_path, const std::atomic_bool& metrics_found) : VerifyC2Base(test_file_path), metrics_found_(metrics_found) {
   }
 
   void testSetup() override {
@@ -157,7 +157,7 @@
 
 TEST_CASE("C2DescribeMetricsTest", "[c2test]") {
   std::atomic_bool metrics_found{false};
-  VerifyEmptyC2Metric harness(metrics_found);
+  VerifyEmptyC2Metric harness(std::filesystem::path(TEST_RESOURCES) / "TestSameProcessorMetrics.yml", metrics_found);
   harness.getConfiguration()->set("nifi.c2.root.class.definitions", "metrics");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.name", "metrics");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.metrics", "processormetrics,systemmetrics");
@@ -167,8 +167,7 @@
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.metrics.systemmetrics.classes", "QueueMetrics");
   MetricsHandler handler(metrics_found, harness.getConfiguration());
   harness.setUrl("http://localhost:0/api/heartbeat", &handler);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestSameProcessorMetrics.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2EmptyMetricTest.cpp b/libminifi/test/integration/C2EmptyMetricTest.cpp
index 52c1066..0cf71d5 100644
--- a/libminifi/test/integration/C2EmptyMetricTest.cpp
+++ b/libminifi/test/integration/C2EmptyMetricTest.cpp
@@ -37,7 +37,7 @@
 
 class VerifyEmptyC2Metric : public VerifyC2Base {
  public:
-  explicit VerifyEmptyC2Metric(const std::atomic_bool& metrics_found) : metrics_found_(metrics_found) {
+  explicit VerifyEmptyC2Metric(const std::filesystem::path& test_file_path, const std::atomic_bool& metrics_found) : VerifyC2Base(test_file_path), metrics_found_(metrics_found) {
   }
 
   void testSetup() override {
@@ -96,7 +96,7 @@
 
 TEST_CASE("C2EmptyMetricTest", "[c2test]") {
   std::atomic_bool metrics_found{false};
-  VerifyEmptyC2Metric harness(metrics_found);
+  VerifyEmptyC2Metric harness(std::filesystem::path(TEST_RESOURCES) / "TestEmpty.yml", metrics_found);
   harness.getConfiguration()->set("nifi.c2.root.class.definitions", "metrics");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.name", "metrics");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.metrics", "loadmetrics");
@@ -104,8 +104,7 @@
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.metrics.loadmetrics.classes", "QueueMetrics,RepositoryMetrics");
   MetricsHandler handler(metrics_found, harness.getConfiguration());
   harness.setUrl("http://localhost:0/api/heartbeat", &handler);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestEmpty.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2FailedUpdateTest.cpp b/libminifi/test/integration/C2FailedUpdateTest.cpp
index ed55f52..cb02ba3 100644
--- a/libminifi/test/integration/C2FailedUpdateTest.cpp
+++ b/libminifi/test/integration/C2FailedUpdateTest.cpp
@@ -27,12 +27,11 @@
 TEST_CASE("C2FailedUpdateTest", "[c2test]") {
   const auto bad_test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestBad.yml";
   C2FailedUpdateHandler handler(bad_test_file_path.string());
-  VerifyC2FailedUpdate harness(10s);
+  VerifyC2FailedUpdate harness(std::filesystem::path(TEST_RESOURCES) / "TestHTTPGet.yml", 10s);
   harness.setKeyDir(TEST_RESOURCES);
   harness.setUrl("http://localhost:0/update", &handler);
   handler.setC2RestResponse(harness.getC2RestUrl(), "configuration");
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPGet.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2FetchFlowIfMissingTest.cpp b/libminifi/test/integration/C2FetchFlowIfMissingTest.cpp
index a6a7065..a464685 100644
--- a/libminifi/test/integration/C2FetchFlowIfMissingTest.cpp
+++ b/libminifi/test/integration/C2FetchFlowIfMissingTest.cpp
@@ -30,12 +30,11 @@
   auto minifi_home = controller.createTempDirectory();
   const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestEmpty.yml";
   C2FlowProvider handler(test_file_path.string());
-  VerifyFlowFetched harness(10s);
+  VerifyFlowFetched harness(minifi_home / "config.yml", {}, 10s);
   harness.setKeyDir(TEST_RESOURCES);
   harness.setUrl("https://localhost:0/", &handler);
   harness.setFlowUrl(harness.getC2RestUrl());
-
-  harness.run(minifi_home / "config.yml");
+  harness.run();
 
   // check existence of the config file
   REQUIRE(std::ifstream{minifi_home / "config.yml"});
diff --git a/libminifi/test/integration/C2JstackTest.cpp b/libminifi/test/integration/C2JstackTest.cpp
index b347924..22e25b7 100644
--- a/libminifi/test/integration/C2JstackTest.cpp
+++ b/libminifi/test/integration/C2JstackTest.cpp
@@ -46,12 +46,11 @@
 
 TEST_CASE("C2JstackTest", "[c2test]") {
   std::atomic_bool acknowledgement_received{ false };
-  VerifyC2Describe harness{acknowledgement_received};
+  VerifyC2Describe harness{std::filesystem::path(TEST_RESOURCES) / "TestHTTPGet.yml", acknowledgement_received};
   harness.setKeyDir(TEST_RESOURCES);
   DescribeJstackHandler responder{acknowledgement_received, harness.getConfiguration()};
   harness.setUrl("https://localhost:0/heartbeat", &responder);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPGet.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2LogHeartbeatTest.cpp b/libminifi/test/integration/C2LogHeartbeatTest.cpp
index 71755cc..6376944 100644
--- a/libminifi/test/integration/C2LogHeartbeatTest.cpp
+++ b/libminifi/test/integration/C2LogHeartbeatTest.cpp
@@ -38,6 +38,7 @@
 
 class VerifyLogC2Heartbeat : public VerifyC2Base {
  public:
+  using VerifyC2Base::VerifyC2Base;
   void testSetup() override {
     LogTestController::getInstance().setTrace<minifi::c2::C2Agent>();
     LogTestController::getInstance().setDebug<minifi::c2::RESTSender>();
diff --git a/libminifi/test/integration/C2MetricsTest.cpp b/libminifi/test/integration/C2MetricsTest.cpp
index ac82324..275e99d 100644
--- a/libminifi/test/integration/C2MetricsTest.cpp
+++ b/libminifi/test/integration/C2MetricsTest.cpp
@@ -38,7 +38,9 @@
 
 class VerifyC2Metrics : public VerifyC2Base {
  public:
-  explicit VerifyC2Metrics(const std::atomic_bool& metrics_updated_successfully) : metrics_updated_successfully_(metrics_updated_successfully) {
+  explicit VerifyC2Metrics(const std::filesystem::path& test_file_path, const std::atomic_bool& metrics_updated_successfully)
+      : VerifyC2Base(test_file_path),
+        metrics_updated_successfully_(metrics_updated_successfully) {
   }
 
   void testSetup() override {
@@ -257,7 +259,8 @@
 
 TEST_CASE("C2MetricsTest", "[c2test]") {
   std::atomic_bool metrics_updated_successfully{false};
-  VerifyC2Metrics harness(metrics_updated_successfully);
+  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestC2Metrics.yml";
+  VerifyC2Metrics harness(test_file_path, metrics_updated_successfully);
   harness.getConfiguration()->set("nifi.c2.root.classes", "FlowInformation,AgentInformation");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions", "metrics");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.name", "metrics");
@@ -268,12 +271,11 @@
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.metrics.loadmetrics.classes", "QueueMetrics,RepositoryMetrics");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.metrics.processorMetrics.name", "ProcessorMetrics");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.metrics.processorMetrics.classes", "processorMetrics/GetTCP.*");
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestC2Metrics.yml";
   auto replacement_path = test_file_path.string();
   minifi::utils::string::replaceAll(replacement_path, "TestC2Metrics", "TestC2MetricsUpdate");
   MetricsHandler handler(metrics_updated_successfully, harness.getConfiguration(), replacement_path);
   harness.setUrl("https://localhost:0/api/heartbeat", &handler);
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2MultipleCommandsTest.cpp b/libminifi/test/integration/C2MultipleCommandsTest.cpp
index d8d335e..5342408 100644
--- a/libminifi/test/integration/C2MultipleCommandsTest.cpp
+++ b/libminifi/test/integration/C2MultipleCommandsTest.cpp
@@ -95,8 +95,9 @@
 
 class VerifyC2MultipleCommands : public VerifyC2Base {
  public:
-  explicit VerifyC2MultipleCommands(AckAuditor& auditor)
-    : ack_auditor_(auditor) {
+  explicit VerifyC2MultipleCommands(const std::filesystem::path& test_file_path, AckAuditor& auditor)
+    : VerifyC2Base(test_file_path),
+      ack_auditor_(auditor) {
   }
 
   void testSetup() override {
@@ -121,11 +122,10 @@
 
 TEST_CASE("C2MultipleCommandsTest", "[c2test]") {
   AckAuditor ack_auditor;
-  VerifyC2MultipleCommands harness(ack_auditor);
+  VerifyC2MultipleCommands harness(std::filesystem::path(TEST_RESOURCES) / "TestC2DescribeCoreComponentState.yml", ack_auditor);
   MultipleC2CommandHandler responder(ack_auditor, harness.getConfiguration());
   harness.setUrl("https://localhost:0/heartbeat", &responder);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestC2DescribeCoreComponentState.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2NullConfiguration.cpp b/libminifi/test/integration/C2NullConfiguration.cpp
index 9c1189d..7761930 100644
--- a/libminifi/test/integration/C2NullConfiguration.cpp
+++ b/libminifi/test/integration/C2NullConfiguration.cpp
@@ -33,8 +33,8 @@
 
 class VerifyC2Server : public HTTPIntegrationBase {
  public:
-  explicit VerifyC2Server(bool isSecure)
-      : isSecure(isSecure) {
+  explicit VerifyC2Server(const std::filesystem::path& test_file_path)
+      : HTTPIntegrationBase(test_file_path) {
     dir = testController.createTempDirectory();
   }
 
@@ -76,16 +76,14 @@
   }
 
  protected:
-  bool isSecure;
   std::filesystem::path dir;
   TestController testController;
 };
 
 TEST_CASE("C2NullConfiguration", "[c2test]") {
-  VerifyC2Server harness(true);
+  VerifyC2Server harness(std::filesystem::path(TEST_RESOURCES) / "TestNull.yml");
   harness.setKeyDir(TEST_RESOURCES);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestNull.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2PauseResumeTest.cpp b/libminifi/test/integration/C2PauseResumeTest.cpp
index 03009c5..b9e6efe 100644
--- a/libminifi/test/integration/C2PauseResumeTest.cpp
+++ b/libminifi/test/integration/C2PauseResumeTest.cpp
@@ -33,7 +33,9 @@
 
 class VerifyC2PauseResume : public VerifyC2Base {
  public:
-  explicit VerifyC2PauseResume(const std::atomic_bool& flow_resumed_successfully) : VerifyC2Base(), flow_resumed_successfully_(flow_resumed_successfully) {
+  explicit VerifyC2PauseResume(const std::filesystem::path& test_file_path, const std::atomic_bool& flow_resumed_successfully)
+      : VerifyC2Base(test_file_path),
+        flow_resumed_successfully_(flow_resumed_successfully) {
     LogTestController::getInstance().setTrace<minifi::c2::C2Agent>();
     LogTestController::getInstance().setDebug<minifi::c2::RESTSender>();
     LogTestController::getInstance().setDebug<minifi::FlowController>();
@@ -116,13 +118,13 @@
 
 TEST_CASE("C2PauseResumeTest", "[c2test]") {
   std::atomic_bool flow_resumed_successfully{false};
-  VerifyC2PauseResume harness{flow_resumed_successfully};
+  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "C2PauseResumeTest.yml";
+  VerifyC2PauseResume harness{test_file_path, flow_resumed_successfully};
   PauseResumeHandler responder{flow_resumed_successfully, harness.getConfiguration()};
 
   std::shared_ptr<core::Repository> test_repo = std::make_shared<TestThreadedRepository>();
   std::shared_ptr<core::Repository> test_flow_repo = std::make_shared<TestFlowRepository>();
   std::shared_ptr<minifi::Configure> configuration = std::make_shared<minifi::ConfigureImpl>();
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "C2PauseResumeTest.yml";
   configuration->set(minifi::Configure::nifi_flow_configuration_file, test_file_path.string());
 
   std::shared_ptr<core::ContentRepository> content_repo = std::make_shared<core::repository::VolatileContentRepository>();
@@ -163,7 +165,7 @@
   server = std::make_unique<TestServer>(port, path, &responder);
 
   harness.setUrl("http://localhost:0/heartbeat", &responder);
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2PropertiesUpdateTests.cpp b/libminifi/test/integration/C2PropertiesUpdateTests.cpp
index 7600c84..d10fa44 100644
--- a/libminifi/test/integration/C2PropertiesUpdateTests.cpp
+++ b/libminifi/test/integration/C2PropertiesUpdateTests.cpp
@@ -87,8 +87,10 @@
 
 class VerifyPropertyUpdate : public HTTPIntegrationBase {
  public:
-  VerifyPropertyUpdate() :fn_{[]{}} {}
-  explicit VerifyPropertyUpdate(std::function<void()> fn) : fn_(std::move(fn)) {}
+  VerifyPropertyUpdate() : fn_{[]{}} {}
+  explicit VerifyPropertyUpdate(const std::filesystem::path& test_file_path, std::function<void()> fn)
+    : HTTPIntegrationBase(test_file_path),
+      fn_(std::move(fn)) {}
   VerifyPropertyUpdate(const VerifyPropertyUpdate&) = delete;
   VerifyPropertyUpdate(VerifyPropertyUpdate&&) = default;
   VerifyPropertyUpdate& operator=(const VerifyPropertyUpdate&) = delete;
@@ -160,7 +162,7 @@
 
   // On msvc, the passed lambda can't capture a reference to the object under construction, so we need to late-init harness.
   VerifyPropertyUpdate harness;
-  harness = VerifyPropertyUpdate([&] {
+  harness = VerifyPropertyUpdate(home_dir / "conf/config.yml", [&] {
     REQUIRE(utils::verifyEventHappenedInPollTime(10s, [&] {return ack_handler.isAcknowledged("79");}));
     REQUIRE(utils::verifyEventHappenedInPollTime(10s, [&] {
       return ack_handler.getApplyCount("FULLY_APPLIED") == 1;
@@ -227,7 +229,7 @@
     {"nifi.log.logger.org::apache::nifi::minifi::test::DummyClass1", "DEBUG,ostream", true}
   });
 
-  harness.run((home_dir / "conf/config.yml").string());
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2SameProcessorMetrics.cpp b/libminifi/test/integration/C2SameProcessorMetrics.cpp
index e4f93cb..05b0249 100644
--- a/libminifi/test/integration/C2SameProcessorMetrics.cpp
+++ b/libminifi/test/integration/C2SameProcessorMetrics.cpp
@@ -35,7 +35,7 @@
 
 class VerifyEmptyC2Metric : public VerifyC2Base {
  public:
-  explicit VerifyEmptyC2Metric(const std::atomic_bool& metrics_found) : metrics_found_(metrics_found) {
+  explicit VerifyEmptyC2Metric(const std::filesystem::path& test_file_path, const std::atomic_bool& metrics_found) : VerifyC2Base(test_file_path), metrics_found_(metrics_found) {
   }
 
   void testSetup() override {
@@ -96,7 +96,7 @@
 
 TEST_CASE("Test support for setting metrics for multiple processors of the same type in a flow", "[c2test]") {
   std::atomic_bool metrics_found{false};
-  VerifyEmptyC2Metric harness(metrics_found);
+  VerifyEmptyC2Metric harness(std::filesystem::path(TEST_RESOURCES) / "TestSameProcessorMetrics.yml", metrics_found);
   harness.getConfiguration()->set("nifi.c2.root.class.definitions", "metrics");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.name", "metrics");
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.metrics", "processormetrics");
@@ -104,8 +104,7 @@
   harness.getConfiguration()->set("nifi.c2.root.class.definitions.metrics.metrics.processormetrics.classes", "GetFileMetrics,GetTCPMetrics");
   MetricsHandler handler(metrics_found, harness.getConfiguration());
   harness.setUrl("http://localhost:0/api/heartbeat", &handler);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestSameProcessorMetrics.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2UpdateTest.cpp b/libminifi/test/integration/C2UpdateTest.cpp
index 66072df..f8ddee3 100644
--- a/libminifi/test/integration/C2UpdateTest.cpp
+++ b/libminifi/test/integration/C2UpdateTest.cpp
@@ -27,14 +27,14 @@
 TEST_CASE("Test update configuration C2 command", "[c2test]") {
   const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPGet.yml";
   C2UpdateHandler handler(test_file_path.string());
-  VerifyC2Update harness(10s);
+  VerifyC2Update harness(test_file_path, {}, 10s);
   harness.setKeyDir(TEST_RESOURCES);
   harness.setUrl("https://localhost:0/update", &handler);
   handler.setC2RestResponse(harness.getC2RestUrl(), "configuration");
 
   const auto start = std::chrono::steady_clock::now();
 
-  harness.run(test_file_path);
+  harness.run();
 
   const auto then = std::chrono::steady_clock::now();
   const auto seconds = std::chrono::duration_cast<std::chrono::seconds>(then - start).count();
diff --git a/libminifi/test/integration/C2VerifyHeartbeatAndStop.cpp b/libminifi/test/integration/C2VerifyHeartbeatAndStop.cpp
index 700e02e..e1421da 100644
--- a/libminifi/test/integration/C2VerifyHeartbeatAndStop.cpp
+++ b/libminifi/test/integration/C2VerifyHeartbeatAndStop.cpp
@@ -29,6 +29,7 @@
 
 class VerifyC2Heartbeat : public VerifyC2Base {
  public:
+  using VerifyC2Base::VerifyC2Base;
   void testSetup() override {
     LogTestController::getInstance().setTrace<minifi::c2::C2Agent>();
     LogTestController::getInstance().setDebug<minifi::c2::RESTSender>();
@@ -50,7 +51,7 @@
 };
 
 TEST_CASE("Verify C2 heartbeat and stop operation", "[c2test]") {
-  VerifyC2Heartbeat harness;
+  VerifyC2Heartbeat harness(std::filesystem::path(TEST_RESOURCES) / "C2VerifyHeartbeatAndStopSecure.yml");
   StoppingHeartbeatHandler responder(harness.getConfiguration());
   SECTION("Secure") {
     harness.setKeyDir(TEST_RESOURCES);
@@ -59,8 +60,7 @@
   SECTION("Insecure") {
     harness.setUrl("http://localhost:0/heartbeat", &responder);
   }
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "C2VerifyHeartbeatAndStopSecure.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2VerifyLightweightHeartbeatAndStop.cpp b/libminifi/test/integration/C2VerifyLightweightHeartbeatAndStop.cpp
index aa26ce5..f23390b 100644
--- a/libminifi/test/integration/C2VerifyLightweightHeartbeatAndStop.cpp
+++ b/libminifi/test/integration/C2VerifyLightweightHeartbeatAndStop.cpp
@@ -53,6 +53,7 @@
 
 class VerifyLightWeightC2Heartbeat : public VerifyC2Base {
  public:
+  using VerifyC2Base::VerifyC2Base;
   void testSetup() override {
     LogTestController::getInstance().setTrace<minifi::c2::C2Agent>();
     LogTestController::getInstance().setDebug<minifi::c2::RESTSender>();
@@ -73,7 +74,7 @@
 };
 
 TEST_CASE("Verify C2 lightweight heartbeat and stop operation", "[c2test]") {
-  VerifyLightWeightC2Heartbeat harness;
+  VerifyLightWeightC2Heartbeat harness(std::filesystem::path(TEST_RESOURCES) / "C2VerifyHeartbeatAndStopSecure.yml");
   LightWeightC2Handler responder(harness.getConfiguration());
   SECTION("Secure") {
     harness.setKeyDir(TEST_RESOURCES);
@@ -82,8 +83,7 @@
   SECTION("Insecure") {
     harness.setUrl("http://localhost:0/heartbeat", &responder);
   }
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "C2VerifyHeartbeatAndStopSecure.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/C2VerifyResourceConsumptionInHeartbeat.cpp b/libminifi/test/integration/C2VerifyResourceConsumptionInHeartbeat.cpp
index 37e1f07..226944d 100644
--- a/libminifi/test/integration/C2VerifyResourceConsumptionInHeartbeat.cpp
+++ b/libminifi/test/integration/C2VerifyResourceConsumptionInHeartbeat.cpp
@@ -106,6 +106,7 @@
 
 class VerifyResourceConsumptionInHeartbeat : public VerifyC2Base {
  public:
+  using VerifyC2Base::VerifyC2Base;
   void testSetup() override {
     LogTestController::getInstance().setTrace<minifi::c2::C2Agent>();
     LogTestController::getInstance().setDebug<minifi::c2::RESTSender>();
@@ -129,7 +130,7 @@
 };
 
 TEST_CASE("Verify resource consumption in C2 heartbeat", "[c2test]") {
-  VerifyResourceConsumptionInHeartbeat harness;
+  VerifyResourceConsumptionInHeartbeat harness(std::filesystem::path(TEST_RESOURCES) / "C2VerifyHeartbeatAndStop.yml");
   ResourceConsumptionInHeartbeatHandler responder(harness.getConfiguration());
   auto event_to_wait_for = [&responder] {
     return responder.getNumberOfHandledHeartBeats() >= 3;
@@ -137,8 +138,7 @@
 
   harness.setUrl("http://localhost:0/heartbeat", &responder);
   harness.setEventToWaitFor(event_to_wait_for);
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "C2VerifyHeartbeatAndStop.yml";
-  harness.run(test_file_path);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/HTTPSiteToSiteTests.cpp b/libminifi/test/integration/HTTPSiteToSiteTests.cpp
index b5ef811..14391bb 100644
--- a/libminifi/test/integration/HTTPSiteToSiteTests.cpp
+++ b/libminifi/test/integration/HTTPSiteToSiteTests.cpp
@@ -38,8 +38,8 @@
 
 class SiteToSiteTestHarness : public HTTPIntegrationBase {
  public:
-  explicit SiteToSiteTestHarness(std::chrono::seconds waitTime = 2s)
-      : HTTPIntegrationBase(waitTime) {
+  explicit SiteToSiteTestHarness(const std::filesystem::path& test_file_path, std::chrono::seconds waitTime = 2s)
+      : HTTPIntegrationBase(test_file_path, {}, waitTime) {
     dir = testController.createTempDirectory();
   }
 
@@ -86,8 +86,8 @@
   bool invalid_checksum{false};
 };
 
-void run_variance(const std::string& test_file_location, const std::string& url, const struct test_profile &profile) {
-  SiteToSiteTestHarness harness;
+void run_variance(const std::filesystem::path& test_file_location, const std::string& url, const struct test_profile &profile) {
+  SiteToSiteTestHarness harness(test_file_location);
 
   std::string in_port = "471deef6-2a6e-4a7d-912a-81cc17e3a204";
   std::string out_port = "471deef6-2a6e-4a7d-912a-81cc17e3a203";
@@ -154,7 +154,7 @@
   auto *deleteOutputResponse = new DeleteTransactionResponder(delete_output_url, "201 OK", producedFlows);
   harness.setUrl(delete_output_url, deleteOutputResponse);
 
-  harness.run(test_file_location);
+  harness.run();
 
   std::stringstream assertStr;
   if (profile.allFalse()) {
@@ -202,8 +202,7 @@
     profile.invalid_checksum = true;
   }
 
-  const auto test_file_path = std::filesystem::path(TEST_RESOURCES) / "TestHTTPSiteToSite.yml";
-  run_variance(test_file_path.string(), parseUrl("http://localhost:8099/nifi-api"), profile);
+  run_variance(std::filesystem::path(TEST_RESOURCES) / "TestHTTPSiteToSite.yml", parseUrl("http://localhost:8099/nifi-api"), profile);
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/OnScheduleErrorHandlingTests.cpp b/libminifi/test/integration/OnScheduleErrorHandlingTests.cpp
index 4d80a46..e3c2876 100644
--- a/libminifi/test/integration/OnScheduleErrorHandlingTests.cpp
+++ b/libminifi/test/integration/OnScheduleErrorHandlingTests.cpp
@@ -31,6 +31,7 @@
  * KamikazeProcessor is a test processor to trigger errors in these functions */
 class KamikazeErrorHandlingTests : public IntegrationBase {
  public:
+  explicit KamikazeErrorHandlingTests(const std::filesystem::path& test_file_location) : IntegrationBase(test_file_location) {}
   void runAssertions() override {
     using minifi::test::utils::verifyEventHappenedInPollTime;
     REQUIRE(verifyEventHappenedInPollTime(wait_time_, [&] {
@@ -75,6 +76,7 @@
 /*Verify that event driven processors without incoming connections are not scheduled*/
 class EventDriverScheduleErrorHandlingTests: public IntegrationBase {
  public:
+  explicit EventDriverScheduleErrorHandlingTests(const std::filesystem::path& test_file_location) : IntegrationBase(test_file_location) {}
   void updateProperties(minifi::FlowController& fc) override {
     /* This tests depends on a configuration that contains only one KamikazeProcessor named kamikaze
      * (See testOnScheduleRetry.yml)
@@ -115,17 +117,13 @@
 };
 
 TEST_CASE("KamikazeErrorHandlingTests", "[OnScheduleErrorHandlingTests]") {
-  KamikazeErrorHandlingTests harness_kamikaze;
-
-  const auto test_file_location = std::filesystem::path(TEST_RESOURCES) / "TestOnScheduleRetry.yml";
-  harness_kamikaze.run(test_file_location);
+  KamikazeErrorHandlingTests harness_kamikaze(std::filesystem::path(TEST_RESOURCES) / "TestOnScheduleRetry.yml");
+  harness_kamikaze.run();
 }
 
 TEST_CASE("EventDriverScheduleErrorHandlingTests", "[OnScheduleErrorHandlingTests]") {
-  EventDriverScheduleErrorHandlingTests harness_eventdrivenerror;
-
-  const auto test_file_location = std::filesystem::path(TEST_RESOURCES) / "TestOnScheduleRetry.yml";
-  harness_eventdrivenerror.run(test_file_location);
+  EventDriverScheduleErrorHandlingTests harness_eventdrivenerror(std::filesystem::path(TEST_RESOURCES) / "TestOnScheduleRetry.yml");
+  harness_eventdrivenerror.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/SiteToSiteRestTest.cpp b/libminifi/test/integration/SiteToSiteRestTest.cpp
index bc64b7b..185b2ad 100644
--- a/libminifi/test/integration/SiteToSiteRestTest.cpp
+++ b/libminifi/test/integration/SiteToSiteRestTest.cpp
@@ -62,8 +62,9 @@
 
 class SiteToSiteTestHarness : public HTTPIntegrationBase {
  public:
-  explicit SiteToSiteTestHarness(bool isSecure)
-      : isSecure(isSecure) {
+  explicit SiteToSiteTestHarness(const std::filesystem::path& test_file_path, bool isSecure)
+      : HTTPIntegrationBase(test_file_path),
+        isSecure(isSecure) {
     dir_ = test_controller_.createTempDirectory();
   }
 
@@ -104,12 +105,11 @@
 };
 
 TEST_CASE("Test site to site using REST", "[s2s]") {
-  SiteToSiteTestHarness harness(false);
+  SiteToSiteTestHarness harness(std::filesystem::path(TEST_RESOURCES) / "TestSite2SiteRest.yml", false);
   Responder responder(false);
   harness.setKeyDir(TEST_RESOURCES);
   harness.setUrl(parseUrl("http://localhost:8077/nifi-api/site-to-site"), &responder);
-  const auto test_file_location = std::filesystem::path(TEST_RESOURCES) / "TestSite2SiteRest.yml";
-  harness.run(test_file_location);
+  harness.run();
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/integration/StateTransactionalityTests.cpp b/libminifi/test/integration/StateTransactionalityTests.cpp
index 51721b3..9d96c68 100644
--- a/libminifi/test/integration/StateTransactionalityTests.cpp
+++ b/libminifi/test/integration/StateTransactionalityTests.cpp
@@ -36,11 +36,12 @@
 
 class StatefulIntegrationTest : public IntegrationBase {
  public:
-  explicit StatefulIntegrationTest(std::string testCase, HookCollection hookCollection)
-    : on_schedule_hook_(std::move(hookCollection.on_schedule_hook_))
-    , on_trigger_hooks_(std::move(hookCollection.on_trigger_hooks_))
-    , log_checker_(hookCollection.log_checker_)
-    , test_case_(std::move(testCase)) {
+  StatefulIntegrationTest(const std::filesystem::path& test_file_path, std::string testCase, HookCollection hookCollection)
+    : IntegrationBase(test_file_path),
+      on_schedule_hook_(std::move(hookCollection.on_schedule_hook_)),
+      on_trigger_hooks_(std::move(hookCollection.on_trigger_hooks_)),
+      log_checker_(hookCollection.log_checker_),
+      test_case_(std::move(testCase)) {
   }
 
   void testSetup() override {
@@ -584,9 +585,8 @@
 
 TEST_CASE("Test state transactionality", "[statemanagement]") {
   for (const auto& test : testCasesToHookLists) {
-    StatefulIntegrationTest statefulIntegrationTest(test.first, test.second);
-    const auto test_file_location = std::filesystem::path(TEST_RESOURCES) / "TestStateTransactionality.yml";
-    statefulIntegrationTest.run(test_file_location);
+    StatefulIntegrationTest statefulIntegrationTest(std::filesystem::path(TEST_RESOURCES) / "TestStateTransactionality.yml", test.first, test.second);
+    statefulIntegrationTest.run();
   }
 }
 
diff --git a/libminifi/test/integration/TimeoutHTTPSiteToSiteTests.cpp b/libminifi/test/integration/TimeoutHTTPSiteToSiteTests.cpp
index b5e08bc..b313b8c 100644
--- a/libminifi/test/integration/TimeoutHTTPSiteToSiteTests.cpp
+++ b/libminifi/test/integration/TimeoutHTTPSiteToSiteTests.cpp
@@ -39,8 +39,8 @@
 
 class SiteToSiteTestHarness : public HTTPIntegrationBase {
  public:
-  explicit SiteToSiteTestHarness(bool isSecure, std::chrono::seconds waitTime = 1s)
-      : HTTPIntegrationBase(waitTime), isSecure(isSecure) {
+  explicit SiteToSiteTestHarness(const std::filesystem::path& test_file_path, bool isSecure, std::chrono::seconds waitTime = 1s)
+      : HTTPIntegrationBase(test_file_path, {}, waitTime), isSecure(isSecure) {
     dir = testController.createTempDirectory();
   }
 
@@ -97,8 +97,8 @@
   defaulted_handler delete_;
 };
 
-void run_timeout_variance(std::string test_file_location, bool isSecure, const std::string& url, const timeout_test_profile &profile) {
-  SiteToSiteTestHarness harness(isSecure);
+void run_timeout_variance(const std::filesystem::path& test_file_location, bool isSecure, const std::string& url, const timeout_test_profile &profile) {
+  SiteToSiteTestHarness harness(test_file_location, isSecure);
 
   std::string in_port = "471deef6-2a6e-4a7d-912a-81cc17e3a204";
 
@@ -135,7 +135,7 @@
   auto deleteResponse = std::make_unique<DeleteTransactionResponder>(delete_url, "201 OK", 12);
   harness.setUrl(delete_url, profile.delete_.get(deleteResponse.get()));
 
-  harness.run(test_file_location);
+  harness.run();
 
   REQUIRE(LogTestController::getInstance().contains("limit (200ms) reached, terminating connection"));
 
@@ -166,8 +166,7 @@
     profile.peer_.set({timeout});
   }
 
-  const auto test_file_location = std::filesystem::path(TEST_RESOURCES) / "TestTimeoutHTTPSiteToSite.yml";
-  run_timeout_variance(test_file_location.string(), false, parseUrl("http://localhost:8098/nifi-api"), profile);
+  run_timeout_variance(std::filesystem::path(TEST_RESOURCES) / "TestTimeoutHTTPSiteToSite.yml", false, parseUrl("http://localhost:8098/nifi-api"), profile);
 }
 
 }  // namespace org::apache::nifi::minifi::test
diff --git a/libminifi/test/libtest/integration/HTTPIntegrationBase.h b/libminifi/test/libtest/integration/HTTPIntegrationBase.h
index 42a5344..f15e392 100644
--- a/libminifi/test/libtest/integration/HTTPIntegrationBase.h
+++ b/libminifi/test/libtest/integration/HTTPIntegrationBase.h
@@ -39,8 +39,9 @@
 
 class HTTPIntegrationBase : public IntegrationBase {
  public:
-  explicit HTTPIntegrationBase(std::chrono::milliseconds waitTime = std::chrono::milliseconds(DEFAULT_WAITTIME_MSECS))
-      : IntegrationBase(waitTime),
+  explicit HTTPIntegrationBase(const std::optional<std::filesystem::path>& test_file_location = {}, const std::optional<std::filesystem::path>& home_path = {},
+    std::chrono::milliseconds waitTime = std::chrono::milliseconds(DEFAULT_WAITTIME_MSECS))
+      : IntegrationBase(test_file_location, home_path, waitTime),
         server(nullptr) {
   }
   HTTPIntegrationBase(const HTTPIntegrationBase&) = delete;
@@ -86,8 +87,8 @@
 
 class VerifyC2Describe : public VerifyC2Base {
  public:
-  explicit VerifyC2Describe(std::atomic<bool>& verified)
-    : verified_(verified) {
+  explicit VerifyC2Describe(const std::filesystem::path& test_file_path, std::atomic<bool>& verified)
+    : VerifyC2Base(test_file_path), verified_(verified) {
   }
 
   void testSetup() override {
@@ -111,8 +112,9 @@
 
 class VerifyC2Update : public HTTPIntegrationBase {
  public:
-  explicit VerifyC2Update(std::chrono::milliseconds waitTime)
-      : HTTPIntegrationBase(waitTime) {
+  explicit VerifyC2Update(const std::filesystem::path& test_file_location, const std::optional<std::filesystem::path>& home_path = {},
+    std::chrono::milliseconds waitTime = std::chrono::milliseconds(DEFAULT_WAITTIME_MSECS))
+      : HTTPIntegrationBase(test_file_location, home_path, waitTime) {
   }
 
   void testSetup() override {
@@ -171,8 +173,8 @@
 
 class VerifyC2FailedUpdate : public VerifyC2Update {
  public:
-  explicit VerifyC2FailedUpdate(std::chrono::milliseconds waitTime)
-      : VerifyC2Update(waitTime) {
+  explicit VerifyC2FailedUpdate(const std::filesystem::path& test_file_location, std::chrono::milliseconds waitTime)
+      : VerifyC2Update(test_file_location, {}, waitTime) {
   }
 
   void testSetup() override {
diff --git a/libminifi/test/libtest/integration/IntegrationBase.cpp b/libminifi/test/libtest/integration/IntegrationBase.cpp
index a7facb2..61a2ea2 100644
--- a/libminifi/test/libtest/integration/IntegrationBase.cpp
+++ b/libminifi/test/libtest/integration/IntegrationBase.cpp
@@ -18,6 +18,7 @@
 #include "IntegrationBase.h"
 
 #include <future>
+#include <filesystem>
 
 #include "utils/net/DNS.h"
 #include "utils/HTTPUtils.h"
@@ -28,9 +29,18 @@
 
 namespace org::apache::nifi::minifi::test {
 
-IntegrationBase::IntegrationBase(std::chrono::milliseconds waitTime)
+IntegrationBase::IntegrationBase(const std::optional<std::filesystem::path>& test_file_location, const std::optional<std::filesystem::path>& home_path, std::chrono::milliseconds waitTime)
     : configuration(std::make_shared<minifi::ConfigureImpl>()),
-      wait_time_(waitTime) {
+      wait_time_(waitTime),
+      home_path_(home_path) {
+  flow_config_path_.config_path = test_file_location;
+  if (test_file_location && std::filesystem::exists(*test_file_location) && std::filesystem::is_regular_file(*test_file_location)) {
+    // The original configuration file is changed, because after the flow configuration is parsed, the sensitive values are encrypted, and the new configuration is written back to the file
+    // We create a temporary copy of the flow configuration file for the test harness to use, so that multiple tests using the same flow configuration can run in parallel
+    flow_config_path_.temp_dir = std::make_unique<TempDirectory>();
+    flow_config_path_.config_path = flow_config_path_.temp_dir->getPath() / "config.yml";
+    std::filesystem::copy_file(*test_file_location, *flow_config_path_.config_path);
+  }
 }
 
 void IntegrationBase::configureSecurity() {
@@ -43,15 +53,15 @@
   }
 }
 
-void IntegrationBase::run(const std::optional<std::filesystem::path>& test_file_location, const std::optional<std::filesystem::path>& home_path) {
+void IntegrationBase::run() {
   using namespace std::literals::chrono_literals;
   testSetup();
 
   std::shared_ptr<core::Repository> test_repo = std::make_shared<TestThreadedRepository>();
   std::shared_ptr<core::Repository> test_flow_repo = std::make_shared<TestFlowRepository>();
 
-  if (test_file_location) {
-    configuration->set(minifi::Configure::nifi_flow_configuration_file, test_file_location->string());
+  if (flow_config_path_.config_path) {
+    configuration->set(minifi::Configure::nifi_flow_configuration_file, flow_config_path_.config_path->string());
   }
   configuration->set(minifi::Configure::nifi_state_storage_local_class_name, "VolatileMapStateStorage");
 
@@ -71,17 +81,17 @@
         | minifi::utils::andThen(minifi::utils::string::toBool)).value_or(false);
 
     std::shared_ptr<minifi::utils::file::FileSystem> filesystem;
-    if (home_path) {
+    if (home_path_) {
       filesystem = std::make_shared<minifi::utils::file::FileSystem>(
           should_encrypt_flow_config,
-          minifi::utils::crypto::EncryptionProvider::create(*home_path));
+          minifi::utils::crypto::EncryptionProvider::create(*home_path_));
     } else {
       filesystem = std::make_shared<minifi::utils::file::FileSystem>();
     }
 
     std::optional<minifi::utils::crypto::EncryptionProvider> sensitive_values_encryptor = [&]() {
-      if (home_path) {
-        return minifi::utils::crypto::EncryptionProvider::createSensitivePropertiesEncryptor(*home_path);
+      if (home_path_) {
+        return minifi::utils::crypto::EncryptionProvider::createSensitivePropertiesEncryptor(*home_path_);
       } else {
         auto encryption_key = minifi::utils::string::from_hex("e4bce4be67f417ed2530038626da57da7725ff8c0b519b692e4311e4d4fe8a28");
         return minifi::utils::crypto::EncryptionProvider{minifi::utils::crypto::XSalsa20Cipher{encryption_key}};
@@ -97,13 +107,16 @@
             .flow_file_repo = test_repo,
             .content_repo = content_repo,
             .configuration = configuration,
-            .path = test_file_location,
+            .path = flow_config_path_.config_path,
             .filesystem = filesystem,
             .sensitive_values_encryptor = sensitive_values_encryptor,
             .bulletin_store = bulletin_store_.get()
         }, nifi_configuration_class_name);
 
     auto controller_service_provider = flow_config->getControllerServiceProvider();
+    if (!state_dir.empty()) {
+      minifi::utils::file::delete_dir(state_dir);
+    }
     char state_dir_name_template[] = "/var/tmp/integrationstate.XXXXXX";  // NOLINT(cppcoreguidelines-avoid-c-arrays)
     state_dir = minifi::utils::file::create_temp_directory(state_dir_name_template);
     if (!configuration->get(minifi::Configure::nifi_state_storage_local_path)) {
diff --git a/libminifi/test/libtest/integration/IntegrationBase.h b/libminifi/test/libtest/integration/IntegrationBase.h
index 0028204..df7a296 100644
--- a/libminifi/test/libtest/integration/IntegrationBase.h
+++ b/libminifi/test/libtest/integration/IntegrationBase.h
@@ -31,6 +31,7 @@
 #include "utils/file/AssetManager.h"
 #include "utils/file/FileUtils.h"
 #include "core/BulletinStore.h"
+#include "unit/TestBase.h"
 
 namespace minifi = org::apache::nifi::minifi;
 namespace core = minifi::core;
@@ -38,19 +39,27 @@
 
 namespace org::apache::nifi::minifi::test {
 
+struct FlowConfigPath {
+  std::unique_ptr<TempDirectory> temp_dir;
+  std::optional<std::filesystem::path> config_path;
+};
+
 class IntegrationBase {
  public:
-  explicit IntegrationBase(std::chrono::milliseconds waitTime = std::chrono::milliseconds(DEFAULT_WAITTIME_MSECS));
+  explicit IntegrationBase(const std::optional<std::filesystem::path>& test_file_location = {}, const std::optional<std::filesystem::path>& home_path = {},
+      std::chrono::milliseconds waitTime = std::chrono::milliseconds(DEFAULT_WAITTIME_MSECS));
   IntegrationBase(const IntegrationBase&) = delete;
   IntegrationBase(IntegrationBase&& other) noexcept
-      :configuration{std::move(other.configuration)},
-      flowController_{std::move(other.flowController_)},
-      wait_time_{other.wait_time_},
-      port{std::move(other.port)},
-      scheme{std::move(other.scheme)},
-      key_dir{std::move(other.key_dir)},
-      state_dir{std::move(other.state_dir)},
-      restart_requested_count_{other.restart_requested_count_.load()}
+      : configuration{std::move(other.configuration)},
+        flowController_{std::move(other.flowController_)},
+        wait_time_{other.wait_time_},
+        port{std::move(other.port)},
+        scheme{std::move(other.scheme)},
+        key_dir{std::move(other.key_dir)},
+        state_dir{std::move(other.state_dir)},
+        restart_requested_count_{other.restart_requested_count_.load()},
+        home_path_{std::move(other.home_path_)},
+        flow_config_path_{std::move(other.flow_config_path_)}
   {}
   IntegrationBase& operator=(const IntegrationBase&) = delete;
   IntegrationBase& operator=(IntegrationBase&& other) noexcept {
@@ -63,11 +72,13 @@
     key_dir = std::move(other.key_dir);
     state_dir = std::move(other.state_dir);
     restart_requested_count_ = other.restart_requested_count_.load();
+    home_path_ = std::move(other.home_path_);
+    flow_config_path_ = std::move(other.flow_config_path_);
     return *this;
   }
   virtual ~IntegrationBase() = default;
 
-  virtual void run(const std::optional<std::filesystem::path>& test_file_location = {}, const std::optional<std::filesystem::path>& home_path = {});
+  virtual void run();
 
   void setKeyDir(const std::filesystem::path& key_dir) {
     this->key_dir = key_dir;
@@ -95,6 +106,10 @@
 
   virtual void runAssertions() = 0;
 
+  std::optional<std::filesystem::path> getFlowConfigPath() const {
+    return flow_config_path_.config_path;
+  }
+
  protected:
   virtual void configureC2() {
   }
@@ -119,6 +134,8 @@
   std::filesystem::path key_dir;
   std::filesystem::path state_dir;
   std::atomic<int> restart_requested_count_{0};
+  std::optional<std::filesystem::path> home_path_;
+  FlowConfigPath flow_config_path_;
 };
 
 std::string parseUrl(std::string url);