Repository: fedora-infra/bodhi

Issue:
Replacing a build in a testing update does not move it back to a pending state.
It requests that it get pushed to testing again, but it doesn't change the state to pending.

https://bodhi.fedoraproject.org/updates/FEDORA-2015-13571



Discussion:
For whoever looks into this, some logic will need to be added to `bodhi/models/models.py` in `Update.edit(...)`.


Resolving patch:
diff --git a/bodhi/models/models.py b/bodhi/models/models.py
--- a/bodhi/models/models.py
+++ b/bodhi/models/models.py
@@ -726,14 +726,6 @@ def edit(cls, request, data):
                     b.release = up.release
                     db.add(b)
 
-                if up.release.pending_testing_tag:
-                    koji.tagBuild(up.release.pending_testing_tag, build)
-                else:
-                    # EL6 doesn't have these, and that's okay...
-                    # We still warn in case the config gets messed up.
-                    log.warn('%s has no pending_testing_tag for %s' % (
-                        up.release.name, build.nvr))
-
                 up.builds.append(b)
 
         # Determine which builds have been removed
@@ -780,6 +772,24 @@ def edit(cls, request, data):
         if new_builds or removed_builds:
             data['request'] = UpdateRequest.testing
 
+            # Remove all koji tags and change the status back to pending
+            if not up.status is UpdateStatus.pending:
+                up.unpush()
+                caveats.append({
+                    'name': 'status',
+                    'description': 'Builds changed.  Your update is being '
+                    'sent back to testing.',
+                })
+
+            # Add the pending tag to all new builds
+            for build in new_builds:
+                if up.release.pending_testing_tag:
+                    koji.tagBuild(up.release.pending_testing_tag, build)
+                else:
+                    # EL6 doesn't have these, and that's okay...
+                    # We still warn in case the config gets messed up.
+                    log.warn('%s has no pending_testing_tag' % up.release.name)
+
         new_bugs = up.update_bugs(data['bugs'], db)
         del(data['bugs'])
 


Test patch:
diff --git a/bodhi/tests/functional/test_comments.py b/bodhi/tests/functional/test_comments.py
--- a/bodhi/tests/functional/test_comments.py
+++ b/bodhi/tests/functional/test_comments.py
@@ -115,7 +115,6 @@ def test_commenting_with_bug_feedback(self, publish):
         self.assertEquals(len(feedback), 1)
         publish.assert_called_once_with(topic='update.comment', msg=mock.ANY)
 
-
     @mock.patch('bodhi.notifications.publish')
     def test_commenting_with_testcase_feedback(self, publish):
         comment = self.make_comment()
diff --git a/bodhi/tests/functional/test_updates.py b/bodhi/tests/functional/test_updates.py
--- a/bodhi/tests/functional/test_updates.py
+++ b/bodhi/tests/functional/test_updates.py
@@ -1239,6 +1239,75 @@ def test_edit_update(self, publish, *args):
         self.assertEquals(len(publish.call_args_list), 2)
         publish.assert_called_with(topic='update.edit', msg=ANY)
 
