Blame test/mod/test_osbuild.py

Packit a20ca0
#
Packit a20ca0
# Basic tests for a collection of osbuild modules.
Packit a20ca0
#
Packit a20ca0
Packit a20ca0
import json
Packit a20ca0
import os
Packit a20ca0
import unittest
Packit a20ca0
Packit a20ca0
import osbuild
Packit a20ca0
import osbuild.meta
Packit a20ca0
Packit a20ca0
Packit a20ca0
class TestDescriptions(unittest.TestCase):
Packit a20ca0
    def test_canonical(self):
Packit a20ca0
        """Degenerate case. Make sure we always return the same canonical
Packit a20ca0
        description when passing empty or null values."""
Packit a20ca0
Packit a20ca0
        cases = [
Packit a20ca0
            {},
Packit a20ca0
            {"assembler": None},
Packit a20ca0
            {"stages": []},
Packit a20ca0
            {"build": {}},
Packit a20ca0
            {"build": None}
Packit a20ca0
        ]
Packit a20ca0
        for pipeline in cases:
Packit a20ca0
            with self.subTest(pipeline):
Packit a20ca0
                self.assertEqual(osbuild.load(pipeline, {}).description(), {})
Packit a20ca0
Packit a20ca0
    def test_stage(self):
Packit a20ca0
        name = "org.osbuild.test"
Packit a20ca0
        options = {"one": 1}
Packit a20ca0
        cases = [
Packit a20ca0
            (osbuild.Stage(name, {}, None, None, {}), {"name": name}),
Packit a20ca0
            (osbuild.Stage(name, {}, None, None, None), {"name": name}),
Packit a20ca0
            (osbuild.Stage(name, {}, None, None, options), {"name": name, "options": options}),
Packit a20ca0
        ]
Packit a20ca0
        for stage, description in cases:
Packit a20ca0
            with self.subTest(description):
Packit a20ca0
                self.assertEqual(stage.description(), description)
Packit a20ca0
Packit a20ca0
    def test_assembler(self):
Packit a20ca0
        name = "org.osbuild.test"
Packit a20ca0
        options = {"one": 1}
Packit a20ca0
        cases = [
Packit a20ca0
            (osbuild.Assembler(name, None, None, {}), {"name": name}),
Packit a20ca0
            (osbuild.Assembler(name, None, None, None), {"name": name}),
Packit a20ca0
            (osbuild.Assembler(name, None, None, options), {"name": name, "options": options}),
Packit a20ca0
        ]
Packit a20ca0
        for assembler, description in cases:
Packit a20ca0
            with self.subTest(description):
Packit a20ca0
                self.assertEqual(assembler.description(), description)
Packit a20ca0
Packit a20ca0
    def test_pipeline(self):
Packit a20ca0
        build = osbuild.Pipeline("org.osbuild.test")
Packit a20ca0
        build.add_stage("org.osbuild.test", {}, {"one": 1})
Packit a20ca0
Packit a20ca0
        pipeline = osbuild.Pipeline("org.osbuild.test", build)
Packit a20ca0
        pipeline.add_stage("org.osbuild.test", {}, {"one": 2})
Packit a20ca0
        pipeline.set_assembler("org.osbuild.test")
Packit a20ca0
Packit a20ca0
        self.assertEqual(pipeline.description(), {
Packit a20ca0
            "build": {
Packit a20ca0
                "pipeline": {
Packit a20ca0
                    "stages": [
Packit a20ca0
                        {
Packit a20ca0
                            "name": "org.osbuild.test",
Packit a20ca0
                            "options": {"one": 1}
Packit a20ca0
                        }
Packit a20ca0
                    ]
Packit a20ca0
                },
Packit a20ca0
                "runner": "org.osbuild.test"
Packit a20ca0
            },
Packit a20ca0
            "stages": [
Packit a20ca0
                {
Packit a20ca0
                    "name": "org.osbuild.test",
Packit a20ca0
                    "options": {"one": 2}
Packit a20ca0
                }
Packit a20ca0
            ],
Packit a20ca0
            "assembler": {
Packit a20ca0
                "name": "org.osbuild.test"
Packit a20ca0
            }
Packit a20ca0
            })
