Repository: fedora-infra/bodhi

Issue:
Bodhi should not erroneously 'downgrade' bug status from VERIFIED
We frequently play a game of pong with Bodhi, where we verify a fix when a bug is in `MODIFIED` state (i.e. not yet pushed to u-t) and change the state to `VERIFIED`, only for Bodhi to come along and pong the state back to `ON_QA` when it pushes the update. For e.g., every single damn F25 blocker fix:

https://bugzilla.redhat.com/show_bug.cgi?id=1393110
https://bugzilla.redhat.com/show_bug.cgi?id=1376471
https://bugzilla.redhat.com/show_bug.cgi?id=1390607
https://bugzilla.redhat.com/show_bug.cgi?id=1378156
https://bugzilla.redhat.com/show_bug.cgi?id=1392654
https://bugzilla.redhat.com/show_bug.cgi?id=1393109

when Bodhi is only doing the `ON_QA` change for an update being pushed, whose contents have not changed since it was submitted, it should avoid changing the bug's status if it's `VERIFIED`.


Discussion:

Resolving patch:
diff --git a/bodhi/server/bugs.py b/bodhi/server/bugs.py
--- a/bodhi/server/bugs.py
+++ b/bodhi/server/bugs.py
@@ -165,7 +165,7 @@ def comment(self, bug_id, comment):
 
     def on_qa(self, bug_id, comment):
         """
-        Change the status of this bug to ON_QA.
+        Change the status of this bug to ON_QA if it is not already ON_QA, VERIFIED, or CLOSED.
 
         This will also comment on the bug with some details on how to test and provide feedback for
         this update.
@@ -174,10 +174,13 @@ def on_qa(self, bug_id, comment):
             bug_id (int): The bug id you wish to set to ON_QA.
             comment (basestring): The comment to be included with the state change.
         """
-        log.debug("Setting Bug #%d to ON_QA" % bug_id)
         try:
             bug = self.bz.getbug(bug_id)
-            bug.setstatus('ON_QA', comment=comment)
+            if bug.bug_status not in ('ON_QA', 'VERIFIED', 'CLOSED'):
+                log.debug("Setting Bug #%d to ON_QA" % bug_id)
+                bug.setstatus('ON_QA', comment=comment)
+            else:
+                bug.addcomment(comment)
         except Exception:
             log.exception("Unable to alter bug #%d" % bug_id)
 


Test patch:
diff --git a/bodhi/tests/server/test_bugs.py b/bodhi/tests/server/test_bugs.py
--- a/bodhi/tests/server/test_bugs.py
+++ b/bodhi/tests/server/test_bugs.py
@@ -282,6 +282,57 @@ def test_on_qa_success(self, exception):
         bz._bz.getbug.return_value.setstatus.assert_called_once_with('ON_QA', comment='A message.')
         self.assertEqual(exception.call_count, 0)
 
+    @mock.patch('bodhi.server.bugs.log.exception')
+    def test_on_qa_skipped_because_closed(self, exception):
+        """
+        Test the on_qa() method when the bug is already CLOSED.
+        It must not change bug state, only post the comment.
+        """
+        bz = bugs.Bugzilla()
+        bz._bz = mock.MagicMock()
+        bz._bz.getbug.return_value.bug_status = 'CLOSED'
+
+        bz.on_qa(1411188, 'A message.')
+
+        bz._bz.getbug.assert_called_once_with(1411188)
+        bz._bz.getbug.return_value.addcomment.assert_called_once_with('A message.')
+        bz._bz.getbug.return_value.setstatus.assert_not_called()
+        self.assertEqual(exception.call_count, 0)
+
+    @mock.patch('bodhi.server.bugs.log.exception')
+    def test_on_qa_skipped_because_verified(self, exception):
+        """
+        Test the on_qa() method when the bug is already VERIFIED.
+        It must not change bug state, only post the comment.
+        """
+        bz = bugs.Bugzilla()
+        bz._bz = mock.MagicMock()
+        bz._bz.getbug.return_value.bug_status = 'VERIFIED'
+
+        bz.on_qa(1411188, 'A message.')
+
+        bz._bz.getbug.assert_called_once_with(1411188)
+        bz._bz.getbug.return_value.addcomment.assert_called_once_with('A message.')
+        bz._bz.getbug.return_value.setstatus.assert_not_called()
+        self.assertEqual(exception.call_count, 0)
+
+    @mock.patch('bodhi.server.bugs.log.exception')
+    def test_on_qa_skipped_because_already_set(self, exception):
+        """
+        Test the on_qa() method when the bug is already ON_QA.
+        It must not be set again ON_QA, only post the comment.
+        """
+        bz = bugs.Bugzilla()
+        bz._bz = mock.MagicMock()
+        bz._bz.getbug.return_value.bug_status = 'ON_QA'
+
+        bz.on_qa(1411188, 'A message.')
+
+        bz._bz.getbug.assert_called_once_with(1411188)
+        bz._bz.getbug.return_value.addcomment.assert_called_once_with('A message.')
+        bz._bz.getbug.return_value.setstatus.assert_not_called()
+        self.assertEqual(exception.call_count, 0)
+
 
 class TestFakeBugTracker(unittest.TestCase):
     """This test class contains tests for the FakeBugTracker class."""