~launchpad-pqm/launchpad/devel

13811.2.1 by Jeroen Vermeulen
Fix some of the lint people left in the past few days.
1
# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
11526.4.35 by Gavin Panella
Add StructuralSubscription.bug_filters.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
4
"""Tests for `StructuralSubscription`."""
5
6
__metaclass__ = type
7
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
8
from storm.store import (
9
    EmptyResultSet,
10
    ResultSet,
11
    Store,
12
    )
13
from testtools.matchers import StartsWith
12164.3.14 by Gavin Panella
Restrict newBugFilter() to subscriber only.
14
from zope.security.interfaces import Unauthorized
15
12541.2.3 by Gary Poster
move tests from test_structuralsubscriptiontarget.py to test_structuralsubscription.py
16
from lp.bugs.enum import BugNotificationLevel
17
from lp.bugs.interfaces.bugtask import (
18
    BugTaskImportance,
19
    BugTaskStatus,
20
    )
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
21
from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
22
from lp.bugs.model.bugsubscriptionfilter import (
23
    BugSubscriptionFilter,
24
    BugSubscriptionFilterMute,
7675.1094.5 by Graham Binns
Added tests to cover errors when you're not allowed to mute.
25
    MuteNotAllowed,
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
26
    )
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
27
from lp.bugs.model.structuralsubscription import (
28
    get_structural_subscribers,
29
    get_structural_subscription_targets,
14494.6.22 by Gavin Panella
New function get_structural_subscriptions().
30
    get_structural_subscriptions,
13811.2.1 by Jeroen Vermeulen
Fix some of the lint people left in the past few days.
31
    get_structural_subscriptions_for_bug,
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
32
    )
14494.6.27 by Gavin Panella
get_structural_subscriptions() returns DecoratedResultSet now.
33
from lp.services.database.decoratedresultset import DecoratedResultSet
12164.3.14 by Gavin Panella
Restrict newBugFilter() to subscriber only.
34
from lp.testing import (
35
    anonymous_logged_in,
12541.2.3 by Gary Poster
move tests from test_structuralsubscriptiontarget.py to test_structuralsubscription.py
36
    login_person,
12164.3.14 by Gavin Panella
Restrict newBugFilter() to subscriber only.
37
    person_logged_in,
38
    TestCaseWithFactory,
39
    )
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
40
from lp.testing.factory import is_security_proxied_or_harmless
14612.2.1 by William Grant
format-imports on lib/. So many imports.
41
from lp.testing.layers import (
42
    DatabaseFunctionalLayer,
43
    LaunchpadFunctionalLayer,
44
    )
11526.4.35 by Gavin Panella
Add StructuralSubscription.bug_filters.
45
46
14494.6.27 by Gavin Panella
get_structural_subscriptions() returns DecoratedResultSet now.
47
RESULT_SETS = ResultSet, EmptyResultSet, DecoratedResultSet
48
49
11526.4.35 by Gavin Panella
Add StructuralSubscription.bug_filters.
50
class TestStructuralSubscription(TestCaseWithFactory):
51
12164.3.14 by Gavin Panella
Restrict newBugFilter() to subscriber only.
52
    layer = DatabaseFunctionalLayer
11526.4.35 by Gavin Panella
Add StructuralSubscription.bug_filters.
53
12164.3.15 by Gavin Panella
Refactor test_structuralsubscription.
54
    def setUp(self):
55
        super(TestStructuralSubscription, self).setUp()
56
        self.product = self.factory.makeProduct()
57
        with person_logged_in(self.product.owner):
58
            self.subscription = self.product.addSubscription(
59
                self.product.owner, self.product.owner)
12399.3.1 by Danilo Segan
Automatically create an empty BugSubscriptionFilter for every StructuralSubscription.
60
        self.original_filter = self.subscription.bug_filters[0]
12164.3.15 by Gavin Panella
Refactor test_structuralsubscription.
61
7675.1030.2 by Gary Poster
make it possible to delete a structural subscription that has a filter, by adding a delete method to structural subscriptions that performs the cascading delete.
62
    def test_delete_requires_Edit_permission(self):
63
        # delete() is only available to the subscriber.
7675.1030.4 by Gary Poster
improve comments, per review
64
        # We use a lambda here because a security proxy around
65
        # self.subscription is giving us the behavior we want to
66
        # demonstrate.  Merely accessing the "delete" name raises
67
        # Unauthorized, before the method is even called.  Therefore,
68
        # we use a lambda to make the trigger happen within "assertRaises".
7675.1030.2 by Gary Poster
make it possible to delete a structural subscription that has a filter, by adding a delete method to structural subscriptions that performs the cascading delete.
69
        with anonymous_logged_in():
70
            self.assertRaises(Unauthorized, lambda: self.subscription.delete)
71
        with person_logged_in(self.factory.makePerson()):
72
            self.assertRaises(Unauthorized, lambda: self.subscription.delete)
73
74
    def test_simple_delete(self):
75
        with person_logged_in(self.product.owner):
76
            self.subscription.delete()
77
            self.assertEqual(
78
                self.product.getSubscription(self.product.owner), None)
79
80
    def test_delete_cascades_to_filters(self):
81
        with person_logged_in(self.product.owner):
82
            subscription_id = self.subscription.id
83
            self.subscription.newBugFilter()
84
            self.subscription.delete()
85
            self.assertEqual(
86
                self.product.getSubscription(self.product.owner), None)
87
            store = Store.of(self.product)
7675.1030.4 by Gary Poster
improve comments, per review
88
            # We know that the filter is gone, because we know the
89
            # subscription is gone, and the database would have
90
            # prevented the deletion of a subscription without first
91
            # deleting the filters.  We'll double-check, to be sure.
