summaryrefslogtreecommitdiffstats
path: root/test/test_runtime.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--test/test_runtime.py191
1 files changed, 138 insertions, 53 deletions
diff --git a/test/test_runtime.py b/test/test_runtime.py
index 0823f60..ebf99c9 100644
--- a/test/test_runtime.py
+++ b/test/test_runtime.py
@@ -1,6 +1,6 @@
"""Tests for Runtime class."""
-# pylint: disable=protected-access
+# pylint: disable=protected-access,too-many-lines
from __future__ import annotations
import logging
@@ -15,7 +15,6 @@ from typing import TYPE_CHECKING, Any
import pytest
from packaging.version import Version
-from ansible_compat.config import ansible_version
from ansible_compat.constants import INVALID_PREREQUISITES_RC
from ansible_compat.errors import (
AnsibleCommandError,
@@ -182,10 +181,13 @@ def test_runtime_install_role(
runtime.cache_dir = tmp_dir
-def test_prepare_environment_with_collections(tmp_path: pathlib.Path) -> None:
+def test_prepare_environment_with_collections(runtime_tmp: Runtime) -> None:
"""Check that collections are correctly installed."""
- runtime = Runtime(isolated=True, project_dir=tmp_path)
- runtime.prepare_environment(required_collections={"community.molecule": "0.1.0"})
+ runtime_tmp.prepare_environment(
+ required_collections={"community.molecule": "0.1.0"},
+ install_local=True,
+ )
+ assert "community.molecule" in runtime_tmp.collections
def test_runtime_install_requirements_missing_file() -> None:
@@ -438,19 +440,25 @@ def test_require_collection_invalid_collections_path(runtime: Runtime) -> None:
runtime.require_collection("community.molecule")
-def test_require_collection_preexisting_broken(tmp_path: pathlib.Path) -> None:
+def test_require_collection_preexisting_broken(runtime_tmp: Runtime) -> None:
"""Check that require_collection raise with broken pre-existing collection."""
- runtime = Runtime(isolated=True, project_dir=tmp_path)
- dest_path: str = runtime.config.collections_paths[0]
+ dest_path: str = runtime_tmp.config.collections_paths[0]
dest = pathlib.Path(dest_path) / "ansible_collections" / "foo" / "bar"
dest.mkdir(parents=True, exist_ok=True)
with pytest.raises(InvalidPrerequisiteError, match="missing MANIFEST.json"):
- runtime.require_collection("foo.bar")
+ runtime_tmp.require_collection("foo.bar")
-def test_require_collection(runtime_tmp: Runtime) -> None:
- """Check that require collection successful install case."""
- runtime_tmp.require_collection("community.molecule", "0.1.0")
+def test_require_collection_install(runtime_tmp: Runtime) -> None:
+ """Check that require collection successful install case, including upgrade path."""
+ runtime_tmp.install_collection("ansible.posix:==1.5.2")
+ runtime_tmp.load_collections()
+ collection = runtime_tmp.collections["ansible.posix"]
+ assert collection.version == "1.5.2"
+ runtime_tmp.require_collection(name="ansible.posix", version="1.5.4", install=True)
+ runtime_tmp.load_collections()
+ collection = runtime_tmp.collections["ansible.posix"]
+ assert collection.version == "1.5.4"
@pytest.mark.parametrize(
@@ -532,11 +540,14 @@ def test_install_galaxy_role_unlink(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test ability to unlink incorrect symlinked roles."""
- runtime_tmp = Runtime(verbosity=1)
+ runtime_tmp = Runtime(verbosity=1, isolated=True)
runtime_tmp.prepare_environment()
+ assert runtime_tmp.cache_dir is not None
pathlib.Path(f"{runtime_tmp.cache_dir}/roles").mkdir(parents=True, exist_ok=True)
- pathlib.Path(f"{runtime_tmp.cache_dir}/roles/acme.get_rich").symlink_to("/dev/null")
- pathlib.Path(f"{runtime_tmp.project_dir}/meta").mkdir()
+ roledir = pathlib.Path(f"{runtime_tmp.cache_dir}/roles/acme.get_rich")
+ if not roledir.exists():
+ roledir.symlink_to("/dev/null")
+ pathlib.Path(f"{runtime_tmp.project_dir}/meta").mkdir(exist_ok=True)
pathlib.Path(f"{runtime_tmp.project_dir}/meta/main.yml").write_text(
"""galaxy_info:
role_name: get_rich
@@ -546,6 +557,7 @@ def test_install_galaxy_role_unlink(
)
runtime_tmp._install_galaxy_role(runtime_tmp.project_dir)
assert "symlink to current repository" in caplog.text
+ pathlib.Path(f"{runtime_tmp.project_dir}/meta/main.yml").unlink()
def test_install_galaxy_role_bad_namespace(runtime_tmp: Runtime) -> None:
@@ -563,6 +575,18 @@ def test_install_galaxy_role_bad_namespace(runtime_tmp: Runtime) -> None:
runtime_tmp._install_galaxy_role(runtime_tmp.project_dir, role_name_check=1)
+def test_install_galaxy_role_no_meta(runtime_tmp: Runtime) -> None:
+ """Check install role with missing meta/main.yml."""
+ # This should fail because meta/main.yml is missing
+ with pytest.raises(
+ FileNotFoundError,
+ match=f"No such file or directory: '{runtime_tmp.project_dir.absolute()}/meta/main.yaml'",
+ ):
+ runtime_tmp._install_galaxy_role(runtime_tmp.project_dir)
+ # But ignore_errors will return without doing anything
+ runtime_tmp._install_galaxy_role(runtime_tmp.project_dir, ignore_errors=True)
+
+
@pytest.mark.parametrize(
"galaxy_info",
(
@@ -737,11 +761,83 @@ def test_install_collection_from_disk_fail() -> None:
)
-def test_prepare_environment_offline_role() -> None:
+def test_load_collections_failure(mocker: MockerFixture) -> None:
+ """Tests for ansible-galaxy erroring."""
+ mocker.patch(
+ "ansible_compat.runtime.Runtime.run",
+ return_value=CompletedProcess(
+ ["x"],
+ returncode=1,
+ stdout="There was an error",
+ stderr="This is the error",
+ ),
+ autospec=True,
+ )
+ runtime = Runtime()
+ with pytest.raises(RuntimeError, match="Unable to list collections: "):
+ runtime.load_collections()
+
+
+@pytest.mark.parametrize(
+ "value",
+ ("[]", '{"path": "bad data"}', '{"path": {"ansible.posix": 123}}'),
+ ids=["list", "malformed_collection", "bad_collection_data"],
+)
+def test_load_collections_garbage(value: str, mocker: MockerFixture) -> None:
+ """Tests for ansible-galaxy returning bad data."""
+ mocker.patch(
+ "ansible_compat.runtime.Runtime.run",
+ return_value=CompletedProcess(
+ ["x"],
+ returncode=0,
+ stdout=value,
+ stderr="",
+ ),
+ autospec=True,
+ )
+ runtime = Runtime()
+ with pytest.raises(TypeError, match="Unexpected collection data, "):
+ runtime.load_collections()
+
+
+@pytest.mark.parametrize(
+ "value",
+ ("", '{"path": {123: 456}}'),
+ ids=["nothing", "bad_collection_name"],
+)
+def test_load_collections_invalid_json(value: str, mocker: MockerFixture) -> None:
+ """Tests for ansible-galaxy returning bad data."""
+ mocker.patch(
+ "ansible_compat.runtime.Runtime.run",
+ return_value=CompletedProcess(
+ ["x"],
+ returncode=0,
+ stdout=value,
+ stderr="",
+ ),
+ autospec=True,
+ )
+ runtime = Runtime()
+ with pytest.raises(
+ RuntimeError,
+ match=f"Unable to parse galaxy output as JSON: {value}",
+ ):
+ runtime.load_collections()
+
+
+def test_prepare_environment_offline_role(caplog: pytest.LogCaptureFixture) -> None:
"""Ensure that we can make use of offline roles."""
with cwd(Path("test/roles/acme.missing_deps")):
runtime = Runtime(isolated=True)
runtime.prepare_environment(install_local=True, offline=True)
+ assert (
+ "Skipped installing old role dependencies due to running in offline mode."
+ in caplog.text
+ )
+ assert (
+ "Skipped installing collection dependencies due to running in offline mode."
+ in caplog.text
+ )
def test_runtime_run(runtime: Runtime) -> None:
@@ -785,35 +881,18 @@ def test_runtime_plugins(runtime: Runtime) -> None:
assert isinstance(runtime.plugins.role, dict)
assert "become" in runtime.plugins.keyword
- if ansible_version() < Version("2.14.0"):
- assert "sudo" in runtime.plugins.become
- assert "memory" in runtime.plugins.cache
- assert "default" in runtime.plugins.callback
- assert "local" in runtime.plugins.connection
- assert "ini" in runtime.plugins.inventory
- assert "env" in runtime.plugins.lookup
- assert "sh" in runtime.plugins.shell
- assert "host_group_vars" in runtime.plugins.vars
- assert "file" in runtime.plugins.module
- assert "free" in runtime.plugins.strategy
- # ansible-doc below 2.14 does not support listing 'test' and 'filter' types:
- with pytest.raises(RuntimeError):
- assert "is_abs" in runtime.plugins.test
- with pytest.raises(RuntimeError):
- assert "bool" in runtime.plugins.filter
- else:
- assert "ansible.builtin.sudo" in runtime.plugins.become
- assert "ansible.builtin.memory" in runtime.plugins.cache
- assert "ansible.builtin.default" in runtime.plugins.callback
- assert "ansible.builtin.local" in runtime.plugins.connection
- assert "ansible.builtin.ini" in runtime.plugins.inventory
- assert "ansible.builtin.env" in runtime.plugins.lookup
- assert "ansible.builtin.sh" in runtime.plugins.shell
- assert "ansible.builtin.host_group_vars" in runtime.plugins.vars
- assert "ansible.builtin.file" in runtime.plugins.module
- assert "ansible.builtin.free" in runtime.plugins.strategy
- assert "ansible.builtin.is_abs" in runtime.plugins.test
- assert "ansible.builtin.bool" in runtime.plugins.filter
+ assert "ansible.builtin.sudo" in runtime.plugins.become
+ assert "ansible.builtin.memory" in runtime.plugins.cache
+ assert "ansible.builtin.default" in runtime.plugins.callback
+ assert "ansible.builtin.local" in runtime.plugins.connection
+ assert "ansible.builtin.ini" in runtime.plugins.inventory
+ assert "ansible.builtin.env" in runtime.plugins.lookup
+ assert "ansible.builtin.sh" in runtime.plugins.shell
+ assert "ansible.builtin.host_group_vars" in runtime.plugins.vars
+ assert "ansible.builtin.file" in runtime.plugins.module
+ assert "ansible.builtin.free" in runtime.plugins.strategy
+ assert "ansible.builtin.is_abs" in runtime.plugins.test
+ assert "ansible.builtin.bool" in runtime.plugins.filter
@pytest.mark.parametrize(
@@ -866,11 +945,20 @@ def test_is_url(name: str, result: bool) -> None:
assert is_url(name) == result
-def test_prepare_environment_repair_broken_symlink(
+@pytest.mark.parametrize(
+ ("dest", "message"),
+ (
+ ("/invalid/destination", "Collection is symlinked, but not pointing to"),
+ (Path.cwd(), "Found symlinked collection, skipping its installation."),
+ ),
+ ids=["broken", "valid"],
+)
+def test_prepare_environment_symlink(
+ dest: str | Path,
+ message: str,
caplog: pytest.LogCaptureFixture,
) -> None:
- """Ensure we can deal with broken symlinks in collections."""
- caplog.set_level(logging.INFO)
+ """Ensure avalid symlinks to collections are properly detected."""
project_dir = Path(__file__).parent / "collections" / "acme.minimal"
runtime = Runtime(isolated=True, project_dir=project_dir)
assert runtime.cache_dir
@@ -879,12 +967,9 @@ def test_prepare_environment_repair_broken_symlink(
goodies = acme / "minimal"
rmtree(goodies, ignore_errors=True)
goodies.unlink(missing_ok=True)
- goodies.symlink_to("/invalid/destination")
+ goodies.symlink_to(dest)
runtime.prepare_environment(install_local=True)
- assert any(
- msg.startswith("Collection is symlinked, but not pointing to")
- for msg in caplog.messages
- )
+ assert message in caplog.text
def test_get_galaxy_role_name_invalid() -> None: