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