7675.1030.2 by Gary Poster
make it possible to delete a structural subscription that has a filter, by adding a delete method to structural subscriptions that performs the cascading delete.
92
            self.assertEqual(
93
                store.find(
94
                    BugSubscriptionFilter,
95
                    BugSubscriptionFilter.structural_subscription_id ==
12399.3.4 by Danilo Segan
Lint fixes.
96
                        subscription_id).one(),
7675.1030.2 by Gary Poster
make it possible to delete a structural subscription that has a filter, by adding a delete method to structural subscriptions that performs the cascading delete.
97
                None)
98
12399.3.1 by Danilo Segan
Automatically create an empty BugSubscriptionFilter for every StructuralSubscription.
99
    def test_bug_filters_default(self):
12411.3.6 by Danilo Segan
Update StructuralSubscription API test.
100
        # The bug_filters attribute has a default non-filtering bug filter
12399.3.1 by Danilo Segan
Automatically create an empty BugSubscriptionFilter for every StructuralSubscription.
101
        # to begin with.
102
        self.assertEqual([self.original_filter],
103
                         list(self.subscription.bug_filters))
11526.4.35 by Gavin Panella
Add StructuralSubscription.bug_filters.
104
105
    def test_bug_filters(self):
12164.3.15 by Gavin Panella
Refactor test_structuralsubscription.
106
        # The bug_filters attribute returns the BugSubscriptionFilter records
107
        # associated with this subscription.
108
        subscription_filter = BugSubscriptionFilter()
109
        subscription_filter.structural_subscription = self.subscription
12399.3.1 by Danilo Segan
Automatically create an empty BugSubscriptionFilter for every StructuralSubscription.
110
        self.assertContentEqual(
111
            [subscription_filter, self.original_filter],
12164.3.15 by Gavin Panella
Refactor test_structuralsubscription.
112
            list(self.subscription.bug_filters))
11526.4.36 by Gavin Panella
Test for StructuralSubscription.newBugFilter().
113
114
    def test_newBugFilter(self):
12164.3.15 by Gavin Panella
Refactor test_structuralsubscription.
115
        # newBugFilter() creates a new subscription filter linked to the
116
        # subscription.
117
        with person_logged_in(self.product.owner):
118
            subscription_filter = self.subscription.newBugFilter()
119
        self.assertEqual(
120
            self.subscription,
121
            subscription_filter.structural_subscription)
12399.3.1 by Danilo Segan
Automatically create an empty BugSubscriptionFilter for every StructuralSubscription.
122
        self.assertContentEqual(
123
            [subscription_filter, self.original_filter],
12164.3.15 by Gavin Panella
Refactor test_structuralsubscription.
124
            list(self.subscription.bug_filters))
12164.3.14 by Gavin Panella
Restrict newBugFilter() to subscriber only.
125
12164.3.15 by Gavin Panella
Refactor test_structuralsubscription.
126
    def test_newBugFilter_by_anonymous(self):
127
        # newBugFilter() is not available to anonymous users.
12164.3.14 by Gavin Panella
Restrict newBugFilter() to subscriber only.
128
        with anonymous_logged_in():
129
            self.assertRaises(
12164.3.15 by Gavin Panella
Refactor test_structuralsubscription.
130
                Unauthorized, lambda: self.subscription.newBugFilter)
131
132
    def test_newBugFilter_by_other_user(self):
133
        # newBugFilter() is only available to the subscriber.
12164.3.14 by Gavin Panella
Restrict newBugFilter() to subscriber only.
134
        with person_logged_in(self.factory.makePerson()):
135
            self.assertRaises(
12164.3.15 by Gavin Panella
Refactor test_structuralsubscription.
136
                Unauthorized, lambda: self.subscription.newBugFilter)
12541.2.3 by Gary Poster
move tests from test_structuralsubscriptiontarget.py to test_structuralsubscription.py
137
138
139
class FilteredStructuralSubscriptionTestBase:
140
    """Tests for filtered structural subscriptions."""
141
142
    layer = LaunchpadFunctionalLayer
143
144
    def makeTarget(self):
145
        raise NotImplementedError(self.makeTarget)
146
147
    def makeBugTask(self):
148
        return self.factory.makeBugTask(target=self.target)
149
150
    def setUp(self):
151
        super(FilteredStructuralSubscriptionTestBase, self).setUp()
152
        self.ordinary_subscriber = self.factory.makePerson()
153
        login_person(self.ordinary_subscriber)
154
        self.target = self.makeTarget()
155
        self.bugtask = self.makeBugTask()
156
        self.bug = self.bugtask.bug
157
        self.subscription = self.target.addSubscription(
158
            self.ordinary_subscriber, self.ordinary_subscriber)
159
        self.initial_filter = self.subscription.bug_filters[0]
160
161
    def assertSubscribers(
7675.1139.1 by Danilo Segan
Get rid of all NOTHING usage.
162
        self, expected_subscribers, level=BugNotificationLevel.LIFECYCLE):
12541.2.3 by Gary Poster
move tests from test_structuralsubscriptiontarget.py to test_structuralsubscription.py
163
        observed_subscribers = list(
164
            get_structural_subscribers(self.bugtask, None, level))
165
        self.assertEqual(expected_subscribers, observed_subscribers)
166
167
    def test_getStructuralSubscribers(self):
168
        # If no one has a filtered subscription for the given bug, the result
169
        # of get_structural_subscribers() is the same as for
170
        # the set of people from each subscription in getSubscriptions().
171
        subscriptions = self.target.getSubscriptions()
172
        self.assertSubscribers([sub.subscriber for sub in subscriptions])
173
174
    def test_getStructuralSubscribers_with_filter_on_status(self):
175
        # If a status filter exists for a subscription, the result of
176
        # get_structural_subscribers() may be a subset of getSubscriptions().
177
178
        # Without any filters the subscription is found.
179
        self.assertSubscribers([self.ordinary_subscriber])
180
181
        # Filter the subscription to bugs in the CONFIRMED state.
182
        self.initial_filter.statuses = [BugTaskStatus.CONFIRMED]
183
184
        # With the filter the subscription is not found.
185
        self.assertSubscribers([])
186
187
        # If the filter is adjusted, the subscription is found again.
