debian-koji/tests/test_cli/test_image_build_indirection.py
Yuming Zhu ca05418fb5 unittest: use unittest.mock instead of mock
because the absence of unittest.mock on python2.7, we still fallback to
mock
2024-10-23 16:35:30 +00:00

296 lines
12 KiB
Python

from __future__ import absolute_import
import os
import unittest
try:
from unittest import mock
except ImportError:
import mock
import six
import koji
from koji_cli.commands import handle_image_build_indirection, _build_image_indirection
from . import utils
if six.PY2:
ConfigParser = six.moves.configparser.SafeConfigParser
else:
ConfigParser = six.moves.configparser.ConfigParser
TASK_OPTIONS = {
"background": True,
"config": "build-image-config.conf",
"name": "image",
"version": "26",
"release": "1",
"arch": "x86_64 i386",
"target": "target",
"base_image_task": 2,
"base_image_build": None,
"utility_image_task": 4,
"utility_image_build": None,
"indirection_template": "template",
"indirection_template_url": "git://git.github.org/git/",
"results_loc": "results",
"scratch": None,
"wait": False,
"noprogress": None,
"skip_tag": False,
}
class Options(object):
def __init__(self, init_dict):
for k, v in init_dict.items():
setattr(self, k, v)
class TestBuildImageIndirection(utils.CliTestCase):
def setUp(self):
self.maxDiff = None
self.task_id = 1001
self.weburl = 'https://web.url'
self.options = mock.MagicMock()
self.options.quiet = False
self.options.weburl = self.weburl
self.session = mock.MagicMock()
self.activate_session = mock.patch('koji_cli.commands.activate_session').start()
self.unique_path = mock.patch('koji_cli.commands.unique_path').start()
self.task_opts = Options(TASK_OPTIONS)
self.build_target = {
'id': 1,
'name': 'target',
'dest_tag': 1,
'build_tag': 2,
'build_tag_name': 'target-build',
'dest_tag_name': 'target'
}
self.dest_tag = {'id': 1, 'name': 'dest-tag', 'arch': 'x86_64'}
self.session.getBuildTarget.return_value = self.build_target
self.session.getTag.return_value = self.dest_tag
self.session.buildImageIndirection.return_value = self.task_id
self.unique_path.return_value = '/path/to/cli-image-indirection'
self.configparser = mock.patch('six.moves.configparser.ConfigParser').start()
def tearDown(self):
mock.patch.stopall()
def test_build_image_indirection(self):
"""Test _build_image_indirection function"""
expected = "Created task: %d" % self.task_id + "\n"
expected += "Task info: %s/taskinfo?taskID=%s" % (self.weburl, self.task_id) + "\n"
with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout:
_build_image_indirection(
self.options, self.task_opts, self.session, [])
self.assert_console_message(stdout, expected)
def test_build_image_indirection_with_scratch(self):
"""Test _build_image_indirection function with scratch option"""
expected = "\n"
expected += "Created task: %d" % self.task_id + "\n"
expected += "Task info: %s/taskinfo?taskID=%s" % (self.weburl, self.task_id) + "\n"
self.task_opts.indirection_template_url = None
self.task_opts.scratch = True
with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout:
_build_image_indirection(
self.options, self.task_opts, self.session, [])
self.assert_console_message(stdout, expected)
def test_build_image_indirection_with_noprogress(self):
"""Test _build_image_indirection function with noprogress option"""
expected = "\n"
expected += "Created task: %d" % self.task_id + "\n"
expected += "Task info: %s/taskinfo?taskID=%s" % (self.weburl, self.task_id) + "\n"
self.task_opts.indirection_template_url = None
self.task_opts.scratch = True
self.task_opts.noprogress = True
with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout:
_build_image_indirection(
self.options, self.task_opts, self.session, [])
self.assert_console_message(stdout, expected)
args, kwargs = self.session.uploadWrapper.call_args
self.assertEqual(kwargs['callback'], None)
def test_build_image_indirection_expections(self):
"""Test _build_image_indirection all exceptions"""
# Case 1. sanity check for utility image options
self.task_opts.utility_image_task = None
self.task_opts.utility_image_build = None
expected = "You must specify either a utility-image task or build ID/NVR"
with self.assertRaises(koji.GenericError) as cm:
_build_image_indirection(
self.options, self.task_opts, self.session, [])
self.assertEqual(str(cm.exception), expected)
self.activate_session.assert_not_called()
# Case 2. sanity check for base image options
self.task_opts.utility_image_build = 'image-utils-26.1'
self.task_opts.base_image_task = None
self.task_opts.base_image_build = None
expected = "You must specify either a base-image task or build ID/NVR"
with self.assertRaises(koji.GenericError) as cm:
_build_image_indirection(
self.options, self.task_opts, self.session, [])
self.assertEqual(str(cm.exception), expected)
self.activate_session.assert_not_called()
self.task_opts.base_image_build = 'image-base-26.1'
# Case 3. missing required options
required = ['name', 'version', 'arch', 'target',
'indirection_template', 'results_loc']
for r in required:
orig = getattr(self.task_opts, r)
setattr(self.task_opts, r, None)
expected = "Missing the following required options: "
expected += "--" + r.replace('_', '-') + "\n"
with self.assertRaises(koji.GenericError) as cm:
with mock.patch('sys.stdout', new_callable=six.StringIO) as stdout:
_build_image_indirection(
self.options, self.task_opts, self.session, [])
self.assert_console_message(stdout, expected)
self.assertEqual(
str(cm.exception), "Missing required options specified above")
self.activate_session.assert_not_called()
setattr(self.task_opts, r, orig)
# Case 4. target not found error
self.session.getBuildTarget.return_value = {}
expected = "No such build target: %s" % {}
with self.assertRaises(koji.GenericError) as cm:
_build_image_indirection(
self.options, self.task_opts, self.session, [])
self.assertEqual(str(cm.exception), expected)
self.activate_session.assert_called_with(self.session, self.options)
# Case 5. tag not found error
self.session.getBuildTarget.return_value = self.build_target
self.session.getTag.return_value = {}
expected = "No such destination tag: %s" % self.build_target['dest_tag_name']
with self.assertRaises(koji.GenericError) as cm:
_build_image_indirection(
self.options, self.task_opts, self.session, [])
self.assertEqual(str(cm.exception), expected)
self.activate_session.assert_called_with(self.session, self.options)
# Case 6. --indirection-template-url is not given and --scatch is not set
self.session.getTag.return_value = self.dest_tag
self.task_opts.indirection_template_url = None
self.task_opts.scratch = None
expected = "Non-scratch builds must provide a URL for the indirection template"
with self.assertRaises(koji.GenericError) as cm:
_build_image_indirection(
self.options, self.task_opts, self.session, [])
self.assertEqual(str(cm.exception), expected)
self.activate_session.assert_called_with(self.session, self.options)
@mock.patch('koji_cli.commands._build_image_indirection')
def test_image_build_indirection_configuration(self, build_image_indirection_mock):
"""Test handle_image_build."""
self.configparser.return_value = ConfigParser()
config_file = os.path.join(
os.path.dirname(__file__),
'data/image-build-indirection-config.conf'
)
TASK_OPTIONS['config'] = config_file
handle_image_build_indirection(
self.options,
self.session,
['--config', config_file,
'--arch', 'x386']
)
args, _ = build_image_indirection_mock.call_args
merged_options = args[1].__dict__
self.assertEqual("x86_64", merged_options["arch"]) # Overrides command line
self.assertEqual("fedora-base", merged_options["base_image_build"])
self.assertEqual("fedora-indirection.tdl", merged_options["indirection_template"])
self.session.activate_session_mock.assert_not_called()
class TestImageBuildIndirection(utils.CliTestCase):
def setUp(self):
self.maxDiff = None
self.options = mock.MagicMock()
self.session = mock.MagicMock()
self.error_format = """Usage: %s image-build-indirection [base_image] [utility_image] [indirection_build_template]
%s image-build --config <FILE>
(Specify the --help global option for a list of other help options)
%s: error: {message}
""" % (self.progname, self.progname, self.progname)
@mock.patch('koji_cli.commands._build_image_indirection')
def test_handle_image_build_indirection(self, build_image_mock):
"""Test handle_image_build_indirection function"""
handle_image_build_indirection(self.options, self.session, [])
args, kwargs = build_image_mock.call_args
empty_opts = dict((k, None) for k in TASK_OPTIONS)
self.assertDictEqual(empty_opts, args[1].__dict__)
def test_handle_image_build_indirection_help(self):
"""Test handle_image_build_indirection help message"""
self.assert_help(
handle_image_build_indirection,
"""Usage: %s image-build-indirection [base_image] [utility_image] [indirection_build_template]
%s image-build --config <FILE>
(Specify the --help global option for a list of other help options)
Options:
-h, --help show this help message and exit
--config=CONFIG Use a configuration file to define image-build options
instead of command line options (they will be
ignored).
--background Run the image creation task at a lower priority
--name=NAME Name of the output image
--version=VERSION Version of the output image
--release=RELEASE Release of the output image
--arch=ARCH Architecture of the output image and input images
--target=TARGET Build target to use for the indirection build
--skip-tag Do not tag the resulting build
--base-image-task=BASE_IMAGE_TASK
ID of the createImage task of the base image to be
used
--base-image-build=BASE_IMAGE_BUILD
NVR or build ID of the base image to be used
--utility-image-task=UTILITY_IMAGE_TASK
ID of the createImage task of the utility image to be
used
--utility-image-build=UTILITY_IMAGE_BUILD
NVR or build ID of the utility image to be used
--indirection-template=INDIRECTION_TEMPLATE
Name of the local file, or SCM file containing the
template used to drive the indirection plugin
--indirection-template-url=INDIRECTION_TEMPLATE_URL
SCM URL containing the template used to drive the
indirection plugin
--results-loc=RESULTS_LOC
Relative path inside the working space image where the
results should be extracted from
--scratch Create a scratch image
--wait Wait on the image creation, even if running in the
background
--nowait Do not wait on the image creation
--noprogress Do not display progress of the upload
""" % (self.progname, self.progname))
if __name__ == '__main__':
unittest.main()