blob: 559e322cd8a875437d12b17694811ae0c376811e [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2017 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file or at
4# https://developers.google.com/open-source/licenses/bsd
5
6"""Tests for notify_reasons.py."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import unittest
12import os
13
14from features import notify_reasons
15from framework import emailfmt
16from framework import framework_views
17from framework import urls
18from proto import user_pb2
19from proto import usergroup_pb2
20from services import service_manager
21from testing import fake
22from tracker import tracker_bizobj
23
24
25REPLY_NOT_ALLOWED = notify_reasons.REPLY_NOT_ALLOWED
26REPLY_MAY_COMMENT = notify_reasons.REPLY_MAY_COMMENT
27REPLY_MAY_UPDATE = notify_reasons.REPLY_MAY_UPDATE
28
29
30class ComputeIssueChangeAddressPermListTest(unittest.TestCase):
31
32 def setUp(self):
33 self.users_by_id = {
34 111: framework_views.StuffUserView(111, 'owner@example.com', True),
35 222: framework_views.StuffUserView(222, 'member@example.com', True),
36 999: framework_views.StuffUserView(999, 'visitor@example.com', True),
37 }
38 self.services = service_manager.Services(
39 project=fake.ProjectService(),
40 config=fake.ConfigService(),
41 issue=fake.IssueService(),
42 user=fake.UserService(),
43 usergroup=fake.UserGroupService())
44 self.owner = self.services.user.TestAddUser('owner@example.com', 111)
45 self.member = self.services.user.TestAddUser('member@example.com', 222)
46 self.visitor = self.services.user.TestAddUser('visitor@example.com', 999)
47 self.project = self.services.project.TestAddProject(
48 'proj', owner_ids=[111], committer_ids=[222])
49 self.project.process_inbound_email = True
50 self.issue = fake.MakeTestIssue(
51 self.project.project_id, 1, 'summary', 'New', 111)
52
53 def testEmptyIDs(self):
54 cnxn = 'fake cnxn'
55 addr_perm_list = notify_reasons.ComputeIssueChangeAddressPermList(
56 cnxn, [], self.project, self.issue, self.services, [], {})
57 self.assertEqual([], addr_perm_list)
58
59 def testRecipientIsMember(self):
60 cnxn = 'fake cnxn'
61 ids_to_consider = [111, 222, 999]
62 addr_perm_list = notify_reasons.ComputeIssueChangeAddressPermList(
63 cnxn, ids_to_consider, self.project, self.issue, self.services, set(),
64 self.users_by_id, pref_check_function=lambda *args: True)
65 self.assertEqual(
66 [notify_reasons.AddrPerm(
67 True, 'owner@example.com', self.owner, REPLY_MAY_UPDATE,
68 user_pb2.UserPrefs(user_id=111)),
69 notify_reasons.AddrPerm(
70 True, 'member@example.com', self.member, REPLY_MAY_UPDATE,
71 user_pb2.UserPrefs(user_id=222)),
72 notify_reasons.AddrPerm(
73 False, 'visitor@example.com', self.visitor, REPLY_MAY_COMMENT,
74 user_pb2.UserPrefs(user_id=999))],
75 addr_perm_list)
76
77
78class ComputeProjectAndIssueNotificationAddrListTest(unittest.TestCase):
79
80 def setUp(self):
81 self.cnxn = 'fake cnxn'
82 self.services = service_manager.Services(
83 project=fake.ProjectService(),
84 user=fake.UserService())
85 self.project = self.services.project.TestAddProject('project')
86 self.services.user.TestAddUser('alice@gmail.com', 111)
87 self.services.user.TestAddUser('bob@gmail.com', 222)
88 self.services.user.TestAddUser('fred@gmail.com', 555)
89
90 def testNotifyAddress(self):
91 # No mailing list or filter rules are defined
92 addr_perm_list = notify_reasons.ComputeProjectNotificationAddrList(
93 self.cnxn, self.services, self.project, True, set())
94 self.assertListEqual([], addr_perm_list)
95
96 # Only mailing list is notified.
97 self.project.issue_notify_address = 'mailing-list@domain.com'
98 addr_perm_list = notify_reasons.ComputeProjectNotificationAddrList(
99 self.cnxn, self.services, self.project, True, set())
100 self.assertListEqual(
101 [notify_reasons.AddrPerm(
102 False, 'mailing-list@domain.com', None, REPLY_NOT_ALLOWED,
103 user_pb2.UserPrefs())],
104 addr_perm_list)
105
106 # No one is notified because mailing list was already notified.
107 omit_addrs = {'mailing-list@domain.com'}
108 addr_perm_list = notify_reasons.ComputeProjectNotificationAddrList(
109 self.cnxn, self.services, self.project, False, omit_addrs)
110 self.assertListEqual([], addr_perm_list)
111
112 # No one is notified because anon users cannot view.
113 addr_perm_list = notify_reasons.ComputeProjectNotificationAddrList(
114 self.cnxn, self.services, self.project, False, set())
115 self.assertListEqual([], addr_perm_list)
116
117 def testFilterRuleNotifyAddresses(self):
118 issue = fake.MakeTestIssue(
119 self.project.project_id, 1, 'summary', 'New', 555)
120 issue.derived_notify_addrs.extend(['notify@domain.com'])
121
122 addr_perm_list = notify_reasons.ComputeIssueNotificationAddrList(
123 self.cnxn, self.services, issue, set())
124 self.assertListEqual(
125 [notify_reasons.AddrPerm(
126 False, 'notify@domain.com', None, REPLY_NOT_ALLOWED,
127 user_pb2.UserPrefs())],
128 addr_perm_list)
129
130 # Also-notify addresses can be omitted (e.g., if it is the same as
131 # the email address of the user who made the change).
132 addr_perm_list = notify_reasons.ComputeIssueNotificationAddrList(
133 self.cnxn, self.services, issue, {'notify@domain.com'})
134 self.assertListEqual([], addr_perm_list)
135
136
137class ComputeGroupReasonListTest(unittest.TestCase):
138
139 def setUp(self):
140 self.services = service_manager.Services(
141 project=fake.ProjectService(),
142 config=fake.ConfigService(),
143 issue=fake.IssueService(),
144 features=fake.FeaturesService(),
145 user=fake.UserService(),
146 usergroup=fake.UserGroupService())
147 self.project = self.services.project.TestAddProject(
148 'project', project_id=789)
149 self.config = self.services.config.GetProjectConfig('cnxn', 789)
150 self.alice = self.services.user.TestAddUser('alice@example.com', 111)
151 self.bob = self.services.user.TestAddUser('bob@example.com', 222)
152 self.fred = self.services.user.TestAddUser('fred@example.com', 555)
153 self.users_by_id = framework_views.MakeAllUserViews(
154 'cnxn', self.services.user, [111, 222, 555])
155 self.issue = fake.MakeTestIssue(
156 self.project.project_id, 1, 'summary', 'New', 555)
157
158 def CheckGroupReasonList(
159 self,
160 actual,
161 reporter_apl=None,
162 owner_apl=None,
163 old_owner_apl=None,
164 default_owner_apl=None,
165 ccd_apl=None,
166 group_ccd_apl=None,
167 default_ccd_apl=None,
168 starrer_apl=None,
169 subscriber_apl=None,
170 also_notified_apl=None,
171 all_notifications_apl=None):
172 (
173 you_report, you_own, you_old_owner, you_default_owner, you_ccd,
174 you_group_ccd, you_default_ccd, you_star, you_subscribe,
175 you_also_notify, all_notifications) = actual
176 self.assertEqual(
177 (reporter_apl or [], notify_reasons.REASON_REPORTER),
178 you_report)
179 self.assertEqual(
180 (owner_apl or [], notify_reasons.REASON_OWNER),
181 you_own)
182 self.assertEqual(
183 (old_owner_apl or [], notify_reasons.REASON_OLD_OWNER),
184 you_old_owner)
185 self.assertEqual(
186 (default_owner_apl or [], notify_reasons.REASON_DEFAULT_OWNER),
187 you_default_owner)
188 self.assertEqual(
189 (ccd_apl or [], notify_reasons.REASON_CCD),
190 you_ccd)
191 self.assertEqual(
192 (group_ccd_apl or [], notify_reasons.REASON_GROUP_CCD), you_group_ccd)
193 self.assertEqual(
194 (default_ccd_apl or [], notify_reasons.REASON_DEFAULT_CCD),
195 you_default_ccd)
196 self.assertEqual(
197 (starrer_apl or [], notify_reasons.REASON_STARRER),
198 you_star)
199 self.assertEqual(
200 (subscriber_apl or [], notify_reasons.REASON_SUBSCRIBER),
201 you_subscribe)
202 self.assertEqual(
203 (also_notified_apl or [], notify_reasons.REASON_ALSO_NOTIFY),
204 you_also_notify)
205 self.assertEqual(
206 (all_notifications_apl or [], notify_reasons.REASON_ALL_NOTIFICATIONS),
207 all_notifications)
208
209 def testComputeGroupReasonList_OwnerAndCC(self):
210 """Fred owns the issue, Alice is CC'd."""
211 self.issue.cc_ids = [self.alice.user_id]
212 actual = notify_reasons.ComputeGroupReasonList(
213 'cnxn', self.services, self.project, self.issue, self.config,
214 self.users_by_id, [], True)
215 self.CheckGroupReasonList(
216 actual,
217 owner_apl=[notify_reasons.AddrPerm(
218 False, self.fred.email, self.fred, REPLY_NOT_ALLOWED,
219 user_pb2.UserPrefs(user_id=self.fred.user_id))],
220 ccd_apl=[notify_reasons.AddrPerm(
221 False, self.alice.email, self.alice, REPLY_NOT_ALLOWED,
222 user_pb2.UserPrefs(user_id=self.alice.user_id))])
223
224 def testComputeGroupReasonList_DerivedCcs(self):
225 """Check we correctly compute reasons for component and rule ccs."""
226 member_1 = self.services.user.TestAddUser('member_1@example.com', 991)
227 member_2 = self.services.user.TestAddUser('member_2@example.com', 992)
228 member_3 = self.services.user.TestAddUser('member_3@example.com', 993)
229
230 expanded_group = self.services.user.TestAddUser('group@example.com', 999)
231 self.services.usergroup.CreateGroup(
232 'cnxn', self.services, expanded_group.email, 'owners')
233 self.services.usergroup.TestAddGroupSettings(
234 expanded_group.user_id,
235 expanded_group.email,
236 notify_members=True,
237 notify_group=False)
238 self.services.usergroup.TestAddMembers(
239 expanded_group.user_id, [member_1.user_id, member_2.user_id])
240
241 group = self.services.user.TestAddUser('group_1@example.com', 888)
242 self.services.usergroup.CreateGroup(
243 'cnxn', self.services, group.email, 'owners')
244 self.services.usergroup.TestAddGroupSettings(
245 group.user_id, group.email, notify_members=False, notify_group=True)
246 self.services.usergroup.TestAddMembers(
247 group.user_id, [member_2.user_id, member_3.user_id])
248
249 users_by_id = framework_views.MakeAllUserViews(
250 'cnxn', self.services.user, [
251 self.alice.user_id, self.fred.user_id, member_1.user_id,
252 member_2.user_id, member_3.user_id, group.user_id,
253 expanded_group.user_id
254 ])
255
256 comp_id = 123
257 self.config.component_defs = [
258 fake.MakeTestComponentDef(
259 self.project.project_id,
260 comp_id,
261 path='Chicken',
262 cc_ids=[group.user_id, expanded_group.user_id, self.alice.user_id])
263 ]
264 derived_cc_ids = [
265 self.fred.user_id, # cc'd directly due to a rule
266 self.alice.user_id, # cc'd due to the component
267 expanded_group
268 .user_id, # cc'd due to the component, members notified directly
269 group.user_id, # cc'd due to the component
270 # cc'd directly due to a rule,
271 # not removed from rule cc notifications due to transitive cc of
272 # expanded_group.
273 member_1.user_id,
274 member_3.user_id,
275 ]
276 issue = fake.MakeTestIssue(
277 self.project.project_id,
278 2,
279 'summary',
280 'New',
281 0,
282 derived_cc_ids=derived_cc_ids,
283 component_ids=[comp_id])
284 actual = notify_reasons.ComputeGroupReasonList(
285 'cnxn', self.services, self.project, issue, self.config, users_by_id,
286 [], True)
287
288 # Asserts list/reason of derived ccs from rules (not components).
289 # The derived ccs list/reason is the 7th tuple returned by
290 # ComputeGroupReasonList()
291 actual_ccd_apl, actual_ccd_reason = actual[6]
292 self.assertEqual(
293 actual_ccd_apl, [
294 notify_reasons.AddrPerm(
295 False, member_3.email, member_3, REPLY_NOT_ALLOWED,
296 user_pb2.UserPrefs(user_id=member_3.user_id)),
297 notify_reasons.AddrPerm(
298 False, self.fred.email, self.fred, REPLY_NOT_ALLOWED,
299 user_pb2.UserPrefs(user_id=self.fred.user_id)),
300 notify_reasons.AddrPerm(
301 False, member_1.email, member_1, REPLY_NOT_ALLOWED,
302 user_pb2.UserPrefs(user_id=member_1.user_id)),
303 ])
304 self.assertEqual(actual_ccd_reason, notify_reasons.REASON_DEFAULT_CCD)
305
306 # Asserts list/reason of derived ccs from components.
307 # The component derived ccs list/reason is hte 8th tuple returned by
308 # ComputeGroupReasonList() when there are component derived ccs.
309 actual_component_apl, actual_comp_reason = actual[7]
310 self.assertEqual(
311 actual_component_apl, [
312 notify_reasons.AddrPerm(
313 False, group.email, group, REPLY_NOT_ALLOWED,
314 user_pb2.UserPrefs(user_id=group.user_id)),
315 notify_reasons.AddrPerm(
316 False, self.alice.email, self.alice, REPLY_NOT_ALLOWED,
317 user_pb2.UserPrefs(user_id=self.alice.user_id)),
318 notify_reasons.AddrPerm(
319 False, member_2.email, member_2, REPLY_NOT_ALLOWED,
320 user_pb2.UserPrefs(user_id=member_2.user_id)),
321 notify_reasons.AddrPerm(
322 False, member_1.email, member_1, REPLY_NOT_ALLOWED,
323 user_pb2.UserPrefs(user_id=member_1.user_id)),
324 ])
325 self.assertEqual(
326 actual_comp_reason,
327 "You are auto-CC'd on all issues in component Chicken")
328
329 def testComputeGroupReasonList_Starrers(self):
330 """Bob and Alice starred it, but Alice opts out of notifications."""
331 self.alice.notify_starred_issue_change = False
332 actual = notify_reasons.ComputeGroupReasonList(
333 'cnxn', self.services, self.project, self.issue, self.config,
334 self.users_by_id, [], True,
335 starrer_ids=[self.alice.user_id, self.bob.user_id])
336 self.CheckGroupReasonList(
337 actual,
338 owner_apl=[notify_reasons.AddrPerm(
339 False, self.fred.email, self.fred, REPLY_NOT_ALLOWED,
340 user_pb2.UserPrefs(user_id=self.fred.user_id))],
341 starrer_apl=[notify_reasons.AddrPerm(
342 False, self.bob.email, self.bob, REPLY_NOT_ALLOWED,
343 user_pb2.UserPrefs(user_id=self.bob.user_id))])
344
345 def testComputeGroupReasonList_Subscribers(self):
346 """Bob subscribed."""
347 sq = tracker_bizobj.MakeSavedQuery(
348 1, 'freds issues', 1, 'owner:fred@example.com',
349 subscription_mode='immediate', executes_in_project_ids=[789])
350 self.services.features.UpdateUserSavedQueries(
351 'cnxn', self.bob.user_id, [sq])
352 actual = notify_reasons.ComputeGroupReasonList(
353 'cnxn', self.services, self.project, self.issue, self.config,
354 self.users_by_id, [], True)
355 self.CheckGroupReasonList(
356 actual,
357 owner_apl=[notify_reasons.AddrPerm(
358 False, self.fred.email, self.fred, REPLY_NOT_ALLOWED,
359 user_pb2.UserPrefs(user_id=self.fred.user_id))],
360 subscriber_apl=[notify_reasons.AddrPerm(
361 False, self.bob.email, self.bob, REPLY_NOT_ALLOWED,
362 user_pb2.UserPrefs(user_id=self.bob.user_id))])
363
364 # Now with subscriber notifications disabled.
365 actual = notify_reasons.ComputeGroupReasonList(
366 'cnxn', self.services, self.project, self.issue, self.config,
367 self.users_by_id, [], True, include_subscribers=False)
368 self.CheckGroupReasonList(
369 actual,
370 owner_apl=[notify_reasons.AddrPerm(
371 False, self.fred.email, self.fred, REPLY_NOT_ALLOWED,
372 user_pb2.UserPrefs(user_id=self.fred.user_id))])
373
374 def testComputeGroupReasonList_NotifyAll(self):
375 """Project is configured to always notify issues@example.com."""
376 self.project.issue_notify_address = 'issues@example.com'
377 actual = notify_reasons.ComputeGroupReasonList(
378 'cnxn', self.services, self.project, self.issue, self.config,
379 self.users_by_id, [], True)
380 self.CheckGroupReasonList(
381 actual,
382 owner_apl=[notify_reasons.AddrPerm(
383 False, self.fred.email, self.fred, REPLY_NOT_ALLOWED,
384 user_pb2.UserPrefs(user_id=self.fred.user_id))],
385 all_notifications_apl=[notify_reasons.AddrPerm(
386 False, 'issues@example.com', None, REPLY_NOT_ALLOWED,
387 user_pb2.UserPrefs())])
388
389 # We don't use the notify-all address when the issue is not public.
390 actual = notify_reasons.ComputeGroupReasonList(
391 'cnxn', self.services, self.project, self.issue, self.config,
392 self.users_by_id, [], False)
393 self.CheckGroupReasonList(
394 actual,
395 owner_apl=[notify_reasons.AddrPerm(
396 False, self.fred.email, self.fred, REPLY_NOT_ALLOWED,
397 user_pb2.UserPrefs(user_id=self.fred.user_id))])
398
399 # Now with the notify-all address disabled.
400 actual = notify_reasons.ComputeGroupReasonList(
401 'cnxn', self.services, self.project, self.issue, self.config,
402 self.users_by_id, [], True, include_notify_all=False)
403 self.CheckGroupReasonList(
404 actual,
405 owner_apl=[notify_reasons.AddrPerm(
406 False, self.fred.email, self.fred, REPLY_NOT_ALLOWED,
407 user_pb2.UserPrefs(user_id=self.fred.user_id))])