188
        self.initial_filter.statuses = [self.bugtask.status]
189
        self.assertSubscribers([self.ordinary_subscriber])
190
191
    def test_getStructuralSubscribers_with_filter_on_importance(self):
192
        # If an importance filter exists for a subscription, the result of
193
        # get_structural_subscribers() may be a subset of getSubscriptions().
194
195
        # Without any filters the subscription is found.
196
        self.assertSubscribers([self.ordinary_subscriber])
197
198
        # Filter the subscription to bugs in the CRITICAL state.
199
        self.initial_filter.importances = [BugTaskImportance.CRITICAL]
200
201
        # With the filter the subscription is not found.
202
        self.assertSubscribers([])
203
204
        # If the filter is adjusted, the subscription is found again.
205
        self.initial_filter.importances = [self.bugtask.importance]
206
        self.assertSubscribers([self.ordinary_subscriber])
207
208
    def test_getStructuralSubscribers_with_filter_on_level(self):
209
        # All structural subscriptions have a level for bug notifications
210
        # which get_structural_subscribers() observes.
211
212
        # Adjust the subscription level to METADATA.
213
        self.initial_filter.bug_notification_level = (
214
            BugNotificationLevel.METADATA)
215
7675.1139.1 by Danilo Segan
Get rid of all NOTHING usage.
216
        # The subscription is found when looking for LIFECYCLE or above.
12541.2.3 by Gary Poster
move tests from test_structuralsubscriptiontarget.py to test_structuralsubscription.py
217
        self.assertSubscribers(
7675.1139.1 by Danilo Segan
Get rid of all NOTHING usage.
218
            [self.ordinary_subscriber], BugNotificationLevel.LIFECYCLE)
12541.2.3 by Gary Poster
move tests from test_structuralsubscriptiontarget.py to test_structuralsubscription.py
219
        # The subscription is found when looking for METADATA or above.
220
        self.assertSubscribers(
221
            [self.ordinary_subscriber], BugNotificationLevel.METADATA)
222
        # The subscription is not found when looking for COMMENTS or above.
223
        self.assertSubscribers(
224
            [], BugNotificationLevel.COMMENTS)
225
226
    def test_getStructuralSubscribers_with_filter_include_any_tags(self):
227
        # If a subscription filter has include_any_tags, a bug with one or
228
        # more tags is matched.
229
230
        self.initial_filter.include_any_tags = True
231
232
        # Without any tags the subscription is not found.
233
        self.assertSubscribers([])
234
235
        # With any tag the subscription is found.
236
        self.bug.tags = ["foo"]
237
        self.assertSubscribers([self.ordinary_subscriber])
238
239
    def test_getStructuralSubscribers_with_filter_exclude_any_tags(self):
240
        # If a subscription filter has exclude_any_tags, only bugs with no
241
        # tags are matched.
242
243
        self.initial_filter.exclude_any_tags = True
244
245
        # Without any tags the subscription is found.
246
        self.assertSubscribers([self.ordinary_subscriber])
247
248
        # With any tag the subscription is not found.
249
        self.bug.tags = ["foo"]
250
        self.assertSubscribers([])
251
252
    def test_getStructuralSubscribers_with_filter_for_any_tag(self):
253
        # If a subscription filter specifies that any of one or more specific
254
        # tags must be present, bugs with any of those tags are matched.
255
256
        # Looking for either the "foo" or the "bar" tag.
257
        self.initial_filter.tags = [u"foo", u"bar"]
258
        self.initial_filter.find_all_tags = False
259
260
        # Without either tag the subscription is not found.
261
        self.assertSubscribers([])
262
263
        # With either tag the subscription is found.
264
        self.bug.tags = ["bar", "baz"]
265
        self.assertSubscribers([self.ordinary_subscriber])
266
267
    def test_getStructuralSubscribers_with_filter_for_all_tags(self):
268
        # If a subscription filter specifies that all of one or more specific
269
        # tags must be present, bugs with all of those tags are matched.
270
271
        # Looking for both the "foo" and the "bar" tag.
272
        self.initial_filter.tags = [u"foo", u"bar"]
273
        self.initial_filter.find_all_tags = True
274
275
        # Without either tag the subscription is not found.
276
        self.assertSubscribers([])
277
278
        # Without only one of the required tags the subscription is not found.
279
        self.bug.tags = ["foo"]
280
        self.assertSubscribers([])
281
282
        # With both required tags the subscription is found.
283
        self.bug.tags = ["foo", "bar"]
284
        self.assertSubscribers([self.ordinary_subscriber])
285
286
    def test_getStructuralSubscribers_with_filter_for_not_any_tag(self):
287
        # If a subscription filter specifies that any of one or more specific
288
        # tags must not be present, bugs without any of those tags are
289
        # matched.
290
291
        # Looking to exclude the "foo" or "bar" tags.
292
        self.initial_filter.tags = [u"-foo", u"-bar"]
293
        self.initial_filter.find_all_tags = False
294
295
        # Without either tag the subscription is found.
296
        self.assertSubscribers([self.ordinary_subscriber])
297
298
        # With both tags, the subscription is omitted.
299
        self.bug.tags = ["foo", "bar"]
300
        self.assertSubscribers([])
301
302
        # With only one tag, the subscription is found again.
303
        self.bug.tags = ["foo"]
304
        self.assertSubscribers([self.ordinary_subscriber])
305
306
        # However, if find_all_tags is True, even a single excluded tag
307
        # causes the subscription to be skipped.
308
        self.initial_filter.find_all_tags = True
309
        self.assertSubscribers([])
310
311
        # This is also true, of course, if the bug has both tags.
312
        self.bug.tags = ["foo", "bar"]
313
        self.assertSubscribers([])
314
315
    def test_getStructuralSubscribers_with_filter_for_not_all_tags(self):
316
        # If a subscription filter specifies that all of one or more specific
317
        # tags must not be present, bugs without all of those tags are
318
        # matched.
