diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3f2b9db4..8afa0b78 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,13 +39,13 @@ jobs: make lint - name: Run unit tests + if: matrix.python-version != '3.8' run: | make test/unit - - name: Run functional tests - run: | - make test/functional - + - name: run the integration tests + run: make test/integration + publish: name: Build and publish to PyPI registry diff --git a/Makefile b/Makefile index c272c4a8..df8bee9e 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,6 @@ test/unit/annotate: test/unit/annotate/clean: find galaxy_importer -type f -name '*,cover' -delete -.PHONY: test/functional -test/functional: - @sh tests/functional/* +.PHONY: test/integration +test/integration: + @pytest tests/integration -v diff --git a/tests/functional/run_importer.sh b/tests/functional/run_importer.sh deleted file mode 100644 index 00157668..00000000 --- a/tests/functional/run_importer.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -echo "Starting integration test..." - -TMPDIR=$(mktemp -d -t galaxy-importer-XXXXXXXX) -python3 -m venv $TMPDIR/venv -source $TMPDIR/venv/bin/activate -pip install . -cd $TMPDIR - -pwd -pip freeze | grep importer - - -################################### -# IMPORT SMOKETEST -################################### - -python -c 'from galaxy_importer import main' - -################################### -# RUNTIME VALIDATION -################################### - -# make and build a collection -ansible-galaxy collection init foo.bar -cd foo/bar -mkdir meta -cd meta -printf "requires_ansible: '>=2.9.10,<2.11.5'" > runtime.yml -cd .. -ansible-galaxy collection build -cd $TMPDIR - -# create config file to run ansible-test sanity in locally built container -printf "[galaxy-importer]\nRUN_ANSIBLE_TEST = True\nANSIBLE_TEST_LOCAL_IMAGE = True\nLOCAL_IMAGE_DOCKER = True\n" > galaxy-importer.cfg -export GALAXY_IMPORTER_CONFIG=galaxy-importer.cfg -echo "Using galaxy-importer.cfg:" -cat galaxy-importer.cfg - -# run the importer with file artifact tarball -python3 -m galaxy_importer.main foo/bar/foo-bar-*.tar.gz -RETURN_CODE=$? - -# run the importer with git_clone_path and output_path -rm foo/bar/foo-bar-*.tar.gz -python3 -m galaxy_importer.main --git-clone-path=foo/bar/ --output-path=foo/bar/ -RETURN_CODE=$? - -# build a legacy role -ansible-galaxy role init bar_role - -# run the importer with legacy role directory -python3 -m galaxy_importer.main bar_role --legacy-role --namespace foo-namespace -RETURN_CODE=$? - -# cleanup -cd /tmp -rm -rf $TMPDIR - -exit $RETURN_CODE diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py new file mode 100644 index 00000000..39bd6330 --- /dev/null +++ b/tests/integration/conftest.py @@ -0,0 +1,85 @@ +import atexit +import os +import shutil +import subprocess +import tempfile + +import pytest + + +def clean_files(path): + shutil.rmtree(path) + + +@pytest.fixture +def workdir(): + tdir = tempfile.mkdtemp() + atexit.register(clean_files, tdir) + return tdir + + +@pytest.fixture +def local_image_config(): + config = [ + "[galaxy-importer]", + "RUN_ANSIBLE_TEST=True", + "ANSIBLE_TEST_LOCAL_IMAGE=True", + "LOCAL_IMAGE_DOCKER=True", + ] + config = "\n".join(config) + + tdir = tempfile.mkdtemp() + atexit.register(clean_files, tdir) + + config_path = os.path.join(tdir, "galaxy-importer.cfg") + with open(config_path, "w") as f: + f.write(config) + + return {"GALAXY_IMPORTER_CONFIG": config_path} + + +@pytest.fixture +def simple_artifact(): + tdir = tempfile.mkdtemp() + atexit.register(clean_files, tdir) + + cmd = "ansible-galaxy collection init foo.bar" + pid = subprocess.run( + cmd, shell=True, cwd=tdir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) + assert pid.returncode == 0, pid.stdout + + coldir = os.path.join(tdir, "foo", "bar") + metadir = os.path.join(coldir, "meta") + + if not os.path.exists(metadir): + os.makedirs(metadir) + with open(os.path.join(metadir, "runtime.yml"), "w") as f: + f.write("requires_ansible: '>=2.9.10,<2.11.5'\n") + + cmd = "ansible-galaxy collection build ." + pid = subprocess.run( + cmd, shell=True, cwd=coldir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) + lines = pid.stdout.decode("utf-8").split("\n") + tarball = None + for line in lines: + line = line.strip() + if not line or not line.endswith(".tar.gz"): + continue + tarball = line.split()[-1] + assert tarball, lines + return tarball + + +@pytest.fixture +def simple_legacy_role(): + tdir = tempfile.mkdtemp() + atexit.register(clean_files, tdir) + + cmd = "ansible-galaxy role init bar_role" + pid = subprocess.run( + cmd, shell=True, cwd=tdir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) + assert pid.returncode == 0, pid.stdout + return os.path.join(tdir, "bar_role") diff --git a/tests/integration/test_containers.py b/tests/integration/test_containers.py new file mode 100644 index 00000000..908ce971 --- /dev/null +++ b/tests/integration/test_containers.py @@ -0,0 +1,90 @@ +import copy +import json +import os +import subprocess + + +def test_local_build_container_with_collection(workdir, local_image_config, simple_artifact): + # use the parent env to preserve venv vars + env = copy.deepcopy(dict(os.environ)) + env.update(local_image_config) + + # this relies on the GALAXY_IMPORTER_CONFIG to know where to find the config + # it should also spawn an image build and use that image for the ansible-test phase + cmd = f"python3 -m galaxy_importer.main {simple_artifact}" + pid = subprocess.run( + cmd, shell=True, cwd=workdir, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) + assert pid.returncode == 0, pid.stdout + + # the log should contain all the relevant messages + log = pid.stdout.decode("utf-8") + assert "Running ansible-test sanity on" in log + assert "Running sanity test" in log + assert "ansible-test sanity complete." in log + assert "No EDA content found. Skipping linters." in log + assert "Removing temporary files, image and container" in log + assert "Importer processing completed successfully" in log + + # it should have stored structured data in the pwd + results_file = os.path.join(workdir, "importer_result.json") + assert os.path.exists(results_file) + with open(results_file, "r") as f: + results = json.loads(f.read()) + + # the data should have all the relevant bits + assert results["contents"] == [] + assert results["docs_blob"]["contents"] == [] + assert results["docs_blob"]["collection_readme"]["name"] == "README.md" + assert results["docs_blob"]["collection_readme"]["html"] + assert results["docs_blob"]["documentation_files"] == [] + assert results["metadata"]["namespace"] == "foo" + assert results["metadata"]["name"] == "bar" + assert results["metadata"]["version"] == "1.0.0" + assert results["requires_ansible"] == ">=2.9.10,<2.11.5" + + +def test_local_build_container_with_legacy_role(local_image_config, simple_legacy_role): + # use the parent env to preserve venv vars + env = copy.deepcopy(dict(os.environ)) + env.update(local_image_config) + + # this relies on the GALAXY_IMPORTER_CONFIG to know where to find the config + # it should also spawn an image build and use that image for the ansible-test phase + cmd = ( + f"python3 -m galaxy_importer.main {simple_legacy_role}" + + " --legacy-role --namespace foo-namespace" + ) + workdir = os.path.dirname(simple_legacy_role) + pid = subprocess.run( + cmd, shell=True, cwd=workdir, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) + assert pid.returncode == 0, pid.stdout + + # the log should contain all the relevant messages + log = pid.stdout.decode("utf-8") + + assert "Determined role name to be bar_role" in log + assert "Linting role bar_role via ansible-lint..." in log + assert "Should change default metadata: author" in log + assert "Should change default metadata: company" in log + assert "Should change default metadata: license" in log + assert "Role info should contain platforms" in log + assert "All plays should be named." in log + assert "...ansible-lint run complete" in log + assert "Legacy role loading complete" in log + + # it should have stored structured data in the pwd + results_file = os.path.join(workdir, "importer_result.json") + assert os.path.exists(results_file) + with open(results_file, "r") as f: + results = json.loads(f.read()) + + # the data should have all the relevant bits + assert results["metadata"]["dependencies"] == [] + assert results["metadata"]["galaxy_info"]["author"] == "your name" + assert results["metadata"]["galaxy_info"]["description"] == "your role description" + assert results["metadata"]["galaxy_info"]["role_name"] is None + assert results["name"] == "bar_role" + assert results["namespace"] == "foo-namespace" + assert results["readme_html"]