diff --git a/tests/integration/example.test.env.yaml b/tests/integration/example.test.env.yaml index 8ab67a9f..3f5c385e 100644 --- a/tests/integration/example.test.env.yaml +++ b/tests/integration/example.test.env.yaml @@ -17,3 +17,14 @@ testdata: module: testmodule # Branch which is going to be built for this test. branch: scratch-build-branch +failed_build: + module: testmodule + branch: failed-build-branch + # Batch considered by this test. + batch: 2 + # List of components expected to fail in the batch. + failing_components: + - comp1 + # List of components expected to complete or canceled in the batch. + canceled_components: + - comp2 diff --git a/tests/integration/test_failed_build.py b/tests/integration/test_failed_build.py new file mode 100644 index 00000000..61a6fa19 --- /dev/null +++ b/tests/integration/test_failed_build.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# SPDX-License-Identifier: MIT + +import utils + + +def test_failed_build(test_env, repo, koji): + """ + Run a scratch build with "rebuild_strategy=all". + + Check that: + * Check that the module build eventually fails + * Check that any other components in the same batch as the failed component are + cancelled, if not completed. + """ + build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"]) + build.run("--watch", "--scratch", "--optional", "rebuild_strategy=all") + + assert build.state_name == "failed" + batch = test_env["testdata"]["failed_build"]["batch"] + failing_components = test_env["testdata"]["failed_build"]["failing_components"] + canceled_components = test_env["testdata"]["failed_build"]["canceled_components"] + assert sorted(failing_components) == sorted(build.components(state="FAILED", batch=batch)) + assert sorted(canceled_components) == sorted( + build.components(state="COMPLETE", batch=batch) + + build.components(state="CANCELED", batch=batch) + ) diff --git a/tests/integration/test_scratch_build.py b/tests/integration/test_scratch_build.py index 24feb248..addc0ac0 100644 --- a/tests/integration/test_scratch_build.py +++ b/tests/integration/test_scratch_build.py @@ -18,7 +18,7 @@ def test_scratch_build(test_env, repo, koji): build.run("--watch", "--scratch", "--optional", "rebuild_strategy=all") assert build.state_name == "done" - assert sorted(build.components()) == sorted( + assert sorted(build.components(state="COMPLETE")) == sorted( repo.components + ["module-build-macros"] ) diff --git a/tests/integration/utils.py b/tests/integration/utils.py index d1d34526..efaaa27d 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -81,8 +81,9 @@ class Build: def __init__(self, packaging_utility, mbs_api): self._packaging_utility = Command(packaging_utility) self._mbs_api = mbs_api - self._url = None self._data = None + self._component_data = None + self._build_id = None def run(self, *args): """Run a module build @@ -92,35 +93,51 @@ class Build: :rtype: string """ stdout = self._packaging_utility("module-build", *args).stdout.decode("utf-8") - self._url = re.search(self._mbs_api + r"module-builds/\d+", stdout).group(0) - return self._url + self._build_id = re.search(self._mbs_api + r"module-builds/(\d+)", stdout).group(1) + return self._build_id @property def data(self): """Module build data cache for this build fetched from MBS""" - if self._data is None: - r = requests.get(self._url) + if self._data is None and self._build_id: + r = requests.get(f"{self._mbs_api}module-builds/{self._build_id}") r.raise_for_status() self._data = r.json() return self._data + @property + def component_data(self): + """Component data for the module build""" + if self._component_data is None and self._build_id: + params = { + "module_build": self._build_id, + "verbose": True, + } + r = requests.get(f"{self._mbs_api}component-builds/", params=params) + r.raise_for_status() + self._component_data = r.json() + return self._component_data + @property def state_name(self): """Name of the state of this module build""" return self.data["state_name"] - def components(self, state="COMPLETE"): - """Components of this module build which are in some state + def components(self, state="COMPLETE", batch=None): + """Components of this module build which are in some state and in some batch :param string state: Koji build state the components should be in - :return: List of components + :param int batch: the number of the batch the components should be in + :return: List of filtered components :rtype: list of strings """ - comps = [] - for rpm, info in self.data["tasks"]["rpms"].items(): - if info["state"] == koji.BUILD_STATES[state]: - comps.append(rpm) - return comps + filtered = self.component_data["items"] + if batch is not None: + filtered = filter(lambda x: x["batch"] == batch, filtered) + if state is not None: + filtered = filter(lambda x: x["state_name"] == state, filtered) + + return [item["package"] for item in filtered] def nvr(self, name_suffix=""): """NVR dictionary of this module build