From faab309cde01a4fc60a7887cbde642db4ffee5c3 Mon Sep 17 00:00:00 2001
From: Jana Cupova <root@localhost>
Date: Mar 16 2023 08:58:17 +0000
Subject: Increase CLI uni tests


---

diff --git a/tests/test_cli/test_list_builds.py b/tests/test_cli/test_list_builds.py
index 6e67c88..0bc8035 100644
--- a/tests/test_cli/test_list_builds.py
+++ b/tests/test_cli/test_list_builds.py
@@ -1,6 +1,8 @@
 from __future__ import absolute_import
 
 import mock
+import os
+import time
 from six.moves import StringIO
 
 import koji
@@ -16,6 +18,9 @@ class TestListBuilds(utils.CliTestCase):
         self.session = mock.MagicMock()
         self.session.getAPIVersion.return_value = koji.API_VERSION
         self.ensure_connection_mock = mock.patch('koji_cli.commands.ensure_connection').start()
+        self.original_timezone = os.environ.get('TZ')
+        os.environ['TZ'] = 'UTC'
+        time.tzset()
         self.user_info = {'id': 1, 'name': 'kojiadmin', 'status': 0, 'usertype': 0,
                           'krb_principals': []}
         self.owner = 'kojiadmin'
@@ -28,23 +33,31 @@ class TestListBuilds(utils.CliTestCase):
             {'build_id': 1, 'epoch': 34, 'name': 'test-build', 'volume_id': 1,
              'nvr': 'test-build-11-12', 'owner_name': 'kojiadmin', 'task_id': None,
              'release': '12', 'state': 1, 'version': '11', 'package_id': 1,
-             'source': 'test-source-1'},
+             'source': 'test-source-1', 'completion_time': '2023-02-03 14:35'},
             {'build_id': 4, 'epoch': 34, 'name': 'test-build', 'volume_id': 0,
              'nvr': 'test-build-8-12', 'owner_name': 'kojiadmin', 'task_id': 40,
              'release': '12', 'state': 2, 'version': '8', 'package_id': 1,
-             'source': 'test-source-2'},
+             'source': 'test-source-2', 'completion_time': '2023-02-01 14:35'},
             {'build_id': 2, 'epoch': 34, 'name': 'test-build', 'volume_id': 0,
              'nvr': 'test-build-11-9', 'owner_name': 'kojitest', 'task_id': 20,
              'release': '9', 'state': 1, 'version': '11', 'package_id': 1,
-             'source': 'test-source-3'},
+             'source': 'test-source-3', 'completion_time': '2023-01-03 14:35'},
             {'build_id': 3, 'epoch': 34, 'name': 'test-build', 'volume_id': 0,
              'nvr': 'test-build-10-12', 'owner_name': 'kojitest', 'task_id': None,
              'release': '12', 'state': 4, 'version': '10', 'package_id': 1,
-             'source': 'test-source-4'},
+             'source': 'test-source-4', 'completion_time': '2023-02-08 14:35'},
             {'build_id': 5, 'epoch': 34, 'name': 'test-zx-build', 'volume_id': 1,
              'nvr': 'build-test-1-12', 'owner_name': 'kojiadmin', 'task_id': 50,
              'release': '12', 'state': 4, 'version': '1', 'package_id': 2,
-             'source': 'test-source-5'}]
+             'source': 'test-source-5', 'completion_time': '2023-02-04 14:35'}]
+
+    def tearDown(self):
+        mock.patch.stopall()
+        if self.original_timezone is None:
+            del os.environ['TZ']
+        else:
+            os.environ['TZ'] = self.original_timezone
+        time.tzset()
 
     def test_list_buildroot_with_args(self):
         self.assert_system_exit(
@@ -215,6 +228,46 @@ test-build-8-12                                          kojiadmin         DELET
         self.session.listBuilds.assert_called_once_with(userID=1)
 
     @mock.patch('sys.stdout', new_callable=StringIO)
+    def test_list_builds_opt_before(self, stdout):
+        expected_output = """test-build-11-9                                          kojitest          COMPLETE
+test-build-8-12                                          kojiadmin         DELETED
+"""
+        self.session.getUser.return_value = self.user_info
+        self.session.listBuilds.return_value = [self.list_build[1], self.list_build[2]]
+        rv = anon_handle_list_builds(self.options, self.session, ['--before', "2023-02-01 23:59",
+                                                                  '--sort-key', 'nvr'])
+        self.assertEqual(rv, None)
+        self.assert_console_message(stdout, expected_output)
+
+        self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
+        self.session.getPackageID.assert_not_called()
+        self.session.getUser.assert_not_called()
+        self.session.listVolumes.assert_not_called()
+        self.session.getBuild.assert_not_called()
+        self.session.listBuilds.assert_called_once_with(completeBefore=1675295940.0)
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    def test_list_builds_opt_after(self, stdout):
+        expected_output = """build-test-1-12                                          kojiadmin         CANCELED
+test-build-10-12                                         kojitest          CANCELED
+test-build-11-12                                         kojiadmin         COMPLETE
+"""
+        self.session.getUser.return_value = self.user_info
+        self.session.listBuilds.return_value = [self.list_build[0], self.list_build[3],
+                                                self.list_build[4]]
+        rv = anon_handle_list_builds(self.options, self.session, ['--after', "2023-02-01 23:59",
+                                                                  '--sort-key', 'nvr'])
+        self.assertEqual(rv, None)
+        self.assert_console_message(stdout, expected_output)
+
+        self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
+        self.session.getPackageID.assert_not_called()
+        self.session.getUser.assert_not_called()
+        self.session.listVolumes.assert_not_called()
+        self.session.getBuild.assert_not_called()
+        self.session.listBuilds.assert_called_once_with(completeAfter=1675295940.0)
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
     def test_list_builds_opt_owner_sorted_state(self, stdout):
         expected_output = """test-build-11-12                                         kojiadmin         COMPLETE
 test-build-8-12                                          kojiadmin         DELETED
diff --git a/tests/test_cli/test_list_external_repos.py b/tests/test_cli/test_list_external_repos.py
index c576053..392f569 100644
--- a/tests/test_cli/test_list_external_repos.py
+++ b/tests/test_cli/test_list_external_repos.py
@@ -1,6 +1,9 @@
 from __future__ import absolute_import
 
 import mock
+import os
+import time
+from six.moves import StringIO
 
 from koji_cli.commands import anon_handle_list_external_repos
 from . import utils
@@ -18,6 +21,27 @@ class TestListExternalRepo(utils.CliTestCase):
 
 %s: error: {message}
 """ % (self.progname, self.progname)
+        self.original_timezone = os.environ.get('TZ')
+        os.environ['TZ'] = 'UTC'
+        time.tzset()
+        self.repo_id = 123
+        self.repo_url = 'http://path_to_ext_repo.com'
+        self.repo_name = 'test-repo'
+        self.repo_info = {'id': self.repo_id, 'name': self.repo_name, 'url': self.repo_url}
+        self.event = {'id': 1000, 'ts': 1000000.11, 'timestr': 'Mon Jan 12 13:46:40 1970'}
+        self.test_tag = 'test-tag'
+        self.tag_external_repos = [
+            {'external_repo_id': 234, 'external_repo_name': 'text-ext-repo-1', 'priority': 12,
+             'tag_name': self.test_tag, 'url': self.repo_url},
+            {'external_repo_id': 345, 'external_repo_name': 'text-ext-repo-2', 'priority': 11,
+             'tag_name': self.test_tag, 'url': self.repo_url}]
+
+    def tearDown(self):
+        if self.original_timezone is None:
+            del os.environ['TZ']
+        else:
+            os.environ['TZ'] = self.original_timezone
+        time.tzset()
 
     def test_list_external_repos_with_args(self):
         arguments = ['arg']
@@ -30,6 +54,159 @@ class TestListExternalRepo(utils.CliTestCase):
             activate_session=None)
         self.ensure_connection_mock.assert_not_called()
 
