Merge pull request #31479 from isbm/isbm-zypper-unittest

Zypper unit tests & fixes
This commit is contained in:
Mike Place 2016-02-25 08:58:15 -07:00
commit f027dc0cf8
5 changed files with 443 additions and 33 deletions

View file

@ -87,34 +87,21 @@ def list_upgrades(refresh=True):
'''
if refresh:
refresh_db()
ret = {}
call = __salt__['cmd.run_all'](
_zypper('list-updates'), output_loglevel='trace'
)
if call['retcode'] != 0:
comment = ''
if 'stderr' in call:
comment += call['stderr']
if 'stdout' in call:
comment += call['stdout']
raise CommandExecutionError(
'{0}'.format(comment)
)
else:
out = call['stdout']
ret = dict()
run_data = __salt__['cmd.run_all'](_zypper('-x', 'list-updates'), output_loglevel='trace')
if run_data['retcode'] != 0:
msg = list()
for chnl in ['stderr', 'stdout']:
if run_data.get(chnl, ''):
msg.append(run_data[chnl])
raise CommandExecutionError(os.linesep.join(msg) or
'Zypper returned non-zero system exit. See Zypper logs for more details.')
doc = dom.parseString(run_data['stdout'])
for update_node in doc.getElementsByTagName('update'):
if update_node.getAttribute('kind') == 'package':
ret[update_node.getAttribute('name')] = update_node.getAttribute('edition')
for line in out.splitlines():
if not line:
continue
if '|' not in line:
continue
try:
status, repo, name, cur, avail, arch = \
[x.strip() for x in line.split('|')]
except (ValueError, IndexError):
continue
if status == 'v':
ret[name] = avail
return ret
# Provide a list_updates function for those used to using zypper list-updates
@ -299,7 +286,7 @@ def upgrade_available(name):
salt '*' pkg.upgrade_available <package name>
'''
return latest_version(name).get(name) is not None
return not not latest_version(name)
def version(*names, **kwargs):
@ -902,7 +889,7 @@ def upgrade(refresh=True):
return ret
def _uninstall(action='remove', name=None, pkgs=None):
def _uninstall(name=None, pkgs=None):
'''
remove and purge do identical things but with different zypper commands,
this function performs the common logic.
@ -912,13 +899,12 @@ def _uninstall(action='remove', name=None, pkgs=None):
except MinionError as exc:
raise CommandExecutionError(exc)
purge_arg = '-u' if action == 'purge' else ''
old = list_pkgs()
targets = [x for x in pkg_params if x in old]
if not targets:
return {}
while targets:
cmd = _zypper('remove', purge_arg, *targets[:500])
cmd = _zypper('remove', *targets[:500])
__salt__['cmd.run'](cmd, output_loglevel='trace')
targets = targets[500:]
__context__.pop('pkg.list_pkgs', None)
@ -953,7 +939,7 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
salt '*' pkg.remove <package1>,<package2>,<package3>
salt '*' pkg.remove pkgs='["foo", "bar"]'
'''
return _uninstall(action='remove', name=name, pkgs=pkgs)
return _uninstall(name=name, pkgs=pkgs)
def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
@ -984,7 +970,7 @@ def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
salt '*' pkg.purge <package1>,<package2>,<package3>
salt '*' pkg.purge pkgs='["foo", "bar"]'
'''
return _uninstall(action='purge', name=name, pkgs=pkgs)
return _uninstall(name=name, pkgs=pkgs)
def list_locks():

View file

@ -0,0 +1,64 @@
Loading repository data...
Reading installed packages...
Information for package vim:
----------------------------
Repository: SLE12-SP1-x86_64-Pool
Name: vim
Version: 7.4.326-2.62
Arch: x86_64
Vendor: SUSE LLC <https://www.suse.com/>
Support Level: Level 3
Installed: No
Status: not installed
Installed Size: 2,6 MiB
Summary: Vi IMproved
Description:
Vim (Vi IMproved) is an almost compatible version of the UNIX editor
vi. Almost every possible command can be performed using only ASCII
characters. Only the 'Q' command is missing (you do not need it). Many
new features have been added: multilevel undo, command line history,
file name completion, block operations, and editing of binary data.
Vi is available for the AMIGA, MS-DOS, Windows NT, and various versions
of UNIX.
For SUSE Linux, Vim is used as /usr/bin/vi.
Information for package python:
-------------------------------
Repository: SLE12-SP1-x86_64-Pool
Name: python
Version: 2.7.9-20.2
Arch: x86_64
Vendor: SUSE LLC <https://www.suse.com/>
Support Level: Level 3
Installed: Yes
Status: up-to-date
Installed Size: 1,4 MiB
Summary: Python Interpreter
Description:
Python is an interpreted, object-oriented programming language, and is
often compared to Tcl, Perl, Scheme, or Java. You can find an overview
of Python in the documentation and tutorials included in the python-doc
(HTML) or python-doc-pdf (PDF) packages.
If you want to install third party modules using distutils, you need to
install python-devel package.
Information for package emacs:
------------------------------
Repository: SLE12-SP1-x86_64-Pool
Name: emacs
Version: 24.3-14.44
Arch: x86_64
Vendor: SUSE LLC <https://www.suse.com/>
Support Level: Level 3
Installed: Yes
Status: up-to-date
Installed Size: 63,9 MiB
Summary: GNU Emacs Base Package
Description:
Basic package for the GNU Emacs editor. Requires emacs-x11 or
emacs-nox.

View file

@ -0,0 +1,37 @@
<?xml version='1.0'?>
<stream>
<message type="info">Loading repository data...</message>
<message type="info">Reading installed packages...</message>
<product-list>
<product name="SLES" version="12.1" release="0" epoch="0" arch="x86_64" vendor="SUSE LLC &lt;https://www.suse.com/&gt;" summary="SUSE Linux Enterprise Server 12 SP1" repo="SLE12-SP1-x86_64-Pool" productline="" registerrelease="" shortname="SLES12-SP1" flavor="POOL" isbase="false" installed="false"><endoflife time_t="1730332800" text="2024-10-31T01:00:00+01"/><registerflavor/><description>SUSE Linux Enterprise offers a comprehensive
suite of products built on a single code base.
The platform addresses business needs from
the smallest thin-client devices to the world&apos;s
most powerful high-performance computing
and mainframe servers. SUSE Linux Enterprise
offers common management tools and technology
certifications across the platform, and
each product is enterprise-class.</description></product>
<product name="SUSE-Manager-Proxy" version="3.0" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Proxy" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="SUSE Manager Proxy" flavor="DVD" isbase="false" installed="false"><endoflife time_t="1522454400" text="2018-03-31T02:00:00+02"/><registerflavor>extension</registerflavor><description>SUSE Manager Proxies extend large and/or geographically
dispersed SUSE Manager environments to reduce load on the SUSE Manager
Server, lower bandwidth needs, and provide faster local
updates.</description></product>
<product name="SUSE-Manager-Server" version="3.0" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Server" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="SUSE Manager Server" flavor="DVD" isbase="false" installed="false"><endoflife time_t="1522454400" text="2018-03-31T02:00:00+02"/><registerflavor>extension</registerflavor><description>SUSE Manager lets you efficiently manage physical, virtual,
and cloud-based Linux systems. It provides automated and cost-effective
configuration and software management, asset management, and system
provisioning.</description></product>
<product name="sle-manager-tools-beta" version="12" release="0" epoch="0" arch="x86_64" vendor="obs://build.suse.de/Devel:Galaxy:Manager:Head" summary="SUSE Manager Tools" repo="SUSE-Manager-Head" productline="" registerrelease="" shortname="Manager-Tools" flavor="POOL" isbase="false" installed="false"><endoflife time_t="1509408000" text="2017-10-31T01:00:00+01"/><registerflavor>extension</registerflavor><description>&lt;p&gt;
SUSE Manager Tools provide packages required to connect to a
SUSE Manager Server.
&lt;p&gt;</description></product>
<product name="SLES" version="12.1" release="0" epoch="0" arch="x86_64" vendor="SUSE" summary="SUSE Linux Enterprise Server 12 SP1" repo="@System" productline="sles" registerrelease="" shortname="SLES12-SP1" flavor="DVD" isbase="true" installed="true"><endoflife time_t="1730332800" text="2024-10-31T01:00:00+01"/><registerflavor/><description>SUSE Linux Enterprise offers a comprehensive
suite of products built on a single code base.
The platform addresses business needs from
the smallest thin-client devices to the world&apos;s
most powerful high-performance computing
and mainframe servers. SUSE Linux Enterprise
offers common management tools and technology
certifications across the platform, and
each product is enterprise-class.</description></product>
</product-list>
</stream>

View file

@ -0,0 +1,33 @@
<?xml version='1.0'?>
<stream>
<message type="info">Loading repository data...</message>
<message type="info">Reading installed packages...</message>
<update-status version="0.6">
<update-list>
<update name="SUSEConnect" edition="0.2.33-7.1" arch="x86_64" kind="package" >
<summary>Utility to register a system with the SUSE Customer Center </summary>
<description>This package provides a command line tool and rubygem library for connecting a
client system to the SUSE Customer Center. It will connect the system to your
product subscriptions and enable the product repositories/services locally.</description>
<license></license>
<source url="http://scc.suse.de/SLE-SERVER/12-SP1/x86_64/update/" alias="SLE12-SP1-x86_64-Update"/>
</update>
<update name="bind-libs" edition="9.9.6P1-35.1" arch="x86_64" kind="package" >
<summary>Shared libraries of BIND </summary>
<description>This package contains the shared libraries of the Berkeley Internet
Name Domain (BIND) Domain Name System implementation of the Domain Name
System (DNS) protocols.</description>
<license></license>
<source url="http://scc.suse.de/SLE-SERVER/12-SP1/x86_64/update/" alias="SLE12-SP1-x86_64-Update"/>
</update>
<update name="bind-utils" edition="9.9.6P1-35.1" arch="x86_64" kind="package" >
<summary>Utilities to query and test DNS </summary>
<description>This package includes the utilities host, dig, and nslookup used to
test and query the Domain Name System (DNS). The Berkeley Internet
Name Domain (BIND) DNS server is found in the package named bind.</description>
<license></license>
<source url="http://scc.suse.de/SLE-SERVER/12-SP1/x86_64/update/" alias="SLE12-SP1-x86_64-Update"/>
</update>
</update-list>
</update-status>
</stream>

View file

@ -0,0 +1,290 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Bo Maryniuk <bo@suse.de>`
'''
# Import Python Libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import TestCase, skipIf
from salttesting.mock import (
MagicMock,
patch,
NO_MOCK,
NO_MOCK_REASON
)
from salt.exceptions import CommandExecutionError
import os
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
def get_test_data(filename):
'''
Return static test data
'''
return open(os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'zypp'), filename)).read()
# Import Salt Libs
from salt.modules import zypper
# Globals
zypper.__salt__ = dict()
zypper.__context__ = dict()
zypper.rpm = None
@skipIf(NO_MOCK, NO_MOCK_REASON)
class ZypperTestCase(TestCase):
'''
Test cases for salt.modules.zypper
'''
def test_list_upgrades(self):
'''
List package upgrades
:return:
'''
ref_out = {
'stdout': get_test_data('zypper-updates.xml'),
'stderr': None,
'retcode': 0
}
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
upgrades = zypper.list_upgrades(refresh=False)
assert len(upgrades) == 3
for pkg, version in {'SUSEConnect': '0.2.33-7.1',
'bind-utils': '9.9.6P1-35.1',
'bind-libs': '9.9.6P1-35.1'}.items():
assert pkg in upgrades
assert upgrades[pkg] == version
def test_list_upgrades_error_handling(self):
'''
Test error handling in the list package upgrades.
:return:
'''
# Test handled errors
ref_out = {
'stderr': 'Some handled zypper internal error',
'retcode': 1
}
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
try:
zypper.list_upgrades(refresh=False)
except CommandExecutionError as error:
assert error.message == ref_out['stderr']
# Test unhandled error
ref_out = {
'retcode': 1
}
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}):
try:
zypper.list_upgrades(refresh=False)
except CommandExecutionError as error:
assert error.message == 'Zypper returned non-zero system exit. See Zypper logs for more details.'
def test_list_products(self):
'''
List products test.
'''
ref_out = get_test_data('zypper-products.xml')
with patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=ref_out)}):
products = zypper.list_products()
assert len(products) == 5
assert (['SLES', 'SLES', 'SUSE-Manager-Proxy', 'SUSE-Manager-Server', 'sle-manager-tools-beta'] ==
sorted([prod['name'] for prod in products]))
assert ('SUSE LLC <https://www.suse.com/>' in [product['vendor'] for product in products])
assert ([False, False, False, False, True] ==
sorted([product['isbase'] for product in products]))
assert ([False, False, False, False, True] ==
sorted([product['installed'] for product in products]))
assert (['0', '0', '0', '0', '0'] ==
sorted([product['release'] for product in products]))
assert ([False, False, False, False, u'sles'] ==
sorted([product['productline'] for product in products]))
assert ([1509408000, 1522454400, 1522454400, 1730332800, 1730332800] ==
sorted([product['eol_t'] for product in products]))
def test_refresh_db(self):
'''
Test if refresh DB handled correctly
'''
ref_out = [
"Repository 'openSUSE-Leap-42.1-LATEST' is up to date.",
"Repository 'openSUSE-Leap-42.1-Update' is up to date.",
"Retrieving repository 'openSUSE-Leap-42.1-Update-Non-Oss' metadata",
"Forcing building of repository cache",
"Building repository 'openSUSE-Leap-42.1-Update-Non-Oss' cache ..........[done]",
"Building repository 'salt-dev' cache",
"All repositories have been refreshed."
]
run_out = {
'stderr': '', 'stdout': '\n'.join(ref_out), 'retcode': 0
}
with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=run_out)}):
result = zypper.refresh_db()
self.assertEqual(result.get("openSUSE-Leap-42.1-LATEST"), False)
self.assertEqual(result.get("openSUSE-Leap-42.1-Update"), False)
self.assertEqual(result.get("openSUSE-Leap-42.1-Update-Non-Oss"), True)
def test_info_installed(self):
'''
Test the return information of the named package(s), installed on the system.
:return:
'''
run_out = {
'virgo-dummy':
{'build_date': '2015-07-09T10:55:19Z',
'vendor': 'openSUSE Build Service',
'description': 'This is the Virgo dummy package used for testing SUSE Manager',
'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com',
'build_date_time_t': 1436432119, 'relocations': '(not relocatable)',
'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z',
'install_date_time_t': 1456241517, 'summary': 'Virgo dummy package', 'version': '1.0',
'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9',
'release': '1.1', 'group': 'Applications/System', 'arch': 'noarch', 'size': '17992'},
'libopenssl1_0_0':
{'build_date': '2015-11-04T23:20:34Z', 'vendor': 'SUSE LLC <https://www.suse.com/>',
'description': 'The OpenSSL Project is a collaborative effort.',
'license': 'OpenSSL', 'build_host': 'sheep11', 'url': 'https://www.openssl.org/',
'build_date_time_t': 1446675634, 'relocations': '(not relocatable)',
'source_rpm': 'openssl-1.0.1i-34.1.src.rpm', 'install_date': '2016-02-23T16:31:35Z',
'install_date_time_t': 1456241495, 'summary': 'Secure Sockets and Transport Layer Security',
'version': '1.0.1i', 'signature': 'RSA/SHA256, Wed Nov 4 22:21:34 2015, Key ID 70af9e8139db7c82',
'release': '34.1', 'group': 'Productivity/Networking/Security', 'packager': 'https://www.suse.com/',
'arch': 'x86_64', 'size': '2576912'},
}
with patch.dict(zypper.__salt__, {'lowpkg.info': MagicMock(return_value=run_out)}):
installed = zypper.info_installed()
# Test overall products length
assert len(installed) == 2
# Test translated fields
for pkg_name, pkg_info in installed.items():
assert installed[pkg_name].get('source') == run_out[pkg_name]['source_rpm']
# Test keys transition from the lowpkg.info
for pn_key, pn_val in run_out['virgo-dummy'].items():
if pn_key == 'source_rpm':
continue
assert installed['virgo-dummy'][pn_key] == pn_val
def test_info_available(self):
'''
Test return the information of the named package available for the system.
:return:
'''
test_pkgs = ['vim', 'emacs', 'python']
ref_out = get_test_data('zypper-available.txt')
with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
available = zypper.info_available(*test_pkgs, refresh=False)
assert len(available) == 3
for pkg_name, pkg_info in available.items():
assert pkg_name in test_pkgs
assert available['emacs']['status'] == 'up-to-date'
assert available['emacs']['installed']
assert available['emacs']['support level'] == 'Level 3'
assert available['emacs']['vendor'] == 'SUSE LLC <https://www.suse.com/>'
assert available['emacs']['summary'] == 'GNU Emacs Base Package'
assert available['vim']['status'] == 'not installed'
assert not available['vim']['installed']
assert available['vim']['support level'] == 'Level 3'
assert available['vim']['vendor'] == 'SUSE LLC <https://www.suse.com/>'
assert available['vim']['summary'] == 'Vi IMproved'
@patch('salt.modules.zypper.refresh_db', MagicMock(return_value=True))
def test_latest_version(self):
'''
Test the latest version of the named package available for upgrade or installation.
:return:
'''
ref_out = get_test_data('zypper-available.txt')
with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
assert zypper.latest_version('vim') == '7.4.326-2.62'
@patch('salt.modules.zypper.refresh_db', MagicMock(return_value=True))
def test_upgrade_available(self):
'''
Test whether or not an upgrade is available for a given package.
:return:
'''
ref_out = get_test_data('zypper-available.txt')
with patch.dict(zypper.__salt__, {'cmd.run_stdout': MagicMock(return_value=ref_out)}):
for pkg_name in ['emacs', 'python']:
assert not zypper.upgrade_available(pkg_name)
assert zypper.upgrade_available('vim')
@patch('salt.modules.zypper.HAS_RPM', True)
def test_version_cmp_rpm(self):
'''
Test package version is called RPM version if RPM-Python is installed
:return:
'''
with patch('salt.modules.zypper.rpm', MagicMock(return_value=MagicMock)):
with patch('salt.modules.zypper.rpm.labelCompare', MagicMock(return_value=0)):
assert 0 == zypper.version_cmp('1', '2') # mock returns 0, which means RPM was called
@patch('salt.modules.zypper.HAS_RPM', False)
def test_version_cmp_fallback(self):
'''
Test package version is called RPM version if RPM-Python is installed
:return:
'''
with patch('salt.modules.zypper.rpm', MagicMock(return_value=MagicMock)):
with patch('salt.modules.zypper.rpm.labelCompare', MagicMock(return_value=0)):
assert -1 == zypper.version_cmp('1', '2') # mock returns -1, a python implementation was called
def test_list_pkgs(self):
'''
Test packages listing.
:return:
'''
def _add_data(data, key, value):
data[key] = value
rpm_out = [
'protobuf-java_|-2.6.1_|-3.1.develHead_|-',
'yast2-ftp-server_|-3.1.8_|-8.1_|-',
'jose4j_|-0.4.4_|-2.1.develHead_|-',
'apache-commons-cli_|-1.2_|-1.233_|-',
'jakarta-commons-discovery_|-0.4_|-129.686_|-',
'susemanager-build-keys-web_|-12.0_|-5.1.develHead_|-',
]
with patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(rpm_out))}):
with patch.dict(zypper.__salt__, {'pkg_resource.add_pkg': _add_data}):
with patch.dict(zypper.__salt__, {'pkg_resource.sort_pkglist': MagicMock()}):
with patch.dict(zypper.__salt__, {'pkg_resource.stringify': MagicMock()}):
pkgs = zypper.list_pkgs()
for pkg_name, pkg_version in {
'jakarta-commons-discovery': '0.4-129.686',
'yast2-ftp-server': '3.1.8-8.1',
'protobuf-java': '2.6.1-3.1.develHead',
'susemanager-build-keys-web': '12.0-5.1.develHead',
'apache-commons-cli': '1.2-1.233',
'jose4j': '0.4.4-2.1.develHead'}.items():
assert pkgs.get(pkg_name)
assert pkgs[pkg_name] == pkg_version
if __name__ == '__main__':
from integration import run_tests
run_tests(ZypperTestCase, needs_daemon=False)