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