diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py index ea49ee42..25190606 100644 --- a/cli/koji_cli/commands.py +++ b/cli/koji_cli/commands.py @@ -6163,12 +6163,12 @@ def handle_cancel(goptions, session, args): for task_id in tlist: results.append(remote_fn(task_id, **opts)) for build in blist: - results.append(m.cancelBuild(build)) + results.append(m.cancelBuild(build, strict=True)) err = False for r in results: - if isinstance(r.result, dict): - warn(r.result['faultString']) + if isinstance(r._result, dict): + warn(r._result['faultString']) err = True if err: return 1 diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py index f7d5a44a..f7058373 100644 --- a/kojihub/kojihub.py +++ b/kojihub/kojihub.py @@ -11620,13 +11620,18 @@ class RootExports(object): results = _applyQueryOpts(results, queryOpts) return koji.fixEncodingRecurse(results, remove_nonprintable=True) - def cancelBuild(self, buildID): + def cancelBuild(self, buildID, strict=False): """Cancel the build with the given buildID + :param int|str|dict buildID: int ID, a string NVR, or + a map containing 'name', 'version' and 'release'. + :param bool strict: if strict is True and build is not existing, an exception is raised, + if strict is False and build is not existing, returns False + If the build is associated with a task, cancel the task as well. Return True if the build was successfully canceled, False if not.""" context.session.assertLogin() - build = get_build(buildID) + build = get_build(buildID, strict) if build is None: return False if build['owner_id'] != context.session.user_id: diff --git a/tests/test_cli/test_cancel.py b/tests/test_cli/test_cancel.py index 161c8dfb..c8f50564 100644 --- a/tests/test_cli/test_cancel.py +++ b/tests/test_cli/test_cancel.py @@ -3,12 +3,21 @@ from __future__ import absolute_import import mock import koji +import six from koji_cli.commands import handle_cancel from . import utils class TestCancel(utils.CliTestCase): + def __vm(self, result): + m = koji.VirtualCall('mcall_method', [], {}) + if isinstance(result, dict) and result.get('faultCode'): + m._result = result + else: + m._result = (result,) + return m + def setUp(self): self.maxDiff = None self.options = mock.MagicMock() @@ -16,7 +25,8 @@ class TestCancel(utils.CliTestCase): self.session = mock.MagicMock() self.session.multicall.return_value.__enter__.return_value = self.session self.activate_session_mock = mock.patch('koji_cli.commands.activate_session').start() - + self.stderr = mock.patch('sys.stderr', new_callable=six.StringIO).start() + self.stdout = mock.patch('sys.stdout', new_callable=six.StringIO).start() self.error_format = """Usage: %s cancel [options] [ ...] (Specify the --help global option for a list of other help options) @@ -68,7 +78,7 @@ class TestCancel(utils.CliTestCase): self.activate_session_mock.assert_called_once_with(self.session, self.options) self.session.cancelTask.assert_not_called() self.session.cancelTaskFull.assert_not_called() - self.session.cancelBuild.assert_called_once_with(args[0]) + self.session.cancelBuild.assert_called_once_with(args[0], strict=True) def test_cancel_builds_unused_options(self): # it is good for nothing here @@ -78,7 +88,7 @@ class TestCancel(utils.CliTestCase): self.activate_session_mock.assert_called_once_with(self.session, self.options) self.session.cancelTask.assert_not_called() self.session.cancelTaskFull.assert_not_called() - self.session.cancelBuild.assert_called_once_with(args[0]) + self.session.cancelBuild.assert_called_once_with(args[0], strict=True) def test_cancel_tasks_full(self): args = ['123', '--full'] @@ -124,6 +134,24 @@ class TestCancel(utils.CliTestCase): self.session.cancelTaskFull.assert_not_called() self.session.cancelBuild.assert_not_called() + def test_non_exist_build_and_task(self): + args = ['11111', 'nvr-1-30.1'] + expected_warn = """No such task: %s +No such build: '%s' +""" % (args[0], args[1]) + mcall = self.session.multicall.return_value.__enter__.return_value + mcall.cancelTask.return_value = self.__vm( + {'faultCode': 1000, 'faultString': 'No such task: %s' % args[0]}) + mcall.cancelBuild.return_value = self.__vm( + {'faultCode': 1000, 'faultString': "No such build: '%s'" % args[1]}) + rv = handle_cancel(self.options, self.session, args) + self.assertEqual(rv, 1) + self.assert_console_message(self.stderr, expected_warn) + self.activate_session_mock.assert_called_once_with(self.session, self.options) + self.session.cancelTask.assert_called_once_with(int(args[0])) + self.session.cancelTaskFull.assert_not_called() + self.session.cancelBuild.assert_called_once_with(args[1], strict=True) + def test_cancel_help(self): self.assert_help( handle_cancel,