+    @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, 'ts': 1000000.11})
+    def test_list_external_repos_tag_with_inherit_and_repo_info(self, event_opts):
+        args = ['--event', str(self.event['id']), '--tag', self.test_tag, '--inherit',
+                '--id', str(self.repo_id)]
+        self.assert_system_exit(
+            anon_handle_list_external_repos,
+            self.options, self.session, args,
+            stdout="Querying at event %(id)i (%(timestr)s)\n" % self.event,
+            stderr=self.format_error_message("Can't select by repo when using --inherit"),
+            exit_code=2,
+            activate_session=None)
+        self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, 'ts': 1000000.11})
+    def test_list_external_repos_tag_with_inherit_and_repo_info_none(self, event_opts, stdout):
+        args = ['--event', str(self.event['id']), '--tag', self.test_tag, '--inherit']
+        external_repo_list = [{'arches': None,
+                               'external_repo_id': 201,
+                               'external_repo_name': 'ext-repo',
+                               'merge_mode': 'koji',
+                               'priority': 20,
+                               'tag_name': 'test-tag'}]
+        self.session.getExternalRepoList.return_value = external_repo_list
+        anon_handle_list_external_repos(self.options, self.session, args)
+        actual = stdout.getvalue()
+        expected = """Querying at event %i (%s)
+%s             %i  %s       %s
+""" % (self.event['id'], self.event['timestr'], external_repo_list[0]['tag_name'],
+       external_repo_list[0]['priority'], external_repo_list[0]['merge_mode'],
+       external_repo_list[0]['external_repo_name'])
+        self.assertMultiLineEqual(actual, expected)
+        self.session.getTagExternalRepos.assert_not_called()
+        self.session.getExternalRepoList.assert_called_once_with(
+            event=self.event['id'], tag_info=self.test_tag)
+        self.session.listExternalRepos.assert_not_called()
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, 'ts': 1000000.11})
+    def test_list_external_repos_tag_without_inherit(self, event_opts, stdout):
+        args = ['--event', str(self.event['id']), '--tag', self.test_tag]
+
+        self.session.getTagExternalRepos.return_value = self.tag_external_repos
+        anon_handle_list_external_repos(self.options, self.session, args)
+        actual = stdout.getvalue()
+        expected = """Querying at event %i (%s)
+%i  %s           None       %s
+%i  %s           None       %s
+""" % (self.event['id'], self.event['timestr'], self.tag_external_repos[0]['priority'],
+       self.tag_external_repos[0]['external_repo_name'], self.tag_external_repos[0]['url'],
+       self.tag_external_repos[1]['priority'], self.tag_external_repos[1]['external_repo_name'],
+       self.tag_external_repos[1]['url'])
+        self.assertMultiLineEqual(actual, expected)
+        self.session.getTagExternalRepos.assert_called_once_with(
+            event=self.event['id'], repo_info=None, tag_info=self.test_tag)
+        self.session.getExternalRepoList.assert_not_called()
+        self.session.listExternalRepos.assert_not_called()
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, 'ts': 1000000.11})
+    def test_list_external_repos_tag_without_inherit_without_quiet(self, event_opts, stdout):
+        self.options.quiet = False
+        args = ['--event', str(self.event['id']), '--tag', self.test_tag]
+        self.session.getTagExternalRepos.return_value = self.tag_external_repos
+        anon_handle_list_external_repos(self.options, self.session, args)
+        actual = stdout.getvalue()
+        expected = """Querying at event %i (%s)
+Pri External repo name        Mode       URL
+--- ------------------------- ---------- ----------------------------------------
+%i  %s           None       %s
+%i  %s           None       %s
+""" % (self.event['id'], self.event['timestr'], self.tag_external_repos[0]['priority'],
+       self.tag_external_repos[0]['external_repo_name'], self.tag_external_repos[0]['url'],
+       self.tag_external_repos[1]['priority'], self.tag_external_repos[1]['external_repo_name'],
+       self.tag_external_repos[1]['url'])
+        self.assertMultiLineEqual(actual, expected)
+        self.session.getTagExternalRepos.assert_called_once_with(
+            event=self.event['id'], repo_info=None, tag_info=self.test_tag)
+        self.session.getExternalRepoList.assert_not_called()
+        self.session.listExternalRepos.assert_not_called()
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji.util.eventFromOpts', return_value=None)
+    def test_list_external_repos_used(self, event_opts, stdout):
+        args = ['--used', '--id', str(self.repo_id)]
+        self.session.getTagExternalRepos.return_value = self.tag_external_repos
+        anon_handle_list_external_repos(self.options, self.session, args)
+        actual = stdout.getvalue()
+        expected = """%s             %i  None       %s
+%s             %i  None       %s
+""" % (self.tag_external_repos[0]['tag_name'], self.tag_external_repos[0]['priority'],
+       self.tag_external_repos[0]['external_repo_name'], self.tag_external_repos[1]['tag_name'],
+       self.tag_external_repos[1]['priority'], self.tag_external_repos[1]['external_repo_name'])
+        self.assertMultiLineEqual(actual, expected)
+        self.session.getTagExternalRepos.assert_called_once_with(repo_info=self.repo_id)
+        self.session.getExternalRepoList.assert_not_called()
+        self.session.listExternalRepos.assert_not_called()
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji.util.eventFromOpts', return_value=None)
+    def test_list_external_repos_used_without_quiet(self, event_opts, stdout):
+        self.options.quiet = False
+        args = ['--used', '--id', str(self.repo_id)]
+        self.session.getTagExternalRepos.return_value = self.tag_external_repos
+        anon_handle_list_external_repos(self.options, self.session, args)
+        actual = stdout.getvalue()
+        expected = """Tag                  Pri Mode       External repo name
+-------------------- --- ---------- -------------------------
+%s             %i  None       %s
+%s             %i  None       %s
+""" % (self.tag_external_repos[0]['tag_name'], self.tag_external_repos[0]['priority'],
+       self.tag_external_repos[0]['external_repo_name'], self.tag_external_repos[1]['tag_name'],
+       self.tag_external_repos[1]['priority'], self.tag_external_repos[1]['external_repo_name'])
+        self.assertMultiLineEqual(actual, expected)
+        self.session.getTagExternalRepos.assert_called_once_with(repo_info=self.repo_id)
+        self.session.getExternalRepoList.assert_not_called()
+        self.session.listExternalRepos.assert_not_called()
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, 'ts': 1000000.11})
+    def test_list_external_repos_basic(self, event_opts, stdout):
+        args = ['--event', str(self.event['id']), '--name', self.repo_name, '--url', self.repo_url]
+        self.session.listExternalRepos.return_value = [self.repo_info]
+        anon_handle_list_external_repos(self.options, self.session, args)
+        actual = stdout.getvalue()
+        expected = """Querying at event %i (%s)
+%s                 %s
+""" % (self.event['id'], self.event['timestr'], self.repo_name, self.repo_url)
+        self.assertMultiLineEqual(actual, expected)
+        self.session.listExternalRepos.assert_called_once_with(
+            event=self.event['id'], info=self.repo_name, url=self.repo_url)
+        self.session.getTagExternalRepos.assert_not_called()
+        self.session.getExternalRepoList.assert_not_called()
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, 'ts': 1000000.11})
+    def test_list_external_repos_basic_without_queit(self, event_opts, stdout):
+        self.options.quiet = False
+        args = ['--event', str(self.event['id']), '--name', self.repo_name, '--url', self.repo_url]
+        self.session.listExternalRepos.return_value = [self.repo_info]
+        anon_handle_list_external_repos(self.options, self.session, args)
+        actual = stdout.getvalue()
+        expected = """Querying at event %i (%s)
+External repo name        URL
+------------------------- ----------------------------------------
+%s                 %s
+""" % (self.event['id'], self.event['timestr'], self.repo_name, self.repo_url)
+        self.assertMultiLineEqual(actual, expected)
+        self.session.listExternalRepos.assert_called_once_with(
+            event=self.event['id'], info=self.repo_name, url=self.repo_url)
+        self.session.getTagExternalRepos.assert_not_called()
+        self.session.getExternalRepoList.assert_not_called()
+
     def test_list_external_repos_help(self):
         self.assert_help(
             anon_handle_list_external_repos,
diff --git a/tests/test_cli/test_list_groups.py b/tests/test_cli/test_list_groups.py
index cb7501a..441d746 100644
--- a/tests/test_cli/test_list_groups.py
+++ b/tests/test_cli/test_list_groups.py
@@ -40,6 +40,14 @@ class TestListGroups(utils.CliTestCase):
         self.event_from_opts.return_value = {}
         self.__list_groups('', [], '')
 
+    def test_anon_handle_list_groups_list_show_blocked(self):
+        self.event_from_opts.return_value = {}
+        self.__list_groups('', ['--show-blocked'], '')
+
+    def test_anon_handle_list_groups_list_other_tag(self):
+        self.event_from_opts.return_value = {}
+        self.__list_groups('srpm-build', [], '')
+
     def test_anon_handle_list_groups_list_with_group(self):
         self.event_from_opts.return_value = {}
         self.__list_groups('build', [], '')
@@ -57,7 +65,7 @@ class TestListGroups(utils.CliTestCase):
         self.__list_groups('build', ['--ts', '1234567'], expected)
 
     @mock.patch('koji_cli.commands.ensure_connection')
-    def __list_groups(self, query_group, options, expected, ensure_connection_mock):
+    def __list_groups(self, query_group, options, expected, ensure_connection_mock, tag=True):
         _list_tags = [
             {
                 'maven_support': False,
@@ -122,7 +130,7 @@ class TestListGroups(utils.CliTestCase):
                     {
                         "package": "bash",
                         "requires": None,
-                        "tag_id": 2,
+                        "tag_id": 3,
                         "group_id": 2,
                         "type": "mandatory",
                         "basearchonly": None,
@@ -133,13 +141,13 @@ class TestListGroups(utils.CliTestCase):
                 "name": "srpm-build",
                 "uservisible": True,
                 "description": None,
-                "tag_id": 2,
+                "tag_id": 3,
                 "is_default": None,
                 "biarchonly": False,
                 "exported": True,
                 "langonly": None,
                 "group_id": 2,
-                "blocked": False
+                "blocked": True
             },
         ]
 
@@ -166,6 +174,7 @@ class TestListGroups(utils.CliTestCase):
                 if tag['id'] == tag_id:
                     return tag
             return None
+
         self.session.getTag.side_effect = get_tag
         self.session.getTagGroups.return_value = _get_tag_groups
         args = ['fedora26-build']
diff --git a/tests/test_cli/test_list_history.py b/tests/test_cli/test_list_history.py
index 656b5a4..c3286dc 100644
--- a/tests/test_cli/test_list_history.py
+++ b/tests/test_cli/test_list_history.py
@@ -27,6 +27,9 @@ class TestListHistory(utils.CliTestCase):
 %s: error: {message}
 """ % (self.progname, self.progname)
 
+    def tearDown(self):
+        mock.patch.stopall()
+
     @staticmethod
     def get_expected_date_active_action(item, act='add', utc=False):
         if act == 'add':
@@ -40,7 +43,7 @@ class TestListHistory(utils.CliTestCase):
                 active = ''
         else:
             if utc:
-                dt = datetime.fromtimestamp(item['create_ts'], tzutc())
+                dt = datetime.fromtimestamp(item['revoke_ts'], tzutc())
             else:
                 dt = datetime.fromtimestamp(item['revoke_ts'])
             active = ''
@@ -244,7 +247,7 @@ class TestListHistory(utils.CliTestCase):
         }
         self.session.queryHistory.return_value = dict_history
         expected = self.get_expected_channel(dict_history['host_channels'][0], utc=True)
-        anon_handle_list_history(self.options, self.session, ['--channel', channel_name])
+        anon_handle_list_history(self.options, self.session, ['--channel', channel_name, '--utc'])
         self.assert_console_message(stdout, expected)
         self.ensure_connection.assert_called_once_with(self.session, self.options)
 
diff --git a/tests/test_cli/test_make_task.py b/tests/test_cli/test_make_task.py
new file mode 100644
index 0000000..c4c8c64
--- /dev/null
+++ b/tests/test_cli/test_make_task.py
@@ -0,0 +1,109 @@
+from __future__ import absolute_import
+import koji
+import mock
+from six.moves import StringIO
+
+from koji_cli.commands import handle_make_task
+from . import utils
+
+
+class TestAddNotification(utils.CliTestCase):
+    def setUp(self):
+        self.maxDiff = None
+        self.options = mock.MagicMock()
+        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 make-task [options] <method> [<arg> ...]
+(Specify the --help global option for a list of other help options)
+
+%s: error: {message}
+""" % (self.progname, self.progname)
+        self.watch_tasks_mock = mock.patch('koji_cli.commands.watch_tasks').start()
+        self.watch_tasks_mock.return_value = 0
+        self.task_id = 111
+        self.method = 'test_method'
+        self.channel = 'test-channel'
+        self.priority = 1
+        self.arch = 'test-arch'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_make_task_without_args(self):
+        arguments = []
+        self.assert_system_exit(
+            handle_make_task,
+            self.options, self.session, arguments,
+            stdout='',
+            stderr=self.format_error_message('Please specify task method at least'),
+            exit_code=2,
+            activate_session=None)
+        self.activate_session_mock.assert_not_called()
+        self.session.makeTask.assert_not_called()
+        self.watch_tasks_mock.assert_not_called()
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji_cli.commands._running_in_bg', return_value=True)
+    def test_make_task_running_bg_without_watch(self, running_in_bg, stdout):
+        arguments = ['--channel', self.channel, '--priority', self.priority, '--arch', self.arch,
+                     self.method]
+        self.session.makeTask.return_value = self.task_id
+        rv = handle_make_task(self.options, self.session, arguments)
+        actual = stdout.getvalue()
+        expected = "Created task id %i\n" % self.task_id
+        self.assertMultiLineEqual(actual, expected)
+        self.assertEqual(rv, None)
+        self.activate_session_mock.assert_called_once_with(self.session, self.options)
+        self.session.makeTask.assert_called_once_with(
+            arch=self.arch, arglist=[], channel=self.channel, method=self.method,
+            priority=self.priority)
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji_cli.commands._running_in_bg', return_value=False)
+    def test_make_task_not_running_bg_with_watch(self, running_in_bg, stdout):
+        arguments = ['--channel', self.channel, '--priority', self.priority, '--arch', self.arch,
+                     '--watch', self.method]
+        task_id = 111
+        self.session.makeTask.return_value = task_id
+        rv = handle_make_task(self.options, self.session, arguments)
+        actual = stdout.getvalue()
+        expected = "Created task id %i\n" % task_id
+        self.assertMultiLineEqual(actual, expected)
+        self.assertEqual(rv, 0)
+        self.activate_session_mock.assert_called_once_with(self.session, self.options)
+        self.session.makeTask.assert_called_once_with(
+            arch=self.arch, arglist=[], channel=self.channel, method=self.method,
+            priority=self.priority)
+
+    @mock.patch('sys.stdout', new_callable=StringIO)
+    @mock.patch('koji_cli.commands._running_in_bg', return_value=False)
+    def test_make_task_not_running_bg_without_watch(self, running_in_bg, stdout):
+        arguments = ['--channel', self.channel, '--priority', self.priority, '--arch', self.arch,
+                     self.method]
+        task_id = 111
+        self.session.makeTask.return_value = task_id
+        rv = handle_make_task(self.options, self.session, arguments)
+        actual = stdout.getvalue()
+        expected = "Created task id %i\n" % task_id
+        self.assertMultiLineEqual(actual, expected)
+        self.assertEqual(rv, None)
+        self.activate_session_mock.assert_called_once_with(self.session, self.options)
+        self.session.makeTask.assert_called_once_with(
+            arch=self.arch, arglist=[], channel=self.channel, method=self.method,
+            priority=self.priority)
+        self.watch_tasks_mock.assert_not_called()
+
+    def test_handle_make_task_help(self):
+        self.assert_help(
+            handle_make_task,
+            """Usage: %s make-task [options] <method> [<arg> ...]
+(Specify the --help global option for a list of other help options)
+
+Options:
+  -h, --help           show this help message and exit
+  --channel=CHANNEL    set channel
+  --priority=PRIORITY  set priority
+  --watch              watch the task
+  --arch=ARCH          set arch
+""" % self.progname)
diff --git a/tests/test_cli/test_show_groups.py b/tests/test_cli/test_show_groups.py
index fb10a63..73e6deb 100644
--- a/tests/test_cli/test_show_groups.py
+++ b/tests/test_cli/test_show_groups.py
@@ -1,6 +1,8 @@
 from __future__ import absolute_import
 
 import mock
+import pprint
+from six.moves import StringIO
 
 from koji_cli.commands import anon_handle_show_groups
 from . import utils
@@ -13,12 +15,42 @@ class TestShowGroups(utils.CliTestCase):
         self.options = mock.MagicMock()
         self.session = mock.MagicMock()
         self.ensure_connection_mock = mock.patch('koji_cli.commands.ensure_connection').start()
+        self.stderr = mock.patch('sys.stderr', new_callable=StringIO).start()
+        self.stdout = mock.patch('sys.stdout', new_callable=StringIO).start()
         self.error_format = """Usage: %s show-groups [options] <tag>
 (Specify the --help global option for a list of other help options)
 
 %s: error: {message}
 """ % (self.progname, self.progname)
         self.tag = 'test-tag'
+        self.tag_groups = [
+            {'grouplist': [],
+             'packagelist': [],
+             'description': None,
+             'uservisible': True,
+             'tag_id': 3,
+             'is_default': None,
+             'biarchonly': False,
+             'exported': True,
+             'blocked': True,
+             'display_name': 'group-1',
+             'name': 'group-1',
+             'langonly': None,
+             'group_id': 2},
+            {'grouplist': [],
+             'packagelist': [],
+             'description': None,
+             'uservisible': True,
+             'tag_id': 5,
+             'is_default': None,
+             'biarchonly': False,
+             'exported': True,
+             'blocked': False,
+             'display_name': 'group-2',
+             'name': 'group-2',
+             'langonly': None,
+             'group_id': 3}
+        ]
 
     def test_show_groups_incorrect_num_of_args(self):
         arguments = []
@@ -55,6 +87,101 @@ class TestShowGroups(utils.CliTestCase):
             activate_session=None)
         self.ensure_connection_mock.assert_not_called()
 
+    def test_show_groups_blocked(self):
+        self.session.getTagGroups.return_value = self.tag_groups
+        rv = anon_handle_show_groups(self.options, self.session, [self.tag, '--show-blocked'])
+        self.assertEqual(rv, None)
+        self.assert_console_message(self.stdout, pprint.pprint(self.tag_groups))
+        self.assert_console_message(self.stderr, "")
+        self.session.getTagGroups.assert_called_once_with(self.tag, incl_blocked=True)
+
+    def test_show_groups_comps(self):
+        expected_output = """<?xml version="1.0"?>
+<!DOCTYPE comps PUBLIC "-//Red Hat, Inc.//DTD Comps info//EN" "comps.dtd">
+
+<!-- Auto-generated by the build system -->
+<comps>
+  <group>
+    <id>group-2</id>
+    <name>group-2</name>
+    <description>None</description>
+    <default>false</default>
+    <uservisible>true</uservisible>
+    <packagelist>
+    </packagelist>
+  </group>
+</comps>
+
+"""
+        self.session.getTagGroups.return_value = [self.tag_groups[1]]
+        rv = anon_handle_show_groups(self.options, self.session, [self.tag, '--comps'])
+        self.assertEqual(rv, None)
+        self.assert_console_message(self.stdout, expected_output)
+        self.assert_console_message(self.stderr, "")
+        self.session.getTagGroups.assert_called_once_with(self.tag)
+
+    def test_show_groups_comps_with_expand(self):
+        expected_output = """<?xml version="1.0"?>
+<!DOCTYPE comps PUBLIC "-//Red Hat, Inc.//DTD Comps info//EN" "comps.dtd">
+
+<!-- Auto-generated by the build system -->
+<comps>
+  <group>
+    <id>group-2</id>
+    <name>group-2</name>
+    <description>None</description>
+    <default>false</default>
+    <uservisible>true</uservisible>
+    <packagelist>
+    </packagelist>
+  </group>
+</comps>
+
+"""
+        self.session.getTagGroups.return_value = [self.tag_groups[1]]
+        rv = anon_handle_show_groups(self.options, self.session, [self.tag, '--comps', '--expand'])
+        self.assertEqual(rv, None)
+        self.assert_console_message(self.stdout, expected_output)
+        self.assert_console_message(self.stderr, "")
+        self.session.getTagGroups.assert_called_once_with(self.tag)
+
+    def test_show_groups_spec(self):
+        expected_output = """#
+# This specfile represents buildgroups for mock
+# Autogenerated by the build system
+#
+Summary: The base set of packages for a mock chroot
+Name: buildgroups
+Version: 1
+Release: 1
+License: GPL
+Group: Development/Build Tools
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+BuildArch: noarch
+
+#package requirements
+#MISSING GROUP: build
+
+%description
+This is a meta-package that requires a defined group of packages
+
+%prep
+%build
+%install
+%clean
+
+%files
+%defattr(-,root,root,-)
+%doc
+
+"""
+        self.session.getTagGroups.return_value = [self.tag_groups[1]]
+        rv = anon_handle_show_groups(self.options, self.session, [self.tag, '--spec'])
+        self.assertEqual(rv, None)
+        self.assert_console_message(self.stdout, expected_output)
+        self.assert_console_message(self.stderr, "")
+        self.session.getTagGroups.assert_called_once_with(self.tag)
+
     def test_show_groups_help(self):
         self.assert_help(
             anon_handle_show_groups,
diff --git a/tests/test_cli/test_win_build.py b/tests/test_cli/test_win_build.py
index fa7f321..bb66f59 100644
--- a/tests/test_cli/test_win_build.py
+++ b/tests/test_cli/test_win_build.py
@@ -12,8 +12,13 @@ class TestWinBuild(utils.CliTestCase):
     def setUp(self):
         self.options = mock.MagicMock()
         self.options.debug = False
+        self.options.quiet = None
+        self.options.weburl = 'https://testwebkoji.org'
+        self.options.poll_interval = 0
         self.session = mock.MagicMock()
         self.session.getAPIVersion.return_value = koji.API_VERSION
+        self.stderr = mock.patch('sys.stderr', new_callable=StringIO).start()
+        self.stdout = mock.patch('sys.stdout', new_callable=StringIO).start()
         self.target = 'test-target'
         self.dest_tag = 'destination-test_tag'
         self.target_info = {'build_tag': 4,
@@ -25,57 +30,119 @@ class TestWinBuild(utils.CliTestCase):
         self.scm_url = 'git://test.redhat.com/rpms/pkg-1.1.0' \
                        '?#3fab2ea42ecdc30a41daf1306154dfa04c4d64fd'
         self.vm = 'test-vm'
+        self.watch_tasks_mock = mock.patch('koji_cli.commands.watch_tasks').start()
+        self.watch_tasks_mock.return_value = 0
+        self.running_in_bg_mock = mock.patch('koji_cli.commands._running_in_bg').start()
+        self.error_format = """Usage: %s win-build [options] <target> <URL> <VM>
+(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=StringIO)
-    def test_win_build_without_option(self, stderr):
-        expected = "Usage: %s win-build [options] <target> <URL> <VM>\n" \
-                   "(Specify the --help global option for a list of other help options)\n\n" \
-                   "%s: error: Exactly three arguments (a build target, a SCM URL, " \
-                   "and a VM name) are required\n" % (self.progname, self.progname)
-        with self.assertRaises(SystemExit) as ex:
-            handle_win_build(self.options, self.session, [])
-        self.assertExitCode(ex, 2)
-        self.assert_console_message(stderr, expected)
+    def test_win_build_without_option(self):
+        self.assert_system_exit(
+            handle_win_build,
+            self.options, self.session, [],
+            stderr=self.format_error_message(
+                "Exactly three arguments (a build target, a SCM URL, and a VM name) are required"),
+            stdout='',
+            activate_session=None,
+            exit_code=2)
+        self.session.getBuildTarget.assert_not_called()
+        self.session.getTag.assert_not_called()
+        self.running_in_bg_mock.assert_not_called()
+        self.session.logout.assert_not_called()
+        self.watch_tasks_mock.assert_not_called()
 
-    @mock.patch('sys.stderr', new_callable=StringIO)
-    def test_win_build_non_exist_build_target(self, stderr):
-        expected = "Usage: %s win-build [options] <target> <URL> <VM>\n" \
-                   "(Specify the --help global option for a list of other help options)\n\n" \
-                   "%s: error: No such build target: %s\n" % (self.progname, self.progname,
-                                                              self.target)
+    def test_win_build_non_exist_build_target(self):
         self.session.getBuildTarget.return_value = None
-        with self.assertRaises(SystemExit) as ex:
-            handle_win_build(self.options, self.session, [self.target, self.scm_url, self.vm])
-        self.assertExitCode(ex, 2)
-        self.assert_console_message(stderr, expected)
+        self.assert_system_exit(
+            handle_win_build,
+            self.options, self.session, [self.target, self.scm_url, self.vm],
+            stderr=self.format_error_message("No such build target: %s" % self.target),
+            stdout='',
+            activate_session=None,
+            exit_code=2)
+        self.session.getBuildTarget.assert_called_once_with(self.target)
+        self.session.getTag.assert_not_called()
+        self.running_in_bg_mock.assert_not_called()
+        self.session.logout.assert_not_called()
+        self.watch_tasks_mock.assert_not_called()
 
-    @mock.patch('sys.stderr', new_callable=StringIO)
-    def test_win_build_non_exist_dest_tag(self, stderr):
-        expected = "Usage: %s win-build [options] <target> <URL> <VM>\n" \
-                   "(Specify the --help global option for a list of other help options)\n\n" \
-                   "%s: error: No such destination tag: %s\n" % (self.progname, self.progname,
-                                                                 self.dest_tag)
+    def test_win_build_non_exist_dest_tag(self):
         self.session.getBuildTarget.return_value = self.target_info
         self.session.getTag.return_value = None
-        with self.assertRaises(SystemExit) as ex:
-            handle_win_build(self.options, self.session, [self.target, self.scm_url, self.vm])
-        self.assertExitCode(ex, 2)
-        self.assert_console_message(stderr, expected)
+        self.assert_system_exit(
+            handle_win_build,
+            self.options, self.session, [self.target, self.scm_url, self.vm],
+            stderr=self.format_error_message("No such destination tag: %s" % self.dest_tag),
+            stdout='',
+            activate_session=None,
+            exit_code=2)
+        self.session.getBuildTarget.assert_called_once_with(self.target)
+        self.session.getTag.assert_called_once_with(self.target_info['dest_tag'])
+        self.running_in_bg_mock.assert_not_called()
+        self.session.logout.assert_not_called()
+        self.watch_tasks_mock.assert_not_called()
 
-    @mock.patch('sys.stderr', new_callable=StringIO)
-    def test_win_build_dest_tag_locked(self, stderr):
-        expected = "Usage: %s win-build [options] <target> <URL> <VM>\n" \
-                   "(Specify the --help global option for a list of other help options)\n\n" \
-                   "%s: error: Destination tag %s is locked\n" % (self.progname, self.progname,
-                                                                  self.dest_tag)
+    def test_win_build_dest_tag_locked(self):
         dest_tag_info = {'name': self.dest_tag, 'locked': True}
 
         self.session.getBuildTarget.return_value = self.target_info
         self.session.getTag.return_value = dest_tag_info
-        with self.assertRaises(SystemExit) as ex:
-            handle_win_build(self.options, self.session, [self.target, self.scm_url, self.vm])
-        self.assertExitCode(ex, 2)
-        self.assert_console_message(stderr, expected)
+        self.assert_system_exit(
+            handle_win_build,
+            self.options, self.session, [self.target, self.scm_url, self.vm],
+            stderr=self.format_error_message("Destination tag %s is locked" % self.dest_tag),
+            stdout='',
+            activate_session=None,
+            exit_code=2)
+        self.session.getBuildTarget.assert_called_once_with(self.target)
+        self.session.getTag.assert_called_once_with(self.target_info['dest_tag'])
+        self.running_in_bg_mock.assert_not_called()
+        self.session.logout.assert_not_called()
+        self.watch_tasks_mock.assert_not_called()
+
+    def test_win_build_not_queit_and_repo_id(self):
+        self.running_in_bg_mock.return_value = True
+        task_id = 111
+        expected_output = """Created task: %d
+Task info: %s/taskinfo?taskID=%s
+""" % (task_id, self.options.weburl, task_id)
+        self.session.winBuild.return_value = task_id
+        rv = handle_win_build(self.options, self.session,
+                              ["none", self.scm_url, self.vm, '--background', '--repo-id=10'])
+        self.assertEqual(rv, None)
+        self.assert_console_message(self.stdout, expected_output)
+        self.assert_console_message(self.stderr, "")
+        self.session.getBuildTarget.assert_not_called()
+        self.session.getTag.assert_not_called()
+        self.running_in_bg_mock.assert_called_once()
+        self.session.logout.assert_not_called()
+        self.watch_tasks_mock.assert_not_called()
+
+    def test_win_build_wait_opt(self):
+        task_id = 111
+        dest_tag_info = {'name': self.dest_tag, 'locked': False}
+
+        self.session.getBuildTarget.return_value = self.target_info
+        self.session.getTag.return_value = dest_tag_info
+        expected_output = """Created task: %d
+Task info: %s/taskinfo?taskID=%s
+""" % (task_id, self.options.weburl, task_id)
+        self.session.winBuild.return_value = task_id
+        rv = handle_win_build(self.options, self.session,
+                              [self.target, self.scm_url, self.vm, '--wait'])
+        self.assertEqual(rv, 0)
+        self.assert_console_message(self.stdout, expected_output)
+        self.assert_console_message(self.stderr, "")
+        self.session.getBuildTarget.assert_called_once_with(self.target)
+        self.session.getTag.assert_called_once_with(self.target_info['dest_tag'])
+        self.running_in_bg_mock.assert_not_called()
+        self.session.logout.assert_called_once()
+        self.watch_tasks_mock.assert_called_once_with(
+            self.session, [task_id], quiet=self.options.quiet,
+            poll_interval=self.options.poll_interval, topurl=self.options.topurl)
 
     def test_win_build_help(self):
         self.assert_help(