319
320
        # Looking to exclude the "foo" and "bar" tags.
321
        self.initial_filter.tags = [u"-foo", u"-bar"]
322
        self.initial_filter.find_all_tags = True
323
324
        # Without either tag the subscription is found.
325
        self.assertSubscribers([self.ordinary_subscriber])
326
327
        # With only one of the excluded tags the subscription is not
328
        # found--we are saying that we want to find both an absence of foo
329
        # and an absence of bar, and yet foo exists.
330
        self.bug.tags = ["foo"]
331
        self.assertSubscribers([])
332
333
        # With both tags the subscription is also not found.
334
        self.bug.tags = ["foo", "bar"]
335
        self.assertSubscribers([])
336
337
    def test_getStructuralSubscribers_with_multiple_filters(self):
338
        # If multiple filters exist for a subscription, all filters must
339
        # match.
340
341
        # Add the "foo" tag to the bug.
342
        self.bug.tags = ["foo"]
343
        self.assertSubscribers([self.ordinary_subscriber])
344
345
        # Filter the subscription to bugs in the CRITICAL state.
346
        self.initial_filter.statuses = [BugTaskStatus.CONFIRMED]
347
        self.initial_filter.importances = [BugTaskImportance.CRITICAL]
348
349
        # With the filter the subscription is not found.
350
        self.assertSubscribers([])
351
352
        # If the filter is adjusted to match status but not importance, the
353
        # subscription is still not found.
354
        self.initial_filter.statuses = [self.bugtask.status]
355
        self.assertSubscribers([])
356
357
        # If the filter is adjusted to also match importance, the subscription
358
        # is found again.
359
        self.initial_filter.importances = [self.bugtask.importance]
360
        self.assertSubscribers([self.ordinary_subscriber])
361
362
        # If the filter is given some tag criteria, the subscription is not
363
        # found.
364
        self.initial_filter.tags = [u"-foo", u"bar", u"baz"]
365
        self.initial_filter.find_all_tags = False
366
        self.assertSubscribers([])
367
368
        # After removing the "foo" tag and adding the "bar" tag, the
369
        # subscription is found.
370
        self.bug.tags = ["bar"]
371
        self.assertSubscribers([self.ordinary_subscriber])
372
373
        # Requiring that all tag criteria are fulfilled causes the
374
        # subscription to no longer be found.
375
        self.initial_filter.find_all_tags = True
376
        self.assertSubscribers([])
377
378
        # After adding the "baz" tag, the subscription is found again.
379
        self.bug.tags = ["bar", "baz"]
380
        self.assertSubscribers([self.ordinary_subscriber])
381
382
    def test_getStructuralSubscribers_any_filter_is_a_match(self):
383
        # If a subscription has multiple filters, the subscription is selected
384
        # when any filter is found to match. Put another way, the filters are
385
        # ORed together.
386
        subscription_filter1 = self.initial_filter
387
        subscription_filter1.statuses = [BugTaskStatus.CONFIRMED]
388
        subscription_filter2 = self.subscription.newBugFilter()
389
        subscription_filter2.tags = [u"foo"]
390
391
        # With the filter the subscription is not found.
392
        self.assertSubscribers([])
393
394
        # If the bugtask is adjusted to match the criteria of the first filter
395
        # but not those of the second, the subscription is found.
396
        self.bugtask.transitionToStatus(
397
            BugTaskStatus.CONFIRMED, self.ordinary_subscriber)
398
        self.assertSubscribers([self.ordinary_subscriber])
399
400
        # If the filter is adjusted to also match the criteria of the second
401
        # filter, the subscription is still found.
402
        self.bugtask.bug.tags = [u"foo"]
403
        self.assertSubscribers([self.ordinary_subscriber])
404
405
        # If the bugtask is adjusted to no longer match the criteria of the
406
        # first filter, the subscription is still found.
407
        self.bugtask.transitionToStatus(
408
            BugTaskStatus.INPROGRESS, self.ordinary_subscriber)
409
        self.assertSubscribers([self.ordinary_subscriber])
410
411
412
class TestStructuralSubscriptionFiltersForDistro(
413
    FilteredStructuralSubscriptionTestBase, TestCaseWithFactory):
414
415
    def makeTarget(self):
416
        return self.factory.makeDistribution()
417
418
419
class TestStructuralSubscriptionFiltersForProduct(
420
    FilteredStructuralSubscriptionTestBase, TestCaseWithFactory):
421
422
    def makeTarget(self):
423
        return self.factory.makeProduct()
424
425
426
class TestStructuralSubscriptionFiltersForDistroSourcePackage(
427
    FilteredStructuralSubscriptionTestBase, TestCaseWithFactory):
428
429
    def makeTarget(self):
430
        return self.factory.makeDistributionSourcePackage()
431
432
433
class TestStructuralSubscriptionFiltersForMilestone(
434
    FilteredStructuralSubscriptionTestBase, TestCaseWithFactory):
435
436
    def makeTarget(self):
437
        return self.factory.makeMilestone()
438
439
    def makeBugTask(self):
440
        bug = self.factory.makeBug(milestone=self.target)
441
        return bug.bugtasks[0]
442
443
444
class TestStructuralSubscriptionFiltersForDistroSeries(
445
    FilteredStructuralSubscriptionTestBase, TestCaseWithFactory):
446
447
    def makeTarget(self):
448
        return self.factory.makeDistroSeries()
449
450
451
class TestStructuralSubscriptionFiltersForProjectGroup(
452
    FilteredStructuralSubscriptionTestBase, TestCaseWithFactory):
453
454
    def makeTarget(self):
455
        return self.factory.makeProject()
456
457
    def makeBugTask(self):
458
        return self.factory.makeBugTask(
459
            target=self.factory.makeProduct(project=self.target))
460
461
462
class TestStructuralSubscriptionFiltersForProductSeries(
463
    FilteredStructuralSubscriptionTestBase, TestCaseWithFactory):
464
465
    def makeTarget(self):
