Repository: fedora-infra/bodhi

Issue:
CLI crashes when karma is set to None
Updates with None on the stable or unstable karma thresholds will cause a crash:

```
rbarlow@planck ~/d/bodhi> time vagrant ssh -c "bodhi updates query --updateid FEDORA-2016-37c1b46c83"
Traceback (most recent call last):
  File "/usr/bin/bodhi", line 9, in <module>
    load_entry_point('bodhi-client', 'console_scripts', 'bodhi')()
  File "/usr/lib/python2.7/site-packages/click/core.py", line 716, in __call__
    return self.main(*args, **kwargs)
  File "/usr/lib/python2.7/site-packages/click/core.py", line 696, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python2.7/site-packages/click/core.py", line 1060, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python2.7/site-packages/click/core.py", line 1060, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python2.7/site-packages/click/core.py", line 889, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python2.7/site-packages/click/core.py", line 534, in invoke
    return callback(*args, **kwargs)
  File "/vagrant/bodhi/client/__init__.py", line 120, in query
    print_resp(resp, client)
  File "/vagrant/bodhi/client/__init__.py", line 236, in print_resp
    click.echo(client.update_str(resp.updates[0]))
  File "/vagrant/bodhi/client/bindings.py", line 432, in update_str
    update['unstable_karma'], update['stable_karma'])
TypeError: %d format: a number is required, not NoneType
Connection to 192.168.121.116 closed.
0.64user 0.06system 0:03.81elapsed 18%CPU (0avgtext+0avgdata 58316maxresident)k
0inputs+24outputs (0major+24426minor)pagefaults 0swap
```

I'm labelling this critical since it's a regression introduced on the develop branch (it is not present in the 2.3.3 release), so we should not release without fixing this.


Discussion:

Resolving patch:
diff --git a/bodhi/client/bindings.py b/bodhi/client/bindings.py
--- a/bodhi/client/bindings.py
+++ b/bodhi/client/bindings.py
@@ -427,7 +427,7 @@ def update_str(self, update, minimal=False):
      Status: %s
        Type: %s
       Karma: %d
-  Autokarma: %s  [%d, %d]""" % (update['release']['long_name'], update['status'],
+  Autokarma: %s  [%s, %s]""" % (update['release']['long_name'], update['status'],
                       update['type'], update['karma'], update['autokarma'],
                       update['unstable_karma'], update['stable_karma'])
         if update['request'] is not None:


Test patch:
diff --git a/bodhi/tests/client/__init__.py b/bodhi/tests/client/__init__.py
--- a/bodhi/tests/client/__init__.py
+++ b/bodhi/tests/client/__init__.py
@@ -70,3 +70,30 @@
         u'override_tag': u'epel7-override', u'branch': u'epel7', u'id_prefix': u'FEDORA-EPEL',
         u'pending_testing_tag': u'epel7-testing-pending', u'stable_tag': u'epel7',
         u'candidate_tag': u'epel7-testing-candidate'}), u'date_stable': u'2016-10-21 13:23:01'})
+
+
+# EXAMPLE_UPDATE_MUNCH is expected to generate this output in update_str
+EXPECTED_UPDATE_OUTPUT = u"""================================================================================
+     bodhi-2.2.4-1.el7
+================================================================================
+  Update ID: FEDORA-EPEL-2016-3081a94111
+    Release: Fedora EPEL 7
+     Status: stable
+       Type: bugfix
+      Karma: 0
+  Autokarma: True  [-3, 3]
+      Notes: Update to 2.2.4. Release notes available at https://github.com
+           : /fedora-infra/bodhi/releases/tag/2.2.4
+  Submitter: bowlofeggs
+  Submitted: 2016-10-05 18:10:22
+   Comments: bodhi - 2016-10-05 18:10:22 (karma 0)
+             This update has been submitted for testing by
+             bowlofeggs.
+             bodhi - 2016-10-05 18:10:27 (karma 0)
+             This update has obsoleted [bodhi-2.2.3-1.el7](https://
+             bodhi.fedoraproject.org/updates/FEDORA-
+             EPEL-2016-a0eb4cc41f), and has inherited its bugs and
+             notes.
+
+  http://example.com/tests/updates/FEDORA-EPEL-2016-3081a94111
+"""
diff --git a/bodhi/tests/client/test___init__.py b/bodhi/tests/client/test___init__.py
--- a/bodhi/tests/client/test___init__.py
+++ b/bodhi/tests/client/test___init__.py
@@ -25,33 +25,6 @@
 from bodhi.tests import client as client_test_data
 
 
