launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #02885
[Merge] lp:~gary/launchpad/bug723999-2d into lp:launchpad
Gary Poster has proposed merging lp:~gary/launchpad/bug723999-2d into lp:launchpad with lp:~gary/launchpad/bug723999-2c as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~gary/launchpad/bug723999-2d/+merge/52478
This is the final clean-up branch from my work on bug723999, building most recently on ~gary/launchpad/bug723999-2c.
This branch is all about removing three methods on the bugtaskset utility (lib/lp/bugs/model/bugtask.py) that were simply operating as middlemen to functions in the structuralsubscription module.
- _getStructuralSubscriptionTargets was just a helper function that had stayed around because it was tested. Those tests are now repointed to look at the underlying get_structural_subscription_targets function, and moved from test_bugtask to test_structuralsubscription.
- At the end of the 2c branch, getStructuralSubscribers was no longer called by anything in LP. It was only around because useful tests looked at it. Those tests are now repointed and moved, in the same way as described for _getStructuralSubscriptionTargets. This also meant that I was able to remove get_structural_subscribers_for_bugtasks from structuralsubscription.py, because that function was really only a shim against get_structural_subscribers for the method to use. I wrote some new tests for the method, which also prove that the function does what we want (TestGetStructuralSubscriptionsForPerson).
- getAllStructuralSubscriptions was now only used by a browser view. It essentially wanted the structural subscriptions for a bug, for a person. I eliminated this bugtaskset method and added a getStructuralSubscriptionsForPerson to IBug. The implementation again is just a middleman, but because this is called by browser code, we want to be able to control who calls what from a security perspective, so I thought it made sense to make the browser code call a method off of a class rather than a function. This is arguable, but it made sense to me at the time.
That's pretty much it.
Thank you!
Gary
--
https://code.launchpad.net/~gary/launchpad/bug723999-2d/+merge/52478
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~gary/launchpad/bug723999-2d into lp:launchpad.
=== modified file 'lib/lp/bugs/browser/bugsubscription.py'
--- lib/lp/bugs/browser/bugsubscription.py 2011-03-04 11:00:04 +0000
+++ lib/lp/bugs/browser/bugsubscription.py 2011-03-07 21:39:06 +0000
@@ -19,7 +19,6 @@
from zope import formlib
from zope.app.form import CustomWidgetFactory
from zope.app.form.browser.itemswidgets import RadioWidget
-from zope.component import getUtility
from zope.schema import Choice
from zope.schema.vocabulary import (
SimpleTerm,
@@ -41,7 +40,6 @@
from lp.bugs.browser.bug import BugViewMixin
from lp.bugs.enum import BugNotificationLevel, HIDDEN_BUG_NOTIFICATION_LEVELS
from lp.bugs.interfaces.bugsubscription import IBugSubscription
-from lp.bugs.interfaces.bugtask import IBugTaskSet
from lp.services import features
from lp.services.propertycache import cachedproperty
@@ -559,5 +557,4 @@
@property
def structural_subscriptions(self):
- return getUtility(IBugTaskSet).getAllStructuralSubscriptions(
- self.context.bug.bugtasks, self.user)
+ return self.context.bug.getStructuralSubscriptionsForPerson(self.user)
=== modified file 'lib/lp/bugs/configure.zcml'
--- lib/lp/bugs/configure.zcml 2011-03-07 17:54:44 +0000
+++ lib/lp/bugs/configure.zcml 2011-03-07 21:39:06 +0000
@@ -747,6 +747,7 @@
getSubscriptionsFromDuplicates
getSubscribersForPerson
getSubscriptionForPerson
+ getStructuralSubscriptionsForPerson
getSubscriptionInfo
indexed_messages
_indexed_messages
=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2011-03-07 21:39:04 +0000
+++ lib/lp/bugs/interfaces/bug.py 2011-03-07 21:39:06 +0000
@@ -533,6 +533,12 @@
If no such `BugSubscription` exists, return None.
"""
+ def getStructuralSubscriptionsForPerson(person):
+ """Return the `StructuralSubscription`s for a `Person` to this `Bug`.
+
+ This disregards filters.
+ """
+
def getSubscriptionInfo(level):
"""Return a `BugSubscriptionInfo` at the given `level`.
=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
--- lib/lp/bugs/interfaces/bugtask.py 2011-03-03 18:58:49 +0000
+++ lib/lp/bugs/interfaces/bugtask.py 2011-03-07 21:39:06 +0000
@@ -1597,18 +1597,6 @@
def getOpenBugTasksPerProduct(user, products):
"""Return open bugtask count for multiple products."""
- def getAllStructuralSubscriptions(bugtasks):
- """Return all potential structural subscriptions for the bugtasks.
-
- This method ignores subscription filters.
- """
-
- def getStructuralSubscribers(bugtasks, recipients=None, level=None):
- """Return `IPerson`s subscribed to the given bug tasks.
-
- This takes into account bug subscription filters.
- """
-
def getPrecachedNonConjoinedBugTasks(user, milestone):
"""List of non-conjoined bugtasks targeted to the milestone.
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2011-03-07 21:39:04 +0000
+++ lib/lp/bugs/model/bug.py 2011-03-07 21:39:06 +0000
@@ -982,6 +982,10 @@
BugSubscription.person == person,
BugSubscription.bug == self).one()
+ def getStructuralSubscriptionsForPerson(self, person):
+ """See `IBug`."""
+ return get_all_structural_subscriptions(self.bugtasks, person)
+
def getAlsoNotifiedSubscribers(self, recipients=None, level=None):
"""See `IBug`.
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2011-03-07 07:27:56 +0000
+++ lib/lp/bugs/model/bugtask.py 2011-03-07 21:39:06 +0000
@@ -132,9 +132,6 @@
from lp.bugs.model.bugnomination import BugNomination
from lp.bugs.model.bugsubscription import BugSubscription
from lp.bugs.model.structuralsubscription import (
- get_all_structural_subscriptions,
- get_structural_subscribers_for_bugtasks,
- get_structural_subscription_targets,
StructuralSubscription,
)
from lp.registry.interfaces.distribution import (
@@ -3107,20 +3104,3 @@
counts.append(package_counts)
return counts
-
- def _getStructuralSubscriptionTargets(self, bugtasks):
- """Return (bugtask, target) pairs for each target of the bugtasks.
-
- Each bugtask may be responsible theoretically for 0 or more targets.
- In practice, each generates one, two or three.
- """
- return get_structural_subscription_targets(bugtasks)
-
- def getAllStructuralSubscriptions(self, bugtasks, recipient=None):
- """See `IBugTaskSet`."""
- return get_all_structural_subscriptions(bugtasks, recipient)
-
- def getStructuralSubscribers(self, bugtasks, recipients=None, level=None):
- """See `IBugTaskSet`."""
- return get_structural_subscribers_for_bugtasks(
- bugtasks, recipients, level)
=== modified file 'lib/lp/bugs/model/structuralsubscription.py'
--- lib/lp/bugs/model/structuralsubscription.py 2011-03-07 21:39:04 +0000
+++ lib/lp/bugs/model/structuralsubscription.py 2011-03-07 21:39:06 +0000
@@ -5,7 +5,6 @@
__all__ = [
'get_all_structural_subscriptions',
'get_structural_subscribers',
- 'get_structural_subscribers_for_bugtasks',
'get_structural_subscription_targets',
'StructuralSubscription',
'StructuralSubscriptionTargetMixin',
@@ -608,33 +607,6 @@
return subscribers
-def get_structural_subscribers_for_bugtasks(bugtasks,
- recipients=None,
- level=None):
- """Return subscribers for structural filters for the bugtasks at "level".
-
- :param bugtasks: an iterable of bugtasks. Should be a single bugtask, or
- all of the bugtasks from one bug.
- :param recipients: a BugNotificationRecipients object or None.
- Populates if given.
- :param level: a level from lp.bugs.enum.BugNotificationLevel.
-
- Excludes structural subscriptions for people who are directly subscribed
- to the bug."""
- bugtasks = list(bugtasks)
- if not bugtasks:
- return EmptyResultSet()
- if len(bugtasks) == 1:
- target = bugtasks[0]
- else:
- target = bugtasks[0].bug
- if target.bugtasks != bugtasks:
- raise NotImplementedError(
- 'bugtasks must be one, or the full set of bugtasks from one '
- 'bug')
- return get_structural_subscribers(target, recipients, level)
-
-
class ArrayAgg(NamedFunc):
"Aggregate values (within a GROUP BY) into an array."
__slots__ = ()
=== modified file 'lib/lp/bugs/model/tests/test_bug.py'
--- lib/lp/bugs/model/tests/test_bug.py 2011-02-18 12:51:40 +0000
+++ lib/lp/bugs/model/tests/test_bug.py 2011-03-07 21:39:06 +0000
@@ -12,6 +12,7 @@
person_logged_in,
TestCaseWithFactory,
)
+from lp.testing.factory import is_security_proxied_or_harmless
class TestBug(TestCaseWithFactory):
@@ -247,3 +248,36 @@
with person_logged_in(bug.owner):
info = bug.getSubscriptionInfo(BugNotificationLevel.METADATA)
self.assertEqual(BugNotificationLevel.METADATA, info.level)
+
+
+class TestGetStructuralSubscriptionsForPerson(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def getStructuralSubscriptionsForPerson(self, bug, recipient):
+ # Call bug.getStructuralSubscriptionsForPerson() and check that the
+ # result is security proxied.
+ result = bug.getStructuralSubscriptionsForPerson(recipient)
+ self.assertTrue(is_security_proxied_or_harmless(result))
+ return result
+
+ def setUp(self):
+ super(TestGetStructuralSubscriptionsForPerson, self).setUp()
+ self.subscriber = self.factory.makePerson()
+ login_person(self.subscriber)
+ self.product = self.factory.makeProduct()
+ self.milestone = self.factory.makeMilestone(product=self.product)
+ self.bug = self.factory.makeBug(
+ product=self.product, milestone=self.milestone)
+
+ def test_no_subscriptions(self):
+ subscriptions = self.getStructuralSubscriptionsForPerson(
+ self.bug, self.subscriber)
+ self.assertEqual([], list(subscriptions))
+
+ def test_one_subscription(self):
+ sub = self.product.addBugSubscription(
+ self.subscriber, self.subscriber)
+ subscriptions = self.getStructuralSubscriptionsForPerson(
+ self.bug, self.subscriber)
+ self.assertEqual([sub], list(subscriptions))
=== modified file 'lib/lp/bugs/model/tests/test_bugtask.py'
--- lib/lp/bugs/model/tests/test_bugtask.py 2011-03-03 02:14:14 +0000
+++ lib/lp/bugs/model/tests/test_bugtask.py 2011-03-07 21:39:06 +0000
@@ -8,17 +8,9 @@
import unittest
from lazr.lifecycle.snapshot import Snapshot
-from storm.store import (
- EmptyResultSet,
- ResultSet,
- )
-from testtools.matchers import (
- Equals,
- StartsWith,
- )
+from testtools.matchers import Equals
from zope.component import getUtility
from zope.interface import providedBy
-from zope.security.proxy import removeSecurityProxy
from canonical.database.sqlbase import flush_database_updates
from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
@@ -32,7 +24,6 @@
LaunchpadZopelessLayer,
)
from lp.app.enums import ServiceUsage
-from lp.bugs.enum import BugNotificationLevel
from lp.bugs.interfaces.bug import IBugSet
from lp.bugs.interfaces.bugtarget import IBugTarget
from lp.bugs.interfaces.bugtask import (
@@ -45,7 +36,6 @@
UNRESOLVED_BUGTASK_STATUSES,
)
from lp.bugs.interfaces.bugwatch import IBugWatchSet
-from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients
from lp.bugs.model.bugtask import build_tag_search_clause
from lp.bugs.tests.bug import (
create_old_bug,
@@ -72,10 +62,7 @@
TestCase,
TestCaseWithFactory,
)
-from lp.testing.factory import (
- is_security_proxied_or_harmless,
- LaunchpadObjectFactory,
- )
+from lp.testing.factory import LaunchpadObjectFactory
from lp.testing.matchers import HasQueryCount
@@ -1346,239 +1333,6 @@
self.assertNotIn(BugTaskStatus.UNKNOWN, UNRESOLVED_BUGTASK_STATUSES)
-class TestGetStructuralSubscriptionTargets(TestCaseWithFactory):
- # This tests a private method because it has some subtleties that are
- # nice to test in isolation.
-
- layer = DatabaseFunctionalLayer
-
- def getStructuralSubscriptionTargets(self, bugtasks):
- unwrapped = removeSecurityProxy(getUtility(IBugTaskSet))
- return unwrapped._getStructuralSubscriptionTargets(bugtasks)
-
- def test_product_target(self):
- product = self.factory.makeProduct()
- bug = self.factory.makeBug(product=product)
- bugtask = bug.bugtasks[0]
- result = self.getStructuralSubscriptionTargets(bug.bugtasks)
- self.assertEqual(list(result), [(bugtask, product)])
-
- def test_milestone_target(self):
- actor = self.factory.makePerson()
- login_person(actor)
- product = self.factory.makeProduct()
- milestone = self.factory.makeMilestone(product=product)
- bug = self.factory.makeBug(product=product, milestone=milestone)
- bugtask = bug.bugtasks[0]
- result = self.getStructuralSubscriptionTargets(bug.bugtasks)
- self.assertEqual(set(result), set(
- ((bugtask, product), (bugtask, milestone))))
-
- def test_sourcepackage_target(self):
- actor = self.factory.makePerson()
- login_person(actor)
- distroseries = self.factory.makeDistroSeries()
- sourcepackage = self.factory.makeSourcePackage(
- distroseries=distroseries)
- product = self.factory.makeProduct()
- bug = self.factory.makeBug(product=product)
- bug.addTask(actor, sourcepackage)
- product_bugtask = bug.bugtasks[0]
- sourcepackage_bugtask = bug.bugtasks[1]
- result = self.getStructuralSubscriptionTargets(bug.bugtasks)
- self.assertEqual(set(result), set(
- ((product_bugtask, product),
- (sourcepackage_bugtask, distroseries))))
-
- def test_distribution_source_package_target(self):
- actor = self.factory.makePerson()
- login_person(actor)
- distribution = self.factory.makeDistribution()
- dist_sourcepackage = self.factory.makeDistributionSourcePackage(
- distribution=distribution)
- product = self.factory.makeProduct()
- bug = self.factory.makeBug(product=product)
- bug.addTask(actor, dist_sourcepackage)
- product_bugtask = bug.bugtasks[0]
- dist_sourcepackage_bugtask = bug.bugtasks[1]
- result = self.getStructuralSubscriptionTargets(bug.bugtasks)
- self.assertEqual(set(result), set(
- ((product_bugtask, product),
- (dist_sourcepackage_bugtask, dist_sourcepackage),
- (dist_sourcepackage_bugtask, distribution))))
-
-
-class TestGetAllStructuralSubscriptions(TestCaseWithFactory):
-
- layer = DatabaseFunctionalLayer
-
- def getAllStructuralSubscriptions(self, bugtasks, recipient):
- # Call IBugTaskSet.getAllStructuralSubscriptions() and check that the
- # result is security proxied.
- result = getUtility(IBugTaskSet).getAllStructuralSubscriptions(
- bugtasks, recipient)
- self.assertTrue(is_security_proxied_or_harmless(result))
- return result
-
- def setUp(self):
- super(TestGetAllStructuralSubscriptions, self).setUp()
- self.subscriber = self.factory.makePerson()
- login_person(self.subscriber)
- self.product = self.factory.makeProduct()
- self.milestone = self.factory.makeMilestone(product=self.product)
- self.bug = self.factory.makeBug(
- product=self.product, milestone=self.milestone)
-
- def test_no_subscriptions(self):
- subscriptions = self.getAllStructuralSubscriptions(
- self.bug.bugtasks, self.subscriber)
- self.assertEqual([], list(subscriptions))
-
- def test_one_subscription(self):
- sub = self.product.addBugSubscription(
- self.subscriber, self.subscriber)
- subscriptions = self.getAllStructuralSubscriptions(
- self.bug.bugtasks, self.subscriber)
- self.assertEqual([sub], list(subscriptions))
-
- def test_two_subscriptions(self):
- sub1 = self.product.addBugSubscription(
- self.subscriber, self.subscriber)
- sub2 = self.milestone.addBugSubscription(
- self.subscriber, self.subscriber)
- subscriptions = self.getAllStructuralSubscriptions(
- self.bug.bugtasks, self.subscriber)
- self.assertEqual(set([sub1, sub2]), set(subscriptions))
-
- def test_two_bugtasks_one_subscription(self):
- sub = self.product.addBugSubscription(
- self.subscriber, self.subscriber)
- product2 = self.factory.makeProduct()
- self.bug.addTask(self.subscriber, product2)
- subscriptions = self.getAllStructuralSubscriptions(
- self.bug.bugtasks, self.subscriber)
- self.assertEqual([sub], list(subscriptions))
-
- def test_two_bugtasks_two_subscriptions(self):
- sub1 = self.product.addBugSubscription(
- self.subscriber, self.subscriber)
- product2 = self.factory.makeProduct()
- self.bug.addTask(self.subscriber, product2)
- sub2 = product2.addBugSubscription(
- self.subscriber, self.subscriber)
- subscriptions = self.getAllStructuralSubscriptions(
- self.bug.bugtasks, self.subscriber)
- self.assertEqual(set([sub1, sub2]), set(subscriptions))
-
- def test_ignore_other_subscriptions(self):
- sub1 = self.product.addBugSubscription(
- self.subscriber, self.subscriber)
- another_subscriber = self.factory.makePerson()
- login_person(another_subscriber)
- sub2 = self.product.addBugSubscription(
- another_subscriber, another_subscriber)
- subscriptions = self.getAllStructuralSubscriptions(
- self.bug.bugtasks, self.subscriber)
- self.assertEqual([sub1], list(subscriptions))
- subscriptions = self.getAllStructuralSubscriptions(
- self.bug.bugtasks, another_subscriber)
- self.assertEqual([sub2], list(subscriptions))
-
-
-class TestGetStructuralSubscribers(TestCaseWithFactory):
-
- layer = DatabaseFunctionalLayer
-
- def make_product_with_bug(self):
- product = self.factory.makeProduct()
- bug = self.factory.makeBug(product=product)
- return product, bug
-
- def getStructuralSubscribers(self, bugtasks, *args, **kwargs):
- # Call IBugTaskSet.getStructuralSubscribers() and check that the
- # result is security proxied.
- result = getUtility(IBugTaskSet).getStructuralSubscribers(
- bugtasks, *args, **kwargs)
- self.assertTrue(is_security_proxied_or_harmless(result))
- return result
-
- def test_getStructuralSubscribers_no_subscribers(self):
- # If there are no subscribers for any of the bug's targets then no
- # subscribers will be returned by getStructuralSubscribers().
- product, bug = self.make_product_with_bug()
- subscribers = self.getStructuralSubscribers(bug.bugtasks)
- self.assertIsInstance(subscribers, (ResultSet, EmptyResultSet))
- self.assertEqual([], list(subscribers))
-
- def test_getStructuralSubscribers_single_target(self):
- # Subscribers for any of the bug's targets are returned.
- subscriber = self.factory.makePerson()
- login_person(subscriber)
- product, bug = self.make_product_with_bug()
- product.addBugSubscription(subscriber, subscriber)
- self.assertEqual(
- [subscriber], list(
- self.getStructuralSubscribers(bug.bugtasks)))
-
- def test_getStructuralSubscribers_multiple_targets(self):
- # Subscribers for any of the bug's targets are returned.
- actor = self.factory.makePerson()
- login_person(actor)
-
- subscriber1 = self.factory.makePerson()
- subscriber2 = self.factory.makePerson()
-
- product1 = self.factory.makeProduct(owner=actor)
- product1.addBugSubscription(subscriber1, subscriber1)
- product2 = self.factory.makeProduct(owner=actor)
- product2.addBugSubscription(subscriber2, subscriber2)
-
- bug = self.factory.makeBug(product=product1)
- bug.addTask(actor, product2)
-
- subscribers = self.getStructuralSubscribers(bug.bugtasks)
- self.assertIsInstance(subscribers, ResultSet)
- self.assertEqual(set([subscriber1, subscriber2]), set(subscribers))
-
- def test_getStructuralSubscribers_recipients(self):
- # If provided, getStructuralSubscribers() calls the appropriate
- # methods on a BugNotificationRecipients object.
- subscriber = self.factory.makePerson()
- login_person(subscriber)
- product, bug = self.make_product_with_bug()
- product.addBugSubscription(subscriber, subscriber)
- recipients = BugNotificationRecipients()
- subscribers = self.getStructuralSubscribers(
- bug.bugtasks, recipients=recipients)
- # The return value is a list only when populating recipients.
- self.assertIsInstance(subscribers, list)
- self.assertEqual([subscriber], recipients.getRecipients())
- reason, header = recipients.getReason(subscriber)
- self.assertThat(
- reason, StartsWith(
- u"You received this bug notification because "
- u"you are subscribed to "))
- self.assertThat(header, StartsWith(u"Subscriber "))
-
- def test_getStructuralSubscribers_level(self):
- # getStructuralSubscribers() respects the given level.
- subscriber = self.factory.makePerson()
- login_person(subscriber)
- product, bug = self.make_product_with_bug()
- subscription = product.addBugSubscription(subscriber, subscriber)
- filter = subscription.bug_filters.one()
- filter.bug_notification_level = BugNotificationLevel.METADATA
- self.assertEqual(
- [subscriber], list(
- self.getStructuralSubscribers(
- bug.bugtasks, level=BugNotificationLevel.METADATA)))
- filter.bug_notification_level = BugNotificationLevel.METADATA
- self.assertEqual(
- [], list(
- self.getStructuralSubscribers(
- bug.bugtasks, level=BugNotificationLevel.COMMENTS)))
-
-
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.TestLoader().loadTestsFromName(__name__))
=== modified file 'lib/lp/bugs/tests/test_structuralsubscription.py'
--- lib/lp/bugs/tests/test_structuralsubscription.py 2011-03-07 21:39:04 +0000
+++ lib/lp/bugs/tests/test_structuralsubscription.py 2011-03-07 21:39:06 +0000
@@ -5,7 +5,12 @@
__metaclass__ = type
-from storm.store import Store
+from storm.store import (
+ EmptyResultSet,
+ ResultSet,
+ Store,
+ )
+from testtools.matchers import StartsWith
from zope.security.interfaces import Unauthorized
from canonical.testing.layers import (
@@ -17,8 +22,13 @@
BugTaskImportance,
BugTaskStatus,
)
+from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients
from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilter
-from lp.bugs.model.structuralsubscription import get_structural_subscribers
+from lp.bugs.model.structuralsubscription import (
+ get_all_structural_subscriptions,
+ get_structural_subscribers,
+ get_structural_subscription_targets,
+ )
from lp.testing import (
anonymous_logged_in,
login_person,
@@ -444,3 +454,213 @@
def makeTarget(self):
return self.factory.makeProductSeries()
+
+
+class TestGetStructuralSubscriptionTargets(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def test_product_target(self):
+ product = self.factory.makeProduct()
+ bug = self.factory.makeBug(product=product)
+ bugtask = bug.bugtasks[0]
+ result = get_structural_subscription_targets(bug.bugtasks)
+ self.assertEqual(list(result), [(bugtask, product)])
+
+ def test_milestone_target(self):
+ actor = self.factory.makePerson()
+ login_person(actor)
+ product = self.factory.makeProduct()
+ milestone = self.factory.makeMilestone(product=product)
+ bug = self.factory.makeBug(product=product, milestone=milestone)
+ bugtask = bug.bugtasks[0]
+ result = get_structural_subscription_targets(bug.bugtasks)
+ self.assertEqual(set(result), set(
+ ((bugtask, product), (bugtask, milestone))))
+
+ def test_sourcepackage_target(self):
+ actor = self.factory.makePerson()
+ login_person(actor)
+ distroseries = self.factory.makeDistroSeries()
+ sourcepackage = self.factory.makeSourcePackage(
+ distroseries=distroseries)
+ product = self.factory.makeProduct()
+ bug = self.factory.makeBug(product=product)
+ bug.addTask(actor, sourcepackage)
+ product_bugtask = bug.bugtasks[0]
+ sourcepackage_bugtask = bug.bugtasks[1]
+ result = get_structural_subscription_targets(bug.bugtasks)
+ self.assertEqual(set(result), set(
+ ((product_bugtask, product),
+ (sourcepackage_bugtask, distroseries))))
+
+ def test_distribution_source_package_target(self):
+ actor = self.factory.makePerson()
+ login_person(actor)
+ distribution = self.factory.makeDistribution()
+ dist_sourcepackage = self.factory.makeDistributionSourcePackage(
+ distribution=distribution)
+ product = self.factory.makeProduct()
+ bug = self.factory.makeBug(product=product)
+ bug.addTask(actor, dist_sourcepackage)
+ product_bugtask = bug.bugtasks[0]
+ dist_sourcepackage_bugtask = bug.bugtasks[1]
+ result = get_structural_subscription_targets(bug.bugtasks)
+ self.assertEqual(set(result), set(
+ ((product_bugtask, product),
+ (dist_sourcepackage_bugtask, dist_sourcepackage),
+ (dist_sourcepackage_bugtask, distribution))))
+
+
+class TestGetAllStructuralSubscriptions(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestGetAllStructuralSubscriptions, self).setUp()
+ self.subscriber = self.factory.makePerson()
+ login_person(self.subscriber)
+ self.product = self.factory.makeProduct()
+ self.milestone = self.factory.makeMilestone(product=self.product)
+ self.bug = self.factory.makeBug(
+ product=self.product, milestone=self.milestone)
+
+ def test_no_subscriptions(self):
+ subscriptions = get_all_structural_subscriptions(
+ self.bug.bugtasks, self.subscriber)
+ self.assertEqual([], list(subscriptions))
+
+ def test_one_subscription(self):
+ sub = self.product.addBugSubscription(
+ self.subscriber, self.subscriber)
+ subscriptions = get_all_structural_subscriptions(
+ self.bug.bugtasks, self.subscriber)
+ self.assertEqual([sub], list(subscriptions))
+
+ def test_two_subscriptions(self):
+ sub1 = self.product.addBugSubscription(
+ self.subscriber, self.subscriber)
+ sub2 = self.milestone.addBugSubscription(
+ self.subscriber, self.subscriber)
+ subscriptions = get_all_structural_subscriptions(
+ self.bug.bugtasks, self.subscriber)
+ self.assertEqual(set([sub1, sub2]), set(subscriptions))
+
+ def test_two_bugtasks_one_subscription(self):
+ sub = self.product.addBugSubscription(
+ self.subscriber, self.subscriber)
+ product2 = self.factory.makeProduct()
+ self.bug.addTask(self.subscriber, product2)
+ subscriptions = get_all_structural_subscriptions(
+ self.bug.bugtasks, self.subscriber)
+ self.assertEqual([sub], list(subscriptions))
+
+ def test_two_bugtasks_two_subscriptions(self):
+ sub1 = self.product.addBugSubscription(
+ self.subscriber, self.subscriber)
+ product2 = self.factory.makeProduct()
+ self.bug.addTask(self.subscriber, product2)
+ sub2 = product2.addBugSubscription(
+ self.subscriber, self.subscriber)
+ subscriptions = get_all_structural_subscriptions(
+ self.bug.bugtasks, self.subscriber)
+ self.assertEqual(set([sub1, sub2]), set(subscriptions))
+
+ def test_ignore_other_subscriptions(self):
+ sub1 = self.product.addBugSubscription(
+ self.subscriber, self.subscriber)
+ another_subscriber = self.factory.makePerson()
+ login_person(another_subscriber)
+ sub2 = self.product.addBugSubscription(
+ another_subscriber, another_subscriber)
+ subscriptions = get_all_structural_subscriptions(
+ self.bug.bugtasks, self.subscriber)
+ self.assertEqual([sub1], list(subscriptions))
+ subscriptions = get_all_structural_subscriptions(
+ self.bug.bugtasks, another_subscriber)
+ self.assertEqual([sub2], list(subscriptions))
+
+
+class TestGetStructuralSubscribers(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def make_product_with_bug(self):
+ product = self.factory.makeProduct()
+ bug = self.factory.makeBug(product=product)
+ return product, bug
+
+ def test_getStructuralSubscribers_no_subscribers(self):
+ # If there are no subscribers for any of the bug's targets then no
+ # subscribers will be returned by get_structural_subscribers().
+ product, bug = self.make_product_with_bug()
+ subscribers = get_structural_subscribers(bug, None, None, None)
+ self.assertIsInstance(subscribers, (ResultSet, EmptyResultSet))
+ self.assertEqual([], list(subscribers))
+
+ def test_getStructuralSubscribers_single_target(self):
+ # Subscribers for any of the bug's targets are returned.
+ subscriber = self.factory.makePerson()
+ login_person(subscriber)
+ product, bug = self.make_product_with_bug()
+ product.addBugSubscription(subscriber, subscriber)
+ self.assertEqual(
+ [subscriber], list(
+ get_structural_subscribers(bug, None, None, None)))
+
+ def test_getStructuralSubscribers_multiple_targets(self):
+ # Subscribers for any of the bug's targets are returned.
+ actor = self.factory.makePerson()
+ login_person(actor)
+
+ subscriber1 = self.factory.makePerson()
+ subscriber2 = self.factory.makePerson()
+
+ product1 = self.factory.makeProduct(owner=actor)
+ product1.addBugSubscription(subscriber1, subscriber1)
+ product2 = self.factory.makeProduct(owner=actor)
+ product2.addBugSubscription(subscriber2, subscriber2)
+
+ bug = self.factory.makeBug(product=product1)
+ bug.addTask(actor, product2)
+
+ subscribers = get_structural_subscribers(bug, None, None, None)
+ self.assertIsInstance(subscribers, ResultSet)
+ self.assertEqual(set([subscriber1, subscriber2]), set(subscribers))
+
+ def test_getStructuralSubscribers_recipients(self):
+ # If provided, get_structural_subscribers() calls the appropriate
+ # methods on a BugNotificationRecipients object.
+ subscriber = self.factory.makePerson()
+ login_person(subscriber)
+ product, bug = self.make_product_with_bug()
+ product.addBugSubscription(subscriber, subscriber)
+ recipients = BugNotificationRecipients()
+ subscribers = get_structural_subscribers(bug, recipients, None, None)
+ # The return value is a list only when populating recipients.
+ self.assertIsInstance(subscribers, list)
+ self.assertEqual([subscriber], recipients.getRecipients())
+ reason, header = recipients.getReason(subscriber)
+ self.assertThat(
+ reason, StartsWith(
+ u"You received this bug notification because "
+ u"you are subscribed to "))
+ self.assertThat(header, StartsWith(u"Subscriber "))
+
+ def test_getStructuralSubscribers_level(self):
+ # get_structural_subscribers() respects the given level.
+ subscriber = self.factory.makePerson()
+ login_person(subscriber)
+ product, bug = self.make_product_with_bug()
+ subscription = product.addBugSubscription(subscriber, subscriber)
+ filter = subscription.bug_filters.one()
+ filter.bug_notification_level = BugNotificationLevel.METADATA
+ self.assertEqual(
+ [subscriber], list(
+ get_structural_subscribers(
+ bug, None, BugNotificationLevel.METADATA, None)))
+ filter.bug_notification_level = BugNotificationLevel.METADATA
+ self.assertEqual(
+ [], list(
+ get_structural_subscribers(
+ bug, None, BugNotificationLevel.COMMENTS, None)))