466
        return self.factory.makeProductSeries()
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
467
468
469
class TestGetStructuralSubscriptionTargets(TestCaseWithFactory):
470
471
    layer = DatabaseFunctionalLayer
472
473
    def test_product_target(self):
474
        product = self.factory.makeProduct()
475
        bug = self.factory.makeBug(product=product)
476
        bugtask = bug.bugtasks[0]
477
        result = get_structural_subscription_targets(bug.bugtasks)
478
        self.assertEqual(list(result), [(bugtask, product)])
479
480
    def test_milestone_target(self):
481
        actor = self.factory.makePerson()
482
        login_person(actor)
483
        product = self.factory.makeProduct()
484
        milestone = self.factory.makeMilestone(product=product)
485
        bug = self.factory.makeBug(product=product, milestone=milestone)
486
        bugtask = bug.bugtasks[0]
487
        result = get_structural_subscription_targets(bug.bugtasks)
488
        self.assertEqual(set(result), set(
489
            ((bugtask, product), (bugtask, milestone))))
490
491
    def test_sourcepackage_target(self):
492
        actor = self.factory.makePerson()
493
        login_person(actor)
494
        distroseries = self.factory.makeDistroSeries()
495
        sourcepackage = self.factory.makeSourcePackage(
13756.1.1 by William Grant
Fix tests broken by fixing tests in r13754.
496
            distroseries=distroseries, publish=True)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
497
        product = self.factory.makeProduct()
498
        bug = self.factory.makeBug(product=product)
499
        bug.addTask(actor, sourcepackage)
500
        product_bugtask = bug.bugtasks[0]
501
        sourcepackage_bugtask = bug.bugtasks[1]
502
        result = get_structural_subscription_targets(bug.bugtasks)
503
        self.assertEqual(set(result), set(
504
            ((product_bugtask, product),
505
             (sourcepackage_bugtask, distroseries))))
506
507
    def test_distribution_source_package_target(self):
508
        actor = self.factory.makePerson()
509
        login_person(actor)
510
        distribution = self.factory.makeDistribution()
511
        dist_sourcepackage = self.factory.makeDistributionSourcePackage(
512
            distribution=distribution)
513
        product = self.factory.makeProduct()
514
        bug = self.factory.makeBug(product=product)
515
        bug.addTask(actor, dist_sourcepackage)
516
        product_bugtask = bug.bugtasks[0]
517
        dist_sourcepackage_bugtask = bug.bugtasks[1]
518
        result = get_structural_subscription_targets(bug.bugtasks)
519
        self.assertEqual(set(result), set(
520
            ((product_bugtask, product),
521
             (dist_sourcepackage_bugtask, dist_sourcepackage),
522
             (dist_sourcepackage_bugtask, distribution))))
523
13023.5.1 by Graham Binns
Added regression tests.
524
    def test_product_with_project_group(self):
525
        # get_structural_subscription_targets() will yield both a
526
        # product and its parent project group if it has one.
527
        project = self.factory.makeProject()
528
        product = self.factory.makeProduct(
529
            project=project, owner=project.owner)
530
        subscriber = self.factory.makePerson()
531
        with person_logged_in(subscriber):
13811.2.1 by Jeroen Vermeulen
Fix some of the lint people left in the past few days.
532
            project.addBugSubscription(subscriber, subscriber)
13023.5.1 by Graham Binns
Added regression tests.
533
        # This is a sanity check.
534
        self.assertEqual(project, product.parent_subscription_target)
535
        bug = self.factory.makeBug(product=product)
536
        result = get_structural_subscription_targets(bug.bugtasks)
537
        self.assertEqual(
538
            set([(bug.bugtasks[0], product), (bug.bugtasks[0], project)]),
539
            set(result))
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
540
13811.2.1 by Jeroen Vermeulen
Fix some of the lint people left in the past few days.
541
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
542
class TestGetStructuralSubscriptionsForBug(TestCaseWithFactory):
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
543
544
    layer = DatabaseFunctionalLayer
545
546
    def setUp(self):
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
547
        super(TestGetStructuralSubscriptionsForBug, self).setUp()
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
548
        self.subscriber = self.factory.makePerson()
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
549
        self.team = self.factory.makeTeam(members=[self.subscriber])
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
550
        login_person(self.subscriber)
551
        self.product = self.factory.makeProduct()
552
        self.milestone = self.factory.makeMilestone(product=self.product)
553
        self.bug = self.factory.makeBug(
554
            product=self.product, milestone=self.milestone)
555
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
556
    def getSubscriptions(self, person=None):
557
        result = get_structural_subscriptions_for_bug(self.bug, person)
558
        self.assertTrue(is_security_proxied_or_harmless(result))
559
        return result
560
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
561
    def test_no_subscriptions(self):
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
562
        subscriptions = self.getSubscriptions(self.subscriber)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
563
        self.assertEqual([], list(subscriptions))
564
565
    def test_one_subscription(self):
566
        sub = self.product.addBugSubscription(
567
            self.subscriber, self.subscriber)
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
568
        subscriptions = self.getSubscriptions(self.subscriber)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
569
        self.assertEqual([sub], list(subscriptions))
570
571
    def test_two_subscriptions(self):
572
        sub1 = self.product.addBugSubscription(
573
            self.subscriber, self.subscriber)
574
        sub2 = self.milestone.addBugSubscription(
575
            self.subscriber, self.subscriber)
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
576
        subscriptions = self.getSubscriptions(self.subscriber)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
577
        self.assertEqual(set([sub1, sub2]), set(subscriptions))
578
579
    def test_two_bugtasks_one_subscription(self):
580
        sub = self.product.addBugSubscription(
581
            self.subscriber, self.subscriber)
582
        product2 = self.factory.makeProduct()
583
        self.bug.addTask(self.subscriber, product2)
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
584
        subscriptions = self.getSubscriptions(self.subscriber)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
585
        self.assertEqual([sub], list(subscriptions))