-EXAMPLE_UPDATE_OUTPUT = u"""================================================================================
-     bodhi-2.2.4-1.el7
-================================================================================
-  Update ID: FEDORA-EPEL-2016-3081a94111
-    Release: Fedora EPEL 7
-     Status: stable
-       Type: bugfix
-      Karma: 0
-  Autokarma: True  [-3, 3]
-      Notes: Update to 2.2.4. Release notes available at https://github.com
-           : /fedora-infra/bodhi/releases/tag/2.2.4
-  Submitter: bowlofeggs
-  Submitted: 2016-10-05 18:10:22
-   Comments: bodhi - 2016-10-05 18:10:22 (karma 0)
-             This update has been submitted for testing by
-             bowlofeggs.
-             bodhi - 2016-10-05 18:10:27 (karma 0)
-             This update has obsoleted [bodhi-2.2.3-1.el7](https://
-             bodhi.fedoraproject.org/updates/FEDORA-
-             EPEL-2016-a0eb4cc41f), and has inherited its bugs and
-             notes.
-
-  http://example.com/tests/updates/FEDORA-EPEL-2016-3081a94111
-
-"""
-
-
 class TestRequest(unittest.TestCase):
     """
     This class tests the request() function.
@@ -73,7 +46,7 @@ def test_successful_operation(self, send_request, __init__):
                                                 'some_user', '--password', 's3kr3t'])
 
         self.assertEqual(result.exit_code, 0)
-        self.assertEqual(result.output, EXAMPLE_UPDATE_OUTPUT)
+        self.assertEqual(result.output, client_test_data.EXPECTED_UPDATE_OUTPUT + '\n')
         send_request.assert_called_once_with(
             'updates/bodhi-2.2.4-1.el7/request', verb='POST', auth=True,
             data={'csrf_token': 'a_csrf_token', 'request': u'revoke',
diff --git a/bodhi/tests/client/test_bindings.py b/bodhi/tests/client/test_bindings.py
--- a/bodhi/tests/client/test_bindings.py
+++ b/bodhi/tests/client/test_bindings.py
@@ -14,7 +14,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 """This module contains tests for bodhi.client.bindings."""
-
+import copy
 import unittest
 
 import fedora.client
@@ -104,6 +104,39 @@ def test_other_ServerError(self, send_request, __init__):
         __init__.assert_called_once_with(username='some_user', password='s3kr3t', staging=False)
 
 
+class TestBodhiClient_update_str(unittest.TestCase):
+    """This test contains tests for BodhiClient.update_str."""
+    def test_with_autokarma_set(self):
+        """
+        Ensure correct operation when autokarma is True, and stable/unstable karmas are set.
+        """
+        client = bindings.BodhiClient(username='some_user', password='s3kr3t')
+        client.base_url = 'http://example.com/tests/'
+
+        text = client.update_str(client_test_data.EXAMPLE_UPDATE_MUNCH)
+
+        self.assertEqual(text, client_test_data.EXPECTED_UPDATE_OUTPUT)
+
+    def test_with_autokarma_unset(self):
+        """
+        Ensure correct operation when autokarma is Fale, and stable/unstable karmas are None.
+        """
+        client = bindings.BodhiClient(username='some_user', password='s3kr3t')
+        client.base_url = 'http://example.com/tests/'
+        update = copy.deepcopy(client_test_data.EXAMPLE_UPDATE_MUNCH)
+        # Set the update's autokarma and thresholds to False/None.
+        update.autokarma = False
+        update.unstable_karma = None
+        update.stable_karma = None
+
+        text = client.update_str(update)
+
+        self.maxDiff = None
+        expected_output = client_test_data.EXPECTED_UPDATE_OUTPUT.replace(
+            'Autokarma: True  [-3, 3]', 'Autokarma: False  [None, None]')
+        self.assertEqual(text, expected_output)
+
+
 class TestUpdateNotFound(unittest.TestCase):
     """
     This class tests the UpdateNotFound class.