| Index: appengine/monorail/features/test/notify_helpers_test.py
|
| diff --git a/appengine/monorail/features/test/notify_helpers_test.py b/appengine/monorail/features/test/notify_helpers_test.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7267269e93b76f646ff2d24c3462025de81a6fb3
|
| --- /dev/null
|
| +++ b/appengine/monorail/features/test/notify_helpers_test.py
|
| @@ -0,0 +1,384 @@
|
| +# -*- coding: utf8 -*-
|
| +# Copyright 2016 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is govered by a BSD-style
|
| +# license that can be found in the LICENSE file or at
|
| +# https://developers.google.com/open-source/licenses/bsd
|
| +
|
| +"""Tests for notify_helpers.py."""
|
| +
|
| +import unittest
|
| +
|
| +from features import notify_helpers
|
| +from framework import emailfmt
|
| +from framework import framework_views
|
| +from services import service_manager
|
| +from testing import fake
|
| +
|
| +
|
| +REPLY_NOT_ALLOWED = notify_helpers.REPLY_NOT_ALLOWED
|
| +REPLY_MAY_COMMENT = notify_helpers.REPLY_MAY_COMMENT
|
| +REPLY_MAY_UPDATE = notify_helpers.REPLY_MAY_UPDATE
|
| +
|
| +
|
| +class ComputeIssueChangeAddressPermListTest(unittest.TestCase):
|
| +
|
| + def setUp(self):
|
| + self.users_by_id = {
|
| + 111L: framework_views.UserView(111L, 'owner@example.com', True),
|
| + 222L: framework_views.UserView(222L, 'member@example.com', True),
|
| + 999L: framework_views.UserView(999L, 'visitor@example.com', True),
|
| + }
|
| + self.services = service_manager.Services(
|
| + project=fake.ProjectService(),
|
| + config=fake.ConfigService(),
|
| + issue=fake.IssueService(),
|
| + user=fake.UserService(),
|
| + usergroup=fake.UserGroupService())
|
| + self.services.user.TestAddUser('owner@example.com', 111L)
|
| + self.services.user.TestAddUser('member@example.com', 222L)
|
| + self.services.user.TestAddUser('visitor@example.com', 999L)
|
| + self.project = self.services.project.TestAddProject(
|
| + 'proj', owner_ids=[111L], committer_ids=[222L])
|
| + self.project.process_inbound_email = True
|
| + self.issue = fake.MakeTestIssue(
|
| + self.project.project_id, 1, 'summary', 'New', 111L)
|
| +
|
| + def testEmptyIDs(self):
|
| + cnxn = 'fake cnxn'
|
| + addr_perm_list = notify_helpers.ComputeIssueChangeAddressPermList(
|
| + cnxn, [], self.project, self.issue, self.services, [], {},
|
| + pref_check_function=lambda *args: True)
|
| + self.assertEqual([], addr_perm_list)
|
| +
|
| + def testRecipientIsMember(self):
|
| + cnxn = 'fake cnxn'
|
| + ids_to_consider = [111L, 222L, 999L]
|
| + addr_perm_list = notify_helpers.ComputeIssueChangeAddressPermList(
|
| + cnxn, ids_to_consider, self.project, self.issue, self.services, set(),
|
| + self.users_by_id, pref_check_function=lambda *args: True)
|
| + self.assertEqual(
|
| + [(True, 'owner@example.com', REPLY_MAY_UPDATE),
|
| + (True, 'member@example.com', REPLY_MAY_UPDATE),
|
| + (False, 'visitor@example.com', REPLY_MAY_COMMENT)],
|
| + addr_perm_list)
|
| +
|
| +
|
| +class ComputeProjectAndIssueNotificationAddrListTest(unittest.TestCase):
|
| +
|
| + def setUp(self):
|
| + self.services = service_manager.Services(
|
| + project=fake.ProjectService(),
|
| + user=fake.UserService())
|
| + self.project = self.services.project.TestAddProject('project')
|
| + self.services.user.TestAddUser('alice@gmail.com', 111L)
|
| + self.services.user.TestAddUser('bob@gmail.com', 222L)
|
| + self.services.user.TestAddUser('fred@gmail.com', 555L)
|
| +
|
| + def testNotifyAddress(self):
|
| + # No mailing list or filter rules are defined
|
| + addr_perm_list = notify_helpers.ComputeProjectNotificationAddrList(
|
| + self.project, True, set())
|
| + self.assertListEqual([], addr_perm_list)
|
| +
|
| + # Only mailing list is notified.
|
| + self.project.issue_notify_address = 'mailing-list@domain.com'
|
| + addr_perm_list = notify_helpers.ComputeProjectNotificationAddrList(
|
| + self.project, True, set())
|
| + self.assertListEqual(
|
| + [(False, 'mailing-list@domain.com', REPLY_NOT_ALLOWED)],
|
| + addr_perm_list)
|
| +
|
| + # No one is notified because mailing list was already notified.
|
| + omit_addrs = {'mailing-list@domain.com'}
|
| + addr_perm_list = notify_helpers.ComputeProjectNotificationAddrList(
|
| + self.project, False, omit_addrs)
|
| + self.assertListEqual([], addr_perm_list)
|
| +
|
| + # No one is notified because anon users cannot view.
|
| + addr_perm_list = notify_helpers.ComputeProjectNotificationAddrList(
|
| + self.project, False, set())
|
| + self.assertListEqual([], addr_perm_list)
|
| +
|
| + def testFilterRuleNotifyAddresses(self):
|
| + issue = fake.MakeTestIssue(
|
| + self.project.project_id, 1, 'summary', 'New', 555L)
|
| + issue.derived_notify_addrs.extend(['notify@domain.com'])
|
| +
|
| + addr_perm_list = notify_helpers.ComputeIssueNotificationAddrList(
|
| + issue, set())
|
| + self.assertListEqual(
|
| + [(False, 'notify@domain.com', REPLY_NOT_ALLOWED)],
|
| + addr_perm_list)
|
| +
|
| + # Also-notify addresses can be omitted (e.g., if it is the same as
|
| + # the email address of the user who made the change).
|
| + addr_perm_list = notify_helpers.ComputeIssueNotificationAddrList(
|
| + issue, {'notify@domain.com'})
|
| + self.assertListEqual([], addr_perm_list)
|
| +
|
| +
|
| +class MakeBulletedEmailWorkItemsTest(unittest.TestCase):
|
| +
|
| + def setUp(self):
|
| + self.project = fake.Project(project_name='proj1')
|
| + self.commenter_view = framework_views.UserView(
|
| + 111L, 'test@example.com', True)
|
| +
|
| + def testEmptyAddrs(self):
|
| + """Test the case where we found zero users to notify."""
|
| + email_tasks = notify_helpers.MakeBulletedEmailWorkItems(
|
| + [], 'subject', 'body', 'body', self.project, 'example.com',
|
| + self.commenter_view)
|
| + self.assertEqual([], email_tasks)
|
| + email_tasks = notify_helpers.MakeBulletedEmailWorkItems(
|
| + [([], 'reason')], 'subject', 'body', 'body', self.project,
|
| + 'example.com', self.commenter_view)
|
| + self.assertEqual([], email_tasks)
|
| +
|
| +
|
| +class MakeEmailWorkItemTest(unittest.TestCase):
|
| +
|
| + def setUp(self):
|
| + self.project = fake.Project(project_name='proj1')
|
| + self.project.process_inbound_email = True
|
| + self.commenter_view = framework_views.UserView(
|
| + 111L, 'test@example.com', True)
|
| + self.expected_html_footer = (
|
| + 'You received this message because:<br/> 1. reason<br/><br/>You may '
|
| + 'adjust your notification preferences at:<br/><a href="https://'
|
| + 'example.com/hosting/settings">https://example.com/hosting/settings'
|
| + '</a>')
|
| +
|
| + def testBodySelection(self):
|
| + """We send non-members the email body that is indented for non-members."""
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (False, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', 'body non', 'body mem', self.project,
|
| + 'example.com', self.commenter_view)
|
| +
|
| + self.assertEqual('a@a.com', email_task['to'])
|
| + self.assertEqual('subject', email_task['subject'])
|
| + self.assertIn('body non', email_task['body'])
|
| + self.assertEqual(
|
| + emailfmt.FormatFromAddr(self.project, commenter_view=self.commenter_view,
|
| + can_reply_to=False),
|
| + email_task['from_addr'])
|
| + self.assertEqual(emailfmt.NoReplyAddress(), email_task['reply_to'])
|
| +
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (True, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', 'body mem', 'body mem', self.project,
|
| + 'example.com', self.commenter_view)
|
| + self.assertIn('body mem', email_task['body'])
|
| +
|
| + def testHtmlBody_NoDetailUrl(self):
|
| + """"An html body is not be sent if detail_url is not specified."""
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (False, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', 'body non', 'body mem', self.project,
|
| + 'example.com', self.commenter_view, detail_url=None)
|
| +
|
| + self.assertIsNone(email_task['html_body'])
|
| +
|
| + def testHtmlBody_WithDetailUrl(self):
|
| + """"An html body is sent if a detail_url is specified."""
|
| + detail_url = 'http://test-detail-url.com/id=1234'
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (False, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', 'body non', 'body mem', self.project,
|
| + 'example.com', self.commenter_view, detail_url=detail_url)
|
| +
|
| + expected_html_body = (
|
| + notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % (
|
| + detail_url,
|
| + 'body non-- <br/>%s' % self.expected_html_footer))
|
| + self.assertEquals(expected_html_body, email_task['html_body'])
|
| +
|
| + def testHtmlBody_WithUnicodeChars(self):
|
| + """"An html body is sent if a detail_url is specified."""
|
| + detail_url = 'http://test-detail-url.com/id=1234'
|
| + unicode_content = '\xe2\x9d\xa4 â â'
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (False, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', unicode_content, 'unused body mem',
|
| + self.project, 'example.com', self.commenter_view, detail_url=detail_url)
|
| +
|
| + expected_html_body = (
|
| + notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % (
|
| + detail_url,
|
| + '%s-- <br/>%s' % (unicode_content.decode('utf-8'),
|
| + self.expected_html_footer)))
|
| + self.assertEquals(expected_html_body, email_task['html_body'])
|
| +
|
| + def testHtmlBody_WithLinks(self):
|
| + """"An html body is sent if a detail_url is specified."""
|
| + detail_url = 'http://test-detail-url.com/id=1234'
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (False, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', 'test google.com test', 'unused body mem',
|
| + self.project, 'example.com', self.commenter_view, detail_url=detail_url)
|
| +
|
| + expected_html_body = (
|
| + notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % (
|
| + detail_url,
|
| + 'test <a href="http://google.com">google.com</a> test-- <br/>%s' % (
|
| + self.expected_html_footer)))
|
| + self.assertEquals(expected_html_body, email_task['html_body'])
|
| +
|
| + def testHtmlBody_LinkWithinTags(self):
|
| + """"An html body is sent with correct <a href>s."""
|
| + detail_url = 'http://test-detail-url.com/id=1234'
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (False, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', 'test <http://google.com> test', 'unused body',
|
| + self.project, 'example.com', self.commenter_view, detail_url=detail_url)
|
| +
|
| + expected_html_body = (
|
| + notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % (
|
| + detail_url,
|
| + 'test <a href="http://google.com"><http://google.com></a> '
|
| + 'test-- <br/>%s' % self.expected_html_footer))
|
| + self.assertEquals(expected_html_body, email_task['html_body'])
|
| +
|
| + def testHtmlBody_EmailWithinTags(self):
|
| + """"An html body is sent with correct <a href>s."""
|
| + detail_url = 'http://test-detail-url.com/id=1234'
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (False, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', 'test <t@chromium.org> <a@chromium.org> test',
|
| + 'unused body mem', self.project, 'example.com', self.commenter_view,
|
| + detail_url=detail_url)
|
| +
|
| + expected_html_body = (
|
| + notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % (
|
| + detail_url,
|
| + 'test <a href="mailto:t@chromium.org"><t@chromium.org></a> '
|
| + '<a href="mailto:a@chromium.org"><a@chromium.org></a> '
|
| + 'test-- <br/>%s' % self.expected_html_footer))
|
| + self.assertEquals(expected_html_body, email_task['html_body'])
|
| +
|
| + def testHtmlBody_WithEscapedHtml(self):
|
| + """"An html body is sent with html content escaped."""
|
| + detail_url = 'http://test-detail-url.com/id=1234'
|
| + body_with_html_content = (
|
| + '<a href="http://www.google.com">test</a> \'something\'')
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (False, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', body_with_html_content, 'unused body mem',
|
| + self.project, 'example.com', self.commenter_view, detail_url=detail_url)
|
| +
|
| + escaped_body_with_html_content = (
|
| + '<a href="http://www.google.com">test</a> '
|
| + ''something'')
|
| + notify_helpers._MakeNotificationFooter(
|
| + ['reason'], REPLY_NOT_ALLOWED, 'example.com')
|
| + expected_html_body = (
|
| + notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % (
|
| + detail_url,
|
| + '%s-- <br/>%s' % (escaped_body_with_html_content,
|
| + self.expected_html_footer)))
|
| + self.assertEquals(expected_html_body, email_task['html_body'])
|
| +
|
| + def testReplyInvitation(self):
|
| + """We include a footer about replying that is appropriate for that user."""
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (True, 'a@a.com', REPLY_NOT_ALLOWED),
|
| + ['reason'], 'subject', 'body non', 'body mem', self.project,
|
| + 'example.com', self.commenter_view)
|
| + self.assertEqual(emailfmt.NoReplyAddress(), email_task['reply_to'])
|
| + self.assertNotIn('Reply to this email', email_task['body'])
|
| +
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (True, 'a@a.com', REPLY_MAY_COMMENT),
|
| + ['reason'], 'subject', 'body non', 'body mem', self.project,
|
| + 'example.com', self.commenter_view)
|
| + self.assertEqual(
|
| + '%s@%s' % (self.project.project_name, emailfmt.MailDomain()),
|
| + email_task['reply_to'])
|
| + self.assertIn('Reply to this email to add a comment', email_task['body'])
|
| + self.assertNotIn('make changes', email_task['body'])
|
| +
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (True, 'a@a.com', REPLY_MAY_UPDATE),
|
| + ['reason'], 'subject', 'body non', 'body mem', self.project,
|
| + 'example.com', self.commenter_view)
|
| + self.assertEqual(
|
| + '%s@%s' % (self.project.project_name, emailfmt.MailDomain()),
|
| + email_task['reply_to'])
|
| + self.assertIn('Reply to this email to add a comment', email_task['body'])
|
| + self.assertIn('make updates', email_task['body'])
|
| +
|
| + def testInboundEmailDisabled(self):
|
| + """We don't invite replies if they are disabled for this project."""
|
| + self.project.process_inbound_email = False
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (True, 'a@a.com', REPLY_MAY_UPDATE),
|
| + ['reason'], 'subject', 'body non', 'body mem', self.project,
|
| + 'example.com', self.commenter_view)
|
| + self.assertEqual(emailfmt.NoReplyAddress(), email_task['reply_to'])
|
| +
|
| + def testReasons(self):
|
| + """The footer lists reasons why that email was sent to that user."""
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (True, 'a@a.com', REPLY_MAY_UPDATE),
|
| + ['Funny', 'Caring', 'Near'], 'subject', 'body', 'body', self.project,
|
| + 'example.com', self.commenter_view)
|
| + self.assertIn('because:', email_task['body'])
|
| + self.assertIn('1. Funny', email_task['body'])
|
| + self.assertIn('2. Caring', email_task['body'])
|
| + self.assertIn('3. Near', email_task['body'])
|
| +
|
| + email_task = notify_helpers._MakeEmailWorkItem(
|
| + (True, 'a@a.com', REPLY_MAY_UPDATE),
|
| + [], 'subject', 'body', 'body', self.project,
|
| + 'example.com', self.commenter_view)
|
| + self.assertNotIn('because', email_task['body'])
|
| +
|
| +
|
| +class MakeNotificationFooterTest(unittest.TestCase):
|
| +
|
| + def testMakeNotificationFooter_NoReason(self):
|
| + footer = notify_helpers._MakeNotificationFooter(
|
| + [], REPLY_NOT_ALLOWED, 'example.com')
|
| + self.assertEqual('', footer)
|
| +
|
| + def testMakeNotificationFooter_WithReason(self):
|
| + footer = notify_helpers._MakeNotificationFooter(
|
| + ['REASON'], REPLY_NOT_ALLOWED, 'example.com')
|
| + self.assertIn('REASON', footer)
|
| + self.assertIn('https://example.com/hosting/settings', footer)
|
| +
|
| + footer = notify_helpers._MakeNotificationFooter(
|
| + ['REASON'], REPLY_NOT_ALLOWED, 'example.com')
|
| + self.assertIn('REASON', footer)
|
| + self.assertIn('https://example.com/hosting/settings', footer)
|
| +
|
| + def testMakeNotificationFooter_ManyReasons(self):
|
| + footer = notify_helpers._MakeNotificationFooter(
|
| + ['Funny', 'Caring', 'Warmblooded'], REPLY_NOT_ALLOWED,
|
| + 'example.com')
|
| + self.assertIn('Funny', footer)
|
| + self.assertIn('Caring', footer)
|
| + self.assertIn('Warmblooded', footer)
|
| +
|
| + def testMakeNotificationFooter_WithReplyInstructions(self):
|
| + footer = notify_helpers._MakeNotificationFooter(
|
| + ['REASON'], REPLY_NOT_ALLOWED, 'example.com')
|
| + self.assertNotIn('Reply', footer)
|
| + self.assertIn('https://example.com/hosting/settings', footer)
|
| +
|
| + footer = notify_helpers._MakeNotificationFooter(
|
| + ['REASON'], REPLY_MAY_COMMENT, 'example.com')
|
| + self.assertIn('add a comment', footer)
|
| + self.assertNotIn('make updates', footer)
|
| + self.assertIn('https://example.com/hosting/settings', footer)
|
| +
|
| + footer = notify_helpers._MakeNotificationFooter(
|
| + ['REASON'], REPLY_MAY_UPDATE, 'example.com')
|
| + self.assertIn('add a comment', footer)
|
| + self.assertIn('make updates', footer)
|
| + self.assertIn('https://example.com/hosting/settings', footer)
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + unittest.main()
|
|
|