diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py index efe07df4..198b6daa 100644 --- a/cli/koji_cli/commands.py +++ b/cli/koji_cli/commands.py @@ -3100,6 +3100,26 @@ def handle_add_group_pkg(goptions, session, args): session.groupPackageListAdd(tag, group, pkg) +def handle_remove_group_pkg(goptions, session, args): + "[admin] Remove a package from a group's package listing" + usage = "usage: %prog remove-group-pkg [options] [ ...]" + parser = OptionParser(usage=get_usage_str(usage)) + parser.add_option("--force", action='store_true', help="Override blocks if necessary") + (options, args) = parser.parse_args(args) + if len(args) < 3: + parser.error("You must specify a tag name, group name, and one or more package names") + tag = args[0] + group = args[1] + opts = {} + opts['force'] = options.force + activate_session(session, goptions) + dsttag = session.getTag(tag) + if dsttag is None: + error("No such tag: %s" % tag) + with session.multicall() as m: + [m.groupPackageListRemove(tag, group, pkg, **opts) for pkg in args[2:]] + + def handle_block_group_pkg(goptions, session, args): "[admin] Block a package from a group's package listing" usage = "usage: %prog block-group-pkg [options] [ ...]" diff --git a/tests/test_cli/data/list-commands-admin.txt b/tests/test_cli/data/list-commands-admin.txt index 66776194..1928c1b9 100644 --- a/tests/test_cli/data/list-commands-admin.txt +++ b/tests/test_cli/data/list-commands-admin.txt @@ -49,6 +49,7 @@ admin commands: regen-repo Generate a current repo if there is not one remove-external-repo Remove an external repo from a tag or tags, or remove entirely remove-group Remove group from tag + remove-group-pkg Remove a package from a group's package listing remove-host-from-channel Remove a host from a channel remove-pkg Remove a package from the listing for tag remove-sig Remove signed RPMs from db and disk diff --git a/tests/test_cli/data/list-commands.txt b/tests/test_cli/data/list-commands.txt index a2057720..809229dd 100644 --- a/tests/test_cli/data/list-commands.txt +++ b/tests/test_cli/data/list-commands.txt @@ -49,6 +49,7 @@ admin commands: regen-repo Generate a current repo if there is not one remove-external-repo Remove an external repo from a tag or tags, or remove entirely remove-group Remove group from tag + remove-group-pkg Remove a package from a group's package listing remove-host-from-channel Remove a host from a channel remove-pkg Remove a package from the listing for tag remove-sig Remove signed RPMs from db and disk diff --git a/tests/test_cli/test_remove_group_pkg.py b/tests/test_cli/test_remove_group_pkg.py new file mode 100644 index 00000000..62277a69 --- /dev/null +++ b/tests/test_cli/test_remove_group_pkg.py @@ -0,0 +1,109 @@ +from __future__ import absolute_import + +import mock +import six + +from koji_cli.commands import handle_remove_group_pkg + +import koji +from . import utils + + +class TestRemoveGroupPkg(utils.CliTestCase): + + def setUp(self): + # Show long diffs in error output... + self.maxDiff = None + self.options = mock.MagicMock() + self.options.debug = False + self.session = mock.MagicMock() + self.session.getAPIVersion.return_value = koji.API_VERSION + self.activate_session_mock = mock.patch('koji_cli.commands.activate_session').start() + self.error_format = """Usage: %s remove-group-pkg [options] [ ...] +(Specify the --help global option for a list of other help options) + +%s: error: {message} +""" % (self.progname, self.progname) + + def test_handle_remove_pkg_not_existing_tag(self): + tag = 'tag' + package = 'package' + group = 'group' + args = [tag, group, package] + + self.session.getTag.return_value = None + self.assert_system_exit( + handle_remove_group_pkg, + self.options, self.session, args, + stderr='No such tag: %s\n' % tag, + stdout='', + activate_session=None, + exit_code=1) + # Finally, assert that things were called as we expected. + self.activate_session_mock.assert_called_once_with(self.session, self.options) + self.session.getTag.assert_called_once_with(tag) + self.session.groupPackageListRemove.assert_not_called() + self.session.multiCall.assert_not_called() + + def test_handle_remove_pkg_wrong_count_args(self): + tag = 'tag' + group = 'group' + args = [tag, group] + expected_error = self.format_error_message( + 'You must specify a tag name, group name, and one or more package names') + self.assert_system_exit( + handle_remove_group_pkg, + self.options, self.session, args, + stderr=expected_error, + stdout='', + activate_session=None, + exit_code=2) + # Finally, assert that things were called as we expected. + self.activate_session_mock.assert_not_called() + self.session.getTag.assert_not_called() + self.session.groupPackageListRemove.assert_not_called() + self.session.multiCall.assert_not_called() + + @mock.patch('sys.stdout', new_callable=six.StringIO) + @mock.patch('sys.stderr', new_callable=six.StringIO) + def test_handle_remove_pkg(self, stderr, stdout): + tag = 'tag' + dsttag = {'name': tag, 'id': 1} + package = 'package' + group = 'group' + args = [tag, group, package] + + self.session.getTag.return_value = dsttag + self.session.groupPackageListRemove.return_value = None + handle_remove_group_pkg(self.options, self.session, args) + actual = stderr.getvalue() + expected = '' + self.assertMultiLineEqual(actual, expected) + actual = stdout.getvalue() + expected = '' + self.assertMultiLineEqual(actual, expected) + # Finally, assert that things were called as we expected. + self.activate_session_mock.assert_called_once_with(self.session, self.options) + self.session.getTag.assert_called_once_with(tag) + + @mock.patch('sys.stdout', new_callable=six.StringIO) + @mock.patch('sys.stderr', new_callable=six.StringIO) + def test_handle_remove_pkg_with_force(self, stderr, stdout): + tag = 'tag' + dsttag = {'name': tag, 'id': 1} + package = 'package' + group = 'group' + args = [tag, group, package, '--force'] + + self.session.getTag.return_value = dsttag + self.session.groupPackageListRemove.return_value = None + handle_remove_group_pkg(self.options, self.session, args) + actual = stderr.getvalue() + expected = '' + self.assertMultiLineEqual(actual, expected) + actual = stdout.getvalue() + expected = '' + self.assertMultiLineEqual(actual, expected) + # Finally, assert that things were called as we expected. + self.activate_session_mock.assert_called_once_with(self.session, self.options) + self.session.getTag.assert_called_once_with(tag)