586
587
    def test_two_bugtasks_two_subscriptions(self):
588
        sub1 = self.product.addBugSubscription(
589
            self.subscriber, self.subscriber)
590
        product2 = self.factory.makeProduct()
591
        self.bug.addTask(self.subscriber, product2)
592
        sub2 = product2.addBugSubscription(
593
            self.subscriber, self.subscriber)
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
594
        subscriptions = self.getSubscriptions(self.subscriber)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
595
        self.assertEqual(set([sub1, sub2]), set(subscriptions))
596
597
    def test_ignore_other_subscriptions(self):
598
        sub1 = self.product.addBugSubscription(
599
            self.subscriber, self.subscriber)
600
        another_subscriber = self.factory.makePerson()
601
        login_person(another_subscriber)
602
        sub2 = self.product.addBugSubscription(
603
            another_subscriber, another_subscriber)
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
604
        subscriptions = self.getSubscriptions(self.subscriber)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
605
        self.assertEqual([sub1], list(subscriptions))
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
606
        subscriptions = self.getSubscriptions(another_subscriber)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
607
        self.assertEqual([sub2], list(subscriptions))
608
12393.10.2 by Gary Poster
fix some broken code, and eliminate some redendant code. JS is still broken.
609
    def test_team_subscription(self):
610
        with person_logged_in(self.team.teamowner):
611
            sub = self.product.addBugSubscription(
612
                self.team, self.team.teamowner)
613
        subscriptions = self.getSubscriptions(self.subscriber)
614
        self.assertEqual([sub], list(subscriptions))
615
616
    def test_both_subscriptions(self):
617
        self_sub = self.product.addBugSubscription(
618
            self.subscriber, self.subscriber)
619
        with person_logged_in(self.team.teamowner):
620
            team_sub = self.product.addBugSubscription(
621
                self.team, self.team.teamowner)
622
        subscriptions = self.getSubscriptions(self.subscriber)
623
        self.assertEqual(set([self_sub, team_sub]), set(subscriptions))
624
13023.5.1 by Graham Binns
Added regression tests.
625
    def test_subscriptions_from_parent(self):
626
        # get_structural_subscriptions_for_bug() will return any
627
        # structural subscriptions from the parents of the targets of
628
        # that bug.
629
        project = self.factory.makeProject()
630
        product = self.factory.makeProduct(
631
            project=project, owner=project.owner)
632
        subscriber = self.factory.makePerson()
633
        self_sub = project.addBugSubscription(subscriber, subscriber)
634
        # This is a sanity check.
635
        self.assertEqual(project, product.parent_subscription_target)
636
        bug = self.factory.makeBug(product=product)
637
        subscriptions = get_structural_subscriptions_for_bug(
638
            bug, subscriber)
639
        self.assertEqual(set([self_sub]), set(subscriptions))
640
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
641
14494.6.22 by Gavin Panella
New function get_structural_subscriptions().
642
class TestGetStructuralSubscriptions(TestCaseWithFactory):
643
644
    layer = DatabaseFunctionalLayer
645
646
    def make_product_with_bug(self):
647
        product = self.factory.makeProduct()
648
        bug = self.factory.makeBug(product=product)
649
        return product, bug
650
651
    def test_get_structural_subscriptions_no_subscriptions(self):
652
        # If there are no subscriptions for any of the bug's targets then no
653
        # subscriptions will be returned by get_structural_subscriptions().
654
        product, bug = self.make_product_with_bug()
655
        subscriptions = get_structural_subscriptions(bug, None)
14494.6.27 by Gavin Panella
get_structural_subscriptions() returns DecoratedResultSet now.
656
        self.assertIsInstance(subscriptions, RESULT_SETS)
14494.6.22 by Gavin Panella
New function get_structural_subscriptions().
657
        self.assertEqual([], list(subscriptions))
658
659
    def test_get_structural_subscriptions_single_target(self):
660
        # Subscriptions for any of the bug's targets are returned.
661
        subscriber = self.factory.makePerson()
662
        login_person(subscriber)
663
        product, bug = self.make_product_with_bug()
664
        subscription = product.addBugSubscription(subscriber, subscriber)
665
        self.assertContentEqual(
666
            [subscription], get_structural_subscriptions(bug, None))
667
668
    def test_get_structural_subscriptions_multiple_targets(self):
669
        # Subscriptions for any of the bug's targets are returned.
670
        actor = self.factory.makePerson()
671
        login_person(actor)
672
673
        subscriber1 = self.factory.makePerson()
674
        subscriber2 = self.factory.makePerson()
675
676
        product1 = self.factory.makeProduct(owner=actor)
677
        subscription1 = product1.addBugSubscription(subscriber1, subscriber1)
678
        product2 = self.factory.makeProduct(owner=actor)
679
        subscription2 = product2.addBugSubscription(subscriber2, subscriber2)
680
681
        bug = self.factory.makeBug(product=product1)
682
        bug.addTask(actor, product2)
683
684
        subscriptions = get_structural_subscriptions(bug, None)
14494.6.27 by Gavin Panella
get_structural_subscriptions() returns DecoratedResultSet now.
685
        self.assertIsInstance(subscriptions, RESULT_SETS)
14494.6.22 by Gavin Panella
New function get_structural_subscriptions().
686
        self.assertContentEqual(
687
            [subscription1, subscription2], subscriptions)
688
689
    def test_get_structural_subscriptions_multiple_targets_2(self):
690
        # Only the first of multiple subscriptions for a person is returned
691
        # when they have multiple matching subscriptions.
692
        actor = self.factory.makePerson()
693
        login_person(actor)
694
695
        subscriber = self.factory.makePerson()
696
        product1 = self.factory.makeProduct(owner=actor)
697
        subscription1 = product1.addBugSubscription(subscriber, subscriber)
698
        product2 = self.factory.makeProduct(owner=actor)
699
        product2.addBugSubscription(subscriber, subscriber)
700
701
        bug = self.factory.makeBug(product=product1)
