Thanks a lot for fixing this bug, and going through the effort for
writing a test for the fix. The bug itself was indeed easy to fix, but
now it's quite clear the test for it was anything but easy.
I have some suggestions for improvement below, most of them are stylish
though and they should all be easy to deal with. If you have any
comments/questions, just ping me on IRC or leave a comment here.
Since this method will fail if a callsite doesn't pass in a person, it
doesn't make sense to specify a default value for the 'person' arg.
I don't see any reason for having a default value for category_id,
either -- it should be a required argument. Also, the argument should
be called 'category' and callsites should pass an IKarmaCategory object
instead of its id.
> + product_id=5):
Similarly, this should be renamed to 'product' and the method must
expect an IProduct instead of an integer. You can specify None as the
default value for that argument and have this method create a product
(using self.factory) when the callsite doesn't specify one.
> + # Create KarmaCache Record
This comment doesn't add much either, but you could add one explaining
that it's only the karmacacheupdater who has write access to the
KarmaCache table, so we switch to that user here.
Here at the end we must also do another switchDbUser() to restore the DB
user that was in use when this method was called. If we don't do that,
other (completely unrelated) tests will fail because the wrong DB user
would be used for them.
Hi Jamal,
Thanks a lot for fixing this bug, and going through the effort for
writing a test for the fix. The bug itself was indeed easy to fix, but
now it's quite clear the test for it was anything but easy.
I have some suggestions for improvement below, most of them are stylish
though and they should all be easy to deal with. If you have any
comments/questions, just ping me on IRC or leave a comment here.
Cheers,
review needs-fixing
On Thu, 2009-08-27 at 03:20 +0000, Jamal Fanaian wrote: registry/ browser/ person. py' registry/ browser/ person. py 2009-07-30 22:35:16 +0000 registry/ browser/ person. py 2009-08-05 19:55:13 +0000 update( category for category in contrib[ 'categories' ]) 'title' )) name]) is_probably_ a_team( self): registry/ browser/ tests/test_ person_ view.py' registry/ browser/ tests/test_ person_ view.py 1970-01-01 00:00:00 +0000 registry/ browser/ tests/test_ person_ view.py 2009-08-27 02:59:16 +0000 launchpad. webapp. servers import LaunchpadTestRe quest ssLayer database. sqlbase import cursor, sqlvalues browser. person import PersonView launchpad. interfaces import IKarmaCacheManager
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -2517,7 +2517,9 @@
> categories = set()
> for contrib in self.contributions:
> categories.
> - return sorted(categories, key=attrgetter(
> + sort = {'code': 0, 'bugs': 1, 'blueprints': 2, 'translations': 3,
> + 'answers': 4, 'specs': 5, 'soyuz': 6}
> + return sorted(categories, key=lambda category: sort[category.
>
> @cachedproperty
> def context_
>
> === added file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -0,0 +1,71 @@
> +# Copyright 2009 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +__metaclass__ = type
> +
> +import unittest
> +
> +from canonical.config import config
> +from canonical.
> +from canonical.testing import LaunchpadZopele
> +from canonical.
> +from lp.registry.
> +from lp.testing import TestCaseWithFactory
> +from zope.component import getUtility
> +from canonical.
> +
There should be one extra blank line here. Our style guide[1] has this
and other conventions we use.
[1] https:/ /dev.launchpad. net/PythonStyle Guide
> +class TestPerson( TestCaseWithFac tory):
TestPersonView would be a better name for this.
> + """Test Person view."""
Since the docstring is nearly identical to the class' name, we can get
rid of it.
> + ssLayer
> + layer = LaunchpadZopele
> +
> + def setUp(self):
> + # Create a person
This comment doesn't add much either, so it can be removed.
> + TestCaseWithFac tory.setUp( self) makePerson( ) self.person, quest() ) category_ sort(self) :
> + self.person = self.factory.
> + self.view = PersonView(
> + LaunchpadTestRe
> +
> + def test_karma_
> + # Add karma to some categories for the user
Same here.
> + self.makeKarmaC ache(person= self.person, category_id=2) ache(person= self.person, category_id=7) ache(person= self.person, category_id=8)
> + self.makeKarmaC
> + self.makeKarmaC
I'd move these lines to the setUp() method above.
> +
> + # Get contributed categories for the factory user
I'd drop this comment too.
> + categories = self.view. contributed_ categories names.append( category. name)
> + category_names = []
> + for category in categories:
> + category_
> +
> + # Assert that the contributed categories are sorted correctly
And this one as well.
> + self.assertEqua l(category_ names, [u'code', u'bugs', u'answers'], self, person=None, value=10, category_id=2,
> + 'Categories are not sorted correctly')
> +
> + def makeKarmaCache(
Since this method will fail if a callsite doesn't pass in a person, it
doesn't make sense to specify a default value for the 'person' arg.
I don't see any reason for having a default value for category_id,
either -- it should be a required argument. Also, the argument should
be called 'category' and callsites should pass an IKarmaCategory object
instead of its id.
> + product_id=5):
Similarly, this should be renamed to 'product' and the method must
expect an IProduct instead of an integer. You can specify None as the
default value for that argument and have this method create a product
(using self.factory) when the callsite doesn't specify one.
> + # Create KarmaCache Record
This comment doesn't add much either, but you could add one explaining
that it's only the karmacacheupdater who has write access to the
KarmaCache table, so we switch to that user here.
> + LaunchpadZopele ssLayer. switchDbUser( config. karmacacheupdat er.dbuser) IKarmaCacheMana ger).new( id=person. id, value=value, category_ id=category_ id, id=product_ id)
> + karmacache = getUtility(
> + person_
> + product_
After the changes to this method's arguments, this is what the above
would look like
if product is None: makeProduct( ) IKarmaCacheMana ger).new( id=product. id)
product = self.factory.
karmacache = getUtility(
value, person.id, category.id, product_
> + LaunchpadZopele ssLayer. commit( ) person= person. id, product=product_id)
> +
> + # Update totals for this product
> + query = """
> + INSERT INTO KarmaCache
> + (person, category, karmavalue, product, distribution,
> + sourcepackagename, project)
> + SELECT person, NULL, SUM(karmavalue), product, NULL, NULL, NULL
> + FROM KarmaCache
> + WHERE product = %(product)s
> + AND person = %(person)s
> + GROUP BY person, product
> + """ % sqlvalues(
> + cur = cursor()
> + cur.execute(query)
Instead of doing this with raw SQL, you could do the following:
try:
cache_ manager. updateKarmaValu e( id=product. id)
cache_ manager. new( id=product. id)
value, person.id, category_id=None, product_
except NotFoundError:
value, person.id, category_id=None, product_
which is a bit simpler. :)
> +
Here at the end we must also do another switchDbUser() to restore the DB
user that was in use when this method was called. If we don't do that,
other (completely unrelated) tests will fail because the wrong DB user
would be used for them.
> + return karmacache TestLoader( ).loadTestsFrom Name(__ name__)
> +
> +def test_suite():
> + return unittest.
--
Guilherme Salgado <email address hidden>