SHA256
1
0
forked from pool/salt
salt/debian-info_installed-compatibility-50453.patch

1498 lines
54 KiB
Diff
Raw Normal View History

From 1ccca51897eb7c7cf1bace7015a4307aa0be7215 Mon Sep 17 00:00:00 2001
From: Bo Maryniuk <bo@suse.de>
Date: Tue, 20 Nov 2018 16:06:31 +0100
Subject: [PATCH] Debian info_installed compatibility (#50453)
Remove unused variable
Get unit ticks installation time
Pass on unix ticks installation date time
Implement function to figure out package build time
Unify arch attribute
Add 'attr' support.
Use attr parameter in aptpkg
Add 'all_versions' output structure backward compatibility
Fix docstring
Add UT for generic test of function 'info'
Add UT for 'info' function with the parameter 'attr'
Add UT for info_installed's 'attr' param
Fix docstring
Add returned type check
Add UT for info_installed with 'all_versions=True' output structure
Refactor UT for 'owner' function
Refactor UT: move to decorators, add more checks
Schedule TODO for next refactoring of UT 'show' function
Refactor UT: get rid of old assertion way, flatten tests
Refactor UT: move to native assertions, cleanup noise, flatten complexity for better visibility what is tested
Lintfix: too many empty lines
Adjust architecture getter according to the lowpkg info
Fix wrong Git merge: missing function signature
Reintroducing reverted changes
Reintroducing changes from commit e20362f6f053eaa4144583604e6aac3d62838419
that got partially reverted by this commit:
https://github.com/openSUSE/salt/commit/d0ef24d113bdaaa29f180031b5da384cffe08c64#diff-820e6ce667fe3afddbc1b9cf1682fdef
---
salt/modules/aptpkg.py | 24 +-
salt/modules/dpkg_lowpkg.py | 108 ++-
tests/unit/modules/test_aptpkg.py | 981 +++++++++++++++++++++++++
tests/unit/modules/test_dpkg_lowpkg.py | 189 +++--
4 files changed, 1203 insertions(+), 99 deletions(-)
create mode 100644 tests/unit/modules/test_aptpkg.py
diff --git a/salt/modules/aptpkg.py b/salt/modules/aptpkg.py
index 86c85bb95c..06db908d3d 100644
--- a/salt/modules/aptpkg.py
+++ b/salt/modules/aptpkg.py
@@ -2920,6 +2920,15 @@ def info_installed(*names, **kwargs):
.. versionadded:: 2016.11.3
+ attr
+ Comma-separated package attributes. If no 'attr' is specified, all available attributes returned.
+
+ Valid attributes are:
+ version, vendor, release, build_date, build_date_time_t, install_date, install_date_time_t,
+ build_host, group, source_rpm, arch, epoch, size, license, signature, packager, url, summary, description.
+
+ .. versionadded:: Neon
+
CLI Example:
.. code-block:: bash
@@ -2930,11 +2939,19 @@ def info_installed(*names, **kwargs):
"""
kwargs = salt.utils.args.clean_kwargs(**kwargs)
failhard = kwargs.pop("failhard", True)
+ kwargs.pop("errors", None) # Only for compatibility with RPM
+ attr = kwargs.pop("attr", None) # Package attributes to return
+ all_versions = kwargs.pop(
+ "all_versions", False
+ ) # This is for backward compatible structure only
+
if kwargs:
salt.utils.args.invalid_kwargs(kwargs)
ret = dict()
- for pkg_name, pkg_nfo in __salt__["lowpkg.info"](*names, failhard=failhard).items():
+ for pkg_name, pkg_nfo in __salt__["lowpkg.info"](
+ *names, failhard=failhard, attr=attr
+ ).items():
t_nfo = dict()
if pkg_nfo.get("status", "ii")[1] != "i":
continue # return only packages that are really installed
@@ -2955,7 +2972,10 @@ def info_installed(*names, **kwargs):
else:
t_nfo[key] = value
- ret[pkg_name] = t_nfo
+ if all_versions:
+ ret.setdefault(pkg_name, []).append(t_nfo)
+ else:
+ ret[pkg_name] = t_nfo
return ret
diff --git a/salt/modules/dpkg_lowpkg.py b/salt/modules/dpkg_lowpkg.py
index 6a88573a8f..afbd619490 100644
--- a/salt/modules/dpkg_lowpkg.py
+++ b/salt/modules/dpkg_lowpkg.py
@@ -234,6 +234,44 @@ def file_dict(*packages, **kwargs):
return {"errors": errors, "packages": ret}
+def _get_pkg_build_time(name):
+ """
+ Get package build time, if possible.
+
+ :param name:
+ :return:
+ """
+ iso_time = iso_time_t = None
+ changelog_dir = os.path.join("/usr/share/doc", name)
+ if os.path.exists(changelog_dir):
+ for fname in os.listdir(changelog_dir):
+ try:
+ iso_time_t = int(os.path.getmtime(os.path.join(changelog_dir, fname)))
+ iso_time = (
+ datetime.datetime.utcfromtimestamp(iso_time_t).isoformat() + "Z"
+ )
+ break
+ except OSError:
+ pass
+
+ # Packager doesn't care about Debian standards, therefore Plan B: brute-force it.
+ if not iso_time:
+ for pkg_f_path in __salt__["cmd.run"](
+ "dpkg-query -L {}".format(name)
+ ).splitlines():
+ if "changelog" in pkg_f_path.lower() and os.path.exists(pkg_f_path):
+ try:
+ iso_time_t = int(os.path.getmtime(pkg_f_path))
+ iso_time = (
+ datetime.datetime.utcfromtimestamp(iso_time_t).isoformat() + "Z"
+ )
+ break
+ except OSError:
+ pass
+
+ return iso_time, iso_time_t
+
+
def _get_pkg_info(*packages, **kwargs):
"""
Return list of package information. If 'packages' parameter is empty,
@@ -257,7 +295,7 @@ def _get_pkg_info(*packages, **kwargs):
cmd = (
"dpkg-query -W -f='package:" + bin_var + "\\n"
"revision:${binary:Revision}\\n"
- "architecture:${Architecture}\\n"
+ "arch:${Architecture}\\n"
"maintainer:${Maintainer}\\n"
"summary:${Summary}\\n"
"source:${source:Package}\\n"
@@ -296,9 +334,16 @@ def _get_pkg_info(*packages, **kwargs):
key, value = pkg_info_line.split(":", 1)
if value:
pkg_data[key] = value
- install_date = _get_pkg_install_time(pkg_data.get("package"))
- if install_date:
- pkg_data["install_date"] = install_date
+ install_date, install_date_t = _get_pkg_install_time(
+ pkg_data.get("package"), pkg_data.get("arch")
+ )
+ if install_date:
+ pkg_data["install_date"] = install_date
+ pkg_data["install_date_time_t"] = install_date_t # Unix ticks
+ build_date, build_date_t = _get_pkg_build_time(pkg_data.get("package"))
+ if build_date:
+ pkg_data["build_date"] = build_date
+ pkg_data["build_date_time_t"] = build_date_t
pkg_data["description"] = pkg_descr.split(":", 1)[-1]
ret.append(pkg_data)
@@ -324,24 +369,34 @@ def _get_pkg_license(pkg):
return ", ".join(sorted(licenses))
-def _get_pkg_install_time(pkg):
+def _get_pkg_install_time(pkg, arch):
"""
Return package install time, based on the /var/lib/dpkg/info/<package>.list
:return:
"""
- iso_time = None
+ iso_time = iso_time_t = None
+ loc_root = "/var/lib/dpkg/info"
if pkg is not None:
- location = "/var/lib/dpkg/info/{}.list".format(pkg)
- if os.path.exists(location):
- iso_time = (
- datetime.datetime.utcfromtimestamp(
- int(os.path.getmtime(location))
- ).isoformat()
- + "Z"
- )
+ locations = []
+ if arch is not None and arch != "all":
+ locations.append(os.path.join(loc_root, "{}:{}.list".format(pkg, arch)))
- return iso_time
+ locations.append(os.path.join(loc_root, "{}.list".format(pkg)))
+ for location in locations:
+ try:
+ iso_time_t = int(os.path.getmtime(location))
+ iso_time = (
+ datetime.datetime.utcfromtimestamp(iso_time_t).isoformat() + "Z"
+ )
+ break
+ except OSError:
+ pass
+
+ if iso_time is None:
+ log.debug('Unable to get package installation time for package "%s".', pkg)
+
+ return iso_time, iso_time_t
def _get_pkg_ds_avail():
@@ -391,6 +446,15 @@ def info(*packages, **kwargs):
.. versionadded:: 2016.11.3
+ attr
+ Comma-separated package attributes. If no 'attr' is specified, all available attributes returned.
+
+ Valid attributes are:
+ version, vendor, release, build_date, build_date_time_t, install_date, install_date_time_t,
+ build_host, group, source_rpm, arch, epoch, size, license, signature, packager, url, summary, description.
+
+ .. versionadded:: Neon
+
CLI Example:
.. code-block:: bash
@@ -405,6 +469,10 @@ def info(*packages, **kwargs):
kwargs = salt.utils.args.clean_kwargs(**kwargs)
failhard = kwargs.pop("failhard", True)
+ attr = kwargs.pop("attr", None) or None
+ if attr:
+ attr = attr.split(",")
+
if kwargs:
salt.utils.args.invalid_kwargs(kwargs)
@@ -432,6 +500,14 @@ def info(*packages, **kwargs):
lic = _get_pkg_license(pkg["package"])
if lic:
pkg["license"] = lic
- ret[pkg["package"]] = pkg
+
+ # Remove keys that aren't in attrs
+ pkg_name = pkg["package"]
+ if attr:
+ for k in list(pkg.keys())[:]:
+ if k not in attr:
+ del pkg[k]
+
+ ret[pkg_name] = pkg
return ret
diff --git a/tests/unit/modules/test_aptpkg.py b/tests/unit/modules/test_aptpkg.py
new file mode 100644
index 0000000000..3c9744e224
--- /dev/null
+++ b/tests/unit/modules/test_aptpkg.py
@@ -0,0 +1,981 @@
+"""
+ :synopsis: Unit Tests for Advanced Packaging Tool module 'module.aptpkg'
+ :platform: Linux
+ :maturity: develop
+ versionadded:: 2017.7.0
+"""
+
+
+import copy
+import logging
+import textwrap
+
+import pytest
+import salt.modules.aptpkg as aptpkg
+from salt.exceptions import CommandExecutionError, SaltInvocationError
+from salt.ext import six
+from tests.support.mixins import LoaderModuleMockMixin
+from tests.support.mock import MagicMock, Mock, call, patch
+from tests.support.unit import TestCase, skipIf
+
+log = logging.getLogger(__name__)
+
+
+APT_KEY_LIST = r"""
+pub:-:1024:17:46181433FBB75451:1104433784:::-:::scSC:
+fpr:::::::::C5986B4F1257FFA86632CBA746181433FBB75451:
+uid:-::::1104433784::B4D41942D4B35FF44182C7F9D00C99AF27B93AD0::Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>:
+"""
+
+REPO_KEYS = {
+ "46181433FBB75451": {
+ "algorithm": 17,
+ "bits": 1024,
+ "capability": "scSC",
+ "date_creation": 1104433784,
+ "date_expiration": None,
+ "fingerprint": "C5986B4F1257FFA86632CBA746181433FBB75451",
+ "keyid": "46181433FBB75451",
+ "uid": "Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>",
+ "uid_hash": "B4D41942D4B35FF44182C7F9D00C99AF27B93AD0",
+ "validity": "-",
+ }
+}
+
+PACKAGES = {"wget": "1.15-1ubuntu1.14.04.2"}
+
+LOWPKG_FILES = {
+ "errors": {},
+ "packages": {
+ "wget": [
+ "/.",
+ "/etc",
+ "/etc/wgetrc",
+ "/usr",
+ "/usr/bin",
+ "/usr/bin/wget",
+ "/usr/share",
+ "/usr/share/info",
+ "/usr/share/info/wget.info.gz",
+ "/usr/share/doc",
+ "/usr/share/doc/wget",
+ "/usr/share/doc/wget/MAILING-LIST",
+ "/usr/share/doc/wget/NEWS.gz",
+ "/usr/share/doc/wget/AUTHORS",
+ "/usr/share/doc/wget/copyright",
+ "/usr/share/doc/wget/changelog.Debian.gz",
+ "/usr/share/doc/wget/README",
+ "/usr/share/man",
+ "/usr/share/man/man1",
+ "/usr/share/man/man1/wget.1.gz",
+ ]
+ },
+}
+
+LOWPKG_INFO = {
+ "wget": {
+ "architecture": "amd64",
+ "description": "retrieves files from the web",
+ "homepage": "http://www.gnu.org/software/wget/",
+ "install_date": "2016-08-30T22:20:15Z",
+ "maintainer": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
+ "name": "wget",
+ "section": "web",
+ "source": "wget",
+ "version": "1.15-1ubuntu1.14.04.2",
+ "status": "ii",
+ },
+ "apache2": {
+ "architecture": "amd64",
+ "description": """Apache HTTP Server
+ The Apache HTTP Server Project's goal is to build a secure, efficient and
+ extensible HTTP server as standards-compliant open source software. The
+ result has long been the number one web server on the Internet.
+ .
+ Installing this package results in a full installation, including the
+ configuration files, init scripts and support scripts.""",
+ "homepage": "http://httpd.apache.org/",
+ "install_date": "2016-08-30T22:20:15Z",
+ "maintainer": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
+ "name": "apache2",
+ "section": "httpd",
+ "source": "apache2",
+ "version": "2.4.18-2ubuntu3.9",
+ "status": "rc",
+ },
+}
+
+APT_Q_UPDATE = """
+Get:1 http://security.ubuntu.com trusty-security InRelease [65 kB]
+Get:2 http://security.ubuntu.com trusty-security/main Sources [120 kB]
+Get:3 http://security.ubuntu.com trusty-security/main amd64 Packages [548 kB]
+Get:4 http://security.ubuntu.com trusty-security/main i386 Packages [507 kB]
+Hit http://security.ubuntu.com trusty-security/main Translation-en
+Fetched 1240 kB in 10s (124 kB/s)
+Reading package lists...
+"""
+
+APT_Q_UPDATE_ERROR = """
+Err http://security.ubuntu.com trusty InRelease
+
+Err http://security.ubuntu.com trusty Release.gpg
+Unable to connect to security.ubuntu.com:http:
+Reading package lists...
+W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/trusty/InRelease
+
+W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/trusty/Release.gpg Unable to connect to security.ubuntu.com:http:
+
+W: Some index files failed to download. They have been ignored, or old ones used instead.
+"""
+
+AUTOREMOVE = """
+Reading package lists... Done
+Building dependency tree
+Reading state information... Done
+0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
+"""
+
+UPGRADE = """
+Reading package lists...
+Building dependency tree...
+Reading state information...
+0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
+"""
+
+UNINSTALL = {"tmux": {"new": "", "old": "1.8-5"}}
+INSTALL = {"tmux": {"new": "1.8-5", "old": ""}}
+
+
+def _get_uri(repo):
+ """
+ Get the URI portion of the a string
+ """
+ splits = repo.split()
+ for val in splits:
+ if any(val.startswith(x) for x in ("http://", "https://", "ftp://")):
+ return val
+
+
+class MockSourceEntry:
+ def __init__(self, uri, source_type, line, invalid, file=None):
+ self.uri = uri
+ self.type = source_type
+ self.line = line
+ self.invalid = invalid
+ self.file = file
+ self.disabled = False
+ self.dist = ""
+
+ def mysplit(self, line):
+ return line.split()
+
+
+class MockSourceList:
+ def __init__(self):
+ self.list = []
+
+
+class AptPkgTestCase(TestCase, LoaderModuleMockMixin):
+ """
+ Test cases for salt.modules.aptpkg
+ """
+
+ def setup_loader_modules(self):
+ return {aptpkg: {"__grains__": {}}}
+
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {
+ "pkg_resource.version": MagicMock(
+ return_value=LOWPKG_INFO["wget"]["version"]
+ )
+ },
+ )
+ def test_version(self):
+ """
+ Test - Returns a string representing the package version or an empty string if
+ not installed.
+ """
+ assert aptpkg.version(*["wget"]) == aptpkg.__salt__["pkg_resource.version"]()
+
+ @patch("salt.modules.aptpkg.latest_version", MagicMock(return_value=""))
+ def test_upgrade_available(self):
+ """
+ Test - Check whether or not an upgrade is available for a given package.
+ """
+ assert not aptpkg.upgrade_available("wget")
+
+ @patch("salt.modules.aptpkg.get_repo_keys", MagicMock(return_value=REPO_KEYS))
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {"cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": "OK"})},
+ )
+ def test_add_repo_key(self):
+ """
+ Test - Add a repo key.
+ """
+ assert aptpkg.add_repo_key(keyserver="keyserver.ubuntu.com", keyid="FBB75451")
+
+ @patch("salt.modules.aptpkg.get_repo_keys", MagicMock(return_value=REPO_KEYS))
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {"cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": "OK"})},
+ )
+ def test_add_repo_key_failed(self):
+ """
+ Test - Add a repo key using incomplete input data.
+ """
+ with pytest.raises(SaltInvocationError) as ex:
+ aptpkg.add_repo_key(keyserver="keyserver.ubuntu.com")
+ assert (
+ " No keyid or keyid too short for keyserver: keyserver.ubuntu.com"
+ in str(ex)
+ )
+
+ def test_get_repo_keys(self):
+ """
+ Test - List known repo key details.
+ """
+ mock = MagicMock(return_value={"retcode": 0, "stdout": APT_KEY_LIST})
+ with patch.dict(aptpkg.__salt__, {"cmd.run_all": mock}):
+ self.assertEqual(aptpkg.get_repo_keys(), REPO_KEYS)
+
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {"lowpkg.file_dict": MagicMock(return_value=LOWPKG_FILES)},
+ )
+ def test_file_dict(self):
+ """
+ Test - List the files that belong to a package, grouped by package.
+ """
+ assert aptpkg.file_dict("wget") == LOWPKG_FILES
+
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {
+ "lowpkg.file_list": MagicMock(
+ return_value={
+ "errors": LOWPKG_FILES["errors"],
+ "files": LOWPKG_FILES["packages"]["wget"],
+ }
+ )
+ },
+ )
+ def test_file_list(self):
+ """
+ Test 'file_list' function, which is just an alias to the lowpkg 'file_list'
+
+ """
+ assert aptpkg.file_list("wget") == aptpkg.__salt__["lowpkg.file_list"]()
+
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {"cmd.run_stdout": MagicMock(return_value="wget\t\t\t\t\t\tinstall")},
+ )
+ def test_get_selections(self):
+ """
+ Test - View package state from the dpkg database.
+ """
+ assert aptpkg.get_selections("wget") == {"install": ["wget"]}
+
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {"lowpkg.info": MagicMock(return_value=LOWPKG_INFO)},
+ )
+ def test_info_installed(self):
+ """
+ Test - Return the information of the named package(s) installed on the system.
+ """
+ names = {"group": "section", "packager": "maintainer", "url": "homepage"}
+
+ installed = copy.deepcopy({"wget": LOWPKG_INFO["wget"]})
+ for name in names:
+ if installed["wget"].get(names[name], False):
+ installed["wget"][name] = installed["wget"].pop(names[name])
+
+ del installed["wget"]["status"]
+ self.assertEqual(aptpkg.info_installed("wget"), installed)
+ self.assertEqual(len(aptpkg.info_installed()), 1)
+
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {"lowpkg.info": MagicMock(return_value=LOWPKG_INFO)},
+ )
+ def test_info_installed_attr(self):
+ """
+ Test info_installed 'attr'.
+ This doesn't test 'attr' behaviour per se, since the underlying function is in dpkg.
+ The test should simply not raise exceptions for invalid parameter.
+
+ :return:
+ """
+ ret = aptpkg.info_installed("emacs", attr="foo,bar")
+ assert isinstance(ret, dict)
+ assert "wget" in ret
+ assert isinstance(ret["wget"], dict)
+
+ wget_pkg = ret["wget"]
+ expected_pkg = {
+ "url": "http://www.gnu.org/software/wget/",
+ "packager": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
+ "name": "wget",
+ "install_date": "2016-08-30T22:20:15Z",
+ "description": "retrieves files from the web",
+ "version": "1.15-1ubuntu1.14.04.2",
+ "architecture": "amd64",
+ "group": "web",
+ "source": "wget",
+ }
+ for k in wget_pkg:
+ assert k in expected_pkg
+ assert wget_pkg[k] == expected_pkg[k]
+
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {"lowpkg.info": MagicMock(return_value=LOWPKG_INFO)},
+ )
+ def test_info_installed_all_versions(self):
+ """
+ Test info_installed 'all_versions'.
+ Since Debian won't return same name packages with the different names,
+ this should just return different structure, backward compatible with
+ the RPM equivalents.
+
+ :return:
+ """
+ print()
+ ret = aptpkg.info_installed("emacs", all_versions=True)
+ assert isinstance(ret, dict)
+ assert "wget" in ret
+ assert isinstance(ret["wget"], list)
+
+ pkgs = ret["wget"]
+
+ assert len(pkgs) == 1
+ assert isinstance(pkgs[0], dict)
+
+ wget_pkg = pkgs[0]
+ expected_pkg = {
+ "url": "http://www.gnu.org/software/wget/",
+ "packager": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
+ "name": "wget",
+ "install_date": "2016-08-30T22:20:15Z",
+ "description": "retrieves files from the web",
+ "version": "1.15-1ubuntu1.14.04.2",
+ "architecture": "amd64",
+ "group": "web",
+ "source": "wget",
+ }
+ for k in wget_pkg:
+ assert k in expected_pkg
+ assert wget_pkg[k] == expected_pkg[k]
+
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {"cmd.run_stdout": MagicMock(return_value="wget: /usr/bin/wget")},
+ )
+ def test_owner(self):
+ """
+ Test - Return the name of the package that owns the file.
+ """
+ assert aptpkg.owner("/usr/bin/wget") == "wget"
+
+ @patch("salt.utils.pkg.clear_rtag", MagicMock())
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {
+ "cmd.run_all": MagicMock(
+ return_value={"retcode": 0, "stdout": APT_Q_UPDATE}
+ ),
+ "config.get": MagicMock(return_value=False),
+ },
+ )
+ def test_refresh_db(self):
+ """
+ Test - Updates the APT database to latest packages based upon repositories.
+ """
+ refresh_db = {
+ "http://security.ubuntu.com trusty-security InRelease": True,
+ "http://security.ubuntu.com trusty-security/main Sources": True,
+ "http://security.ubuntu.com trusty-security/main Translation-en": None,
+ "http://security.ubuntu.com trusty-security/main amd64 Packages": True,
+ "http://security.ubuntu.com trusty-security/main i386 Packages": True,
+ }
+ mock = MagicMock(return_value={"retcode": 0, "stdout": APT_Q_UPDATE})
+ with patch("salt.utils.pkg.clear_rtag", MagicMock()):
+ with patch.dict(
+ aptpkg.__salt__,
+ {"cmd.run_all": mock, "config.get": MagicMock(return_value=False)},
+ ):
+ self.assertEqual(aptpkg.refresh_db(), refresh_db)
+
+ @patch("salt.utils.pkg.clear_rtag", MagicMock())
+ @patch(
+ "salt.modules.aptpkg.__salt__",
+ {
+ "cmd.run_all": MagicMock(
+ return_value={"retcode": 0, "stdout": APT_Q_UPDATE_ERROR}
+ ),
+ "config.get": MagicMock(return_value=False),
+ },
+ )
+ def test_refresh_db_failed(self):
+ """
+ Test - Update the APT database using unreachable repositories.
+ """
+ kwargs = {"failhard": True}
+ mock = MagicMock(return_value={"retcode": 0, "stdout": APT_Q_UPDATE_ERROR})
+ with patch("salt.utils.pkg.clear_rtag", MagicMock()):
+ with patch.dict(
+ aptpkg.__salt__,
+ {"cmd.run_all": mock, "config.get": MagicMock(return_value=False)},
+ ):
+ self.assertRaises(CommandExecutionError, aptpkg.refresh_db, **kwargs)
+
+ def test_autoremove(self):
+ """
+ Test - Remove packages not required by another package.
+ """
+ with patch("salt.modules.aptpkg.list_pkgs", MagicMock(return_value=PACKAGES)):
+ patch_kwargs = {
+ "__salt__": {
+ "config.get": MagicMock(return_value=True),
+ "cmd.run_all": MagicMock(
+ return_value=MagicMock(return_value=AUTOREMOVE)
+ ),
+ }
+ }
+ with patch.multiple(aptpkg, **patch_kwargs):
+ assert aptpkg.autoremove() == {}
+ assert aptpkg.autoremove(purge=True) == {}
+ assert aptpkg.autoremove(list_only=True) == []
+ assert aptpkg.autoremove(list_only=True, purge=True) == []
+
+ @patch("salt.modules.aptpkg._uninstall", MagicMock(return_value=UNINSTALL))
+ def test_remove(self):
+ """
+ Test - Remove packages.
+ """
+ assert aptpkg.remove(name="tmux") == UNINSTALL
+
+ @patch("salt.modules.aptpkg._uninstall", MagicMock(return_value=UNINSTALL))
+ def test_purge(self):
+ """
+ Test - Remove packages along with all configuration files.
+ """
+ assert aptpkg.purge(name="tmux") == UNINSTALL
+
+ @patch("salt.utils.pkg.clear_rtag", MagicMock())
+ @patch("salt.modules.aptpkg.list_pkgs", MagicMock(return_value=UNINSTALL))
+ @patch.multiple(
+ aptpkg,
+ **{
+ "__salt__": {
+ "config.get": MagicMock(return_value=True),
+ "cmd.run_all": MagicMock(
+ return_value={"retcode": 0, "stdout": UPGRADE}
+ ),
+ }
+ }
+ )
+ def test_upgrade(self):
+ """
+ Test - Upgrades all packages.
+ """
+ with patch("salt.utils.pkg.clear_rtag", MagicMock()):
+ with patch(
+ "salt.modules.aptpkg.list_pkgs", MagicMock(return_value=UNINSTALL)
+ ):
+ mock_cmd = MagicMock(return_value={"retcode": 0, "stdout": UPGRADE})
+ patch_kwargs = {
+ "__salt__": {
+ "config.get": MagicMock(return_value=True),
+ "cmd.run_all": mock_cmd,
+ }
+ }
+ with patch.multiple(aptpkg, **patch_kwargs):
+ self.assertEqual(aptpkg.upgrade(), dict())
+ kwargs = {"force_conf_new": True}
+ self.assertEqual(aptpkg.upgrade(**kwargs), dict())
+
+ def test_upgrade_downloadonly(self):
+ """
+ Tests the download-only options for upgrade.
+ """
+ with patch("salt.utils.pkg.clear_rtag", MagicMock()):
+ with patch(
+ "salt.modules.aptpkg.list_pkgs", MagicMock(return_value=UNINSTALL)
+ ):
+ mock_cmd = MagicMock(return_value={"retcode": 0, "stdout": UPGRADE})
+ patch_kwargs = {
+ "__salt__": {
+ "config.get": MagicMock(return_value=True),
+ "cmd.run_all": mock_cmd,
+ },
+ }
+ with patch.multiple(aptpkg, **patch_kwargs):
+ aptpkg.upgrade()
+ args_matching = [
+ True
+ for args in patch_kwargs["__salt__"]["cmd.run_all"].call_args[0]
+ if "--download-only" in args
+ ]
+ # Here we shouldn't see the parameter and args_matching should be empty.
+ self.assertFalse(any(args_matching))
+
+ aptpkg.upgrade(downloadonly=True)
+ args_matching = [
+ True
+ for args in patch_kwargs["__salt__"]["cmd.run_all"].call_args[0]
+ if "--download-only" in args
+ ]
+ # --download-only should be in the args list and we should have at least on True in the list.
+ self.assertTrue(any(args_matching))
+
+ aptpkg.upgrade(download_only=True)
+ args_matching = [
+ True
+ for args in patch_kwargs["__salt__"]["cmd.run_all"].call_args[0]
+ if "--download-only" in args
+ ]
+ # --download-only should be in the args list and we should have at least on True in the list.
+ self.assertTrue(any(args_matching))
+
+ def test_show(self):
+ """
+ Test that the pkg.show function properly parses apt-cache show output.
+ This test uses an abridged output per package, for simplicity.
+ """
+ show_mock_success = MagicMock(
+ return_value={
+ "retcode": 0,
+ "pid": 12345,
+ "stderr": "",
+ "stdout": textwrap.dedent(
+ """\
+ Package: foo1.0
+ Architecture: amd64
+ Version: 1.0.5-3ubuntu4
+ Description: A silly package (1.0 release cycle)
+ Provides: foo
+ Suggests: foo-doc
+
+ Package: foo1.0
+ Architecture: amd64
+ Version: 1.0.4-2ubuntu1
+ Description: A silly package (1.0 release cycle)
+ Provides: foo
+ Suggests: foo-doc
+
+ Package: foo-doc
+ Architecture: all
+ Version: 1.0.5-3ubuntu4
+ Description: Silly documentation for a silly package (1.0 release cycle)
+
+ Package: foo-doc
+ Architecture: all
+ Version: 1.0.4-2ubuntu1
+ Description: Silly documentation for a silly package (1.0 release cycle)
+
+ """
+ ),
+ }
+ )
+
+ show_mock_failure = MagicMock(
+ return_value={
+ "retcode": 1,
+ "pid": 12345,
+ "stderr": textwrap.dedent(
+ """\
+ N: Unable to locate package foo*
+ N: Couldn't find any package by glob 'foo*'
+ N: Couldn't find any package by regex 'foo*'
+ E: No packages found
+ """
+ ),
+ "stdout": "",
+ }
+ )
+
+ refresh_mock = Mock()
+
+ expected = {
+ "foo1.0": {
+ "1.0.5-3ubuntu4": {
+ "Architecture": "amd64",
+ "Description": "A silly package (1.0 release cycle)",
+ "Provides": "foo",
+ "Suggests": "foo-doc",
+ },
+ "1.0.4-2ubuntu1": {
+ "Architecture": "amd64",
+ "Description": "A silly package (1.0 release cycle)",
+ "Provides": "foo",
+ "Suggests": "foo-doc",
+ },
+ },
+ "foo-doc": {
+ "1.0.5-3ubuntu4": {
+ "Architecture": "all",
+ "Description": "Silly documentation for a silly package (1.0 release cycle)",
+ },
+ "1.0.4-2ubuntu1": {
+ "Architecture": "all",
+ "Description": "Silly documentation for a silly package (1.0 release cycle)",
+ },
+ },
+ }
+
+ # Make a copy of the above dict and strip out some keys to produce the
+ # expected filtered result.
+ filtered = copy.deepcopy(expected)
+ for k1 in filtered:
+ for k2 in filtered[k1]:
+ # Using list() because we will modify the dict during iteration
+ for k3 in list(filtered[k1][k2]):
+ if k3 not in ("Description", "Provides"):
+ filtered[k1][k2].pop(k3)
+
+ with patch.dict(
+ aptpkg.__salt__, {"cmd.run_all": show_mock_success}
+ ), patch.object(aptpkg, "refresh_db", refresh_mock):
+
+ # Test success (no refresh)
+ self.assertEqual(aptpkg.show("foo*"), expected)
+ refresh_mock.assert_not_called()
+ refresh_mock.reset_mock()
+
+ # Test success (with refresh)
+ self.assertEqual(aptpkg.show("foo*", refresh=True), expected)
+ self.assert_called_once(refresh_mock)
+ refresh_mock.reset_mock()
+
+ # Test filtered return
+ self.assertEqual(
+ aptpkg.show("foo*", filter="description,provides"), filtered
+ )
+ refresh_mock.assert_not_called()
+ refresh_mock.reset_mock()
+
+ with patch.dict(
+ aptpkg.__salt__, {"cmd.run_all": show_mock_failure}
+ ), patch.object(aptpkg, "refresh_db", refresh_mock):
+
+ # Test failure (no refresh)
+ self.assertEqual(aptpkg.show("foo*"), {})
+ refresh_mock.assert_not_called()
+ refresh_mock.reset_mock()
+
+ # Test failure (with refresh)
+ self.assertEqual(aptpkg.show("foo*", refresh=True), {})
+ self.assert_called_once(refresh_mock)
+ refresh_mock.reset_mock()
+
+ def test_mod_repo_enabled(self):
+ """
+ Checks if a repo is enabled or disabled depending on the passed kwargs.
+ """
+ with patch.dict(
+ aptpkg.__salt__,
+ {"config.option": MagicMock(), "no_proxy": MagicMock(return_value=False)},
+ ):
+ with patch("salt.modules.aptpkg._check_apt", MagicMock(return_value=True)):
+ with patch(
+ "salt.modules.aptpkg.refresh_db", MagicMock(return_value={})
+ ):
+ with patch(
+ "salt.utils.data.is_true", MagicMock(return_value=True)
+ ) as data_is_true:
+ with patch(
+ "salt.modules.aptpkg.sourceslist", MagicMock(), create=True
+ ):
+ repo = aptpkg.mod_repo("foo", enabled=False)
+ data_is_true.assert_called_with(False)
+ # with disabled=True; should call salt.utils.data.is_true True
+ data_is_true.reset_mock()
+ repo = aptpkg.mod_repo("foo", disabled=True)
+ data_is_true.assert_called_with(True)
+ # with enabled=True; should call salt.utils.data.is_true with False
+ data_is_true.reset_mock()
+ repo = aptpkg.mod_repo("foo", enabled=True)
+ data_is_true.assert_called_with(True)
+ # with disabled=True; should call salt.utils.data.is_true False
+ data_is_true.reset_mock()
+ repo = aptpkg.mod_repo("foo", disabled=False)
+ data_is_true.assert_called_with(False)
+
+ @patch(
+ "salt.utils.path.os_walk", MagicMock(return_value=[("test", "test", "test")])
+ )
+ @patch("os.path.getsize", MagicMock(return_value=123456))
+ @patch("os.path.getctime", MagicMock(return_value=1234567890.123456))
+ @patch(
+ "fnmatch.filter",
+ MagicMock(return_value=["/var/cache/apt/archive/test_package.rpm"]),
+ )
+ def test_list_downloaded(self):
+ """
+ Test downloaded packages listing.
+ :return:
+ """
+ DOWNLOADED_RET = {
+ "test-package": {
+ "1.0": {
+ "path": "/var/cache/apt/archive/test_package.rpm",
+ "size": 123456,
+ "creation_date_time_t": 1234567890,
+ "creation_date_time": "2009-02-13T23:31:30",
+ }
+ }
+ }
+
+ with patch.dict(
+ aptpkg.__salt__,
+ {
+ "lowpkg.bin_pkg_info": MagicMock(
+ return_value={"name": "test-package", "version": "1.0"}
+ )
+ },
+ ):
+ list_downloaded = aptpkg.list_downloaded()
+ self.assertEqual(len(list_downloaded), 1)
+ self.assertDictEqual(list_downloaded, DOWNLOADED_RET)
+
+ def test__skip_source(self):
+ """
+ Test __skip_source.
+ :return:
+ """
+ # Valid source
+ source_type = "deb"
+ source_uri = "http://cdn-aws.deb.debian.org/debian"
+ source_line = "deb http://cdn-aws.deb.debian.org/debian stretch main\n"
+
+ mock_source = MockSourceEntry(source_uri, source_type, source_line, False)
+
+ ret = aptpkg._skip_source(mock_source)
+ self.assertFalse(ret)
+
+ # Invalid source type
+ source_type = "ded"
+ source_uri = "http://cdn-aws.deb.debian.org/debian"
+ source_line = "deb http://cdn-aws.deb.debian.org/debian stretch main\n"
+
+ mock_source = MockSourceEntry(source_uri, source_type, source_line, True)
+
+ ret = aptpkg._skip_source(mock_source)
+ self.assertTrue(ret)
+
+ # Invalid source type , not skipped
+ source_type = "deb"
+ source_uri = "http://cdn-aws.deb.debian.org/debian"
+ source_line = "deb [http://cdn-aws.deb.debian.org/debian] stretch main\n"
+
+ mock_source = MockSourceEntry(source_uri, source_type, source_line, True)
+
+ ret = aptpkg._skip_source(mock_source)
+ self.assertFalse(ret)
+
+ def test_normalize_name(self):
+ """
+ Test that package is normalized only when it should be
+ """
+ with patch.dict(aptpkg.__grains__, {"osarch": "amd64"}):
+ result = aptpkg.normalize_name("foo")
+ assert result == "foo", result
+ result = aptpkg.normalize_name("foo:amd64")
+ assert result == "foo", result
+ result = aptpkg.normalize_name("foo:any")
+ assert result == "foo", result
+ result = aptpkg.normalize_name("foo:i386")
+ assert result == "foo:i386", result
+
+ def test_list_repos(self):
+ """
+ Checks results from list_repos
+ """
+ # Valid source
+ source_type = "deb"
+ source_uri = "http://cdn-aws.deb.debian.org/debian/"
+ source_line = "deb http://cdn-aws.deb.debian.org/debian/ stretch main\n"
+
+ mock_source = MockSourceEntry(source_uri, source_type, source_line, False)
+ mock_source_list = MockSourceList()
+ mock_source_list.list = [mock_source]
+
+ with patch("salt.modules.aptpkg._check_apt", MagicMock(return_value=True)):
+ with patch("salt.modules.aptpkg.sourceslist", MagicMock(), create=True):
+ with patch(
+ "salt.modules.aptpkg.sourceslist.SourcesList",
+ MagicMock(return_value=mock_source_list),
+ create=True,
+ ):
+ repos = aptpkg.list_repos()
+ self.assertIn(source_uri, repos)
+
+ assert isinstance(repos[source_uri], list)
+ assert len(repos[source_uri]) == 1
+
+ # Make sure last character in of the URI in line is still a /
+ self.assertIn("line", repos[source_uri][0])
+ _uri = _get_uri(repos[source_uri][0]["line"])
+ self.assertEqual(_uri[-1], "/")
+
+ # Make sure last character in URI is still a /
+ self.assertIn("uri", repos[source_uri][0])
+ self.assertEqual(repos[source_uri][0]["uri"][-1], "/")
+
+ def test_expand_repo_def(self):
+ """
+ Checks results from expand_repo_def
+ """
+ source_type = "deb"
+ source_uri = "http://cdn-aws.deb.debian.org/debian/"
+ source_line = "deb http://cdn-aws.deb.debian.org/debian/ stretch main\n"
+ source_file = "/etc/apt/sources.list"
+
+ mock_source = MockSourceEntry(
+ source_uri, source_type, source_line, False, file=source_file
+ )
+
+ # Valid source
+ with patch("salt.modules.aptpkg._check_apt", MagicMock(return_value=True)):
+ with patch("salt.modules.aptpkg.sourceslist", MagicMock(), create=True):
+ with patch(
+ "salt.modules.aptpkg.sourceslist.SourceEntry",
+ MagicMock(return_value=mock_source),
+ create=True,
+ ):
+ repo = "deb http://cdn-aws.deb.debian.org/debian/ stretch main\n"
+ sanitized = aptpkg.expand_repo_def(repo=repo, file=source_file)
+
+ assert isinstance(sanitized, dict)
+ self.assertIn("uri", sanitized)
+
+ # Make sure last character in of the URI is still a /
+ self.assertEqual(sanitized["uri"][-1], "/")
+
+
+@skipIf(pytest is None, "PyTest is missing")
+class AptUtilsTestCase(TestCase, LoaderModuleMockMixin):
+ """
+ apt utils test case
+ """
+
+ def setup_loader_modules(self):
+ return {aptpkg: {}}
+
+ def test_call_apt_default(self):
+ """
+ Call default apt.
+ :return:
+ """
+ with patch.dict(
+ aptpkg.__salt__,
+ {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=False)},
+ ):
+ aptpkg._call_apt(["apt-get", "install", "emacs"]) # pylint: disable=W0106
+ aptpkg.__salt__["cmd.run_all"].assert_called_once_with(
+ ["apt-get", "install", "emacs"],
+ env={},
+ output_loglevel="trace",
+ python_shell=False,
+ )
+
+ @patch("salt.utils.systemd.has_scope", MagicMock(return_value=True))
+ def test_call_apt_in_scope(self):
+ """
+ Call apt within the scope.
+ :return:
+ """
+ with patch.dict(
+ aptpkg.__salt__,
+ {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=True)},
+ ):
+ aptpkg._call_apt(["apt-get", "purge", "vim"]) # pylint: disable=W0106
+ aptpkg.__salt__["cmd.run_all"].assert_called_once_with(
+ [
+ "systemd-run",
+ "--scope",
+ "--description",
+ '"salt.modules.aptpkg"',
+ "apt-get",
+ "purge",
+ "vim",
+ ],
+ env={},
+ output_loglevel="trace",
+ python_shell=False,
+ )
+
+ def test_call_apt_with_kwargs(self):
+ """
+ Call apt with the optinal keyword arguments.
+ :return:
+ """
+ with patch.dict(
+ aptpkg.__salt__,
+ {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=False)},
+ ):
+ aptpkg._call_apt(
+ ["dpkg", "-l", "python"],
+ python_shell=True,
+ output_loglevel="quiet",
+ ignore_retcode=False,
+ username="Darth Vader",
+ ) # pylint: disable=W0106
+ aptpkg.__salt__["cmd.run_all"].assert_called_once_with(
+ ["dpkg", "-l", "python"],
+ env={},
+ ignore_retcode=False,
+ output_loglevel="quiet",
+ python_shell=True,
+ username="Darth Vader",
+ )
+
+ def test_call_apt_dpkg_lock(self):
+ """
+ Call apt and ensure the dpkg locking is handled
+ :return:
+ """
+ cmd_side_effect = [
+ {"stderr": "Could not get lock"},
+ {"stderr": "Could not get lock"},
+ {"stderr": "Could not get lock"},
+ {"stderr": "Could not get lock"},
+ {"stderr": "", "stdout": ""},
+ ]
+
+ cmd_mock = MagicMock(side_effect=cmd_side_effect)
+ cmd_call = (
+ call(
+ ["dpkg", "-l", "python"],
+ env={},
+ ignore_retcode=False,
+ output_loglevel="quiet",
+ python_shell=True,
+ username="Darth Vader",
+ ),
+ )
+ expected_calls = [cmd_call * 5]
+
+ with patch.dict(
+ aptpkg.__salt__,
+ {"cmd.run_all": cmd_mock, "config.get": MagicMock(return_value=False)},
+ ):
+ with patch("salt.modules.aptpkg.time.sleep", MagicMock()) as sleep_mock:
+ aptpkg._call_apt(
+ ["dpkg", "-l", "python"],
+ python_shell=True,
+ output_loglevel="quiet",
+ ignore_retcode=False,
+ username="Darth Vader",
+ ) # pylint: disable=W0106
+
+ # We should have sleept at least 4 times
+ assert sleep_mock.call_count >= 4
+
+ # We should attempt to call the cmd 5 times
+ self.assertEqual(cmd_mock.call_count, 5)
+ cmd_mock.has_calls(expected_calls)
diff --git a/tests/unit/modules/test_dpkg_lowpkg.py b/tests/unit/modules/test_dpkg_lowpkg.py
index 071c0f0742..160bbcd5b1 100644
--- a/tests/unit/modules/test_dpkg_lowpkg.py
+++ b/tests/unit/modules/test_dpkg_lowpkg.py
@@ -1,18 +1,12 @@
-# -*- coding: utf-8 -*-
"""
:codeauthor: Jayesh Kariya <jayeshk@saltstack.com>
"""
-# Import Python libs
-from __future__ import absolute_import, print_function, unicode_literals
import logging
import os
-# Import Salt Libs
import salt.modules.dpkg_lowpkg as dpkg
-
-# Import Salt Testing Libs
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.mock import MagicMock, patch
from tests.support.unit import TestCase
@@ -65,6 +59,51 @@ class DpkgTestCase(TestCase, LoaderModuleMockMixin):
package = cmd[2]
return DPKG_L_OUTPUT[package]
+ dselect_pkg = {
+ "emacs": {
+ "priority": "optional",
+ "filename": "pool/main/e/emacs-defaults/emacs_46.1_all.deb",
+ "description": "GNU Emacs editor (metapackage)",
+ "md5sum": "766eb2cee55ba0122dac64c4cea04445",
+ "sha256": "d172289b9a1608820eddad85c7ffc15f346a6e755c3120de0f64739c4bbc44ce",
+ "description-md5": "21fb7da111336097a2378959f6d6e6a8",
+ "bugs": "https://bugs.launchpad.net/springfield/+filebug",
+ "depends": "emacs24 | emacs24-lucid | emacs24-nox",
+ "origin": "Simpsons",
+ "version": "46.1",
+ "task": "ubuntu-usb, edubuntu-usb",
+ "original-maintainer": "Homer Simpson <homer@springfield.org>",
+ "package": "emacs",
+ "architecture": "all",
+ "size": "1692",
+ "sha1": "9271bcec53c1f7373902b1e594d9fc0359616407",
+ "source": "emacs-defaults",
+ "maintainer": "Simpsons Developers <simpsons-devel-discuss@lists.springfield.org>",
+ "supported": "9m",
+ "section": "editors",
+ "installed-size": "25",
+ }
+ }
+
+ pkgs_info = [
+ {
+ "version": "46.1",
+ "arch": "all",
+ "build_date": "2014-08-07T16:51:48Z",
+ "install_date_time_t": 1481745778,
+ "section": "editors",
+ "description": "GNU Emacs editor (metapackage)\n GNU Emacs is the extensible "
+ "self-documenting text editor.\n This is a metapackage that will always "
+ "depend on the latest\n recommended Emacs release.\n",
+ "package": "emacs",
+ "source": "emacs-defaults",
+ "maintainer": "Simpsons Developers <simpsons-devel-discuss@lists.springfield.org>",
+ "build_date_time_t": 1407430308,
+ "installed_size": "25",
+ "install_date": "2016-12-14T20:02:58Z",
+ }
+ ]
+
def setup_loader_modules(self):
return {dpkg: {}}
@@ -269,83 +308,71 @@ class DpkgTestCase(TestCase, LoaderModuleMockMixin):
dpkg.bin_pkg_info("package.deb")["name"], "package_name"
)
+ @patch("salt.modules.dpkg._get_pkg_ds_avail", MagicMock(return_value=dselect_pkg))
+ @patch("salt.modules.dpkg._get_pkg_info", MagicMock(return_value=pkgs_info))
+ @patch("salt.modules.dpkg._get_pkg_license", MagicMock(return_value="BSD v3"))
def test_info(self):
"""
- Test package info
+ Test info
+ :return:
"""
- mock = MagicMock(
- return_value={
- "retcode": 0,
- "stderr": "",
- "stdout": os.linesep.join(
- [
- "package:bash",
- "revision:",
- "architecture:amd64",
- "maintainer:Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
- "summary:",
- "source:bash",
- "version:4.4.18-2ubuntu1",
- "section:shells",
- "installed_size:1588",
- "size:",
- "MD5:",
- "SHA1:",
- "SHA256:",
- "origin:",
- "homepage:http://tiswww.case.edu/php/chet/bash/bashtop.html",
- "status:ii ",
- "======",
- "description:GNU Bourne Again SHell",
- " Bash is an sh-compatible command language interpreter that executes",
- " commands read from the standard input or from a file. Bash also",
- " incorporates useful features from the Korn and C shells (ksh and csh).",
- " .",
- " Bash is ultimately intended to be a conformant implementation of the",
- " IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).",
- " .",
- " The Programmable Completion Code, by Ian Macdonald, is now found in",
- " the bash-completion package.",
- "------",
- ]
- ),
- }
+ ret = dpkg.info("emacs")
+
+ assert isinstance(ret, dict)
+ assert len(ret.keys()) == 1
+ assert "emacs" in ret
+
+ pkg_data = ret["emacs"]
+
+ assert isinstance(pkg_data, dict)
+ for pkg_section in [
+ "section",
+ "architecture",
+ "original-maintainer",
+ "maintainer",
+ "package",
+ "installed-size",
+ "build_date_time_t",
+ "sha256",
+ "origin",
+ "build_date",
+ "size",
+ "source",
+ "version",
+ "install_date_time_t",
+ "license",
+ "priority",
+ "description",
+ "md5sum",
+ "supported",
+ "filename",
+ "sha1",
+ "install_date",
+ "arch",
+ ]:
+ assert pkg_section in pkg_data
+
+ assert pkg_data["section"] == "editors"
+ assert (
+ pkg_data["maintainer"]
+ == "Simpsons Developers <simpsons-devel-discuss@lists.springfield.org>"
)
+ assert pkg_data["license"] == "BSD v3"
- with patch.dict(dpkg.__salt__, {"cmd.run_all": mock}), patch.dict(
- dpkg.__grains__, {"os": "Ubuntu", "osrelease_info": (18, 4)}
- ), patch("salt.utils.path.which", MagicMock(return_value=False)), patch(
- "os.path.exists", MagicMock(return_value=False)
- ), patch(
- "os.path.getmtime", MagicMock(return_value=1560199259.0)
- ):
- self.assertDictEqual(
- dpkg.info("bash"),
- {
- "bash": {
- "architecture": "amd64",
- "description": os.linesep.join(
- [
- "GNU Bourne Again SHell",
- " Bash is an sh-compatible command language interpreter that executes",
- " commands read from the standard input or from a file. Bash also",
- " incorporates useful features from the Korn and C shells (ksh and csh).",
- " .",
- " Bash is ultimately intended to be a conformant implementation of the",
- " IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).",
- " .",
- " The Programmable Completion Code, by Ian Macdonald, is now found in",
- " the bash-completion package." + os.linesep,
- ]
- ),
- "homepage": "http://tiswww.case.edu/php/chet/bash/bashtop.html",
- "maintainer": "Ubuntu Developers "
- "<ubuntu-devel-discuss@lists.ubuntu.com>",
- "package": "bash",
- "section": "shells",
- "source": "bash",
- "status": "ii",
- "version": "4.4.18-2ubuntu1",
- }
- },
- )
+ @patch("salt.modules.dpkg._get_pkg_ds_avail", MagicMock(return_value=dselect_pkg))
+ @patch("salt.modules.dpkg._get_pkg_info", MagicMock(return_value=pkgs_info))
+ @patch("salt.modules.dpkg._get_pkg_license", MagicMock(return_value="BSD v3"))
+ def test_info_attr(self):
+ """
+ Test info with 'attr' parameter
+ :return:
+ """
+ ret = dpkg.info("emacs", attr="arch,license,version")
+ assert isinstance(ret, dict)
+ assert "emacs" in ret
+ for attr in ["arch", "license", "version"]:
+ assert attr in ret["emacs"]
+
+ assert ret["emacs"]["arch"] == "all"
+ assert ret["emacs"]["license"] == "BSD v3"
+ assert ret["emacs"]["version"] == "46.1"
--
2.33.0