702
        bug.addTask(actor, product2)
703
704
        subscriptions = get_structural_subscriptions(bug, None)
14494.6.27 by Gavin Panella
get_structural_subscriptions() returns DecoratedResultSet now.
705
        self.assertIsInstance(subscriptions, RESULT_SETS)
14494.6.22 by Gavin Panella
New function get_structural_subscriptions().
706
        self.assertContentEqual([subscription1], subscriptions)
707
708
    def test_get_structural_subscriptions_level(self):
709
        # get_structural_subscriptions() respects the given level.
710
        subscriber = self.factory.makePerson()
711
        login_person(subscriber)
712
        product, bug = self.make_product_with_bug()
713
        subscription = product.addBugSubscription(subscriber, subscriber)
714
        filter = subscription.bug_filters.one()
715
        filter.bug_notification_level = BugNotificationLevel.METADATA
716
        self.assertContentEqual(
717
            [subscription], get_structural_subscriptions(
718
                bug, BugNotificationLevel.METADATA))
719
        self.assertContentEqual(
720
            [], get_structural_subscriptions(
721
                bug, BugNotificationLevel.COMMENTS))
722
723
    def test_get_structural_subscriptions_exclude(self):
724
        # Subscriptions for any of the given excluded subscribers are not
725
        # returned.
726
        subscriber = self.factory.makePerson()
727
        login_person(subscriber)
728
        product, bug = self.make_product_with_bug()
729
        product.addBugSubscription(subscriber, subscriber)
730
        self.assertContentEqual(
731
            [], get_structural_subscriptions(
732
                bug, None, exclude=[subscriber]))
733
734
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
735
class TestGetStructuralSubscribers(TestCaseWithFactory):
736
737
    layer = DatabaseFunctionalLayer
738
739
    def make_product_with_bug(self):
740
        product = self.factory.makeProduct()
741
        bug = self.factory.makeBug(product=product)
742
        return product, bug
743
744
    def test_getStructuralSubscribers_no_subscribers(self):
745
        # If there are no subscribers for any of the bug's targets then no
746
        # subscribers will be returned by get_structural_subscribers().
747
        product, bug = self.make_product_with_bug()
748
        subscribers = get_structural_subscribers(bug, None, None, None)
14494.6.27 by Gavin Panella
get_structural_subscriptions() returns DecoratedResultSet now.
749
        self.assertIsInstance(subscribers, RESULT_SETS)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
750
        self.assertEqual([], list(subscribers))
751
752
    def test_getStructuralSubscribers_single_target(self):
753
        # Subscribers for any of the bug's targets are returned.
754
        subscriber = self.factory.makePerson()
755
        login_person(subscriber)
756
        product, bug = self.make_product_with_bug()
757
        product.addBugSubscription(subscriber, subscriber)
758
        self.assertEqual(
759
            [subscriber], list(
760
                get_structural_subscribers(bug, None, None, None)))
761
762
    def test_getStructuralSubscribers_multiple_targets(self):
763
        # Subscribers for any of the bug's targets are returned.
764
        actor = self.factory.makePerson()
765
        login_person(actor)
766
767
        subscriber1 = self.factory.makePerson()
768
        subscriber2 = self.factory.makePerson()
769
770
        product1 = self.factory.makeProduct(owner=actor)
771
        product1.addBugSubscription(subscriber1, subscriber1)
772
        product2 = self.factory.makeProduct(owner=actor)
773
        product2.addBugSubscription(subscriber2, subscriber2)
774
775
        bug = self.factory.makeBug(product=product1)
776
        bug.addTask(actor, product2)
777
778
        subscribers = get_structural_subscribers(bug, None, None, None)
14494.6.27 by Gavin Panella
get_structural_subscriptions() returns DecoratedResultSet now.
779
        self.assertIsInstance(subscribers, RESULT_SETS)
12541.2.7 by Gary Poster
make remaining changes to eliminate unneccessary APIs
780
        self.assertEqual(set([subscriber1, subscriber2]), set(subscribers))
781
782
    def test_getStructuralSubscribers_recipients(self):
783
        # If provided, get_structural_subscribers() calls the appropriate
784
        # methods on a BugNotificationRecipients object.
785
        subscriber = self.factory.makePerson()
786
        login_person(subscriber)
787
        product, bug = self.make_product_with_bug()
788
        product.addBugSubscription(subscriber, subscriber)
789
        recipients = BugNotificationRecipients()
790
        subscribers = get_structural_subscribers(bug, recipients, None, None)
791
        # The return value is a list only when populating recipients.
792
        self.assertIsInstance(subscribers, list)
793
        self.assertEqual([subscriber], recipients.getRecipients())
794
        reason, header = recipients.getReason(subscriber)
795
        self.assertThat(
796
            reason, StartsWith(
797
                u"You received this bug notification because "
798
                u"you are subscribed to "))
799
        self.assertThat(header, StartsWith(u"Subscriber "))
800
801
    def test_getStructuralSubscribers_level(self):
802
        # get_structural_subscribers() respects the given level.
803
        subscriber = self.factory.makePerson()
804
        login_person(subscriber)
805
        product, bug = self.make_product_with_bug()
806
        subscription = product.addBugSubscription(subscriber, subscriber)
807
        filter = subscription.bug_filters.one()
808
        filter.bug_notification_level = BugNotificationLevel.METADATA
809
        self.assertEqual(
810
            [subscriber], list(
811
                get_structural_subscribers(
812
                    bug, None, BugNotificationLevel.METADATA, None)))
813
        filter.bug_notification_level = BugNotificationLevel.METADATA
814
        self.assertEqual(
815
            [], list(
816
                get_structural_subscribers(
817
                    bug, None, BugNotificationLevel.COMMENTS, None)))
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
818
819
820
class TestBugSubscriptionFilterMute(TestCaseWithFactory):
821
    """Tests for the BugSubscriptionFilterMute class."""
822
823
    layer = DatabaseFunctionalLayer
824
825
    def setUp(self):
