from __future__ import absolute_import import unittest import mock import six from koji_cli.commands import anon_handle_mock_config from . import utils class TestMockConfig(utils.CliTestCase): # Show long diffs in error output... maxDiff = None def setUp(self): self.maxDiff = None self.common_args = [ '--distribution', 'fedora', '--topdir', '/top-dir', '--topurl', '/top-url', '--yum-proxy', '/yum-proxy' ] self.common_opts = { 'distribution': 'fedora', 'mockdir': '/var/lib/mock', 'topdir': '/top-dir', 'topurl': '/top-url', 'yum_proxy': '/yum-proxy', } self.mock_output = """# Auto-generated by the Koji build system config_opts['chroot_setup_cmd'] = 'groupinstall build' config_opts['use_host_resolv'] = False config_opts['root'] = 'fedora26-build-repo_1' config_opts['yum.conf'] = '[main]\ncachedir=/var/cache/yum\ndebuglevel=1\nlogfile=/var/log/yum.log\nreposdir=/dev/null\nretries=20\nobsoletes=1\ngpgcheck=0\nassumeyes=1\nkeepcache=1\ninstall_weak_deps=0\nstrict=1\n\n# repos\n\n[build]\nname=build\nbaseurl=https://fedora.local/kojifiles/repos/fedora26-build/1/x86_64\n' config_opts['rpmbuild_timeout'] = 86400 config_opts['chroothome'] = '/builddir' config_opts['target_arch'] = 'x86_64' config_opts['basedir'] = '/var/lib/mock' config_opts['plugin_conf']['yum_cache_enable'] = False config_opts['plugin_conf']['root_cache_enable'] = False config_opts['plugin_conf']['ccache_enable'] = False config_opts['macros']['%_rpmfilename'] = '%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' config_opts['macros']['%_topdir'] = '/builddir/build' config_opts['macros']['%packager'] = 'Koji' config_opts['macros']['%_host'] = 'x86_64-koji-linux-gnu' config_opts['macros']['%_host_cpu'] = 'x86_64' config_opts['macros']['%vendor'] = 'Koji' config_opts['macros']['%distribution'] = 'Koji Testing' """ self.error_format = """Usage: %s mock-config [options] (Specify the --help global option for a list of other help options) %s: error: {message} """ % (self.progname, self.progname) @mock.patch('sys.stderr', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('koji.genMockConfig') @mock.patch('koji_cli.commands.ensure_connection') def test_handle_mock_config_buildroot_option( self, ensure_connection_mock, gen_config_mock, stdout, stderr): """Test anon_handle_mock_config buildroot options""" arguments = [] options = mock.MagicMock() buildroot_info = { 'repo_id': 101, 'tag_name': 'tag_name', 'arch': 'x86_64' } # Mock out the xmlrpc server session = mock.MagicMock() session.getBuildroot.return_value = buildroot_info # Mock config gen_config_mock.return_value = self.mock_output # buildroot check arguments = ['--buildroot', 'root', self.progname] expected = self.format_error_message("Buildroot id must be an integer") self.assert_system_exit( anon_handle_mock_config, options, session, arguments, stderr=expected, activate_session=None) arguments = self.common_args + ['--buildroot', '1', '--name', self.progname] opts = self.common_opts.copy() opts.update({ 'repoid': buildroot_info['repo_id'], 'tag_name': buildroot_info['tag_name'], 'tag_macros': {}, }) anon_handle_mock_config(options, session, arguments) self.assert_console_message( stdout, "%s\n" % gen_config_mock.return_value) gen_config_mock.assert_called_with( self.progname, buildroot_info['arch'], **opts) arguments = self.common_args + ['--buildroot', '1', '--name', self.progname, '--latest'] opts['repoid'] = 'latest' anon_handle_mock_config(options, session, arguments) self.assert_console_message( stdout, "%s\n" % gen_config_mock.return_value) gen_config_mock.assert_called_with( self.progname, buildroot_info['arch'], **opts) # if buildroot is not existing buildroot_id = '999999' arguments = ['--buildroot', buildroot_id] expected = "No such buildroot: %s" % buildroot_id + "\n" session.getBuildroot.return_value = None with self.assertRaises(SystemExit) as ex: anon_handle_mock_config(options, session, arguments) self.assertExitCode(ex, 1) self.assert_console_message(stderr, expected) @mock.patch('sys.stderr', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('koji.genMockConfig') @mock.patch('koji_cli.commands.ensure_connection') def test_handle_mock_config_task_option( self, ensure_connection_mock, gen_config_mock, stdout, stderr): """Test anon_handle_mock_config task options""" arguments = [] task_id = 1001 options = mock.MagicMock() session = mock.MagicMock() session.listBuildroots.return_value = '' # Mock config gen_config_mock.return_value = '' arguments = ['--task', 'task'] expected = self.format_error_message("Task id must be an integer") self.assert_system_exit( anon_handle_mock_config, options, session, arguments, stderr=expected, activate_session=None) arguments = ['--task', str(task_id)] expected = "No buildroots for task %s (or no such task)\n" % str(task_id) with self.assertRaises(SystemExit) as ex: anon_handle_mock_config(options, session, arguments) self.assertExitCode(ex, 1) self.assert_console_message(stderr, expected) multi_broots = [ {'id': 1101, 'repo_id': 101, 'tag_name': 'tag_101', 'arch': 'x86_64'}, {'id': 1111, 'repo_id': 111, 'tag_name': 'tag_111', 'arch': 'x86_64'}, {'id': 1121, 'repo_id': 121, 'tag_name': 'tag_121', 'arch': 'x86_64'} ] session.listBuildroots.return_value = multi_broots anon_handle_mock_config(options, session, arguments) expected = "Multiple buildroots found: %s" % [br['id'] for br in multi_broots] self.assert_console_message(stdout, "%s\n\n" % expected) opts = self.common_opts.copy() opts.update({ 'repoid': 'latest', 'tag_name': multi_broots[0]['tag_name'], 'tag_macros': {}, }) arguments = self.common_args + ['--task', str(task_id), '--name', self.progname, '--latest'] session.listBuildroots.return_value = [multi_broots[0]] gen_config_mock.return_value = self.mock_output anon_handle_mock_config(options, session, arguments) self.assert_console_message( stdout, "%s\n" % gen_config_mock.return_value) gen_config_mock.assert_called_with( self.progname, multi_broots[0]['arch'], **opts) @mock.patch('sys.stderr', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('koji.genMockConfig') @mock.patch('koji_cli.commands.ensure_connection') def test_handle_mock_config_tag_option( self, ensure_connection_mock, gen_config_mock, stdout, stderr): """Test anon_handle_mock_config with tag option""" arguments = [] tag = 'tag' tag = {'id': 201, 'name': 'tag', 'arch': 'x86_64'} options = mock.MagicMock() # Mock out the xmlrpc server session = mock.MagicMock() session.getTag.return_value = None session.getBuildConfig.return_value = None session.getRepo.return_value = None arguments = ['--tag', tag['name']] expected = "Please specify an arch\n" with self.assertRaises(SystemExit) as ex: anon_handle_mock_config(options, session, arguments) self.assertExitCode(ex, 1) self.assert_console_message(stderr, expected) arguments = ['--tag', tag['name'], '--arch', tag['arch']] expected = self.format_error_message("No such tag: %s" % tag['name']) self.assert_system_exit( anon_handle_mock_config, options, session, arguments, stderr=expected, activate_session=None) # return tag info session.getTag.return_value = tag expected = "Could not get config info for tag: %(name)s\n" % tag with self.assertRaises(SystemExit) as ex: anon_handle_mock_config(options, session, arguments) self.assertExitCode(ex, 1) self.assert_console_message(stderr, expected) # return build config session.getBuildConfig.return_value = { 'id': 301, 'extra': { 'rpm.macro.random_macro1': 'random_macro_content1', 'rpm.macro.random_macro2': 'random_macro_content2', 'mock.package_manager': 'yum', 'mock.yum.module_hotfixes': 1, }, 'arches': 'x86_64', } expected = "Could not get a repo for tag: %(name)s\n" % tag with self.assertRaises(SystemExit) as ex: anon_handle_mock_config(options, session, arguments) self.assertExitCode(ex, 1) self.assert_console_message(stderr, expected) # return repo session.getRepo.return_value = {'id': 101} gen_config_mock.return_value = self.mock_output anon_handle_mock_config(options, session, arguments) self.assert_console_message( stdout, "%s\n" % gen_config_mock.return_value) arguments = self.common_args + ['--tag', tag['name'], '--arch', tag['arch'], '--name', self.progname, '--latest'] opts = self.common_opts.copy() opts.update({ 'repoid': 'latest', 'tag_name': tag['name'], 'tag_macros': { '%random_macro1': 'random_macro_content1', '%random_macro2': 'random_macro_content2', }, 'package_manager': 'yum', 'module_hotfixes': 1, }) anon_handle_mock_config(options, session, arguments) self.assert_console_message( stdout, "%s\n" % gen_config_mock.return_value) gen_config_mock.assert_called_with( self.progname, tag['arch'], **opts) # return arch warning and config arch = 'test' warn_msg = '%s is not in the list of tag arches' % arch gen_config_mock.return_value = self.mock_output arguments = self.common_args + ['--tag', tag['name'], '--arch', arch, '--name', self.progname, '--latest'] anon_handle_mock_config(options, session, arguments) self.assert_console_message(stdout, "%s\n" % gen_config_mock.return_value) self.assert_console_message(stderr, "%s\n" % warn_msg) # return warning that tag arch is empty session.getBuildConfig.return_value = { 'id': 301, 'extra': { 'rpm.macro.random_macro1': 'random_macro_content1', 'rpm.macro.random_macro2': 'random_macro_content2', 'mock.package_manager': 'yum', 'mock.yum.module_hotfixes': 1, }, 'arches': None, } arch = 'test' warn_msg = 'Tag %s has an empty arch list' % tag['name'] gen_config_mock.return_value = self.mock_output arguments = self.common_args + ['--tag', tag['name'], '--arch', arch, '--name', self.progname, '--latest'] anon_handle_mock_config(options, session, arguments) self.assert_console_message(stdout, "%s\n" % gen_config_mock.return_value) self.assert_console_message(stderr, "%s\n" % warn_msg) @mock.patch('sys.stderr', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('koji.genMockConfig') @mock.patch('koji_cli.commands.ensure_connection') @mock.patch('koji_cli.commands.open') def test_handle_mock_config_target_option( self, openf, ensure_connection_mock, gen_config_mock, stdout, stderr): """Test anon_handle_mock_config with target option""" arguments = [] arch = "x86_64" target = {'id': 1, 'name': 'target', 'dest_tag': 1, 'build_tag': 2, 'build_tag_name': 'target-build', 'dest_tag_name': 'target'} options = mock.MagicMock() # Mock out the xmlrpc server session = mock.MagicMock() session.getBuildTarget.return_value = None session.getRepo.return_value = None arguments = ['--target', target['name']] expected = "Please specify an arch\n" with self.assertRaises(SystemExit) as ex: anon_handle_mock_config(options, session, arguments) self.assertExitCode(ex, 1) self.assert_console_message(stderr, expected) arguments = ['--target', target['name'], '--arch', arch] expected = self.format_error_message("No such build target: %s" % target['name']) self.assert_system_exit( anon_handle_mock_config, options, session, arguments, stderr=expected, activate_session=None) session.getBuildTarget.return_value = target session.getBuildConfig.return_value = {'arches': 'x86_64', 'extra': {}} expected = "Could not get a repo for tag: %s\n" % target['build_tag_name'] with self.assertRaises(SystemExit) as ex: anon_handle_mock_config(options, session, arguments) self.assertExitCode(ex, 1) self.assert_console_message(stderr, expected) arguments = self.common_args + ['--target', target['name'], '--arch', arch, '--name', self.progname] opts = self.common_opts.copy() opts.update({ 'repoid': 101, 'tag_name': target['build_tag_name'], 'tag_macros': {}, }) session.getRepo.return_value = {'id': 101} gen_config_mock.return_value = self.mock_output anon_handle_mock_config(options, session, arguments) self.assert_console_message( stdout, "%s\n" % gen_config_mock.return_value) gen_config_mock.assert_called_with( self.progname, arch, **opts) # --latest and -o (output) test opts['repoid'] = 'latest' arguments.extend(['--latest', '-o', '/tmp/mock.out']) fobj = mock.MagicMock() openf.return_value.__enter__.return_value = fobj anon_handle_mock_config(options, session, arguments) openf.assert_called_once() fobj.write.assert_called_once_with(self.mock_output) gen_config_mock.assert_called_with( self.progname, arch, **opts) # return arch warning and config arch = 'test' arguments = self.common_args + ['--target', target['name'], '--arch', arch, '--name', self.progname] warn_msg = '%s is not in the list of tag arches' % arch gen_config_mock.return_value = self.mock_output anon_handle_mock_config(options, session, arguments) self.assert_console_message(stdout, "%s\n" % gen_config_mock.return_value) self.assert_console_message(stderr, "%s\n" % warn_msg) @mock.patch('sys.stderr', new_callable=six.StringIO) @mock.patch('koji_cli.commands.ensure_connection') def test_handle_mock_config_errors(self, ensure_connection_mock, stderr): """Test anon_handle_mock_config general error messages""" arguments = [] options = mock.MagicMock() # Mock out the xmlrpc server session = mock.MagicMock() # Run it and check immediate output # argument is empty expected = self.format_error_message( "Please specify one of: --tag, --target, --task, --buildroot") self.assert_system_exit( anon_handle_mock_config, options, session, arguments, stderr=expected, activate_session=None) # name is specified twice case arguments = [self.progname, '--name', 'name'] expected = self.format_error_message("Name already specified via option") self.assert_system_exit( anon_handle_mock_config, options, session, arguments, stderr=expected, activate_session=None) def test_handle_mock_config_help(self): """Test anon_handle_mock_config help message full output""" self.assert_help( anon_handle_mock_config, """Usage: %s mock-config [options] (Specify the --help global option for a list of other help options) Options: -h, --help show this help message and exit -a ARCH, --arch=ARCH Specify the arch -n NAME, --name=NAME Specify the name for the buildroot --tag=TAG Create a mock config for a tag --target=TARGET Create a mock config for a build target --task=TASK Duplicate the mock config of a previous task --latest use the latest redirect url --buildroot=BUILDROOT Duplicate the mock config for the specified buildroot id --mockdir=DIR Specify mockdir --topdir=DIR Specify topdir --topurl=URL URL under which Koji files are accessible --distribution=DISTRIBUTION Change the distribution macro --yum-proxy=YUM_PROXY Specify a yum proxy -o FILE Output to a file """ % self.progname) if __name__ == '__main__': unittest.main()