diff --git a/stages/test/conftest.py b/stages/test/conftest.py index 4f1bcfef..f2f9d5a7 100644 --- a/stages/test/conftest.py +++ b/stages/test/conftest.py @@ -4,6 +4,7 @@ from types import ModuleType import pytest +import osbuild.meta from osbuild.testutil.imports import import_module_from_path @@ -19,3 +20,26 @@ def stage_module(request: pytest.FixtureRequest) -> ModuleType: caller_dir = pathlib.Path(request.node.fspath).parent module_path = caller_dir.parent / stage_name return import_module_from_path("stage", os.fspath(module_path)) + + +@pytest.fixture +def stage_schema(request: pytest.FixtureRequest) -> osbuild.meta.Schema: + """stage_schema is a fixture returns the schema for a stage module. + + This fixture may be indirectly parametrized with the stage schema version. + If the schema version is not specified, the version "2" is assumed. + The stage name must be defined in STAGE_NAME in the test module. + """ + if hasattr(request, "param") and not isinstance(request.param, str): + raise ValueError( + "stage_schema fixture may be indirectly parametrized only with the stage schema version string") + + if not hasattr(request.module, "STAGE_NAME"): + raise ValueError("stage_schema fixture must be used in a test module that defines STAGE_NAME") + + stage_name = request.module.STAGE_NAME + schema_version = request.param if hasattr(request, "param") else "2" + caller_dir = pathlib.Path(request.node.fspath).parent + root = caller_dir.parent.parent + mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", stage_name) + return osbuild.meta.Schema(mod_info.get_schema(version=schema_version), stage_name) diff --git a/stages/test/test_autotailor.py b/stages/test/test_autotailor.py index 5a99ea0a..3fdd07c0 100644 --- a/stages/test/test_autotailor.py +++ b/stages/test/test_autotailor.py @@ -1,12 +1,10 @@ #!/usr/bin/python3 -import os.path import sys from unittest.mock import call, patch import pytest -import osbuild.meta from osbuild import testutil TEST_INPUT = [ @@ -53,16 +51,6 @@ def fake_input_fixture(): } -def schema_validate_stage_oscap_autotailor(fake_input, test_data): - version = "1" - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version=version), STAGE_NAME) - test_input = fake_input - test_input["options"]["config"].update(test_data) - return schema.validate(test_input) - - @pytest.mark.parametrize("test_overrides,expected", TEST_INPUT) @patch("subprocess.run") def test_oscap_autotailor_overrides_smoke(mock_subprocess_run, fake_input, stage_module, test_overrides, expected): @@ -107,8 +95,10 @@ def test_oscap_autotailor_overrides_smoke(mock_subprocess_run, fake_input, stage }, " is not of type 'string', 'integer'"), ], ) -def test_schema_validation_oscap_autotailor(fake_input, test_data, expected_err): - res = schema_validate_stage_oscap_autotailor(fake_input, test_data) +@pytest.mark.parametrize("stage_schema", ["1"], indirect=True) +def test_schema_validation_oscap_autotailor(fake_input, stage_schema, test_data, expected_err): + fake_input["options"]["config"].update(test_data) + res = stage_schema.validate(fake_input) assert res.valid is False testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1) diff --git a/stages/test/test_bootupd.py b/stages/test/test_bootupd.py index 68d7b241..06cc485b 100644 --- a/stages/test/test_bootupd.py +++ b/stages/test/test_bootupd.py @@ -1,11 +1,9 @@ #!/usr/bin/python3 -import os.path from unittest.mock import call, patch import pytest -import osbuild.meta from osbuild import testutil STAGE_NAME = "org.osbuild.bootupd" @@ -38,18 +36,14 @@ STAGE_NAME = "org.osbuild.bootupd" }, "") ]) -def test_bootupd_schema_validation(test_data, expected_err): - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version="2"), STAGE_NAME) - +def test_bootupd_schema_validation(stage_schema, test_data, expected_err): test_input = { "type": STAGE_NAME, "options": { } } test_input["options"].update(test_data) - res = schema.validate(test_input) + res = stage_schema.validate(test_input) if expected_err == "": assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}" diff --git a/stages/test/test_containers_storage_conf.py b/stages/test/test_containers_storage_conf.py index 787294ed..2aed0a5e 100644 --- a/stages/test/test_containers_storage_conf.py +++ b/stages/test/test_containers_storage_conf.py @@ -10,7 +10,6 @@ try: except ModuleNotFoundError: import pytoml as toml -import osbuild.meta from osbuild import testutil TEST_INPUT = [ @@ -95,12 +94,7 @@ def test_containers_storage_conf_integration(tmp_path, stage_module, test_filena ({}, {"options": {"pull_options": {"use_hard_links": True}}}, "is not of type 'string'"), ], ) -def test_schema_validation_containers_storage_conf(test_data, storage_test_data, expected_err): - version = "2" - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version=version), STAGE_NAME) - +def test_schema_validation_containers_storage_conf(stage_schema, test_data, storage_test_data, expected_err): test_input = { "type": STAGE_NAME, "options": { @@ -113,7 +107,7 @@ def test_schema_validation_containers_storage_conf(test_data, storage_test_data, test_input["options"].update(test_data) test_input["options"]["config"]["storage"].update(storage_test_data) - res = schema.validate(test_input) + res = stage_schema.validate(test_input) if expected_err == "": assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}" diff --git a/stages/test/test_erofs.py b/stages/test/test_erofs.py index d70173dc..3b41519f 100644 --- a/stages/test/test_erofs.py +++ b/stages/test/test_erofs.py @@ -6,7 +6,6 @@ from unittest import mock import pytest -import osbuild.meta from osbuild import testutil from osbuild.testutil import has_executable, make_fake_input_tree @@ -89,11 +88,7 @@ def test_erofs(mock_run, tmp_path, stage_module, test_options, expected): # good ({"compression": {"method": "lz4"}}, ""), ]) -def test_schema_validation_erofs(test_data, expected_err): - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version="2"), STAGE_NAME) - +def test_schema_validation_erofs(stage_schema, test_data, expected_err): test_input = { "type": STAGE_NAME, "options": { @@ -101,7 +96,7 @@ def test_schema_validation_erofs(test_data, expected_err): } } test_input["options"].update(test_data) - res = schema.validate(test_input) + res = stage_schema.validate(test_input) if expected_err == "": assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}" diff --git a/stages/test/test_kickstart.py b/stages/test/test_kickstart.py index 0a515ff3..d4aab3a8 100644 --- a/stages/test/test_kickstart.py +++ b/stages/test/test_kickstart.py @@ -5,7 +5,6 @@ import subprocess import pytest -import osbuild.meta from osbuild import testutil from osbuild.testutil import has_executable @@ -218,25 +217,18 @@ TEST_INPUT = [ STAGE_NAME = "org.osbuild.kickstart" -def schema_validate_kickstart_stage(test_data): - version = "1" - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version=version), STAGE_NAME) - test_input = { +@pytest.mark.parametrize("stage_schema", ["1"], indirect=True) +@pytest.mark.parametrize("test_input,expected", TEST_INPUT) +def test_kickstart_test_cases_valid(stage_schema, test_input, expected): # pylint: disable=unused-argument + """ ensure all test inputs are valid """ + test_data = { "name": STAGE_NAME, "options": { "path": "some-path", } } - test_input["options"].update(test_data) - return schema.validate(test_input) - - -@pytest.mark.parametrize("test_input,expected", TEST_INPUT) -def test_kickstart_test_cases_valid(test_input, expected): # pylint: disable=unused-argument - """ ensure all test inputs are valid """ - res = schema_validate_kickstart_stage(test_input) + test_data["options"].update(test_input) + res = stage_schema.validate(test_data) assert res.valid is True, f"input: {test_input}\nerr: {[e.as_dict() for e in res.errors]}" @@ -270,7 +262,7 @@ def test_kickstart_valid(tmp_path, stage_module, test_input, expected): # pylin @pytest.mark.parametrize( - "test_data,expected_err", + "test_input,expected_err", [ # BAD pattern, ensure some obvious ways to write arbitrary # kickstart files will not work @@ -357,8 +349,16 @@ def test_kickstart_valid(tmp_path, stage_module, test_input, expected): # pylin ], ) -def test_schema_validation_bad_apples(test_data, expected_err): - res = schema_validate_kickstart_stage(test_data) +@pytest.mark.parametrize("stage_schema", ["1"], indirect=True) +def test_schema_validation_bad_apples(stage_schema, test_input, expected_err): + test_data = { + "name": STAGE_NAME, + "options": { + "path": "some-path", + } + } + test_data["options"].update(test_input) + res = stage_schema.validate(test_data) assert res.valid is False testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1) diff --git a/stages/test/test_machine-id.py b/stages/test/test_machine-id.py index 1e164b78..741cfb49 100644 --- a/stages/test/test_machine-id.py +++ b/stages/test/test_machine-id.py @@ -1,12 +1,10 @@ #!/usr/bin/python3 import os -import pathlib import unittest.mock import pytest -import osbuild.meta from osbuild import testutil STAGE_NAME = "org.osbuild.machine-id" @@ -59,17 +57,14 @@ def test_machine_id_first_boot_preserve( @pytest.mark.parametrize("test_data,expected_err", [ ({"first-boot": "invalid-option"}, "'invalid-option' is not one of "), ]) -def test_machine_id_schema_validation(test_data, expected_err): - root = pathlib.Path(__file__).parents[2] - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(), STAGE_NAME) - +@pytest.mark.parametrize("stage_schema", ["1"], indirect=True) +def test_machine_id_schema_validation(stage_schema, test_data, expected_err): test_input = { "name": STAGE_NAME, "options": {}, } test_input["options"].update(test_data) - res = schema.validate(test_input) + res = stage_schema.validate(test_input) assert res.valid is False testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1) diff --git a/stages/test/test_mkfs_ext4.py b/stages/test/test_mkfs_ext4.py index 7cdd74cd..e83666da 100644 --- a/stages/test/test_mkfs_ext4.py +++ b/stages/test/test_mkfs_ext4.py @@ -7,7 +7,6 @@ from unittest import mock import pytest -import osbuild.meta from osbuild import testutil from osbuild.testutil import has_executable @@ -27,11 +26,7 @@ STAGE_NAME = "org.osbuild.mkfs.ext4" # uuid but our schema is not strict enough right now ({"uuid": "some-uuid"}, ""), ]) -def test_schema_validation_mkfs_ext4(test_data, expected_err): - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version="2"), STAGE_NAME) - +def test_schema_validation_mkfs_ext4(stage_schema, test_data, expected_err): test_input = { "type": STAGE_NAME, "devices": { @@ -43,7 +38,7 @@ def test_schema_validation_mkfs_ext4(test_data, expected_err): } } test_input["options"].update(test_data) - res = schema.validate(test_input) + res = stage_schema.validate(test_input) if expected_err == "": assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}" diff --git a/stages/test/test_ostree_post_copy.py b/stages/test/test_ostree_post_copy.py index 836bc7fc..8ad70607 100644 --- a/stages/test/test_ostree_post_copy.py +++ b/stages/test/test_ostree_post_copy.py @@ -1,31 +1,14 @@ #!/usr/bin/python3 -import os.path from unittest.mock import call, patch import pytest -import osbuild.meta from osbuild import testutil STAGE_NAME = "org.osbuild.ostree.post-copy" -def schema_validate_stage_ostree_post_copy(test_data): - version = "2" - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version=version), STAGE_NAME) - test_input = { - "type": STAGE_NAME, - "options": { - "sysroot": "/some/sysroot", - }, - } - test_input.update(test_data) - return schema.validate(test_input) - - @patch("osbuild.util.ostree.cli") def test_ostree_post_copy_smoke(mock_ostree_cli, stage_module): paths = { @@ -50,8 +33,15 @@ def test_ostree_post_copy_smoke(mock_ostree_cli, stage_module): ({"mounts": "must-be-array"}, " is not of type 'array'"), ], ) -def test_schema_validation_ostree_post_copy(test_data, expected_err): - res = schema_validate_stage_ostree_post_copy(test_data) +def test_schema_validation_ostree_post_copy(stage_schema, test_data, expected_err): + test_input = { + "type": STAGE_NAME, + "options": { + "sysroot": "/some/sysroot", + }, + } + test_input.update(test_data) + res = stage_schema.validate(test_input) assert res.valid is False testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1) diff --git a/stages/test/test_selinux.py b/stages/test/test_selinux.py index fda8c633..469d46d4 100644 --- a/stages/test/test_selinux.py +++ b/stages/test/test_selinux.py @@ -5,17 +5,12 @@ from unittest.mock import call, patch import pytest -import osbuild.meta from osbuild import testutil STAGE_NAME = "org.osbuild.selinux" -def schema_validation_selinux(test_data, implicit_file_contexts=True): - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version="1"), STAGE_NAME) - +def get_test_input(test_data, implicit_file_contexts=True): test_input = { "name": STAGE_NAME, "options": {} @@ -24,7 +19,7 @@ def schema_validation_selinux(test_data, implicit_file_contexts=True): test_input["options"]["file_contexts"] = "some-context" test_input["options"].update(test_data) - return schema.validate(test_input) + return test_input @pytest.mark.parametrize("test_data,expected_err", [ @@ -36,8 +31,9 @@ def schema_validation_selinux(test_data, implicit_file_contexts=True): ({"labels": "xxx"}, "'xxx' is not of type 'object'"), ({"force_autorelabel": "foo"}, "'foo' is not of type 'boolean'"), ]) -def test_schema_validation_selinux(test_data, expected_err): - res = schema_validation_selinux(test_data) +@pytest.mark.parametrize("stage_schema", ["1"], indirect=True) +def test_schema_validation_selinux(stage_schema, test_data, expected_err): + res = stage_schema.validate(get_test_input(test_data)) if expected_err == "": assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}" else: @@ -45,9 +41,9 @@ def test_schema_validation_selinux(test_data, expected_err): testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1) -def test_schema_validation_selinux_file_context_required(): - test_data = {} - res = schema_validation_selinux(test_data, implicit_file_contexts=False) +@pytest.mark.parametrize("stage_schema", ["1"], indirect=True) +def test_schema_validation_selinux_file_context_required(stage_schema): + res = stage_schema.validate(get_test_input({}, implicit_file_contexts=False)) assert res.valid is False expected_err = "'file_contexts' is a required property" testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1) @@ -69,7 +65,7 @@ def test_selinux_file_contexts(mocked_setfiles, tmp_path, stage_module): @patch("osbuild.util.selinux.setfilecon") @patch("osbuild.util.selinux.setfiles") def test_selinux_labels(mocked_setfiles, mocked_setfilecon, tmp_path, stage_module): - osbuild.testutil.make_fake_input_tree(tmp_path, { + testutil.make_fake_input_tree(tmp_path, { "/usr/bin/bootc": "I'm only an imposter", }) diff --git a/stages/test/test_skopeo.py b/stages/test/test_skopeo.py index 2b2f0ae8..2b591e72 100644 --- a/stages/test/test_skopeo.py +++ b/stages/test/test_skopeo.py @@ -1,10 +1,7 @@ #!/usr/bin/python3 -import os.path - import pytest -import osbuild.meta from osbuild import testutil STAGE_NAME = "org.osbuild.skopeo" @@ -23,17 +20,13 @@ STAGE_NAME = "org.osbuild.skopeo" # *inputs* and it'll be a no-op in the stage ({"destination": {"type": "containers-storage"}}, ""), ]) -def test_schema_validation_skopeo(test_data, expected_err): - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version="2"), STAGE_NAME) - +def test_schema_validation_skopeo(stage_schema, test_data, expected_err): test_input = { "type": STAGE_NAME, "options": {}, } test_input["options"].update(test_data) - res = schema.validate(test_input) + res = stage_schema.validate(test_input) if expected_err == "": assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}" diff --git a/stages/test/test_xz.py b/stages/test/test_xz.py index 705f959d..02415ce9 100644 --- a/stages/test/test_xz.py +++ b/stages/test/test_xz.py @@ -6,7 +6,6 @@ from unittest import mock import pytest -import osbuild.meta from osbuild import testutil from osbuild.testutil import has_executable, make_fake_input_tree @@ -19,18 +18,14 @@ STAGE_NAME = "org.osbuild.xz" # good ({"filename": "image.xz"}, ""), ]) -def test_schema_validation_xz(test_data, expected_err): - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version="2"), STAGE_NAME) - +def test_schema_validation_xz(stage_schema, test_data, expected_err): test_input = { "type": STAGE_NAME, "options": { } } test_input["options"].update(test_data) - res = schema.validate(test_input) + res = stage_schema.validate(test_input) if expected_err == "": assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}" diff --git a/stages/test/test_zstd.py b/stages/test/test_zstd.py index 8d9be2a8..8cccde13 100644 --- a/stages/test/test_zstd.py +++ b/stages/test/test_zstd.py @@ -6,7 +6,6 @@ from unittest import mock import pytest -import osbuild.meta from osbuild import testutil from osbuild.testutil import has_executable, make_fake_input_tree @@ -19,18 +18,14 @@ STAGE_NAME = "org.osbuild.zstd" # good ({"filename": "image.zst"}, ""), ]) -def test_schema_validation_zstd(test_data, expected_err): - root = os.path.join(os.path.dirname(__file__), "../..") - mod_info = osbuild.meta.ModuleInfo.load(root, "Stage", STAGE_NAME) - schema = osbuild.meta.Schema(mod_info.get_schema(version="2"), STAGE_NAME) - +def test_schema_validation_zstd(stage_schema, test_data, expected_err): test_input = { "type": STAGE_NAME, "options": { } } test_input["options"].update(test_data) - res = schema.validate(test_input) + res = stage_schema.validate(test_input) if expected_err == "": assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}"