Packit a20ca0
Packit a20ca0
    def test_moduleinfo(self):
Packit a20ca0
        index = osbuild.meta.Index(os.curdir)
Packit a20ca0
Packit a20ca0
        modules = []
Packit a20ca0
        for klass in ("Stage", "Assembler", "Source"):
Packit a20ca0
            mods = index.list_modules_for_class(klass)
Packit a20ca0
            modules += [(klass, module) for module in mods]
Packit a20ca0
Packit a20ca0
        self.assertTrue(modules)
Packit a20ca0
Packit a20ca0
        for module in modules:
Packit a20ca0
            klass, name = module
Packit a20ca0
            try:
Packit a20ca0
                info = osbuild.meta.ModuleInfo.load(os.curdir, klass, name)
Packit a20ca0
                schema = osbuild.meta.Schema(info.schema, name)
Packit a20ca0
                res = schema.check()
Packit a20ca0
                if not res:
Packit a20ca0
                    err = "\n  ".join(str(e) for e in res)
Packit a20ca0
                    self.fail(str(res) + "\n  " + err)
Packit a20ca0
            except json.decoder.JSONDecodeError as e:
Packit a20ca0
                msg = f"{klass} '{name}' has invalid STAGE_OPTS\n\t" + str(e)
Packit a20ca0
                self.fail(msg)
Packit a20ca0
Packit a20ca0
    def test_schema(self):
Packit a20ca0
        schema = osbuild.meta.Schema(None)
Packit a20ca0
        self.assertFalse(schema)
Packit a20ca0
Packit a20ca0
        schema = osbuild.meta.Schema({"type": "bool"})  # should be 'boolean'
Packit a20ca0
        self.assertFalse(schema.check().valid)
Packit a20ca0
        self.assertFalse(schema)
Packit a20ca0
Packit a20ca0
        schema = osbuild.meta.Schema({"type": "array", "minItems": 3})
Packit a20ca0
        self.assertTrue(schema.check().valid)
Packit a20ca0
        self.assertTrue(schema)
Packit a20ca0
Packit a20ca0
        res = schema.validate([1, 2])
Packit a20ca0
        self.assertFalse(res)
Packit a20ca0
        res = schema.validate([1, 2, 3])
Packit a20ca0
        self.assertTrue(res)
Packit a20ca0
Packit a20ca0
    def test_validation(self):
Packit a20ca0
        index = osbuild.meta.Index(os.curdir)
Packit a20ca0
Packit a20ca0
        # an empty manifest is OK
Packit a20ca0
        res = osbuild.meta.validate({}, index)
Packit a20ca0
        self.assertEqual(res.valid, True)
Packit a20ca0
Packit a20ca0
        # something totally invalid (by Ondřej Budai)
Packit a20ca0
        totally_invalid = {
Packit a20ca0
            "osbuild": {
Packit a20ca0
                "state": "awesome",
Packit a20ca0
                "but": {
Packit a20ca0
                    "input-validation": 1
Packit a20ca0
                }
Packit a20ca0
            }
Packit a20ca0
        }
Packit a20ca0
Packit a20ca0
        res = osbuild.meta.validate(totally_invalid, index)
Packit a20ca0
        self.assertEqual(res.valid, False)
Packit a20ca0
        # The top-level 'osbuild' is an additional property
Packit a20ca0
        self.assertEqual(len(res), 1)
Packit a20ca0
Packit a20ca0
        # This is missing the runner
Packit a20ca0
        no_runner = {
Packit a20ca0
            "pipeline": {
Packit a20ca0
                "build": {
Packit a20ca0
                    "pipeline": {}
Packit a20ca0
                }
Packit a20ca0
            }
Packit a20ca0
        }
Packit a20ca0
Packit a20ca0
        res = osbuild.meta.validate(no_runner, index)
Packit a20ca0
        self.assertEqual(res.valid, False)
