Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some tests for installer #240

Merged
merged 1 commit into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/ansible_dev_environment/subcommands/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ def __init__(self: Installer, config: Config, output: Output) -> None:

def run(self: Installer) -> None:
"""Run the installer."""
if self._config.args.collection_specifier and "," in self._config.args.collection_specifier:
if self._config.args.collection_specifier and any(
"," in s for s in self._config.args.collection_specifier
):
err = "Multiple optional dependencies are not supported at this time."
self._output.critical(err)

Expand Down
314 changes: 308 additions & 6 deletions tests/unit/test_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

from argparse import Namespace
from pathlib import Path
from typing import Any

import pytest

from ansible_dev_environment.arg_parser import parse
from ansible_dev_environment.config import Config
from ansible_dev_environment.output import Output
from ansible_dev_environment.subcommands.installer import Installer
Expand Down Expand Up @@ -158,16 +160,16 @@ def test_copy_using_ls(tmp_path: Path, output: Output) -> None:


def test_no_adt_install(
tmpdir: Path,
tmp_path: Path,
output: Output,
) -> None:
"""Test only core is installed.
Args:
tmpdir: A temporary directory.
tmp_path: A temporary directory.
output: The output fixture.
"""
venv = tmpdir / "test_venv"
venv = tmp_path / "test_venv"
args = Namespace(
venv=venv,
verbose=0,
Expand All @@ -189,16 +191,16 @@ def test_no_adt_install(


def test_adt_install(
tmpdir: Path,
tmp_path: Path,
output: Output,
) -> None:
"""Test adt is installed.
Args:
tmpdir: A temporary directory.
tmp_path: A temporary directory.
output: The output fixture.
"""
venv = tmpdir / "test_venv"
venv = tmp_path / "test_venv"
args = Namespace(
venv=venv,
verbose=0,
Expand All @@ -217,3 +219,303 @@ def test_adt_install(
assert venv.exists()
assert (venv / "bin" / "ansible").exists()
assert (venv / "bin" / "adt").exists()


def test_multiple_specifiers(
tmp_path: Path,
output: Output,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
"""Test more than one collection specifier.
Args:
tmp_path: A temporary directory.
output: The output fixture.
monkeypatch: The monkeypatch fixture.
capsys: The capsys fixture.
"""
command = ["ade", "install", "ansible.utils[dev,test]", "--venv", str(tmp_path / "venv")]
monkeypatch.setattr("sys.argv", command)
args = parse()
installer = Installer(
output=output,
config=Config(args=args, output=output, term_features=output.term_features),
)
with pytest.raises(SystemExit):
installer.run()
captured = capsys.readouterr()
assert "Multiple optional dependencies are not supported at this time" in captured.err


def test_editable_not_local(
tmp_path: Path,
output: Output,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
"""Test editable with a non-local collection.
Args:
tmp_path: A temporary directory.
output: The output fixture.
monkeypatch: The monkeypatch fixture.
capsys: The capsys fixture.
"""
command = ["ade", "install", "-e", "ansible.utils", "--venv", str(tmp_path / "venv")]
monkeypatch.setattr("sys.argv", command)
args = parse()
config = Config(args=args, output=output, term_features=output.term_features)
config.init()
installer = Installer(
output=output,
config=config,
)

def install_core(self: Installer) -> None: # noqa: ARG001
"""Don't install core.
Args:
self: The installer instance.
"""

monkeypatch.setattr(Installer, "_install_core", install_core)
with pytest.raises(SystemExit):
installer.run()
captured = capsys.readouterr()
assert "Editable installs are only supported for local collections" in captured.err


def test_core_installed(session_venv: Config) -> None:
"""Test that core is installed only once.
Args:
session_venv: The session_venv fixture.
"""
mtime_pre = session_venv.venv_bindir.joinpath("ansible").stat().st_mtime
installer = Installer(config=session_venv, output=session_venv._output)
installer._install_core()
mtime_post = session_venv.venv_bindir.joinpath("ansible").stat().st_mtime
assert mtime_pre == mtime_post


def test_core_install_fails(
session_venv: Config,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
"""Test a clean exit if the core install fails.
Args:
session_venv: The session_venv fixture.
monkeypatch: The monkeypatch fixture.
capsys: The capsys fixture.
"""
orig_exists = Path.exists

def exists(path: Path) -> bool:
"""Selectively return False.
Args:
path: A path to check
Returns:
False if the path is "ansible", otherwise the original exists function result.
"""
if path.name == "ansible":
return False
return orig_exists(path)

monkeypatch.setattr(Path, "exists", exists)

def subprocess_run(*_args: Any, **_kwargs: Any) -> None: # noqa: ANN401
"""Raise an exception.
Args:
*_args: Arguments
**_kwargs: Keyword arguments
Raises:
subprocess.CalledProcessError: Always
"""
raise subprocess.CalledProcessError(1, "ansible")

monkeypatch.setattr(
"ansible_dev_environment.subcommands.installer.subprocess_run",
subprocess_run,
)
installer = Installer(config=session_venv, output=session_venv._output)

with pytest.raises(SystemExit):
installer._install_core()

captured = capsys.readouterr()
assert "Failed to install ansible-core" in captured.err


def test_adt_installed(session_venv: Config) -> None:
"""Test that adt is installed only once.
Args:
session_venv: The session_venv fixture.
"""
orig_exists = Path.exists

def exists(path: Path) -> bool:
"""Selectively return False.
Args:
path: A path to check
Returns:
False if the path is "adt", otherwise the original exists function result.
"""
if path.name == "adt":
return True
return orig_exists(path)

with pytest.MonkeyPatch.context() as m:
m.setattr(Path, "exists", exists)
installer = Installer(config=session_venv, output=session_venv._output)
installer._install_dev_tools()

assert not session_venv.venv_bindir.joinpath("adt").exists()


def test_adt_install_fails(
session_venv: Config,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
"""Test a clean exit if the adt install fails.
Args:
session_venv: The session_venv fixture.
monkeypatch: The monkeypatch fixture.
capsys: The capsys fixture.
"""

def subprocess_run(*_args: Any, **_kwargs: Any) -> None: # noqa: ANN401
"""Raise an exception.
Args:
*_args: Arguments
**_kwargs: Keyword arguments
Raises:
subprocess.CalledProcessError: Always
"""
raise subprocess.CalledProcessError(1, "adt")

monkeypatch.setattr(
"ansible_dev_environment.subcommands.installer.subprocess_run",
subprocess_run,
)
installer = Installer(config=session_venv, output=session_venv._output)

with pytest.raises(SystemExit):
installer._install_dev_tools()

captured = capsys.readouterr()
assert "Failed to install ansible-dev-tools" in captured.err


def test_reinstall(
function_venv: Config,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
"""Test that reinstalling works.
Args:
function_venv: The function_venv fixture.
monkeypatch: The monkeypatch fixture.
capsys: The capsys fixture.
"""
command = [
"ade",
"install",
"ansible.posix",
"--venv",
str(function_venv.venv),
"--ll",
"notset",
"-vvv",
]
monkeypatch.setattr("sys.argv", command)
args = parse()
config = Config(
args=args,
output=function_venv._output,
term_features=function_venv._output.term_features,
)
config.init()
pre_mtime = (function_venv.site_pkg_collections_path / "ansible" / "posix").stat().st_mtime
installer = Installer(config=config, output=function_venv._output)
installer.run()
post_mtime = (function_venv.site_pkg_collections_path / "ansible" / "posix").stat().st_mtime
assert post_mtime > pre_mtime
captured = capsys.readouterr()
assert "Removing installed " in captured.out


def test_install_fails(
function_venv: Config,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
"""Test for a clean exit if a collection install fails.
Args:
function_venv: The function_venv fixture.
monkeypatch: The monkeypatch fixture.
capsys: The capsys fixture.
"""
command = [
"ade",
"install",
"ansible.posix",
"--venv",
str(function_venv.venv),
"--ll",
"notset",
"-vvv",
]
monkeypatch.setattr("sys.argv", command)
args = parse()
config = Config(
args=args,
output=function_venv._output,
term_features=function_venv._output.term_features,
)
config.init()

def subprocess_run(**kwargs: Any) -> subprocess.CompletedProcess[str]: # noqa: ANN401
"""Raise an exception.
Args:
**kwargs: Keyword arguments
Raises:
subprocess.CalledProcessError: if ansible posix is being installed
Returns:
The completed process
"""
if "install 'ansible.posix'" in kwargs["command"]:
raise subprocess.CalledProcessError(1, "ansible.posix")
return subprocess_run(**kwargs)

monkeypatch.setattr(
"ansible_dev_environment.subcommands.installer.subprocess_run",
subprocess_run,
)

installer = Installer(config=config, output=function_venv._output)
with pytest.raises(SystemExit):
installer.run()
captured = capsys.readouterr()
assert "Failed to install collection" in captured.err
Loading