+    @mock.patch(**mock_valid_requirements)
+    @mock.patch('bodhi.notifications.publish')
+    def test_edit_testing_update_with_new_builds(self, publish, *args):
+        nvr = u'bodhi-2.0.0-2.fc17'
+        args = self.get_update(nvr)
+        r = self.app.post_json('/updates/', args)
+        publish.assert_called_with(topic='update.request.testing', msg=ANY)
+
+        # Mark it as testing
+        upd = Update.get(nvr, self.db)
+        upd.status = UpdateStatus.testing
+        upd.request = None
+        self.db.flush()
+
+        args['edited'] = args['builds']
+        args['builds'] = 'bodhi-2.0.0-3.fc17'
+        r = self.app.post_json('/updates/', args)
+        up = r.json_body
+        self.assertEquals(up['title'], u'bodhi-2.0.0-3.fc17')
+        self.assertEquals(up['status'], u'pending')
+        self.assertEquals(up['request'], u'testing')
+        #assert False, '\n'.join([c['text'] for c in up['comments']])
+        self.assertEquals(up['comments'][-1]['text'],
+                          u'This update has been submitted for testing by guest. ')
+        self.assertEquals(up['comments'][-2]['text'],
+                          u'guest edited this update. New build(s): ' +
+                          u'bodhi-2.0.0-3.fc17. Removed build(s): bodhi-2.0.0-2.fc17.')
+        self.assertEquals(up['comments'][-3]['text'],
+                          u'This update has been submitted for testing by guest. ')
+        self.assertEquals(len(up['builds']), 1)
+        self.assertEquals(up['builds'][0]['nvr'], u'bodhi-2.0.0-3.fc17')
+        self.assertEquals(DBSession.query(Build).filter_by(nvr=u'bodhi-2.0.0-2.fc17').first(), None)
+        self.assertEquals(len(publish.call_args_list), 3)
+        publish.assert_called_with(topic='update.edit', msg=ANY)
+
+    @mock.patch(**mock_valid_requirements)
+    @mock.patch('bodhi.notifications.publish')
+    def test_edit_testing_update_with_new_builds_with_stable_request(self, publish, *args):
+        nvr = u'bodhi-2.0.0-2.fc17'
+        args = self.get_update(nvr)
+        r = self.app.post_json('/updates/', args)
+        publish.assert_called_with(topic='update.request.testing', msg=ANY)
+
+        # Mark it as testing
+        upd = Update.get(nvr, self.db)
+        upd.status = UpdateStatus.testing
+        upd.request = UpdateRequest.stable
+        self.db.flush()
+
+        args['edited'] = args['builds']
+        args['builds'] = 'bodhi-2.0.0-3.fc17'
+        r = self.app.post_json('/updates/', args)
+        up = r.json_body
+        self.assertEquals(up['title'], u'bodhi-2.0.0-3.fc17')
+        self.assertEquals(up['status'], u'pending')
+        self.assertEquals(up['request'], u'testing')
+        self.assertEquals(up['comments'][-1]['text'],
+                          u'This update has been submitted for testing by guest. ')
+        self.assertEquals(up['comments'][-2]['text'],
+                          u'guest edited this update. New build(s): ' +
+                          u'bodhi-2.0.0-3.fc17. Removed build(s): bodhi-2.0.0-2.fc17.')
+        self.assertEquals(up['comments'][-3]['text'],
+                          u'This update has been submitted for testing by guest. ')
+        self.assertEquals(len(up['builds']), 1)
+        self.assertEquals(up['builds'][0]['nvr'], u'bodhi-2.0.0-3.fc17')
+        self.assertEquals(DBSession.query(Build).filter_by(nvr=u'bodhi-2.0.0-2.fc17').first(), None)
+        self.assertEquals(len(publish.call_args_list), 3)
+        publish.assert_called_with(topic='update.edit', msg=ANY)
+
     @mock.patch(**mock_valid_requirements)
     @mock.patch('bodhi.notifications.publish')
     def test_edit_update_with_different_release(self, publish, *args):
@@ -1766,8 +1835,8 @@ def test_edit_update_bugs(self, publish, *args):
         bug_ids = [bug['bug_id'] for bug in up['bugs']]
         self.assertIn(56789, bug_ids)
         self.assertIn(98765, bug_ids)
-        self.assertEquals(up['status'], u'testing')
-        self.assertEquals(up['request'], None)
+        self.assertEquals(up['status'], u'pending')
+        self.assertEquals(up['request'], u'testing')
 
         # now remove a bug
         args['edited'] = args['builds']
@@ -1778,8 +1847,8 @@ def test_edit_update_bugs(self, publish, *args):
         self.assertEquals(len(up['bugs']), 1)
         bug_ids = [bug['bug_id'] for bug in up['bugs']]
         self.assertIn(98765, bug_ids)
-        self.assertEquals(up['status'], u'testing')
-        self.assertEquals(up['request'], None)
+        self.assertEquals(up['status'], u'pending')
+        self.assertEquals(up['request'], u'testing')
 
     @mock.patch(**mock_valid_requirements)
     @mock.patch('bodhi.notifications.publish')