Add test and migrate spm test to pytest

This commit is contained in:
Megan Wilhite 2021-11-10 08:23:36 -05:00
parent 76a3d7e562
commit a2ba9b9c40
5 changed files with 253 additions and 213 deletions

View file

@ -232,7 +232,8 @@ salt/serializers/*:
- pytests.unit.serializers.test_serializers
salt/(cli/spm\.py|spm/.+):
- unit.test_spm
- pytests.unit.spm.test_spm
- pytests.unit.spm.test_pkgfiles
- integration.shell.test_spm
- integration.spm.test_build
- integration.spm.test_files

View file

@ -0,0 +1,35 @@
import os
import pathlib
import pytest
import salt.spm.pkgfiles.local as spm
from tests.support.mock import MagicMock
@pytest.fixture()
def configure_loader_modules():
return {spm: {"__opts__": {"spm_node_type": "master"}}}
class MockTar:
def __init__(self):
self.name = str(pathlib.Path("apache", "_README"))
self.path = str(
pathlib.Path(os.sep, "var", "cache", "salt", "master", "extmods")
)
def test_install_file():
"""
test spm.pkgfiles.local
"""
assert (
spm.install_file(
"apache",
formula_tar=MagicMock(),
member=MockTar(),
formula_def={"name": "apache"},
conn={"formula_path": "/tmp/blah"},
)
== MockTar().path
)

View file

@ -0,0 +1,213 @@
import os
import shutil
import pytest
import salt.config
import salt.spm
import salt.utils.files
from tests.support.mixins import AdaptedConfigurationTestCaseMixin
from tests.support.mock import MagicMock, patch
@pytest.fixture()
def f1_content():
_F1 = {
"definition": {
"name": "formula1",
"version": "1.2",
"release": "2",
"summary": "test",
"description": "testing, nothing to see here",
}
}
_F1["contents"] = (
(
"FORMULA",
(
"name: {name}\n"
"version: {version}\n"
"release: {release}\n"
"summary: {summary}\n"
"description: {description}"
).format(**_F1["definition"]),
),
("modules/mod1.py", "# mod1.py"),
("modules/mod2.py", "# mod2.py"),
("states/state1.sls", "# state1.sls"),
("states/state2.sls", "# state2.sls"),
)
return _F1
@pytest.mark.destructive_test
class SPMTestUserInterface(salt.spm.SPMUserInterface):
"""
Unit test user interface to SPMClient
"""
def __init__(self):
self._status = []
self._confirm = []
self._error = []
def status(self, msg):
self._status.append(msg)
def confirm(self, action):
self._confirm.append(action)
def error(self, msg):
self._error.append(msg)
@pytest.fixture()
def setup_spm(tmp_path):
minion_config = AdaptedConfigurationTestCaseMixin.get_temp_config(
"minion",
**{
"spm_logfile": str(tmp_path / "log"),
"spm_repos_config": str(tmp_path / "etc" / "spm.repos"),
"spm_cache_dir": str(tmp_path / "cache"),
"spm_build_dir": str(tmp_path / "build"),
"spm_build_exclude": [".git"],
"spm_db_provider": "sqlite3",
"spm_files_provider": "local",
"spm_db": str(tmp_path / "packages.db"),
"extension_modules": str(tmp_path / "modules"),
"file_roots": {"base": [str(tmp_path)]},
"formula_path": str(tmp_path / "spm"),
"pillar_path": str(tmp_path / "pillar"),
"reactor_path": str(tmp_path / "reactor"),
"assume_yes": True,
"root_dir": str(tmp_path),
"force": False,
"verbose": False,
"cache": "localfs",
"cachedir": str(tmp_path / "cache"),
"spm_repo_dups": "ignore",
"spm_share_dir": str(tmp_path / "share"),
}
)
ui = SPMTestUserInterface()
client = salt.spm.SPMClient(ui, minion_config)
minion_opts = salt.config.DEFAULT_MINION_OPTS.copy()
return tmp_path, ui, client, minion_config, minion_opts
def _create_formula_files(formula, _tmp_spm):
fdir = str(_tmp_spm / formula["definition"]["name"])
shutil.rmtree(fdir, ignore_errors=True)
os.mkdir(fdir)
for path, contents in formula["contents"]:
path = os.path.join(fdir, path)
dirname, _ = os.path.split(path)
if not os.path.exists(dirname):
os.makedirs(dirname)
with salt.utils.files.fopen(path, "w") as f:
f.write(contents)
return fdir
def test_build_install(setup_spm, f1_content):
# Build package
_tmp_spm, ui, client, minion_config, minion_opts = setup_spm
fdir = _create_formula_files(f1_content, _tmp_spm)
with patch("salt.client.Caller", MagicMock(return_value=minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=minion_opts["conf_file"]),
):
client.run(["build", fdir])
pkgpath = ui._status[-1].split()[-1]
assert os.path.exists(pkgpath)
# Install package
with patch("salt.client.Caller", MagicMock(return_value=minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=minion_opts["conf_file"]),
):
client.run(["local", "install", pkgpath])
# Check filesystem
for path, contents in f1_content["contents"]:
path = os.path.join(
minion_config["file_roots"]["base"][0],
f1_content["definition"]["name"],
path,
)
assert os.path.exists(path)
with salt.utils.files.fopen(path, "r") as rfh:
assert rfh.read() == contents
# Check database
with patch("salt.client.Caller", MagicMock(return_value=minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=minion_opts["conf_file"]),
):
client.run(["info", f1_content["definition"]["name"]])
lines = ui._status[-1].split("\n")
for key, line in (
("name", "Name: {0}"),
("version", "Version: {0}"),
("release", "Release: {0}"),
("summary", "Summary: {0}"),
):
assert line.format(f1_content["definition"][key]) in lines
# Reinstall with force=False, should fail
ui._error = []
with patch("salt.client.Caller", MagicMock(return_value=minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=minion_opts["conf_file"]),
):
client.run(["local", "install", pkgpath])
assert len(ui._error) > 0
# Reinstall with force=True, should succeed
with patch.dict(minion_config, {"force": True}):
ui._error = []
with patch("salt.client.Caller", MagicMock(return_value=minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=minion_opts["conf_file"]),
):
client.run(["local", "install", pkgpath])
assert len(ui._error) == 0
def test_failure_paths(setup_spm):
_tmp_spm, ui, client, minion_config, minion_opts = setup_spm
fail_args = (
["bogus", "command"],
["create_repo"],
["build"],
["build", "/nonexistent/path"],
["info"],
["info", "not_installed"],
["files"],
["files", "not_installed"],
["install"],
["install", "nonexistent.spm"],
["remove"],
["remove", "not_installed"],
["local", "bogus", "command"],
["local", "info"],
["local", "info", "/nonexistent/path/junk.spm"],
["local", "files"],
["local", "files", "/nonexistent/path/junk.spm"],
["local", "install"],
["local", "install", "/nonexistent/path/junk.spm"],
["local", "list"],
["local", "list", "/nonexistent/path/junk.spm"],
# XXX install failure due to missing deps
# XXX install failure due to missing field
)
for args in fail_args:
ui._error = []
with patch("salt.client.Caller", MagicMock(return_value=minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=minion_opts["conf_file"]),
):
client.run(args)
assert len(ui._error) > 0

View file

@ -80,9 +80,9 @@ class AdaptedConfigurationTestCaseMixin:
@staticmethod
def get_temp_config(config_for, **config_overrides):
rootdir = config_overrides.get(
"root_dir", tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
)
rootdir = config_overrides.get("root_dir")
if not rootdir:
rootdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
if not os.path.exists(rootdir):
os.makedirs(rootdir)
conf_dir = config_overrides.pop("conf_dir", os.path.join(rootdir, "conf"))

View file

@ -1,209 +0,0 @@
import os
import shutil
import tempfile
import pytest
import salt.config
import salt.spm
import salt.utils.files
from tests.support.mixins import AdaptedConfigurationTestCaseMixin
from tests.support.mock import MagicMock, patch
from tests.support.unit import TestCase
_F1 = {
"definition": {
"name": "formula1",
"version": "1.2",
"release": "2",
"summary": "test",
"description": "testing, nothing to see here",
}
}
_F1["contents"] = (
(
"FORMULA",
(
"name: {name}\n"
"version: {version}\n"
"release: {release}\n"
"summary: {summary}\n"
"description: {description}"
).format(**_F1["definition"]),
),
("modules/mod1.py", "# mod1.py"),
("modules/mod2.py", "# mod2.py"),
("states/state1.sls", "# state1.sls"),
("states/state2.sls", "# state2.sls"),
)
@pytest.mark.destructive_test
class SPMTestUserInterface(salt.spm.SPMUserInterface):
"""
Unit test user interface to SPMClient
"""
def __init__(self):
self._status = []
self._confirm = []
self._error = []
def status(self, msg):
self._status.append(msg)
def confirm(self, action):
self._confirm.append(action)
def error(self, msg):
self._error.append(msg)
class SPMTest(TestCase, AdaptedConfigurationTestCaseMixin):
def setUp(self):
self._tmp_spm = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self._tmp_spm, ignore_errors=True)
minion_config = self.get_temp_config(
"minion",
**{
"spm_logfile": os.path.join(self._tmp_spm, "log"),
"spm_repos_config": os.path.join(self._tmp_spm, "etc", "spm.repos"),
"spm_cache_dir": os.path.join(self._tmp_spm, "cache"),
"spm_build_dir": os.path.join(self._tmp_spm, "build"),
"spm_build_exclude": [".git"],
"spm_db_provider": "sqlite3",
"spm_files_provider": "local",
"spm_db": os.path.join(self._tmp_spm, "packages.db"),
"extension_modules": os.path.join(self._tmp_spm, "modules"),
"file_roots": {"base": [self._tmp_spm]},
"formula_path": os.path.join(self._tmp_spm, "spm"),
"pillar_path": os.path.join(self._tmp_spm, "pillar"),
"reactor_path": os.path.join(self._tmp_spm, "reactor"),
"assume_yes": True,
"force": False,
"verbose": False,
"cache": "localfs",
"cachedir": os.path.join(self._tmp_spm, "cache"),
"spm_repo_dups": "ignore",
"spm_share_dir": os.path.join(self._tmp_spm, "share"),
}
)
self.ui = SPMTestUserInterface()
self.client = salt.spm.SPMClient(self.ui, minion_config)
self.minion_config = minion_config
for attr in ("client", "ui", "_tmp_spm", "minion_config"):
self.addCleanup(delattr, self, attr)
def _create_formula_files(self, formula):
fdir = os.path.join(self._tmp_spm, formula["definition"]["name"])
shutil.rmtree(fdir, ignore_errors=True)
os.mkdir(fdir)
for path, contents in formula["contents"]:
path = os.path.join(fdir, path)
dirname, _ = os.path.split(path)
if not os.path.exists(dirname):
os.makedirs(dirname)
with salt.utils.files.fopen(path, "w") as f:
f.write(contents)
return fdir
def test_build_install(self):
# Build package
fdir = self._create_formula_files(_F1)
with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=self.minion_opts["conf_file"]),
):
self.client.run(["build", fdir])
pkgpath = self.ui._status[-1].split()[-1]
assert os.path.exists(pkgpath)
# Install package
with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=self.minion_opts["conf_file"]),
):
self.client.run(["local", "install", pkgpath])
# Check filesystem
for path, contents in _F1["contents"]:
path = os.path.join(
self.minion_config["file_roots"]["base"][0],
_F1["definition"]["name"],
path,
)
assert os.path.exists(path)
with salt.utils.files.fopen(path, "r") as rfh:
assert rfh.read() == contents
# Check database
with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=self.minion_opts["conf_file"]),
):
self.client.run(["info", _F1["definition"]["name"]])
lines = self.ui._status[-1].split("\n")
for key, line in (
("name", "Name: {0}"),
("version", "Version: {0}"),
("release", "Release: {0}"),
("summary", "Summary: {0}"),
):
assert line.format(_F1["definition"][key]) in lines
# Reinstall with force=False, should fail
self.ui._error = []
with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=self.minion_opts["conf_file"]),
):
self.client.run(["local", "install", pkgpath])
assert len(self.ui._error) > 0
# Reinstall with force=True, should succeed
with patch.dict(self.minion_config, {"force": True}):
self.ui._error = []
with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=self.minion_opts["conf_file"]),
):
self.client.run(["local", "install", pkgpath])
assert len(self.ui._error) == 0
def test_failure_paths(self):
fail_args = (
["bogus", "command"],
["create_repo"],
["build"],
["build", "/nonexistent/path"],
["info"],
["info", "not_installed"],
["files"],
["files", "not_installed"],
["install"],
["install", "nonexistent.spm"],
["remove"],
["remove", "not_installed"],
["local", "bogus", "command"],
["local", "info"],
["local", "info", "/nonexistent/path/junk.spm"],
["local", "files"],
["local", "files", "/nonexistent/path/junk.spm"],
["local", "install"],
["local", "install", "/nonexistent/path/junk.spm"],
["local", "list"],
["local", "list", "/nonexistent/path/junk.spm"],
# XXX install failure due to missing deps
# XXX install failure due to missing field
)
for args in fail_args:
self.ui._error = []
with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)):
with patch(
"salt.client.get_local_client",
MagicMock(return_value=self.minion_opts["conf_file"]),
):
self.client.run(args)
assert len(self.ui._error) > 0