diff --git a/dbuild/test.py b/dbuild/test.py index c3471ed..8d00283 100644 --- a/dbuild/test.py +++ b/dbuild/test.py @@ -1286,14 +1286,15 @@ def run(cfg: Config, args: argparse.Namespace) -> int: # Determine which backends to run if backend_arg == "all": backends = ["podman"] - if cfg.metadata.appjail and not _appjail_cit_applicable(cfg): + if cfg.metadata.appjail is None: + pass # appjail: false + elif not _appjail_cit_applicable(cfg): log.info("appjail backend skipped: this image declares sidecars (appjail.depends_on); " "the single-jail CIT can't run its stack") - elif cfg.metadata.appjail: - if AppJailBackend.available(): - backends.append("appjail") - else: - log.warn("appjail configured but not installed — skipping appjail backend") + elif AppJailBackend.available(): + backends.append("appjail") + else: + log.warn("appjail configured but not installed — skipping appjail backend") else: backends = [backend_arg] @@ -1381,6 +1382,7 @@ def _appjail_cit_applicable(cfg) -> bool: director bundle is what runs the whole stack on AppJail. """ aj = cfg.metadata.appjail - if not aj: + # {} is enabled (appjail: true, or no key); only None is appjail: false + if aj is None: return False return not (isinstance(aj, dict) and aj.get("depends_on")) diff --git a/tests/test_test.py b/tests/test_test.py index 4d0f1c9..c23adba 100644 --- a/tests/test_test.py +++ b/tests/test_test.py @@ -687,3 +687,57 @@ def test_diagnose_port_not_listening(self): if __name__ == "__main__": unittest.main() + + +class TestAppJailBackendSelection(unittest.TestCase): + """`dbuild test` runs the appjail backend for every image that has AppJail + on, including the default (no appjail key) and `appjail: true`, which + parse to {}.""" + + def _backends(self, meta): + from dbuild.config import _parse_appjail + cfg = MagicMock() + cfg.metadata.appjail = _parse_appjail(meta) + cfg.test.puid = False + cfg.variants = [MagicMock(tag="latest", puid=None)] + args = MagicMock(variant=None, json_output=None, backend="all", puid=None) + seen = [] + + def fake_test_variant(cfg, variant, test, **kw): + seen.append(kw["force_backend"]) + return 0 + + with patch("dbuild.ci.detect") as detect, \ + patch.object(cit.AppJailBackend, "available", return_value=True), \ + patch.object(cit, "_test_variant", side_effect=fake_test_variant): + detect.return_value.should_skip.return_value = False + self.assertEqual(cit.run(cfg, args), 0) + return seen + + def test_no_key_is_on(self): + self.assertEqual(self._backends({}), ["podman", "appjail"]) + + def test_true_is_on(self): + self.assertEqual(self._backends({"appjail": True}), ["podman", "appjail"]) + + def test_bare_is_on(self): + self.assertEqual(self._backends({"appjail": None}), ["podman", "appjail"]) + + def test_false_is_off(self): + self.assertEqual(self._backends({"appjail": False}), ["podman"]) + + def test_custom_config_is_on(self): + meta = {"appjail": {"director": {"options": []}}} + self.assertEqual(self._backends(meta), ["podman", "appjail"]) + + def test_sidecars_skip_appjail(self): + meta = {"appjail": {"depends_on": ["postgres"]}} + self.assertEqual(self._backends(meta), ["podman"]) + + def test_applicable_for_default(self): + from dbuild.config import _parse_appjail + cfg = MagicMock() + cfg.metadata.appjail = _parse_appjail({}) + self.assertTrue(cit._appjail_cit_applicable(cfg)) + cfg.metadata.appjail = _parse_appjail({"appjail": False}) + self.assertFalse(cit._appjail_cit_applicable(cfg))