blob: 00de1067c669e3342675f03a0990bc162cf442a1 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2016 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.py."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import json
12import mock
13import unittest
14import webapp2
15
16from google.appengine.ext import testbed
17
18from features import notify
19from features import notify_reasons
20from framework import emailfmt
21from framework import urls
22from proto import tracker_pb2
23from services import service_manager
24from testing import fake
25from testing import testing_helpers
26from tracker import attachment_helpers
27from tracker import tracker_bizobj
28
29from third_party import cloudstorage
30
31
32def MakeTestIssue(project_id, local_id, owner_id, reporter_id, is_spam=False):
33 issue = tracker_pb2.Issue()
34 issue.project_id = project_id
35 issue.local_id = local_id
36 issue.issue_id = 1000 * project_id + local_id
37 issue.owner_id = owner_id
38 issue.reporter_id = reporter_id
39 issue.is_spam = is_spam
40 return issue
41
42
43class NotifyTaskHandleRequestTest(unittest.TestCase):
44
45 def setUp(self):
46 self.services = service_manager.Services(
47 user=fake.UserService(),
48 usergroup=fake.UserGroupService(),
49 project=fake.ProjectService(),
50 config=fake.ConfigService(),
51 issue=fake.IssueService(),
52 issue_star=fake.IssueStarService(),
53 features=fake.FeaturesService())
54 self.requester = self.services.user.TestAddUser('requester@example.com', 1)
55 self.nonmember = self.services.user.TestAddUser('user@example.com', 2)
56 self.member = self.services.user.TestAddUser('member@example.com', 3)
57 self.project = self.services.project.TestAddProject(
58 'test-project', owner_ids=[1, 3], project_id=12345)
59 self.issue1 = MakeTestIssue(
60 project_id=12345, local_id=1, owner_id=2, reporter_id=1)
61 self.issue2 = MakeTestIssue(
62 project_id=12345, local_id=2, owner_id=2, reporter_id=1)
63 self.services.issue.TestAddIssue(self.issue1)
64
65 self._old_gcs_open = cloudstorage.open
66 cloudstorage.open = fake.gcs_open
67 self.orig_sign_attachment_id = attachment_helpers.SignAttachmentID
68 attachment_helpers.SignAttachmentID = (
69 lambda aid: 'signed_%d' % aid)
70
71 self.testbed = testbed.Testbed()
72 self.testbed.activate()
73 self.testbed.init_memcache_stub()
74 self.testbed.init_datastore_v3_stub()
75
76 def tearDown(self):
77 cloudstorage.open = self._old_gcs_open
78 attachment_helpers.SignAttachmentID = self.orig_sign_attachment_id
79
80 def get_filtered_task_call_args(self, create_task_mock, relative_uri):
81 return [
82 (args, kwargs)
83 for (args, kwargs) in create_task_mock.call_args_list
84 if args[0]['app_engine_http_request']['relative_uri'] == relative_uri
85 ]
86
87 def VerifyParams(self, result, params):
88 self.assertEqual(
89 bool(params['send_email']), result['params']['send_email'])
90 if 'issue_id' in params:
91 self.assertEqual(params['issue_id'], result['params']['issue_id'])
92 if 'issue_ids' in params:
93 self.assertEqual([int(p) for p in params['issue_ids'].split(',')],
94 result['params']['issue_ids'])
95
96 def testNotifyIssueChangeTask_Normal(self):
97 task = notify.NotifyIssueChangeTask(
98 request=None, response=None, services=self.services)
99 params = {'send_email': 1, 'issue_id': 12345001, 'seq': 0,
100 'commenter_id': 2}
101 mr = testing_helpers.MakeMonorailRequest(
102 user_info={'user_id': 1},
103 params=params,
104 method='POST',
105 services=self.services)
106 result = task.HandleRequest(mr)
107 self.VerifyParams(result, params)
108
109 @mock.patch('framework.cloud_tasks_helpers.create_task')
110 def testNotifyIssueChangeTask_Spam(self, _create_task_mock):
111 issue = MakeTestIssue(
112 project_id=12345, local_id=1, owner_id=1, reporter_id=1,
113 is_spam=True)
114 self.services.issue.TestAddIssue(issue)
115 task = notify.NotifyIssueChangeTask(
116 request=None, response=None, services=self.services)
117 params = {'send_email': 0, 'issue_id': issue.issue_id, 'seq': 0,
118 'commenter_id': 2}
119 mr = testing_helpers.MakeMonorailRequest(
120 user_info={'user_id': 1},
121 params=params,
122 method='POST',
123 services=self.services)
124 result = task.HandleRequest(mr)
125 self.assertEqual(0, len(result['notified']))
126
127 @mock.patch('framework.cloud_tasks_helpers.create_task')
128 def testNotifyBlockingChangeTask_Normal(self, _create_task_mock):
129 issue2 = MakeTestIssue(
130 project_id=12345, local_id=2, owner_id=2, reporter_id=1)
131 self.services.issue.TestAddIssue(issue2)
132 task = notify.NotifyBlockingChangeTask(
133 request=None, response=None, services=self.services)
134 params = {
135 'send_email': 1, 'issue_id': issue2.issue_id, 'seq': 0,
136 'delta_blocker_iids': self.issue1.issue_id, 'commenter_id': 1,
137 'hostport': 'bugs.chromium.org'}
138 mr = testing_helpers.MakeMonorailRequest(
139 user_info={'user_id': 1},
140 params=params,
141 method='POST',
142 services=self.services)
143 result = task.HandleRequest(mr)
144 self.VerifyParams(result, params)
145
146 def testNotifyBlockingChangeTask_Spam(self):
147 issue2 = MakeTestIssue(
148 project_id=12345, local_id=2, owner_id=2, reporter_id=1,
149 is_spam=True)
150 self.services.issue.TestAddIssue(issue2)
151 task = notify.NotifyBlockingChangeTask(
152 request=None, response=None, services=self.services)
153 params = {
154 'send_email': 1, 'issue_id': issue2.issue_id, 'seq': 0,
155 'delta_blocker_iids': self.issue1.issue_id, 'commenter_id': 1}
156 mr = testing_helpers.MakeMonorailRequest(
157 user_info={'user_id': 1},
158 params=params,
159 method='POST',
160 services=self.services)
161 result = task.HandleRequest(mr)
162 self.assertEqual(0, len(result['notified']))
163
164 @mock.patch('framework.cloud_tasks_helpers.create_task')
165 def testNotifyBulkChangeTask_Normal(self, create_task_mock):
166 """We generate email tasks for each user involved in the issues."""
167 issue2 = MakeTestIssue(
168 project_id=12345, local_id=2, owner_id=2, reporter_id=1)
169 issue2.cc_ids = [3]
170 self.services.issue.TestAddIssue(issue2)
171 task = notify.NotifyBulkChangeTask(
172 request=None, response=None, services=self.services)
173 params = {
174 'send_email': 1, 'seq': 0,
175 'issue_ids': '%d,%d' % (self.issue1.issue_id, issue2.issue_id),
176 'old_owner_ids': '1,1', 'commenter_id': 1}
177 mr = testing_helpers.MakeMonorailRequest(
178 user_info={'user_id': 1},
179 params=params,
180 method='POST',
181 services=self.services)
182 result = task.HandleRequest(mr)
183 self.VerifyParams(result, params)
184
185 call_args_list = self.get_filtered_task_call_args(
186 create_task_mock, urls.OUTBOUND_EMAIL_TASK + '.do')
187 self.assertEqual(2, len(call_args_list))
188
189 for (args, _kwargs) in call_args_list:
190 task = args[0]
191 body = json.loads(task['app_engine_http_request']['body'].decode())
192 if 'user' in body['to']:
193 self.assertIn(u'\u2026', body['from_addr'])
194 # Full email for members
195 if 'member' in body['to']:
196 self.assertNotIn(u'\u2026', body['from_addr'])
197
198 @mock.patch('framework.cloud_tasks_helpers.create_task')
199 def testNotifyBulkChangeTask_AlsoNotify(self, create_task_mock):
200 """We generate email tasks for also-notify addresses."""
201 self.issue1.derived_notify_addrs = [
202 'mailing-list@example.com', 'member@example.com']
203 task = notify.NotifyBulkChangeTask(
204 request=None, response=None, services=self.services)
205 params = {
206 'send_email': 1, 'seq': 0,
207 'issue_ids': '%d' % (self.issue1.issue_id),
208 'old_owner_ids': '1', 'commenter_id': 1}
209 mr = testing_helpers.MakeMonorailRequest(
210 user_info={'user_id': 1},
211 params=params,
212 method='POST',
213 services=self.services)
214 result = task.HandleRequest(mr)
215 self.VerifyParams(result, params)
216
217 call_args_list = self.get_filtered_task_call_args(
218 create_task_mock, urls.OUTBOUND_EMAIL_TASK + '.do')
219 self.assertEqual(3, len(call_args_list))
220
221 self.assertItemsEqual(
222 ['user@example.com', 'mailing-list@example.com', 'member@example.com'],
223 result['notified'])
224 for (args, _kwargs) in call_args_list:
225 task = args[0]
226 body = json.loads(task['app_engine_http_request']['body'].decode())
227 # obfuscated email for non-members
228 if 'user' in body['to']:
229 self.assertIn(u'\u2026', body['from_addr'])
230 # Full email for members
231 if 'member' in body['to']:
232 self.assertNotIn(u'\u2026', body['from_addr'])
233
234 @mock.patch('framework.cloud_tasks_helpers.create_task')
235 def testNotifyBulkChangeTask_ProjectNotify(self, create_task_mock):
236 """We generate email tasks for project.issue_notify_address."""
237 self.project.issue_notify_address = 'mailing-list@example.com'
238 task = notify.NotifyBulkChangeTask(
239 request=None, response=None, services=self.services)
240 params = {
241 'send_email': 1, 'seq': 0,
242 'issue_ids': '%d' % (self.issue1.issue_id),
243 'old_owner_ids': '1', 'commenter_id': 1}
244 mr = testing_helpers.MakeMonorailRequest(
245 user_info={'user_id': 1},
246 params=params,
247 method='POST',
248 services=self.services)
249 result = task.HandleRequest(mr)
250 self.VerifyParams(result, params)
251
252 call_args_list = self.get_filtered_task_call_args(
253 create_task_mock, urls.OUTBOUND_EMAIL_TASK + '.do')
254 self.assertEqual(2, len(call_args_list))
255
256 self.assertItemsEqual(
257 ['user@example.com', 'mailing-list@example.com'],
258 result['notified'])
259
260 for (args, _kwargs) in call_args_list:
261 task = args[0]
262 body = json.loads(task['app_engine_http_request']['body'].decode())
263 # obfuscated email for non-members
264 if 'user' in body['to']:
265 self.assertIn(u'\u2026', body['from_addr'])
266 # Full email for members
267 if 'member' in body['to']:
268 self.assertNotIn(u'\u2026', body['from_addr'])
269
270 @mock.patch('framework.cloud_tasks_helpers.create_task')
271 def testNotifyBulkChangeTask_SubscriberGetsEmail(self, create_task_mock):
272 """If a user subscription matches the issue, notify that user."""
273 task = notify.NotifyBulkChangeTask(
274 request=None, response=None, services=self.services)
275 params = {
276 'send_email': 1,
277 'issue_ids': '%d' % (self.issue1.issue_id),
278 'seq': 0,
279 'old_owner_ids': '1', 'commenter_id': 1}
280 mr = testing_helpers.MakeMonorailRequest(
281 user_info={'user_id': 1},
282 params=params,
283 method='POST',
284 services=self.services)
285 self.services.user.TestAddUser('subscriber@example.com', 4)
286 sq = tracker_bizobj.MakeSavedQuery(
287 1, 'all open issues', 2, '', subscription_mode='immediate',
288 executes_in_project_ids=[self.issue1.project_id])
289 self.services.features.UpdateUserSavedQueries('cnxn', 4, [sq])
290 result = task.HandleRequest(mr)
291 self.VerifyParams(result, params)
292
293 call_args_list = self.get_filtered_task_call_args(
294 create_task_mock, urls.OUTBOUND_EMAIL_TASK + '.do')
295 self.assertEqual(2, len(call_args_list))
296
297 @mock.patch('framework.cloud_tasks_helpers.create_task')
298 def testNotifyBulkChangeTask_CCAndSubscriberListsIssueOnce(
299 self, create_task_mock):
300 """If a user both CCs and subscribes, include issue only once."""
301 task = notify.NotifyBulkChangeTask(
302 request=None, response=None, services=self.services)
303 params = {
304 'send_email': 1,
305 'issue_ids': '%d' % (self.issue1.issue_id),
306 'seq': 0,
307 'old_owner_ids': '1', 'commenter_id': 1}
308 mr = testing_helpers.MakeMonorailRequest(
309 user_info={'user_id': 1},
310 params=params,
311 method='POST',
312 services=self.services)
313 self.services.user.TestAddUser('subscriber@example.com', 4)
314 self.issue1.cc_ids = [4]
315 sq = tracker_bizobj.MakeSavedQuery(
316 1, 'all open issues', 2, '', subscription_mode='immediate',
317 executes_in_project_ids=[self.issue1.project_id])
318 self.services.features.UpdateUserSavedQueries('cnxn', 4, [sq])
319 result = task.HandleRequest(mr)
320 self.VerifyParams(result, params)
321
322 call_args_list = self.get_filtered_task_call_args(
323 create_task_mock, urls.OUTBOUND_EMAIL_TASK + '.do')
324 self.assertEqual(2, len(call_args_list))
325
326 found = False
327 for (args, _kwargs) in call_args_list:
328 task = args[0]
329 body = json.loads(task['app_engine_http_request']['body'].decode())
330 if body['to'] == 'subscriber@example.com':
331 found = True
332 task_body = body['body']
333 self.assertEqual(1, task_body.count('Issue %d' % self.issue1.local_id))
334 self.assertTrue(found)
335
336 @mock.patch('framework.cloud_tasks_helpers.create_task')
337 def testNotifyBulkChangeTask_Spam(self, _create_task_mock):
338 """A spam issue is excluded from notification emails."""
339 issue2 = MakeTestIssue(
340 project_id=12345, local_id=2, owner_id=2, reporter_id=1,
341 is_spam=True)
342 self.services.issue.TestAddIssue(issue2)
343 task = notify.NotifyBulkChangeTask(
344 request=None, response=None, services=self.services)
345 params = {
346 'send_email': 1,
347 'issue_ids': '%d,%d' % (self.issue1.issue_id, issue2.issue_id),
348 'seq': 0,
349 'old_owner_ids': '1,1', 'commenter_id': 1}
350 mr = testing_helpers.MakeMonorailRequest(
351 user_info={'user_id': 1},
352 params=params,
353 method='POST',
354 services=self.services)
355 result = task.HandleRequest(mr)
356 self.assertEqual(1, len(result['notified']))
357
358 def testFormatBulkIssues_Normal_Single(self):
359 """A user may see full notification details for all changed issues."""
360 self.issue1.summary = 'one summary'
361 task = notify.NotifyBulkChangeTask(
362 request=None, response=None, services=self.services)
363 users_by_id = {}
364 commenter_view = None
365 config = self.services.config.GetProjectConfig('cnxn', 12345)
366 addrperm = notify_reasons.AddrPerm(
367 False, 'nonmember@example.com', self.nonmember,
368 notify_reasons.REPLY_NOT_ALLOWED, None)
369
370 subject, body = task._FormatBulkIssues(
371 [self.issue1], users_by_id, commenter_view, 'localhost:8080',
372 'test comment', [], config, addrperm)
373
374 self.assertIn('one summary', subject)
375 self.assertIn('one summary', body)
376 self.assertIn('test comment', body)
377
378 def testFormatBulkIssues_Normal_Multiple(self):
379 """A user may see full notification details for all changed issues."""
380 self.issue1.summary = 'one summary'
381 self.issue2.summary = 'two summary'
382 task = notify.NotifyBulkChangeTask(
383 request=None, response=None, services=self.services)
384 users_by_id = {}
385 commenter_view = None
386 config = self.services.config.GetProjectConfig('cnxn', 12345)
387 addrperm = notify_reasons.AddrPerm(
388 False, 'nonmember@example.com', self.nonmember,
389 notify_reasons.REPLY_NOT_ALLOWED, None)
390
391 subject, body = task._FormatBulkIssues(
392 [self.issue1, self.issue2], users_by_id, commenter_view, 'localhost:8080',
393 'test comment', [], config, addrperm)
394
395 self.assertIn('2 issues changed', subject)
396 self.assertIn('one summary', body)
397 self.assertIn('two summary', body)
398 self.assertIn('test comment', body)
399
400 def testFormatBulkIssues_LinkOnly_Single(self):
401 """A user may not see full notification details for some changed issue."""
402 self.issue1.summary = 'one summary'
403 self.issue1.labels = ['Restrict-View-Google']
404 task = notify.NotifyBulkChangeTask(
405 request=None, response=None, services=self.services)
406 users_by_id = {}
407 commenter_view = None
408 config = self.services.config.GetProjectConfig('cnxn', 12345)
409 addrperm = notify_reasons.AddrPerm(
410 False, 'nonmember@example.com', self.nonmember,
411 notify_reasons.REPLY_NOT_ALLOWED, None)
412
413 subject, body = task._FormatBulkIssues(
414 [self.issue1], users_by_id, commenter_view, 'localhost:8080',
415 'test comment', [], config, addrperm)
416
417 self.assertIn('issue 1', subject)
418 self.assertNotIn('one summary', subject)
419 self.assertNotIn('one summary', body)
420 self.assertNotIn('test comment', body)
421
422 def testFormatBulkIssues_LinkOnly_Multiple(self):
423 """A user may not see full notification details for some changed issue."""
424 self.issue1.summary = 'one summary'
425 self.issue1.labels = ['Restrict-View-Google']
426 self.issue2.summary = 'two summary'
427 task = notify.NotifyBulkChangeTask(
428 request=None, response=None, services=self.services)
429 users_by_id = {}
430 commenter_view = None
431 config = self.services.config.GetProjectConfig('cnxn', 12345)
432 addrperm = notify_reasons.AddrPerm(
433 False, 'nonmember@example.com', self.nonmember,
434 notify_reasons.REPLY_NOT_ALLOWED, None)
435
436 subject, body = task._FormatBulkIssues(
437 [self.issue1, self.issue2], users_by_id, commenter_view, 'localhost:8080',
438 'test comment', [], config, addrperm)
439
440 self.assertIn('2 issues', subject)
441 self.assertNotIn('summary', subject)
442 self.assertNotIn('one summary', body)
443 self.assertIn('two summary', body)
444 self.assertNotIn('test comment', body)
445
446 @mock.patch('framework.cloud_tasks_helpers.create_task')
447 def testNotifyApprovalChangeTask_Normal(self, _create_task_mock):
448 config = self.services.config.GetProjectConfig('cnxn', 12345)
449 config.field_defs = [
450 # issue's User field with any_comment is notified.
451 tracker_bizobj.MakeFieldDef(
452 121, 12345, 'TL', tracker_pb2.FieldTypes.USER_TYPE,
453 '', '', False, False, False, None, None, None, False, '',
454 None, tracker_pb2.NotifyTriggers.ANY_COMMENT, 'no_action',
455 'TL, notified on everything', False),
456 # issue's User field with never is not notified.
457 tracker_bizobj.MakeFieldDef(
458 122, 12345, 'silentTL', tracker_pb2.FieldTypes.USER_TYPE,
459 '', '', False, False, False, None, None, None, False, '',
460 None, tracker_pb2.NotifyTriggers.NEVER, 'no_action',
461 'TL, notified on nothing', False),
462 # approval's User field with any_comment is notified.
463 tracker_bizobj.MakeFieldDef(
464 123, 12345, 'otherapprovalTL', tracker_pb2.FieldTypes.USER_TYPE,
465 '', '', False, False, False, None, None, None, False, '',
466 None, tracker_pb2.NotifyTriggers.ANY_COMMENT, 'no_action',
467 'TL on the approvers team', False, approval_id=3),
468 # another approval's User field with any_comment is not notified.
469 tracker_bizobj.MakeFieldDef(
470 124, 12345, 'otherapprovalTL', tracker_pb2.FieldTypes.USER_TYPE,
471 '', '', False, False, False, None, None, None, False, '',
472 None, tracker_pb2.NotifyTriggers.ANY_COMMENT, 'no_action',
473 'TL on another approvers team', False, approval_id=4),
474 tracker_bizobj.MakeFieldDef(
475 3, 12345, 'Goat-Approval', tracker_pb2.FieldTypes.APPROVAL_TYPE,
476 '', '', False, False, False, None, None, None, False, '',
477 None, tracker_pb2.NotifyTriggers.NEVER, 'no_action',
478 'Get Approval from Goats', False)
479 ]
480 self.services.config.StoreConfig('cnxn', config)
481
482 # Custom user_type field TLs
483 self.services.user.TestAddUser('TL@example.com', 111)
484 self.services.user.TestAddUser('silentTL@example.com', 222)
485 self.services.user.TestAddUser('approvalTL@example.com', 333)
486 self.services.user.TestAddUser('otherapprovalTL@example.com', 444)
487
488 # Approvers
489 self.services.user.TestAddUser('approver_old@example.com', 777)
490 self.services.user.TestAddUser('approver_new@example.com', 888)
491 self.services.user.TestAddUser('approver_still@example.com', 999)
492 self.services.user.TestAddUser('approver_group@example.com', 666)
493 self.services.user.TestAddUser('group_mem1@example.com', 661)
494 self.services.user.TestAddUser('group_mem2@example.com', 662)
495 self.services.user.TestAddUser('group_mem3@example.com', 663)
496 self.services.usergroup.TestAddGroupSettings(
497 666, 'approver_group@example.com')
498 self.services.usergroup.TestAddMembers(666, [661, 662, 663])
499 canary_phase = tracker_pb2.Phase(
500 name='Canary', phase_id=1, rank=1)
501 approval_values = [
502 tracker_pb2.ApprovalValue(approval_id=3,
503 approver_ids=[888, 999, 666, 661])]
504 approval_issue = MakeTestIssue(
505 project_id=12345, local_id=2, owner_id=2, reporter_id=1,
506 is_spam=True)
507 approval_issue.phases = [canary_phase]
508 approval_issue.approval_values = approval_values
509 approval_issue.field_values = [
510 tracker_bizobj.MakeFieldValue(121, None, None, 111, None, None, False),
511 tracker_bizobj.MakeFieldValue(122, None, None, 222, None, None, False),
512 tracker_bizobj.MakeFieldValue(123, None, None, 333, None, None, False),
513 tracker_bizobj.MakeFieldValue(124, None, None, 444, None, None, False),
514 ]
515 self.services.issue.TestAddIssue(approval_issue)
516
517 amend = tracker_bizobj.MakeApprovalApproversAmendment([888], [777])
518
519 comment = tracker_pb2.IssueComment(
520 project_id=12345, user_id=999, issue_id=approval_issue.issue_id,
521 amendments=[amend], timestamp=1234567890, content='just a comment.')
522 attach = tracker_pb2.Attachment(
523 attachment_id=4567, filename='sploot.jpg', mimetype='image/png',
524 gcs_object_id='/pid/attachments/abcd', filesize=(1024 * 1023))
525 comment.attachments.append(attach)
526 self.services.issue.TestAddComment(comment, approval_issue.local_id)
527 self.services.issue.TestAddAttachment(
528 attach, comment.id, approval_issue.issue_id)
529
530 task = notify.NotifyApprovalChangeTask(
531 request=None, response=None, services=self.services)
532 params = {
533 'send_email': 1,
534 'issue_id': approval_issue.issue_id,
535 'approval_id': 3,
536 'comment_id': comment.id,
537 }
538 mr = testing_helpers.MakeMonorailRequest(
539 user_info={'user_id': 1},
540 params=params,
541 method='POST',
542 services=self.services)
543 result = task.HandleRequest(mr)
544 self.assertTrue('just a comment' in result['tasks'][0]['body'])
545 self.assertTrue('Approvers: -appro...' in result['tasks'][0]['body'])
546 self.assertTrue('sploot.jpg' in result['tasks'][0]['body'])
547 self.assertTrue(
548 '/issues/attachment?aid=4567' in result['tasks'][0]['body'])
549 self.assertItemsEqual(
550 ['user@example.com', 'approver_old@example.com',
551 'approver_new@example.com', 'TL@example.com',
552 'approvalTL@example.com', 'group_mem1@example.com',
553 'group_mem2@example.com', 'group_mem3@example.com'],
554 result['notified'])
555
556 # Test no approvers/groups notified
557 # Status change to NEED_INFO does not email approvers.
558 amend2 = tracker_bizobj.MakeApprovalStatusAmendment(
559 tracker_pb2.ApprovalStatus.NEED_INFO)
560 comment2 = tracker_pb2.IssueComment(
561 project_id=12345, user_id=999, issue_id=approval_issue.issue_id,
562 amendments=[amend2], timestamp=1234567891, content='')
563 self.services.issue.TestAddComment(comment2, approval_issue.local_id)
564 task = notify.NotifyApprovalChangeTask(
565 request=None, response=None, services=self.services)
566 params = {
567 'send_email': 1,
568 'issue_id': approval_issue.issue_id,
569 'approval_id': 3,
570 'comment_id': comment2.id,
571 }
572 mr = testing_helpers.MakeMonorailRequest(
573 user_info={'user_id': 1},
574 params=params,
575 method='POST',
576 services=self.services)
577 result = task.HandleRequest(mr)
578
579 self.assertIsNotNone(result['tasks'][0].get('references'))
580 self.assertEqual(result['tasks'][0]['reply_to'], emailfmt.NoReplyAddress())
581 self.assertTrue('Status: need_info' in result['tasks'][0]['body'])
582 self.assertItemsEqual(
583 ['user@example.com', 'TL@example.com', 'approvalTL@example.com'],
584 result['notified'])
585
586 def testNotifyApprovalChangeTask_GetApprovalEmailRecipients(self):
587 task = notify.NotifyApprovalChangeTask(
588 request=None, response=None, services=self.services)
589 issue = fake.MakeTestIssue(789, 1, 'summary', 'New', 111)
590 approval_value = tracker_pb2.ApprovalValue(
591 approver_ids=[222, 333],
592 status=tracker_pb2.ApprovalStatus.APPROVED)
593 comment = tracker_pb2.IssueComment(
594 project_id=789, user_id=1, issue_id=78901)
595
596 # Comment with not amendments notifies everyone.
597 rids = task._GetApprovalEmailRecipients(
598 approval_value, comment, issue, [777, 888])
599 self.assertItemsEqual(rids, [111, 222, 333, 777, 888])
600
601 # New APPROVED status notifies owners and any_comment users.
602 amendment = tracker_bizobj.MakeApprovalStatusAmendment(
603 tracker_pb2.ApprovalStatus.APPROVED)
604 comment.amendments = [amendment]
605 rids = task._GetApprovalEmailRecipients(
606 approval_value, comment, issue, [777, 888])
607 self.assertItemsEqual(rids, [111, 777, 888])
608
609 # New REVIEW_REQUESTED status notifies approvers.
610 approval_value.status = tracker_pb2.ApprovalStatus.REVIEW_REQUESTED
611 amendment = tracker_bizobj.MakeApprovalStatusAmendment(
612 tracker_pb2.ApprovalStatus.REVIEW_REQUESTED)
613 comment.amendments = [amendment]
614 rids = task._GetApprovalEmailRecipients(
615 approval_value, comment, issue, [777, 888])
616 self.assertItemsEqual(rids, [222, 333])
617
618 # Approvers change notifies everyone.
619 amendment = tracker_bizobj.MakeApprovalApproversAmendment(
620 [222], [555])
621 comment.amendments = [amendment]
622 approval_value.approver_ids = [222]
623 rids = task._GetApprovalEmailRecipients(
624 approval_value, comment, issue, [777], omit_ids=[444, 333])
625 self.assertItemsEqual(rids, [111, 222, 555, 777])
626
627 @mock.patch('framework.cloud_tasks_helpers.create_task')
628 def testNotifyRulesDeletedTask(self, _create_task_mock):
629 self.services.project.TestAddProject(
630 'proj', owner_ids=[777, 888], project_id=789)
631 self.services.user.TestAddUser('owner1@test.com', 777)
632 self.services.user.TestAddUser('cow@test.com', 888)
633 task = notify.NotifyRulesDeletedTask(
634 request=None, response=None, services=self.services)
635 params = {'project_id': 789,
636 'filter_rules': 'if green make yellow,if orange make blue'}
637 mr = testing_helpers.MakeMonorailRequest(
638 params=params,
639 method='POST',
640 services=self.services)
641 result = task.HandleRequest(mr)
642 self.assertEqual(len(result['tasks']), 2)
643 body = result['tasks'][0]['body']
644 self.assertTrue('if green make yellow' in body)
645 self.assertTrue('if green make yellow' in body)
646 self.assertTrue('/p/proj/adminRules' in body)
647 self.assertItemsEqual(
648 ['cow@test.com', 'owner1@test.com'], result['notified'])
649
650 def testOutboundEmailTask_Normal(self):
651 """We can send an email."""
652 params = {
653 'from_addr': 'requester@example.com',
654 'reply_to': 'user@example.com',
655 'to': 'user@example.com',
656 'subject': 'Test subject'}
657 body = json.dumps(params)
658 request = webapp2.Request.blank('/', body=body)
659 task = notify.OutboundEmailTask(
660 request=request, response=None, services=self.services)
661 mr = testing_helpers.MakeMonorailRequest(
662 user_info={'user_id': 1},
663 payload=body,
664 method='POST',
665 services=self.services)
666 result = task.HandleRequest(mr)
667 self.assertEqual(params['from_addr'], result['sender'])
668 self.assertEqual(params['subject'], result['subject'])
669
670 def testOutboundEmailTask_MissingTo(self):
671 """We skip emails that don't specify the To-line."""
672 params = {
673 'from_addr': 'requester@example.com',
674 'reply_to': 'user@example.com',
675 'subject': 'Test subject'}
676 body = json.dumps(params)
677 request = webapp2.Request.blank('/', body=body)
678 task = notify.OutboundEmailTask(
679 request=request, response=None, services=self.services)
680 mr = testing_helpers.MakeMonorailRequest(
681 user_info={'user_id': 1},
682 payload=body,
683 method='POST',
684 services=self.services)
685 result = task.HandleRequest(mr)
686 self.assertEqual('Skipping because no "to" address found.', result['note'])
687 self.assertNotIn('from_addr', result)
688
689 def testOutboundEmailTask_BannedUser(self):
690 """We don't send emails to banned users.."""
691 params = {
692 'from_addr': 'requester@example.com',
693 'reply_to': 'user@example.com',
694 'to': 'banned@example.com',
695 'subject': 'Test subject'}
696 body = json.dumps(params)
697 request = webapp2.Request.blank('/', body=body)
698 task = notify.OutboundEmailTask(
699 request=request, response=None, services=self.services)
700 mr = testing_helpers.MakeMonorailRequest(
701 user_info={'user_id': 1},
702 payload=body,
703 method='POST',
704 services=self.services)
705 self.services.user.TestAddUser('banned@example.com', 404, banned=True)
706 result = task.HandleRequest(mr)
707 self.assertEqual('Skipping because user is banned.', result['note'])
708 self.assertNotIn('from_addr', result)