Merge lp:~jamalta/launchpad/bug-127489 into lp:launchpad

Proposed by Jamal Fanaian on 2009-09-18
Status: Merged
Approved by: Guilherme Salgado on 2009-09-18
Approved revision: 9026
Merged at revision: not available
Proposed branch: lp:~jamalta/launchpad/bug-127489
Merge into: lp:launchpad
Diff against target: None lines
To merge this branch: bzr merge lp:~jamalta/launchpad/bug-127489
Reviewer Review Type Date Requested Status
Guilherme Salgado (community) 2009-09-18 Approve on 2009-09-18
Review via email: mp+12018@code.launchpad.net

This proposal supersedes a proposal from 2009-09-13.

To post a comment you must log in.
Stuart Bishop (stub) wrote : Posted in a previous version of this proposal

This is going to need a page test to catch future regressions.

Gavin Panella (allenap) wrote : Posted in a previous version of this proposal

Hi Jamal,

Thank you for working on this bug!

As Stuart commented, this will need a pagetest. In fact, there is
already one that is probably broken by this change. Have a look at the
end of lib/lp/registry/stories/foaf/xx-person-home.txt.

Did you have a mentor for this branch, or a pre-implementation
discussion? Perhaps that might have spotted the need to update the
pagetest. Have a look at https://dev.launchpad.net/PatchSubmission if
you haven't already. It's the same process as all the Launchpad devs
from Canonical use.

