summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/roles/acme.missing_deps/requirements.yml2
-rw-r--r--test/test_config.py21
-rw-r--r--test/test_runtime.py191
-rw-r--r--test/test_schema.py13
-rw-r--r--test/test_types.py9
-rw-r--r--test/test_version.py13
6 files changed, 195 insertions, 54 deletions
diff --git a/test/roles/acme.missing_deps/requirements.yml b/test/roles/acme.missing_deps/requirements.yml
index 53c5937..58d68f1 100644
--- a/test/roles/acme.missing_deps/requirements.yml
+++ b/test/roles/acme.missing_deps/requirements.yml
@@ -1,2 +1,4 @@
collections:
- foo.bar # collection that does not exist, so we can test offline mode
+roles:
+ - this_role_does_not_exist # and also role that does not exist
diff --git a/test/test_config.py b/test/test_config.py
index ebdde00..bedb2d4 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -7,7 +7,12 @@ import pytest
from _pytest.monkeypatch import MonkeyPatch
from packaging.version import Version
-from ansible_compat.config import AnsibleConfig, ansible_version, parse_ansible_version
+from ansible_compat.config import (
+ AnsibleConfig,
+ ansible_collections_path,
+ ansible_version,
+ parse_ansible_version,
+)
from ansible_compat.errors import InvalidPrerequisiteError, MissingAnsibleError
@@ -85,3 +90,17 @@ def test_ansible_version() -> None:
def test_ansible_version_arg() -> None:
"""Validate ansible_version behavior."""
assert ansible_version("2.0") >= Version("1.0")
+
+
+@pytest.mark.parametrize(
+ "var",
+ ("", "ANSIBLE_COLLECTIONS_PATH", "ANSIBLE_COLLECTIONS_PATHS"),
+ ids=["blank", "singular", "plural"],
+)
+def test_ansible_collections_path_env(var: str, monkeypatch: MonkeyPatch) -> None:
+ """Test that ansible_collections_path returns the appropriate env var."""
+ # Set the variable
+ if var:
+ monkeypatch.setenv(var, "")
+
+ assert ansible_collections_path() == (var or "ANSIBLE_COLLECTIONS_PATH")
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:
diff --git a/test/test_schema.py b/test/test_schema.py
index 10c1a9a..91616a9 100644
--- a/test/test_schema.py
+++ b/test/test_schema.py
@@ -72,3 +72,16 @@ def test_schema(index: int) -> None:
def test_json_path() -> None:
"""Test json_path function."""
assert json_path(["a", 1, "b"]) == "$.a[1].b"
+
+
+def test_validate_invalid_schema() -> None:
+ """Test validate function error handling."""
+ schema = "[]"
+ data = json_from_asset("assets/validate0_data.json")
+ errors = validate(schema, data)
+
+ assert len(errors) == 1
+ assert (
+ errors[0].to_friendly()
+ == "In 'schema sanity check': Invalid schema, must be a mapping."
+ )
diff --git a/test/test_types.py b/test/test_types.py
new file mode 100644
index 0000000..6702b48
--- /dev/null
+++ b/test/test_types.py
@@ -0,0 +1,9 @@
+"""Tests for types module."""
+
+import ansible_compat.types
+
+
+def test_types() -> None:
+ """Tests that JSON types are exported."""
+ assert ansible_compat.types.JSON
+ assert ansible_compat.types.JSON_ro
diff --git a/test/test_version.py b/test/test_version.py
new file mode 100644
index 0000000..b5d26ab
--- /dev/null
+++ b/test/test_version.py
@@ -0,0 +1,13 @@
+"""Tests for _version module."""
+
+
+def test_version_module() -> None:
+ """Tests that _version exports are present."""
+ # import kept here to allow mypy/pylint to run when module is not installed
+ # and the generated _version.py is missing.
+ # pylint: disable=no-name-in-module,no-member
+ import ansible_compat._version # type: ignore[import-not-found,unused-ignore]
+
+ assert ansible_compat._version.__version__
+ assert ansible_compat._version.__version_tuple__
+ assert ansible_compat._version.version