Repository: fedora-infra/bodhi

Issue:
bodhi.server.models.Update.num_admin_approvals includes invalid comments
[```bodhi.server.models.Update.num_admin_approvals```](https://github.com/fedora-infra/bodhi/blob/3.0.0/bodhi/server/models.py#L2530-L2542) uses ```self.comments``` when it should use [```self.comments_since_karma_reset()```](https://github.com/fedora-infra/bodhi/blob/3.0.0/bodhi/server/models.py#L1210-L1230) instead.

This means that admin approvals are not cleared when developers change the Builds associated with an Update.


Discussion:
There are a couple other places in the codebase that use the wrong attribute as well, so we should clean those up as well.
Hey I am a second year Engineering student . I want to contribute in this Organization.Any help given from mentors will be very helpful to me on how to contribute. i am a novice at this.
@yashraj-Dhanuka To expand on the description, ```Update.comments``` is a sqlalchemy relationship field that returns all comments that are associated with a Bodhi ```Update```. Bodhi's ```Comment```s carry karma (an up vote or down vote on a proposed update), but developers can do things that should reset the karma on an Update (such as change the builds contained in the Update).

Since developers can do these things, we need to ensure that we don't count the comments in ```self.comments``` that happened before these karma reset events. In fact, we already have a Python property called ```Update.comments_since_karma_reset()``` that does this for us!

Thus, all we need to do here is find the places that use ```Updates.comments``` to get karma and change them to use the ```comments_since_karma_reset()``` instead, then write tests to ensure it works correctly.

Resolving patch:
diff --git a/bodhi/server/models.py b/bodhi/server/models.py
--- a/bodhi/server/models.py
+++ b/bodhi/server/models.py
@@ -1882,11 +1882,13 @@ def comments_since_karma_reset(self):
         Karma is reset when :class:`Builds <Build>` are added or removed from an update.
 
         Returns:
-            generator: :class:`Comments <Comment>` since the karma reset.
+            comments (list): class:`Comments <Comment>` since the karma reset.
         """
         # We want to traverse the comments in reverse order so we only consider
         # the most recent comments from any given user and only the comments
         # since the most recent karma reset event.
+        comments_since_karma_reset = []
+
         for comment in reversed(self.comments):
             if (comment.user.name == u'bodhi' and
                     ('New build' in comment.text or 'Removed build' in comment.text)):
@@ -1896,7 +1898,9 @@ def comments_since_karma_reset(self):
                 # reverse order, once we find one of these comments we can
                 # simply exit this loop.
                 break
-            yield comment
+            comments_since_karma_reset.append(comment)
+
+        return comments_since_karma_reset
 
     @staticmethod
     def contains_critpath_component(builds, release_name):
@@ -2373,7 +2377,7 @@ def get_bug_karma(self, bug):
             positive karma.
         """
         good, bad, seen = 0, 0, set()
-        for comment in reversed(self.comments):
+        for comment in self.comments_since_karma_reset:
             if comment.user.name in seen:
                 continue
             seen.add(comment.user.name)
@@ -2396,7 +2400,7 @@ def get_testcase_karma(self, testcase):
             positive karma.
         """
         good, bad, seen = 0, 0, set()
-        for comment in reversed(self.comments):
+        for comment in self.comments_since_karma_reset:
             if comment.user.name in seen:
                 continue
             seen.add(comment.user.name)
@@ -2846,10 +2850,10 @@ def __str__(self):
         val += u"""
   Submitter: %s
   Submitted: %s\n""" % (username, self.date_submitted)
-        if len(self.comments):
+        if self.comments_since_karma_reset:
             val += u"   Comments: "
             comments = []
-            for comment in self.comments:
+            for comment in self.comments_since_karma_reset:
                 if comment.anonymous:
                     anonymous = " (unauthenticated)"
                 else:
@@ -3508,7 +3512,7 @@ def num_admin_approvals(self):
             int: The number of admin approvals found in the comments of this update.
         """
         approvals = 0
-        for comment in self.comments:
+        for comment in self.comments_since_karma_reset:
             if comment.karma != 1:
                 continue
             admin_groups = config.get('admin_groups')


Test patch:
diff --git a/bodhi/tests/server/test_models.py b/bodhi/tests/server/test_models.py
--- a/bodhi/tests/server/test_models.py
+++ b/bodhi/tests/server/test_models.py
@@ -1444,6 +1444,17 @@ def test_mixed_feedback(self):
         self.assertEqual(bad, -1)
         self.assertEqual(good, 2)
 
+        # This is a "karma reset event", so the above comments should not be counted in the karma.
+        user = model.User(name='bodhi')
+        comment = model.Comment(text=u"New build", karma=0, user=user)
+        self.db.add(comment)
+        update.comments.append(comment)
+
+        bad, good = update.get_bug_karma(update.bugs[0])
+
+        self.assertEqual(bad, 0)
+        self.assertEqual(good, 0)
+
 
 class TestUpdateGetTestcaseKarma(BaseTestCase):
     """Test the get_testcase_karma() method."""
@@ -1480,6 +1491,17 @@ def test_mixed_feedback(self):
         self.assertEqual(bad, -1)
         self.assertEqual(good, 2)
 
+        # This is a "karma reset event", so the above comments should not be counted in the karma.
+        user = model.User(name='bodhi')
+        comment = model.Comment(text=u"New build", karma=0, user=user)
+        self.db.add(comment)
+        update.comments.append(comment)
+
+        bad, good = update.get_testcase_karma(update.builds[0].package.test_cases[0])
+
+        self.assertEqual(bad, 0)
+        self.assertEqual(good, 0)
+
 
 class TestUpdateSigned(BaseTestCase):
     """Test the Update.signed() property."""
@@ -3020,6 +3042,27 @@ def test_check_requirements_test_gating_status_passed(self):
             self.assertEqual(self.obj.check_requirements(self.db, config),
                              (True, 'No checks required.'))
 
+    def test_num_admin_approvals_after_karma_reset(self):
+        """Make sure number of admin approvals is counted correctly for the build."""
+        update = model.Update.query.first()
+
+        # Approval from admin 'bodhiadmin' {config.admin_groups}
+        user_group = [model.Group(name=u'bodhiadmin')]
+        user = model.User(name='bodhiadmin', groups=user_group)
+        comment = model.Comment(text='Test comment', karma=1, user=user)
+        self.db.add(comment)
+        update.comments.append(comment)
+
+        self.assertEqual(update.num_admin_approvals, 1)
+
+        # This is a "karma reset event", so the above comments should not be counted in the karma.
+        user = model.User(name='bodhi')
+        comment = model.Comment(text=u"New build", karma=0, user=user)
+        self.db.add(comment)
+        update.comments.append(comment)
+
+        self.assertEqual(update.num_admin_approvals, 0)
+
     def test_test_cases_with_no_dupes(self):
         update = self.get_update(name=u"FullTestCasesWithNoDupes")
         package = update.builds[0].package