I have a comment about the code, because the cmp argument to sorted()
is deprecated (and I think it's gone entirely in Python 3).

Thanks again!

Gavin.

> === modified file 'lib/lp/registry/browser/person.py'
> --- lib/lp/registry/browser/person.py 2009-07-29 15:34:46 +0000
> +++ lib/lp/registry/browser/person.py 2009-07-31 21:12:09 +0000
> @@ -2517,7 +2517,10 @@
> categories = set()
> for contrib in self.contributions:
> categories.update(category for category in contrib['categories'])
> - return sorted(categories, key=attrgetter('title'))
> + sort = {'code': 0, 'bugs': 1, 'blueprints': 2, 'translations': 3,
> + 'answers': 4, 'specs': 5, 'soyuz': 6}
> + return sorted(categories, key=attrgetter('name'),
> + cmp=lambda x,y: cmp(sort[x], sort[y]))

Because cmp is deprecated, I think this would be better as:

       return sorted(categories, key=lambda category: sort[category.name])

>
> @cachedproperty
> def context_is_probably_a_team(self):

review: Needs Fixing
Jamal Fanaian (jamalta) wrote : Posted in a previous version of this proposal

Hi Gavin,

Thanks for your input regarding this bug. I have actually been working on a test for this. I did not have a mentor when I started working on this issue, but went on #launchpad-dev to get some help. Salgado had recommended that I write a unittest to call the view, and make sure the data was returned in the right order, using a LaunchpadObjectFactory. I had been struggling trying to figure out how to do that exactly, and haven't gotten back to it in a few days now. I will take a look at the test you mentioned and make sure I fix it. Also, I appreciate your comment regarding the depracated cmp() function. I am fairly new to Python and was not aware of this. I will fix that shortly as well, using your recommendation.

> Hi Jamal,
>
> Thank you for working on this bug!
>
> As Stuart commented, this will need a pagetest. In fact, there is
> already one that is probably broken by this change. Have a look at the
> end of lib/lp/registry/stories/foaf/xx-person-home.txt.
>
> Did you have a mentor for this branch, or a pre-implementation
> discussion? Perhaps that might have spotted the need to update the
> pagetest. Have a look at https://dev.launchpad.net/PatchSubmission if
> you haven't already. It's the same process as all the Launchpad devs
> from Canonical use.
>
> I have a comment about the code, because the cmp argument to sorted()
> is deprecated (and I think it's gone entirely in Python 3).
>
> Thanks again!
>
> Gavin.
>
>
> > === modified file 'lib/lp/registry/browser/person.py'
> > --- lib/lp/registry/browser/person.py 2009-07-29 15:34:46 +0000
> > +++ lib/lp/registry/browser/person.py 2009-07-31 21:12:09 +0000
> > @@ -2517,7 +2517,10 @@
> > categories = set()
> > for contrib in self.contributions:
> > categories.update(category for category in
> contrib['categories'])
> > - return sorted(categories, key=attrgetter('title'))
> > + sort = {'code': 0, 'bugs': 1, 'blueprints': 2, 'translations': 3,
> > + 'answers': 4, 'specs': 5, 'soyuz': 6}
> > + return sorted(categories, key=attrgetter('name'),
> > + cmp=lambda x,y: cmp(sort[x], sort[y]))
>
> Because cmp is deprecated, I think this would be better as:
>
> return sorted(categories, key=lambda category: sort[category.name])
>
> >
> > @cachedproperty
> > def context_is_probably_a_team(self):

Jamal Fanaian (jamalta) wrote : Posted in a previous version of this proposal
Download full text (3.2 KiB)

I went through the pagetest you referenced, and it will actually not fail. It is also not possible to test my changes with this because there is not enough data. The problem is that the only two icons displayed are bugs and translations. Sorted alphabetically, bugs will obviously always be first, which is the case with the replacement sort.

To test this I need to be able to add a third entry, say, Answers for example. I have this in my local environment (which I should really revert since it's going to break tests in the future), but I don't know how to properly write a pagetest for it because of this limitation. I think this is the reason salgado recommended writing a unittest.

Let me give the unittests another try to see if I can get them done. Thanks for your help so far!

> Hi Gavin,
>
> Thanks for your input regarding this bug. I have actually been working on a
> test for this. I did not have a mentor when I started working on this issue,
> but went on #launchpad-dev to get some help. Salgado had recommended that I
> write a unittest to call the view, and make sure the data was returned in the
> right order, using a LaunchpadObjectFactory. I had been struggling trying to
> figure out how to do that exactly, and haven't gotten back to it in a few days
> now. I will take a look at the test you mentioned and make sure I fix it.
> Also, I appreciate your comment regarding the depracated cmp() function. I am
> fairly new to Python and was not aware of this. I will fix that shortly as
> well, using your recommendation.
>
> > Hi Jamal,
> >
> > Thank you for working on this bug!
> >
> > As Stuart commented, this will need a pagetest. In fact, there is
> > already one that is probably broken by this change. Have a look at the
> > end of lib/lp/registry/stories/foaf/xx-person-home.txt.
> >
> > Did you have a mentor for this branch, or a pre-implementation
> > discussion? Perhaps that might have spotted the need to update the
> > pagetest. Have a look at https://dev.launchpad.net/PatchSubmission if
> > you haven't already. It's the same process as all the Launchpad devs
> > from Canonical use.
> >
> > I have a comment about the code, because the cmp argument to sorted()
> > is deprecated (and I think it's gone entirely in Python 3).
> >
> > Thanks again!
> >
> > Gavin.
> >
> >
> > > === modified file 'lib/lp/registry/browser/person.py'
> > > --- lib/lp/registry/browser/person.py 2009-07-29 15:34:46 +0000
> > > +++ lib/lp/registry/browser/person.py 2009-07-31 21:12:09 +0000
> > > @@ -2517,7 +2517,10 @@
> > > categories = set()
> > > for contrib in self.contributions:
> > > categories.update(category for category in
> > contrib['categories'])
> > > - return sorted(categories, key=attrgetter('title'))
> > > + sort = {'code': 0, 'bugs': 1, 'blueprints': 2, 'translations': 3,
> > > + 'answers': 4, 'specs': 5, 'soyuz': 6}
> > > + return sorted(categories, key=attrgetter('name'),
> > > + cmp=lambda x,y: cmp(sort[x], sort[y]))
> >
> > Because cmp is deprecated, I think this would be better as:
> >
> > return sorted(categories, key=lambda category: sort[categ...

Read more...

Gavin Panella (allenap) wrote : Posted in a previous version of this proposal

Have a look at using lp.testing.factory.LaunchpadObjectFactory. You can use this to quickly create, say, branches, so that there are code artifacts for your user. That will probably be enough to test this code.

Guilherme Salgado (salgado) wrote : Posted in a previous version of this proposal

On Fri, 2009-08-07 at 09:19 +0000, Gavin Panella wrote:
> Have a look at using lp.testing.factory.LaunchpadObjectFactory. You can use this to quickly create, say, branches, so that there are code artifacts for your user. That will probably be enough to test this code.

I don't think so. That list of categories is generated based on the kind
of karma the person has on the KarmaCache table, and that table is
populated by a script. Even though the person would get karma when
factory.makeBranch() is called, it wouldn't end up in the KarmaCache
table until we ran the script. We could run the script in the test, but
that'd make the test *a lot* slower.

That's why I suggested a new method on LPObjectFactory to create the
KarmaCache entries directly.

Jamal, I remember you were able to write the new factory method, so you
should be quite close to getting a test for your fix. If you need any
help, just drop by #launchpad-reviewers and I'll help you.

Cheers,

--
Guilherme Salgado <email address hidden>

Jamal Fanaian (jamalta) wrote : Posted in a previous version of this proposal

I have added a test that makes sure the categories returned by PersonView.contributed_categories are sorted correctly. Instead of running the script to update Karma I am using the test factories and creating the KarmaCache records based on how the Karma update script does it.

I may not be doing this the best way, so I am requesting a review and hopefully I can chat with someone about it come Monday to figure out what needs to be improved or changed.

Thanks again for all your help and sorry this took so long!

> On Fri, 2009-08-07 at 09:19 +0000, Gavin Panella wrote:
> > Have a look at using lp.testing.factory.LaunchpadObjectFactory. You can use
> this to quickly create, say, branches, so that there are code artifacts for
> your user. That will probably be enough to test this code.
>
> I don't think so. That list of categories is generated based on the kind
> of karma the person has on the KarmaCache table, and that table is
> populated by a script. Even though the person would get karma when
> factory.makeBranch() is called, it wouldn't end up in the KarmaCache
> table until we ran the script. We could run the script in the test, but
> that'd make the test *a lot* slower.
>
> That's why I suggested a new method on LPObjectFactory to create the
> KarmaCache entries directly.
>
> Jamal, I remember you were able to write the new factory method, so you
> should be quite close to getting a test for your fix. If you need any
> help, just drop by #launchpad-reviewers and I'll help you.
>
> Cheers,
>
> --
> Guilherme Salgado <email address hidden>

review: Resubmit
Jamal Fanaian (jamalta) wrote : Posted in a previous version of this proposal

Sorry forgot to mention this on my last comment. I will try to ping someone in #launchpad-reviews come Monday to go over my branch, that way we don't have to go back and forth over email.

Thanks!

> I have added a test that makes sure the categories returned by
> PersonView.contributed_categories are sorted correctly. Instead of running the
> script to update Karma I am using the test factories and creating the
> KarmaCache records based on how the Karma update script does it.
>
> I may not be doing this the best way, so I am requesting a review and
> hopefully I can chat with someone about it come Monday to figure out what
> needs to be improved or changed.
>
> Thanks again for all your help and sorry this took so long!
>
> > On Fri, 2009-08-07 at 09:19 +0000, Gavin Panella wrote:
> > > Have a look at using lp.testing.factory.LaunchpadObjectFactory. You can
> use
> > this to quickly create, say, branches, so that there are code artifacts for
> > your user. That will probably be enough to test this code.
> >
> > I don't think so. That list of categories is generated based on the kind
> > of karma the person has on the KarmaCache table, and that table is
> > populated by a script. Even though the person would get karma when
> > factory.makeBranch() is called, it wouldn't end up in the KarmaCache
> > table until we ran the script. We could run the script in the test, but
> > that'd make the test *a lot* slower.
> >
> > That's why I suggested a new method on LPObjectFactory to create the
> > KarmaCache entries directly.
> >
> > Jamal, I remember you were able to write the new factory method, so you
> > should be quite close to getting a test for your fix. If you need any
> > help, just drop by #launchpad-reviewers and I'll help you.
> >
> > Cheers,
> >
> > --
> > Guilherme Salgado <email address hidden>

Jamal Fanaian (jamalta) wrote : Posted in a previous version of this proposal

After taking to salgado in #launchpad-reviews, he recommended that the makeKarmaCache() be modified so that it creates the total records (with category=NULL), instead of processing all entries in KarmaCache to create the sums.

review: Needs Fixing
Guilherme Salgado (salgado) wrote : Posted in a previous version of this proposal
Download full text (6.5 KiB)

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:
> === modified file 'lib/lp/registry/browser/person.py'
> --- lib/lp/registry/browser/person.py 2009-07-30 22:35:16 +0000
> +++ lib/lp/registry/browser/person.py 2009-08-05 19:55:13 +0000
> @@ -2517,7 +2517,9 @@
> categories = set()
> for contrib in self.contributions:
> categories.update(category for category in contrib['categories'])
> - return sorted(categories, key=attrgetter('title'))
> + sort = {'code': 0, 'bugs': 1, 'blueprints': 2, 'translations': 3,
> + 'answers': 4, 'specs': 5, 'soyuz': 6}
> + return sorted(categories, key=lambda category: sort[category.name])
>
> @cachedproperty
> def context_is_probably_a_team(self):
>
> === added file 'lib/lp/registry/browser/tests/test_person_view.py'
> --- lib/lp/registry/browser/tests/test_person_view.py 1970-01-01 00:00:00 +0000
> +++ lib/lp/registry/browser/tests/test_person_view.py 2009-08-27 02:59:16 +0000
> @@ -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.launchpad.webapp.servers import LaunchpadTestRequest
> +from canonical.testing import LaunchpadZopelessLayer
> +from canonical.database.sqlbase import cursor, sqlvalues
> +from lp.registry.browser.person import PersonView
> +from lp.testing import TestCaseWithFactory
> +from zope.component import getUtility
> +from canonical.launchpad.interfaces import IKarmaCacheManager
> +

There should be one extra blank line here. Our style guide[1] has this
and other conventions we use.

[1] https://dev.launchpad.net/PythonStyleGuide

> +class TestPerson(TestCaseWithFactory):

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.

> +
> + layer = LaunchpadZopelessLayer
> +
> + def setUp(self):
> + # Create a person

This comment doesn't add much either, so it can be removed.

> + TestCaseWithFactory.setUp(self)
> + self.person = self.factory.makePerson()
> + self.view = PersonView(self.person,
> + LaunchpadTestRequest())
> +
> + def test_karma_category_sort(self):
> + # Add karma to some categories for the user

Same here.

> + self.makeKarmaCache(person=self.person, category_id=2)
> + self.makeKarmaCache(person=self.person, category_id=7)
> + self.makeKarmaCache(person=self.person, category_id=8)

I'd move these lines to the setUp() method above.

>...

Read more...

review: Needs Fixing
Guilherme Salgado (salgado) wrote : Posted in a previous version of this proposal
Download full text (5.0 KiB)

Hi Jamal,

This looks very good. I have only some comments/suggestions below.

On Sun, 2009-08-30 at 04:30 +0000, Jamal Fanaian wrote:
> === modified file 'lib/lp/registry/browser/person.py'
> --- lib/lp/registry/browser/person.py 2009-07-30 22:35:16 +0000
> +++ lib/lp/registry/browser/person.py 2009-08-05 19:55:13 +0000
> @@ -2517,7 +2517,9 @@
> categories = set()
> for contrib in self.contributions:
> categories.update(category for category in contrib['categories'])
> - return sorted(categories, key=attrgetter('title'))
> + sort = {'code': 0, 'bugs': 1, 'blueprints': 2, 'translations': 3,
> + 'answers': 4, 'specs': 5, 'soyuz': 6}
> + return sorted(categories, key=lambda category: sort[category.name])
>
> @cachedproperty
> def context_is_probably_a_team(self):
>
> === added file 'lib/lp/registry/browser/tests/test_person_view.py'
> --- lib/lp/registry/browser/tests/test_person_view.py 1970-01-01 00:00:00 +0000
> +++ lib/lp/registry/browser/tests/test_person_view.py 2009-08-30 04:24:15 +0000
> @@ -0,0 +1,68 @@
> +# 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
> +

Some of the imports below are not needed anymore, so they should be
removed. If you run 'make lint', it will tell you which ones are not
used.

> +from canonical.config import config
> +from canonical.launchpad.webapp.servers import LaunchpadTestRequest
> +from canonical.testing import LaunchpadZopelessLayer
> +from canonical.database.sqlbase import cursor, sqlvalues
> +from lp.registry.browser.person import PersonView
> +from lp.testing import TestCaseWithFactory
> +from zope.component import getUtility
> +from canonical.launchpad.interfaces import IKarmaCacheManager, NotFoundError
> +from lp.registry.model.karma import KarmaCategory

These imports should be grouped like this:

      1. standard library imports
      2. related third party imports
      3. local application/library specific imports

(For more conventions about imports and other things, check out
http://www.python.org/dev/peps/pep-0008/)

So, this is what it'd look like

        import unittest

        import zope...

        import canonical...
        import lp...

> +

You're also missing one blank line here.

> +class TestPersonView(TestCaseWithFactory):
> +
> + layer = LaunchpadZopelessLayer
> +
> + def setUp(self):
> + TestCaseWithFactory.setUp(self)
> + self.person = self.factory.makePerson()

I don't think you need to store the newly created person in an instance
variable, as it is only needed here in this method.

> + self.view = PersonView(self.person,
> + LaunchpadTestRequest())
> + self.makeKarmaCache(person=self.person,
> + category=KarmaCategory.byName('bugs'))

You don't need to pass person/category as a keyword argument here, and
the indentation is not correct as per our style guide. This is what it
should look like.

    self.makeKarmaCache(
        person, KarmaCategory.byName...

Read more...

Guilherme Salgado (salgado) : Posted in a previous version of this proposal
review: Needs Fixing
Jamal Fanaian (jamalta) wrote : Posted in a previous version of this proposal

The commit had to stay, if it is removed the data is not seen when PersonView.contributed_categories() is called. Everything else should be good :)

Thanks again, and let me know if there are any outstanding issues. Hopefully this will be the last round, hehe.

Guilherme Salgado (salgado) wrote : Posted in a previous version of this proposal

Hi Jamal,

This branch looks really good, but I'm not quite comfortable having the makeKarmaCache() method do a switchDBUser('launchpad') after the entry is created, as a test calling it might have previously switched to a different DB user and not expect it to be changed to 'launchpad' after the method is called. However, I don't see a significantly better way of creating the KarmaCache entries without doing this, so I think we should rename the method to _makeKarmaCache() to make it clear that it's an internal API, drop the last switchDBUser() call and add a docstring to it, mentioning that after completion, the current transaction will be using the 'karma' db user and callsites must deal with it if they need. I also think we should have a comment explaining why the commit is necessary. Something like

    def _makeKarmaCache(...):
        """Create and return a KarmaCache entry with the given arguments.

        In order to create a KarmaCache entry we'll switch the DB user 'karma',
        so tests that need a different user after calling this method should do
        a switchDBUser() themselves.
        """
        ...
        # Must commit here so that the change is seen in other transactions
        # (e.g. when the callsite issues a switchDBUser() after we return).
        LaunchpadZopelessLayer.commit()
        return karmacache

Also, please run the 'make lint' command as I see some unused imports in test_person_view.py. That command will tell you which imports are unused (among other things) and then you can remove them.

Cheers,
Guilherme

Jamal Fanaian (jamalta) wrote :

Hi Guilherme,

Thanks for your comments. I have made the modifications as you requested. I ran `make lint` but it didn't tell me I had any unused libraries. I proceeded to double-check by hand and I am using all the included libraries. Please let me know if this is incorrect.

Thanks again!
Jamal

Guilherme Salgado (salgado) wrote :

On Fri, 2009-09-18 at 00:45 +0000, Jamal Fanaian wrote:
> Thanks for your comments. I have made the modifications as you
> requested. I ran `make lint` but it didn't tell me I had any unused
> libraries. I proceeded to double-check by hand and I am using all the
> included libraries. Please let me know if this is incorrect.

Indeed; I thought your test was not using LaunchpadTestRequest but it
is. I'll submit your changes on your behalf.

 status approved

--
Guilherme Salgado <email address hidden>

review: Approve
Jamal Fanaian (jamalta) wrote :

> On Fri, 2009-09-18 at 00:45 +0000, Jamal Fanaian wrote:
> > Thanks for your comments. I have made the modifications as you
> > requested. I ran `make lint` but it didn't tell me I had any unused
> > libraries. I proceeded to double-check by hand and I am using all the
> > included libraries. Please let me know if this is incorrect.
>
> Indeed; I thought your test was not using LaunchpadTestRequest but it
> is. I'll submit your changes on your behalf.
>
> status approved
>
> --
> Guilherme Salgado <email address hidden>

Ah, thanks! I'm glad to hear that. I really appreciate all the help and guidance you provided through the completion of this test. It took a long time, but I definitely learned a lot from it.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/person.py'
2--- lib/lp/registry/browser/person.py 2009-07-30 22:35:16 +0000
3+++ lib/lp/registry/browser/person.py 2009-08-05 19:55:13 +0000
4@@ -2517,7 +2517,9 @@
5 categories = set()
6 for contrib in self.contributions:
7 categories.update(category for category in contrib['categories'])
8- return sorted(categories, key=attrgetter('title'))
9+ sort = {'code': 0, 'bugs': 1, 'blueprints': 2, 'translations': 3,
10+ 'answers': 4, 'specs': 5, 'soyuz': 6}
11+ return sorted(categories, key=lambda category: sort[category.name])
12
13 @cachedproperty
14 def context_is_probably_a_team(self):
15
16=== added file 'lib/lp/registry/browser/tests/test_person_view.py'
17--- lib/lp/registry/browser/tests/test_person_view.py 1970-01-01 00:00:00 +0000
18+++ lib/lp/registry/browser/tests/test_person_view.py 2009-09-18 00:27:26 +0000
19@@ -0,0 +1,72 @@
20+# Copyright 2009 Canonical Ltd. This software is licensed under the
21+# GNU Affero General Public License version 3 (see the file LICENSE).
22+
23+__metaclass__ = type
24+
25+import unittest
26+
27+from zope.component import getUtility
28+
29+from canonical.launchpad.interfaces import IKarmaCacheManager, NotFoundError
30+from canonical.launchpad.webapp.servers import LaunchpadTestRequest
31+from canonical.testing import LaunchpadZopelessLayer
32+from lp.registry.browser.person import PersonView
33+from lp.registry.model.karma import KarmaCategory
34+from lp.testing import TestCaseWithFactory
35+
36+
37+class TestPersonView(TestCaseWithFactory):
38+
39+ layer = LaunchpadZopelessLayer
40+
41+ def setUp(self):
42+ TestCaseWithFactory.setUp(self)
43+ person = self.factory.makePerson()
44+ product = self.factory.makeProduct()
45+ self.view = PersonView(
46+ person, LaunchpadTestRequest())
47+ self._makeKarmaCache(
48+ person, product, KarmaCategory.byName('bugs'))
49+ self._makeKarmaCache(
50+ person, product, KarmaCategory.byName('answers'))
51+ self._makeKarmaCache(
52+ person, product, KarmaCategory.byName('code'))
53+
54+ def test_karma_category_sort(self):
55+ categories = self.view.contributed_categories
56+ category_names = []
57+ for category in categories:
58+ category_names.append(category.name)
59+
60+ self.assertEqual(category_names, [u'code', u'bugs', u'answers'],
61+ 'Categories are not sorted correctly')
62+
63+ def _makeKarmaCache(self, person, product, category, value=10):
64+ """ Create and return a KarmaCache entry with the given arguments.
65+
66+ In order to create the KarmaCache record we must switch to the DB
67+ user 'karma', so tests that need a different user after calling
68+ this method should do run switchDbUser() themselves.
69+ """
70+
71+ LaunchpadZopelessLayer.switchDbUser('karma')
72+
73+ cache_manager = getUtility(IKarmaCacheManager)
74+ karmacache = cache_manager.new(
75+ value, person.id, category.id, product_id=product.id)
76+
77+ try:
78+ cache_manager.updateKarmaValue(
79+ value, person.id, category_id=None, product_id=product.id)
80+ except NotFoundError:
81+ cache_manager.new(
82+ value, person.id, category_id=None, product_id=product.id)
83+
84+ # We must commit here so that the change is seen in other transactions
85+ # (e.g. when the callsite issues a switchDbUser() after we return).
86+ LaunchpadZopelessLayer.commit()
87+ return karmacache
88+
89+
90+def test_suite():
91+ return unittest.TestLoader().loadTestsFromName(__name__)