Packit a20ca0
        self.assertEqual(len(res), 1)  # missing runner
Packit a20ca0
        lst = res[".pipeline.build"]
Packit a20ca0
        self.assertEqual(len(lst), 1)
Packit a20ca0
Packit a20ca0
        # de-dup issues: the manifest checking will report
Packit a20ca0
        # the extra element and the recursive build pipeline
Packit a20ca0
        # check will also report that same error; make sure
Packit a20ca0
        # they get properly de-duplicated
Packit a20ca0
        no_runner_extra = {
Packit a20ca0
            "pipeline": {
Packit a20ca0
                "build": {  # missing runner
Packit a20ca0
                    "pipeline": {
Packit a20ca0
                        "extra": True,  # should not be there
Packit a20ca0
                        "stages": [{
Packit a20ca0
                            "name": "org.osbuild.chrony",
Packit a20ca0
                            "options": {
Packit a20ca0
                                "timeservers": "string"  # should be an array
Packit a20ca0
                            }
Packit a20ca0
                        }]
Packit a20ca0
                    }
Packit a20ca0
                }
Packit a20ca0
            }
Packit a20ca0
        }
Packit a20ca0
Packit a20ca0
        res = osbuild.meta.validate(no_runner_extra, index)
Packit a20ca0
        self.assertEqual(res.valid, False)
Packit a20ca0
        self.assertEqual(len(res), 3)
Packit a20ca0
        lst = res[".pipeline.build.pipeline"]
Packit a20ca0
        self.assertEqual(len(lst), 1)  # should only have one
Packit a20ca0
        lst = res[".pipeline.build.pipeline.stages[0].options.timeservers"]
Packit a20ca0
        self.assertEqual(len(lst), 1)  # should only have one
Packit a20ca0
Packit a20ca0
        # stage issues
Packit a20ca0
        stage_check = {
Packit a20ca0
            "pipeline": {
Packit a20ca0
                "stages": [{
Packit a20ca0
                    "name": "org.osbuild.grub2",
Packit a20ca0
                    "options": {
Packit a20ca0
                        "uefi": {
Packit a20ca0
                            "install": False,
Packit a20ca0
                            # missing "vendor"
Packit a20ca0
                        },
Packit a20ca0
                        # missing rootfs or root_fs_uuid
Packit a20ca0
                    }
Packit a20ca0
                }]
Packit a20ca0
            }
Packit a20ca0
        }
Packit a20ca0
Packit a20ca0
        res = osbuild.meta.validate(stage_check, index)
Packit a20ca0
        self.assertEqual(res.valid, False)
Packit a20ca0
        self.assertEqual(len(res), 2)
Packit a20ca0
        lst = res[".pipeline.stages[0].options"]
Packit a20ca0
        self.assertEqual(len(lst), 1)  #  missing rootfs
Packit a20ca0
        lst = res[".pipeline.stages[0].options.uefi"]
Packit a20ca0
        self.assertEqual(len(lst), 1)  #  missing "osname"
Packit a20ca0
Packit a20ca0
        assembler_check = {
Packit a20ca0
            "pipeline": {
Packit a20ca0
                "assembler": {
Packit a20ca0
                    "name": "org.osbuild.tar",
Packit a20ca0
                    "options": {
Packit a20ca0
                        "compression": "foobar"
Packit a20ca0
                    }
Packit a20ca0
                }
Packit a20ca0
            }
Packit a20ca0
        }
Packit a20ca0
Packit a20ca0
        res = osbuild.meta.validate(assembler_check, index)
Packit a20ca0
        self.assertEqual(res.valid, False)
Packit a20ca0
        self.assertEqual(len(res), 2)
Packit a20ca0
        lst = res[".pipeline.assembler.options"]
Packit a20ca0
        self.assertEqual(len(lst), 1)  #  missing "filename"
Packit a20ca0
        lst = res[".pipeline.assembler.options.compression"]
Packit a20ca0
        self.assertEqual(len(lst), 1)  #  wrong compression method