826
        super(TestBugSubscriptionFilterMute, self).setUp()
827
        self.target = self.factory.makeProduct()
828
        self.team = self.factory.makeTeam()
829
        self.team_member = self.factory.makePerson()
830
        with person_logged_in(self.team.teamowner):
831
            self.team.addMember(self.team_member, self.team.teamowner)
832
            self.team_subscription = self.target.addBugSubscription(
833
                self.team, self.team.teamowner)
834
            self.filter = self.team_subscription.bug_filters.one()
835
836
    def test_isMuteAllowed_returns_true_for_team_subscriptions(self):
837
        # BugSubscriptionFilter.isMuteAllowed() will return True for
838
        # subscriptions where the owner of the subscription is a team.
7675.1094.5 by Graham Binns
Added tests to cover errors when you're not allowed to mute.
839
        self.assertTrue(self.filter.isMuteAllowed(self.team_member))
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
840
841
    def test_isMuteAllowed_returns_false_for_non_team_subscriptions(self):
842
        # BugSubscriptionFilter.isMuteAllowed() will return False for
843
        # subscriptions where the owner of the subscription is not a team.
844
        person = self.factory.makePerson()
845
        with person_logged_in(person):
846
            non_team_subscription = self.target.addBugSubscription(
847
                person, person)
848
        filter = non_team_subscription.bug_filters.one()
7675.1094.5 by Graham Binns
Added tests to cover errors when you're not allowed to mute.
849
        self.assertFalse(filter.isMuteAllowed(person))
850
851
    def test_isMuteAllowed_returns_false_for_non_team_members(self):
852
        # BugSubscriptionFilter.isMuteAllowed() will return False if the
853
        # user passed to it is not a member of the subscribing team.
854
        non_team_person = self.factory.makePerson()
855
        self.assertFalse(self.filter.isMuteAllowed(non_team_person))
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
856
857
    def test_mute_adds_mute(self):
858
        # BugSubscriptionFilter.mute() adds a mute for the filter.
859
        filter_id = self.filter.id
860
        person_id = self.team_member.id
861
        store = Store.of(self.filter)
862
        mutes = store.find(
863
            BugSubscriptionFilterMute,
864
            BugSubscriptionFilterMute.filter == filter_id,
865
            BugSubscriptionFilterMute.person == person_id)
866
        self.assertTrue(mutes.is_empty())
7675.1096.4 by Gary Poster
add UI to mute and unmute structural subsscriptions per person
867
        self.assertFalse(self.filter.muted(self.team_member))
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
868
        self.filter.mute(self.team_member)
7675.1096.4 by Gary Poster
add UI to mute and unmute structural subsscriptions per person
869
        self.assertTrue(self.filter.muted(self.team_member))
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
870
        store.flush()
7675.1096.3 by Gary Poster
add more server side changes needed for UI
871
        self.assertFalse(mutes.is_empty())
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
872
873
    def test_unmute_removes_mute(self):
874
        # BugSubscriptionFilter.unmute() removes any mute for a given
875
        # person on that filter.
876
        filter_id = self.filter.id
877
        person_id = self.team_member.id
878
        store = Store.of(self.filter)
879
        self.filter.mute(self.team_member)
880
        store.flush()
881
        mutes = store.find(
882
            BugSubscriptionFilterMute,
883
            BugSubscriptionFilterMute.filter == filter_id,
884
            BugSubscriptionFilterMute.person == person_id)
885
        self.assertFalse(mutes.is_empty())
7675.1096.4 by Gary Poster
add UI to mute and unmute structural subsscriptions per person
886
        self.assertTrue(self.filter.muted(self.team_member))
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
887
        self.filter.unmute(self.team_member)
7675.1096.4 by Gary Poster
add UI to mute and unmute structural subsscriptions per person
888
        self.assertFalse(self.filter.muted(self.team_member))
7675.1094.3 by Graham Binns
Added mute, unmute and isMuteAllowed() methods. Bit of a code-drop.
889
        store.flush()
890
        self.assertTrue(mutes.is_empty())
7675.1094.4 by Graham Binns
Added tests for idempotence.
891
892
    def test_mute_is_idempotent(self):
893
        # Muting works even if the user is already muted.
894
        store = Store.of(self.filter)
895
        mute = self.filter.mute(self.team_member)
896
        store.flush()
897
        second_mute = self.filter.mute(self.team_member)
898
        self.assertEqual(mute, second_mute)
899
900
    def test_unmute_is_idempotent(self):
901
        # Unmuting works even if the user is not muted
902
        store = Store.of(self.filter)
903
        mutes = store.find(
904
            BugSubscriptionFilterMute,
905
            BugSubscriptionFilterMute.filter == self.filter.id,
906
            BugSubscriptionFilterMute.person == self.team_member.id)
907
        self.assertTrue(mutes.is_empty())
908
        self.filter.unmute(self.team_member)
909
        self.assertTrue(mutes.is_empty())
7675.1094.5 by Graham Binns
Added tests to cover errors when you're not allowed to mute.
910
911
    def test_mute_raises_error_for_non_team_subscriptions(self):
912
        # BugSubscriptionFilter.mute() will raise an error if called on
913
        # a non-team subscription.
914
        person = self.factory.makePerson()
915
        with person_logged_in(person):
916
            non_team_subscription = self.target.addBugSubscription(
917
                person, person)
918
        filter = non_team_subscription.bug_filters.one()
919
        self.assertFalse(filter.isMuteAllowed(person))
920
        self.assertRaises(MuteNotAllowed, filter.mute, person)
921
922
    def test_mute_raises_error_for_non_team_members(self):
923
        # BugSubscriptionFilter.mute() will raise an error if called on
924
        # a subscription of which the calling person is not a member.
925
        non_team_person = self.factory.makePerson()
926
        self.assertFalse(self.filter.isMuteAllowed(non_team_person))
927
        self.assertRaises(MuteNotAllowed, self.filter.mute, non_team_person)