Repository: opensearch-project/opensearch-build

Issue:
Add support for running bwctest.sh



Discussion:

Resolving patch:
diff --git a/bundle-workflow/src/paths/tree_walker.py b/bundle-workflow/src/paths/tree_walker.py
--- a/bundle-workflow/src/paths/tree_walker.py
+++ b/bundle-workflow/src/paths/tree_walker.py
@@ -8,7 +8,7 @@
 
 
 def walk(root):
-    print(f'Walking tree from {root}')
+    print(f"Walking tree from {root}")
     for dir, dirs, files in os.walk(root):
         for file_name in files:
             absolute_path = os.path.join(dir, file_name)
diff --git a/bundle-workflow/src/system/execute.py b/bundle-workflow/src/system/execute.py
--- a/bundle-workflow/src/system/execute.py
+++ b/bundle-workflow/src/system/execute.py
@@ -15,7 +15,9 @@ def execute(command, dir, capture=True, raise_on_failure=True):
     :returns a tuple containing the exit code, stdout, and stderr.
     """
     print(f'Executing "{command}" in {dir}')
-    result = subprocess.run(command, cwd=dir, shell=True, capture_output=capture, text=True)
+    result = subprocess.run(
+        command, cwd=dir, shell=True, capture_output=capture, text=True
+    )
     if raise_on_failure:
         result.check_returncode()
     return (result.returncode, result.stdout, result.stderr)


Test patch:
diff --git a/bundle-workflow/src/test.py b/bundle-workflow/src/test.py
--- a/bundle-workflow/src/test.py
+++ b/bundle-workflow/src/test.py
@@ -6,49 +6,50 @@
 # this file be licensed under the Apache-2.0 license or a
 # compatible open source license.
 
-import argparse
 import os
 
-from git.git_repository import GitRepository
 from manifests.bundle_manifest import BundleManifest
 from system.temporary_directory import TemporaryDirectory
+from test_workflow.bwc_test_suite import BwcTestSuite
 from test_workflow.integ_test_suite import IntegTestSuite
 from test_workflow.local_test_cluster import LocalTestCluster
+from test_workflow.test_args import TestArgs
+from test_workflow.test_component import TestComponent
 from test_workflow.test_recorder import TestRecorder
 
-parser = argparse.ArgumentParser(description="Test an OpenSearch Bundle")
-parser.add_argument("manifest", type=argparse.FileType("r"), help="Manifest file.")
-parser.add_argument(
-    "--keep",
-    dest="keep",
-    action="store_true",
-    help="Do not delete the working temporary directory.",
-)
-args = parser.parse_args()
-
+args = TestArgs()
 manifest = BundleManifest.from_file(args.manifest)
-test_recorder = TestRecorder(os.path.dirname(args.manifest.name))
+test_recorder = TestRecorder(os.path.dirname(manifest.name))
+
 
-with TemporaryDirectory(keep=args.keep) as work_dir:
-    os.chdir(work_dir)
+def integ_test_suite():
+    with TemporaryDirectory(keep=args.keep) as work_dir:
+        os.chdir(work_dir)
 
-    # Spin up a test cluster with security
-    # TODO: Refactor this into a TestRun class so we can run with-security and without-security without cut-and-pasting a bunch of code
-    cluster = LocalTestCluster(work_dir, manifest, True)
-    try:
+        # Spin up a test cluster
+        cluster = LocalTestCluster(manifest)
         cluster.create()
 
         # For each component, check out the git repo and run `integtest.sh`
-        for component in manifest.components:
-            print(f'Testing {component.name}')
-            repo = GitRepository(
-                component.repository,
-                component.commit_id,
-                os.path.join(work_dir, component.name),
-            )
-            test_suite = IntegTestSuite(component.name, repo, test_recorder)
-            test_suite.execute(cluster, True)
-    finally:
-        cluster.destroy(test_recorder)
-
-    # TODO: Store test results, send notification.
+        try:
+            for component in manifest.components:
+                print(component.name)
+                test_component = TestComponent(
+                    component.repository, component.commit_id
+                )
+                repo = test_component.checkout(os.path.join(work_dir, component.name))
+                test_suite = IntegTestSuite(component.name, repo)
+                test_suite.execute(cluster)
+        finally:
+            cluster.destroy()
+
+        # TODO: Store test results, send notification.
+
+
+def bwc_test_suite():
+    test_suite = BwcTestSuite(manifest, args.component, args.keep)
+    test_suite.execute()
+
+
+integ_test_suite()
+bwc_test_suite()
diff --git a/bundle-workflow/src/test_workflow/bwc_test_suite.py b/bundle-workflow/src/test_workflow/bwc_test_suite.py
new file mode 100644
--- /dev/null
+++ b/bundle-workflow/src/test_workflow/bwc_test_suite.py
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: Apache-2.0
+#
+# The OpenSearch Contributors require contributions made to
+# this file be licensed under the Apache-2.0 license or a
+# compatible open source license.
+#
+# Modifications Copyright OpenSearch Contributors. See
+# GitHub history for details.
+
+import os
+import subprocess
+
+from system.temporary_directory import TemporaryDirectory
+from test_workflow.test_component import TestComponent
+
+
+class BwcTestSuite:
+    manifest: str
+    component: str
+    keep: bool
+
+    def __init__(self, manifest, component, keep):
+        self.manifest = manifest
+        self.component = component
+        self.keep = keep
+
+    def run_tests(self, work_dir):
+        bwc_script = "bwctest.sh"
+        run_bwctests = f"./{bwc_script}"
+        output = subprocess.check_output(run_bwctests, cwd=work_dir, shell=True)
+        return output
+
+    def component_bwc_tests(self, component, work_dir):
+        test_component = TestComponent(component.repository, component.commit_id)
+        test_component.checkout(os.path.join(work_dir, component.name))
+        try:
+            console_output = self.run_tests(work_dir + "/" + component.name)
+            return console_output
+        except:
+            # TODO: Store and report test failures for {component}
+            print(f"Exception while running BWC tests for {component.name}")
+
+    def execute(self):
+        # TODO copy all maven dependencies from S3 to local
+        with TemporaryDirectory(keep=self.keep) as work_dir:
+            os.chdir(work_dir)
+            # For each component, check out the git repo and run `bwctest.sh`
+            for component in self.manifest.components:
+                if self.component is None or self.component == component.name:
+                    # TODO: Store and report test results, send notification via {console_output}
+                    self.component_bwc_tests(component, work_dir)
diff --git a/bundle-workflow/src/test_workflow/integ_test_suite.py b/bundle-workflow/src/test_workflow/integ_test_suite.py
--- a/bundle-workflow/src/test_workflow/integ_test_suite.py
+++ b/bundle-workflow/src/test_workflow/integ_test_suite.py
@@ -19,15 +19,14 @@ def __init__(self, name, repo, test_recorder):
 
     def execute(self, cluster, security):
         script = ScriptFinder.find_integ_test_script(self.name, self.repo.dir)
-        if (os.path.exists(script)):
-            cmd = f'sh {script} -b {cluster.endpoint()} -p {cluster.port()} -s {str(security).lower()}'
+        if os.path.exists(script):
+            cmd = f"sh {script} -b {cluster.endpoint()} -p {cluster.port()} -s {str(security).lower()}"
             (status, stdout, stderr) = execute(cmd, self.repo.dir, True, False)
-            results_dir = os.path.join(self.repo.dir,
-                                       'integ-test',
-                                       'build',
-                                       'reports',
-                                       'tests',
-                                       'integTest')
-            self.test_recorder.record_integ_test_outcome(self.name, status, stdout, stderr, walk(results_dir))
+            results_dir = os.path.join(
+                self.repo.dir, "integ-test", "build", "reports", "tests", "integTest"
+            )
+            self.test_recorder.record_integ_test_outcome(
+                self.name, status, stdout, stderr, walk(results_dir)
+            )
         else:
             print(f"{script} does not exist. Skipping integ tests for {self.name}")
diff --git a/bundle-workflow/src/test_workflow/local_test_cluster.py b/bundle-workflow/src/test_workflow/local_test_cluster.py
--- a/bundle-workflow/src/test_workflow/local_test_cluster.py
+++ b/bundle-workflow/src/test_workflow/local_test_cluster.py
@@ -35,8 +35,13 @@ def create(self):
         self.install_dir = f"opensearch-{self.manifest.build.version}"
         if not self.security_enabled:
             self.disable_security(self.install_dir)
-        self.process = subprocess.Popen("./opensearch-tar-install.sh", cwd=self.install_dir, shell=True,
-                                        stdout=self.stdout, stderr=self.stderr)
+        self.process = subprocess.Popen(
+            "./opensearch-tar-install.sh",
+            cwd=self.install_dir,
+            shell=True,
+            stdout=self.stdout,
+            stderr=self.stderr,
+        )
         print(f"Started OpenSearch with PID {self.process.pid}")
         self.wait_for_service()
 
@@ -51,9 +56,15 @@ def destroy(self, test_recorder):
             print("Local test cluster is not started")
             return
         self.terminate_process()
-        test_recorder.record_cluster_logs(itertools.chain(
-            [(os.path.realpath(self.stdout.name), "stdout"), (os.path.realpath(self.stderr.name), "stderr")],
-            walk(os.path.join(self.install_dir, "logs"))))
+        test_recorder.record_cluster_logs(
+            itertools.chain(
+                [
+                    (os.path.realpath(self.stdout.name), "stdout"),
+                    (os.path.realpath(self.stderr.name), "stderr"),
+                ],
+                walk(os.path.join(self.install_dir, "logs")),
+            )
+        )
 
     def url(self, path=""):
         return f'{"https" if self.security_enabled else "http"}://{self.endpoint()}:{self.port()}{path}'
@@ -71,7 +82,9 @@ def download(self):
 
     def disable_security(self, dir):
         subprocess.check_call(
-            f'echo "plugins.security.disabled: true" >> {os.path.join(dir, "config", "opensearch.yml")}', shell=True)
+            f'echo "plugins.security.disabled: true" >> {os.path.join(dir, "config", "opensearch.yml")}',
+            shell=True,
+        )
 
     def wait_for_service(self):
         print("Waiting for service to become available")
diff --git a/bundle-workflow/src/test_workflow/test_args.py b/bundle-workflow/src/test_workflow/test_args.py
new file mode 100644
--- /dev/null
+++ b/bundle-workflow/src/test_workflow/test_args.py
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: Apache-2.0
+#
+# The OpenSearch Contributors require contributions made to
+# this file be licensed under the Apache-2.0 license or a
+# compatible open source license.
+#
+# Modifications Copyright OpenSearch Contributors. See
+# GitHub history for details.
+
+import argparse
+
+
+class TestArgs:
+    manifest: str
+    component: str
+    keep: bool
+
+    def __init__(self):
+        parser = argparse.ArgumentParser(description="Test an OpenSearch Bundle")
+        parser.add_argument(
+            "manifest", type=argparse.FileType("r"), help="Manifest file."
+        )
+        parser.add_argument("--component", help="Test a specific component")
+        parser.add_argument(
+            "--keep",
+            dest="keep",
+            action="store_true",
+            help="Do not delete the working temporary directory.",
+        )
+        args = parser.parse_args()
+        self.manifest = args.manifest
+        self.component = args.component
+        self.keep = args.keep
diff --git a/bundle-workflow/src/test_workflow/test_component.py b/bundle-workflow/src/test_workflow/test_component.py
new file mode 100644
--- /dev/null
+++ b/bundle-workflow/src/test_workflow/test_component.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# SPDX-License-Identifier: Apache-2.0
+#
+# The OpenSearch Contributors require contributions made to
+# this file be licensed under the Apache-2.0 license or a
+# compatible open source license.
+
+from git.git_repository import GitRepository
+
+
+class TestComponent:
+    repository: str
+    commit_id: str
+
+    def __init__(self, respository, commit_id):
+        self.repository = respository
+        self.commit_id = commit_id
+
+    def checkout(self, directory):
+        return GitRepository(self.repository, self.commit_id, directory)
diff --git a/bundle-workflow/src/test_workflow/test_recorder.py b/bundle-workflow/src/test_workflow/test_recorder.py
--- a/bundle-workflow/src/test_workflow/test_recorder.py
+++ b/bundle-workflow/src/test_workflow/test_recorder.py
@@ -4,19 +4,22 @@
 # this file be licensed under the Apache-2.0 license or a
 # compatible open source license.
 
+
 class TestRecorder:
     def __init__(self, location):
         self.location = location
-        print(f'TestRecorder storing results in {location}')
+        print(f"TestRecorder storing results in {location}")
 
     def record_cluster_logs(self, log_files):
         """
         Record the test cluster logs.
         :param results: A generator that yields tuples containing test cluster log files, in the form (absolute_path, relative_path)
         """
-        print(f'Recording log files: {list(log_files)}')
+        print(f"Recording log files: {list(log_files)}")
 
-    def record_integ_test_outcome(self, component_name, exit_status, stdout, stderr, results):
+    def record_integ_test_outcome(
+        self, component_name, exit_status, stdout, stderr, results
+    ):
         """
         Record the outcome of a integration test run.
         :param component_name: The name of the component that ran tests.
