stages/test: introduce 'stage_schema' fixture

Introduce a 'stage_schema' fixture, which will load the stage schema
by the stage name defined in the STAGE_NAME defined in the test module
and optionally provided schema version and return it. If no schema
version is specified, version "2" is assumed. Modify all stage unit
tests to use this fixture, instead of loading the stage schema on their
own.

Signed-off-by: Tomáš Hozza <thozza@redhat.com>
This commit is contained in:
Tomáš Hozza 2024-01-30 14:05:56 +01:00 committed by Michael Vogt
parent 23c8f5b432
commit 2581160cfc
13 changed files with 81 additions and 125 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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]}"

View file

@ -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]}"

View file

@ -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]}"

View file

@ -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)

View file

@ -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)

View file

@ -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]}"

View file

@ -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)

View file

@ -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",
})

View file

@ -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]}"

View file

@ -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]}"

View file

@ -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]}"