diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4358c162..e548fc58 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -56,3 +56,23 @@ jobs: # don't check /etc/os-release sourcing, allow useless cats to live inside our codebase, and # allow seemingly unreachable commands SHELLCHECK_OPTS: -e SC1091 -e SC2002 -e SC2317 + + integration: + # TODO: run this also via tmt/testing-farm + name: "Integration" + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Setup up python + uses: actions/setup-python@v4 + - name: Install test dependencies + run: | + sudo apt install -y podman python3-pytest flake8 + - name: Run tests + run: | + # podman needs (parts of) the environment but will break when + # XDG_RUNTIME_DIR is set. + # TODO: figure out what exactly podman needs + sudo -E XDG_RUNTIME_DIR= pytest-3 -s -vv diff --git a/plans/all.fmf b/plans/all.fmf new file mode 100644 index 00000000..60768f98 --- /dev/null +++ b/plans/all.fmf @@ -0,0 +1,13 @@ +summary: Run all tests inside a VM environment +provision: + how: virtual + image: fedora:39 +prepare: + how: install + package: + - podman + - pytest + - python3-flake8 +execute: + how: tmt + script: pytest -s -vv diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..a31ae1d5 --- /dev/null +++ b/test/README.md @@ -0,0 +1,12 @@ +Integration tests for osbuild-deploy-container +---------------------------------------------- + +This directory contans integration tests for osbuild-deploy-container. +They can be run in two ways: +1. On the local machine by just running `sudo pytest -s -v` +2. Via `tmt` [0] which will spin up a clean VM and run the tests inside: + + tmt run -vvv + + +[0] https://github.com/teemtee/tmt diff --git a/test/test_flake8.py b/test/test_flake8.py new file mode 100644 index 00000000..5cb61a7b --- /dev/null +++ b/test/test_flake8.py @@ -0,0 +1,11 @@ +import os +import pathlib +import subprocess + + +def test_flake8(): + p = pathlib.Path(__file__).parent + # TODO: use all static checks from osbuild instead + subprocess.check_call( + ["flake8", "--ignore=E402", "--max-line-length=120", + os.fspath(p)]) diff --git a/test/test_smoke.py b/test/test_smoke.py new file mode 100644 index 00000000..174b1b07 --- /dev/null +++ b/test/test_smoke.py @@ -0,0 +1,67 @@ +import json +import os +import pathlib +import subprocess + +import pytest + +# local test utils +import testutil + + +@pytest.fixture(name="output_path") +def output_path_fixture(tmp_path): + output_path = tmp_path / "output" + output_path.mkdir(exist_ok=True) + return output_path + + +@pytest.fixture(name="config_json") +def config_json_fixture(output_path): + CFG = { + "blueprint": { + "customizations": { + "user": [ + { + "name": "test", + "password": "password", + "groups": ["wheel"], + }, + ], + }, + }, + } + config_json_path = output_path / "config.json" + config_json_path.write_text(json.dumps(CFG), encoding="utf-8") + return config_json_path + + +@pytest.mark.skipif(os.getuid() != 0, reason="needs root") +@pytest.mark.skipif(not testutil.has_executable("podman"), reason="need podman") +def test_smoke(output_path, config_json): + # build local container + subprocess.check_call([ + "podman", "build", + "-f", "Containerfile", + "-t", "osbuild-deploy-container-test", + ]) + cursor = testutil.journal_cursor() + # and run container to deploy an image into output/disk.qcow2 + subprocess.check_call([ + "podman", "run", "--rm", + "--privileged", + "--security-opt", "label=type:unconfined_t", + "-v", f"{output_path}:/output", + "osbuild-deploy-container-test", + "quay.io/centos-bootc/centos-bootc:stream9", + "--config", "/output/config.json", + ]) + # check that there are no denials + # TODO: actually check this once https://github.com/osbuild/images/pull/287 + # is merged + journal_output = testutil.journal_after_cursor(cursor) + assert journal_output != "" + generated_img = pathlib.Path(output_path) / "qcow2/disk.qcow2" + assert generated_img.exists(), f"output file missing, dir content: {os.listdir(os.fspath(output_path))}" + # TODO: boot and do basic checks, see + # https://github.com/osbuild/osbuild-deploy-container/compare/main...mvo5:integration-test?expand=1 diff --git a/test/testutil.py b/test/testutil.py new file mode 100644 index 00000000..47da18e0 --- /dev/null +++ b/test/testutil.py @@ -0,0 +1,17 @@ +import shutil +import subprocess + + +def journal_cursor(): + output = subprocess.check_output(["journalctl", "-n0", "--show-cursor"], encoding="utf-8").strip() + cursor = output.split("\n")[-1] + return cursor.split("cursor: ")[-1] + + +def journal_after_cursor(cursor): + output = subprocess.check_output(["journalctl", f"--after-cursor={cursor}"]) + return output + + +def has_executable(name): + return shutil.which(name) is not None