@@ -26,4 +29,6 @@ def record_integ_test_outcome(self, component_name, exit_status, stdout, stderr,
         :param stderr: A string containing the stderr stream from the test process.
         :param results: A generator that yields tuples containing test results files, in the form (absolute_path, relative_path).
         """
-        print(f'Recording test results for {component_name}. Exit status: {exit_status}, stdout: {stdout}, stderr: {stderr}, results files: {results}')
+        print(
+            f"Recording test results for {component_name}. Exit status: {exit_status}, stdout: {stdout}, stderr: {stderr}, results files: {results}"
+        )
diff --git a/bundle-workflow/tests/test_bwc_workflow/__init__.py b/bundle-workflow/tests/test_bwc_workflow/__init__.py
new file mode 100644
--- /dev/null
+++ b/bundle-workflow/tests/test_bwc_workflow/__init__.py
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: Apache-2.0
+#
+# The OpenSearch Contributors require contributions made to
+# this file be licensed under the Apache-2.0 license or a
+# compatible open source license.
+
+import os
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../src"))
diff --git a/bundle-workflow/tests/test_bwc_workflow/data/test_manifest.yaml b/bundle-workflow/tests/test_bwc_workflow/data/test_manifest.yaml
new file mode 100644
--- /dev/null
+++ b/bundle-workflow/tests/test_bwc_workflow/data/test_manifest.yaml
@@ -0,0 +1,63 @@
+build:
+  architecture: x64
+  id: 41d5ae25183d4e699e92debfbe3f83bd
+  location: https://artifacts.opensearch.org/bundles/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/opensearch-1.0.0-linux-x64.tar.gz
+  name: OpenSearch
+  version: 1.0.0
+components:
+  - commit_id: fb25458f38c30a7ab06de21b0068f1fe3ad56134
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/bundle/opensearch-min-1.0.0-linux-x64.tar.gz
+    name: OpenSearch
+    ref: 1.0
+    repository: https://github.com/saratvemulapalli/OpenSearch.git
+  - commit_id: 7fad9529358259de529763c1c923fd947817a3bd
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-job-scheduler-1.0.0.0.zip
+    name: job-scheduler
+    ref: 1.0.0.0
+    repository: https://github.com/opensearch-project/job-scheduler.git
+  - commit_id: 65bb94fb7d46a88b07b61622585ed701918b19c5
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-sql-1.0.0.0.zip
+    name: sql
+    ref: 1.0.0.0
+    repository: https://github.com/opensearch-project/sql.git
+  - commit_id: a14ccd49389ca41446acc3200e3e870cde15a68e
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-alerting-1.0.0.0.zip
+    name: alerting
+    ref: 1.0.0.0
+    repository: https://github.com/opensearch-project/alerting.git
+  - commit_id: 2e21d59749526baa8e4666168643c4594cdadf79
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-security-1.0.0.0.zip
+    name: security
+    ref: 1.0.0.0
+    repository: https://github.com/opensearch-project/security.git
+  - commit_id: 091fe9f6612cd7e85054918036587bcd3c67eab1
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-index-management-1.0.0.0.zip
+    name: index-management
+    ref: 1.0.0.0
+    repository: https://github.com/opensearch-project/index-management.git
+  - commit_id: 9b29a99b05f2c8cd9d54dc868994cab8460ff0db
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-knn-1.0.0.0.zip
+    name: k-NN
+    ref: 1.0.0.0
+    repository: https://github.com/opensearch-project/k-NN.git
+  - commit_id: 502c96e54fae1cec9fee1fafd77ad92fde9d2459
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-anomaly-detection-1.0.0.0.zip
+    name: anomaly-detection
+    ref: 1.0
+    repository: https://github.com/opensearch-project/anomaly-detection.git
+  - commit_id: bd31e80adf6d52c1b4662d0d2cc9b30d8ae14309
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-asynchronous-search-1.0.0.0.zip
+    name: asynchronous-search
+    ref: main
+    repository: https://github.com/opensearch-project/asynchronous-search.git
+  - commit_id: 72705e2dfcad760c5de7609891700aa11d767884
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-reports-scheduler-1.0.0.0.zip
+    name: dashboards-reports
+    ref: 1.0.0.0
+    repository: https://github.com/opensearch-project/dashboards-reports.git
+  - commit_id: fd745a77c19df4991254b495cf0ec3730c66534d
+    location: https://artifacts.opensearch.org/builds/1.0.0/41d5ae25183d4e699e92debfbe3f83bd/plugins/opensearch-notebooks-1.0.0.0.zip
+    name: dashboards-notebooks
+    ref: 1.0.0.0
+    repository: https://github.com/opensearch-project/dashboards-notebooks.git
+schema-version: '1.0'
\ No newline at end of file
diff --git a/bundle-workflow/tests/test_bwc_workflow/test_bwc_suite.py b/bundle-workflow/tests/test_bwc_workflow/test_bwc_suite.py
new file mode 100644
--- /dev/null
+++ b/bundle-workflow/tests/test_bwc_workflow/test_bwc_suite.py
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: Apache-2.0
+#
+# The OpenSearch Contributors require contributions made to
+# this file be licensed under the Apache-2.0 license or a
+# compatible open source license.
+
+import os
+import subprocess
+import unittest
+from unittest.mock import ANY, MagicMock, call, patch
+
+from manifests.bundle_manifest import BundleManifest
+from test_workflow.bwc_test_suite import BwcTestSuite
+
+
+class TestBwcSuite(unittest.TestCase):
+    def setUp(self):
+        os.chdir(os.path.dirname(__file__))
+        manifest_file_handle = open("data/test_manifest.yaml", "r")
+        self.manifest = BundleManifest.from_file(manifest_file_handle)
+        self.bwc_test_suite = BwcTestSuite(
+            manifest=self.manifest, component=None, keep=False
+        )
+        manifest_file_handle.close()
+
+    def test_execute(self):
+        expected = []
+        for component in self.manifest.components:
+            expected.append(call(component, ANY))
+        self.bwc_test_suite.component_bwc_tests = MagicMock()
+        self.bwc_test_suite.execute()
+        self.assertEqual(
+            self.bwc_test_suite.component_bwc_tests.call_args_list, expected
+        )
+
+    def test_run_bwctest(self):
+        with self.assertRaises(subprocess.CalledProcessError) as process_ret:
+            self.bwc_test_suite.run_tests(".")
+        # 127 ret code == could not find the script to run the command
+        self.assertEqual(process_ret.exception.returncode, 127)
+        self.assertEqual(process_ret.exception.cmd, "./bwctest.sh")
+
+    @patch("test_workflow.bwc_test_suite.TestComponent")
+    def test_component_bwctest(self, test_component_mock):
+        component = self.manifest.components[1]
+        self.bwc_test_suite.run_tests = MagicMock()
+        expected = [call("./" + self.manifest.components[1].name)]
+
+        self.bwc_test_suite.component_bwc_tests(component, ".")
+        test_component_mock.return_value.checkout.assert_called_with(
+            "./" + component.name
+        )
+        self.assertEqual(self.bwc_test_suite.run_tests.call_args_list, expected)
diff --git a/bundle-workflow/tests/tests_build_workflow/test_builder.py b/bundle-workflow/tests/tests_build_workflow/test_builder.py
--- a/bundle-workflow/tests/tests_build_workflow/test_builder.py
+++ b/bundle-workflow/tests/tests_build_workflow/test_builder.py
@@ -30,9 +30,7 @@ def test_build(self):
             " ".join(
                 [
                     os.path.realpath(
-                        os.path.join(
-                            ScriptFinder.default_scripts_path, "build.sh"
-                        )
+                        os.path.join(ScriptFinder.default_scripts_path, "build.sh")
                     ),
                     "-v 1.0.0",
                     "-a x64",
@@ -51,9 +49,7 @@ def test_build_snapshot(self):
             " ".join(
                 [
                     os.path.realpath(
-                        os.path.join(
-                            ScriptFinder.default_scripts_path, "build.sh"
-                        )
+                        os.path.join(ScriptFinder.default_scripts_path, "build.sh")
                     ),
                     "-v 1.0.0",
                     "-a x64",
diff --git a/bundle-workflow/tests/tests_paths/test_script_finder.py b/bundle-workflow/tests/tests_paths/test_script_finder.py
--- a/bundle-workflow/tests/tests_paths/test_script_finder.py
+++ b/bundle-workflow/tests/tests_paths/test_script_finder.py
@@ -32,17 +32,13 @@ def setUp(self):
     def test_find_build_script_default(self):
         self.assertEqual(
             os.path.join(ScriptFinder.default_scripts_path, "build.sh"),
-            ScriptFinder.find_build_script(
-                "invalid", self.component_without_scripts
-            ),
+            ScriptFinder.find_build_script("invalid", self.component_without_scripts),
             msg="A component without an override resolves to a default.",
         )
 
     def test_find_build_script_component_override(self):
         self.assertEqual(
-            os.path.join(
-                ScriptFinder.component_scripts_path, "OpenSearch/build.sh"
-            ),
+            os.path.join(ScriptFinder.component_scripts_path, "OpenSearch/build.sh"),
             ScriptFinder.find_build_script(
                 "OpenSearch", self.component_without_scripts
             ),
@@ -52,9 +48,7 @@ def test_find_build_script_component_override(self):
     def test_find_build_script_component_script(self):
         self.assertEqual(
             os.path.join(self.component_with_scripts, "build.sh"),
-            ScriptFinder.find_build_script(
-                "OpenSearch", self.component_with_scripts
-            ),
+            ScriptFinder.find_build_script("OpenSearch", self.component_with_scripts),
             msg="A component with a script resolves to the script at the root.",
         )
 
@@ -69,7 +63,10 @@ def test_find_build_script_component_script_in_folder(self):
 
     @patch("os.path.exists", return_value=False)
     def test_find_build_script_does_not_exist(self, *mocks):
-        with self.assertRaisesRegex(ScriptFinder.ScriptNotFoundError, "Could not find build.sh script. Looked in .*"):
+        with self.assertRaisesRegex(
+            ScriptFinder.ScriptNotFoundError,
+            "Could not find build.sh script. Looked in .*",
+        ):
             ScriptFinder.find_build_script("anything", self.component_without_scripts)
 
     # find_integ_test_script
@@ -114,8 +111,13 @@ def test_find_integ_test_script_component_script_in_folder(self):
 
     @patch("os.path.exists", return_value=False)
     def test_find_integ_test_script_does_not_exist(self, *mocks):
-        with self.assertRaisesRegex(ScriptFinder.ScriptNotFoundError, "Could not find integtest.sh script. Looked in .*"):
-            ScriptFinder.find_integ_test_script("anything", self.component_without_scripts)
+        with self.assertRaisesRegex(
+            ScriptFinder.ScriptNotFoundError,
+            "Could not find integtest.sh script. Looked in .*",
+        ):
+            ScriptFinder.find_integ_test_script(
+                "anything", self.component_without_scripts
+            )
 
     # find_install_script
 
@@ -135,5 +137,8 @@ def test_find_install_script_component_override(self):
 
     @patch("os.path.exists", return_value=False)
     def test_find_install_script_does_not_exist(self, *mocks):
-        with self.assertRaisesRegex(ScriptFinder.ScriptNotFoundError, "Could not find install.sh script. Looked in .*"):
+        with self.assertRaisesRegex(
+            ScriptFinder.ScriptNotFoundError,
+            "Could not find install.sh script. Looked in .*",
+        ):
             ScriptFinder.find_install_script("anything")