OLD | NEW |
(Empty) | |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is govered 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 """Unittests for monorail.feature.inboundemail.""" |
| 7 |
| 8 import unittest |
| 9 |
| 10 import mox |
| 11 |
| 12 from features import commitlogcommands |
| 13 from features import inboundemail |
| 14 from framework import emailfmt |
| 15 from framework import monorailrequest |
| 16 from framework import permissions |
| 17 from proto import project_pb2 |
| 18 from proto import tracker_pb2 |
| 19 from proto import user_pb2 |
| 20 from services import service_manager |
| 21 from testing import fake |
| 22 from testing import testing_helpers |
| 23 |
| 24 |
| 25 class InboundEmailTest(unittest.TestCase): |
| 26 |
| 27 def setUp(self): |
| 28 self.cnxn = 'fake cnxn' |
| 29 self.services = service_manager.Services( |
| 30 issue=fake.IssueService(), |
| 31 user=fake.UserService(), |
| 32 project=fake.ProjectService()) |
| 33 self.project = self.services.project.TestAddProject( |
| 34 'proj', project_id=987, process_inbound_email=True) |
| 35 self.project_addr = 'proj@monorail.example.com' |
| 36 |
| 37 self.issue = tracker_pb2.Issue() |
| 38 self.issue.project_id = 987 |
| 39 self.issue.local_id = 100 |
| 40 self.services.issue.TestAddIssue(self.issue) |
| 41 |
| 42 self.msg = testing_helpers.MakeMessage( |
| 43 testing_helpers.HEADER_LINES, 'awesome!') |
| 44 |
| 45 request, _ = testing_helpers.GetRequestObjects() |
| 46 self.inbound = inboundemail.InboundEmail(request, None, self.services) |
| 47 self.mox = mox.Mox() |
| 48 |
| 49 def tearDown(self): |
| 50 self.mox.UnsetStubs() |
| 51 self.mox.ResetAll() |
| 52 |
| 53 def testTemplates(self): |
| 54 for name, template_path in self.inbound._templates.iteritems(): |
| 55 assert(name in inboundemail.MSG_TEMPLATES) |
| 56 assert( |
| 57 template_path.GetTemplatePath().endswith( |
| 58 inboundemail.MSG_TEMPLATES[name])) |
| 59 |
| 60 def testProcessMail_MsgTooBig(self): |
| 61 self.mox.StubOutWithMock(emailfmt, 'IsBodyTooBigToParse') |
| 62 emailfmt.IsBodyTooBigToParse(mox.IgnoreArg()).AndReturn(True) |
| 63 self.mox.ReplayAll() |
| 64 |
| 65 email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) |
| 66 self.mox.VerifyAll() |
| 67 self.assertEquals(1, len(email_tasks)) |
| 68 email_task = email_tasks[0] |
| 69 self.assertEquals('user@example.com', email_task['to']) |
| 70 self.assertEquals('Email body too long', email_task['subject']) |
| 71 |
| 72 def testProcessMail_NoProjectOnToLine(self): |
| 73 self.mox.StubOutWithMock(emailfmt, 'IsProjectAddressOnToLine') |
| 74 emailfmt.IsProjectAddressOnToLine( |
| 75 self.project_addr, [self.project_addr]).AndReturn(False) |
| 76 self.mox.ReplayAll() |
| 77 |
| 78 ret = self.inbound.ProcessMail(self.msg, self.project_addr) |
| 79 self.mox.VerifyAll() |
| 80 self.assertIsNone(ret) |
| 81 |
| 82 def testProcessMail_ProjectUnidentified(self): |
| 83 self.mox.StubOutWithMock(emailfmt, 'IdentifyProjectAndIssue') |
| 84 emailfmt.IdentifyProjectAndIssue( |
| 85 self.project_addr, mox.IgnoreArg()).AndReturn((None, None)) |
| 86 self.mox.ReplayAll() |
| 87 |
| 88 ret = self.inbound.ProcessMail(self.msg, self.project_addr) |
| 89 self.mox.VerifyAll() |
| 90 self.assertIsNone(ret) |
| 91 |
| 92 def testProcessMail_ProjectNotLive(self): |
| 93 self.project.state = project_pb2.ProjectState.DELETABLE |
| 94 email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) |
| 95 email_task = email_tasks[0] |
| 96 self.assertEquals('user@example.com', email_task['to']) |
| 97 self.assertEquals('Project not found', email_task['subject']) |
| 98 |
| 99 def testProcessMail_ProjectInboundEmailDisabled(self): |
| 100 self.project.process_inbound_email = False |
| 101 email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) |
| 102 email_task = email_tasks[0] |
| 103 self.assertEquals('user@example.com', email_task['to']) |
| 104 self.assertEquals('Email replies are not enabled in project proj', |
| 105 email_task['subject']) |
| 106 |
| 107 def testProcessMail_NoRefHeader(self): |
| 108 self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') |
| 109 emailfmt.ValidateReferencesHeader( |
| 110 mox.IgnoreArg(), self.project, mox.IgnoreArg(), |
| 111 mox.IgnoreArg()).AndReturn(False) |
| 112 self.mox.ReplayAll() |
| 113 |
| 114 email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) |
| 115 self.mox.VerifyAll() |
| 116 self.assertEquals(1, len(email_tasks)) |
| 117 email_task = email_tasks[0] |
| 118 self.assertEquals('user@example.com', email_task['to']) |
| 119 self.assertEquals('Your message is not a reply to a notification email', |
| 120 email_task['subject']) |
| 121 |
| 122 def testProcessMail_NoAccount(self): |
| 123 self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') |
| 124 emailfmt.ValidateReferencesHeader( |
| 125 mox.IgnoreArg(), self.project, mox.IgnoreArg(), |
| 126 mox.IgnoreArg()).AndReturn(True) |
| 127 self.mox.ReplayAll() |
| 128 |
| 129 email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) |
| 130 self.mox.VerifyAll() |
| 131 self.assertEquals(1, len(email_tasks)) |
| 132 email_task = email_tasks[0] |
| 133 self.assertEquals('user@example.com', email_task['to']) |
| 134 self.assertEquals('Could not determine account of sender', |
| 135 email_task['subject']) |
| 136 |
| 137 def testProcessMail_BannedAccount(self): |
| 138 self.services.user.TestAddUser('user@example.com', 111L) |
| 139 class MockAuthData: |
| 140 def __init__(self): |
| 141 self.user_pb = user_pb2.MakeUser() |
| 142 self.effective_ids = set([1, 2, 3]) |
| 143 self.user_id = 111L |
| 144 mock_auth_data = MockAuthData() |
| 145 mock_auth_data.user_pb.banned = 'banned' |
| 146 |
| 147 self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') |
| 148 emailfmt.ValidateReferencesHeader( |
| 149 mox.IgnoreArg(), self.project, mox.IgnoreArg(), |
| 150 mox.IgnoreArg()).AndReturn(True) |
| 151 self.mox.StubOutWithMock(monorailrequest.AuthData, 'FromEmail') |
| 152 monorailrequest.AuthData.FromEmail( |
| 153 mox.IgnoreArg(), 'user@example.com', self.services).AndReturn( |
| 154 mock_auth_data) |
| 155 self.mox.ReplayAll() |
| 156 |
| 157 email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) |
| 158 self.mox.VerifyAll() |
| 159 self.assertEquals(1, len(email_tasks)) |
| 160 email_task = email_tasks[0] |
| 161 self.assertEquals('user@example.com', email_task['to']) |
| 162 self.assertEquals('You are banned from using this issue tracker', |
| 163 email_task['subject']) |
| 164 |
| 165 def testProcessMail_Success(self): |
| 166 self.services.user.TestAddUser('user@example.com', 111L) |
| 167 class MockAuthData: |
| 168 def __init__(self): |
| 169 self.user_pb = user_pb2.MakeUser() |
| 170 self.effective_ids = set([1, 2, 3]) |
| 171 self.user_id = 111L |
| 172 mock_auth_data = MockAuthData() |
| 173 |
| 174 self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') |
| 175 emailfmt.ValidateReferencesHeader( |
| 176 mox.IgnoreArg(), self.project, mox.IgnoreArg(), |
| 177 mox.IgnoreArg()).AndReturn(True) |
| 178 |
| 179 self.mox.StubOutWithMock(monorailrequest.AuthData, 'FromEmail') |
| 180 monorailrequest.AuthData.FromEmail( |
| 181 mox.IgnoreArg(), 'user@example.com', self.services).AndReturn( |
| 182 mock_auth_data) |
| 183 |
| 184 self.mox.StubOutWithMock(permissions, 'GetPermissions') |
| 185 permissions.GetPermissions( |
| 186 mock_auth_data.user_pb, mock_auth_data.effective_ids, |
| 187 self.project).AndReturn('test permissions') |
| 188 |
| 189 self.mox.StubOutWithMock(self.inbound, 'ProcessIssueReply') |
| 190 self.inbound.ProcessIssueReply( |
| 191 mox.IgnoreArg(), self.project, 123, self.project_addr, |
| 192 'user@example.com', 111L, mock_auth_data.effective_ids, |
| 193 'test permissions', 'awesome!') |
| 194 |
| 195 self.mox.ReplayAll() |
| 196 |
| 197 ret = self.inbound.ProcessMail(self.msg, self.project_addr) |
| 198 self.mox.VerifyAll() |
| 199 self.assertIsNone(ret) |
| 200 |
| 201 def testProcessIssueReply_NoIssue(self): |
| 202 nonexistant_local_id = 200 |
| 203 email_tasks = self.inbound.ProcessIssueReply( |
| 204 self.cnxn, self.project, nonexistant_local_id, self.project_addr, |
| 205 'user@example.com', 111L, [1, 2, 3], permissions.USER_PERMISSIONSET, |
| 206 'awesome!') |
| 207 self.assertEquals(1, len(email_tasks)) |
| 208 email_task = email_tasks[0] |
| 209 self.assertEquals('user@example.com', email_task['to']) |
| 210 self.assertEquals('Could not find issue %d in project %s' % ( |
| 211 nonexistant_local_id, self.project.project_name), |
| 212 email_task['subject']) |
| 213 |
| 214 def testProcessIssueReply_DeletedIssue(self): |
| 215 self.issue.deleted = True |
| 216 email_tasks = self.inbound.ProcessIssueReply( |
| 217 self.cnxn, self.project, self.issue.local_id, self.project_addr, |
| 218 'user@example.com', 111L, [1, 2, 3], permissions.USER_PERMISSIONSET, |
| 219 'awesome!') |
| 220 self.assertEquals(1, len(email_tasks)) |
| 221 email_task = email_tasks[0] |
| 222 self.assertEquals('user@example.com', email_task['to']) |
| 223 self.assertEquals('Could not find issue %d in project %s' % ( |
| 224 self.issue.local_id, self.project.project_name), |
| 225 email_task['subject']) |
| 226 |
| 227 def testProcessIssueReply_NoAddIssuePerm(self): |
| 228 perms = permissions.READ_ONLY_PERMISSIONSET |
| 229 email_tasks = self.inbound.ProcessIssueReply( |
| 230 self.cnxn, self.project, self.issue.local_id, self.project_addr, |
| 231 'user@example.com', 111L, [1, 2, 3], perms, 'awesome!') |
| 232 self.assertEquals(1, len(email_tasks)) |
| 233 email_task = email_tasks[0] |
| 234 self.assertEquals('user@example.com', email_task['to']) |
| 235 self.assertEquals('User does not have permission to add a comment', |
| 236 email_task['subject']) |
| 237 |
| 238 def testProcessIssueReply_NoEditIssuePerm(self): |
| 239 perms = permissions.USER_PERMISSIONSET |
| 240 mock_uia = commitlogcommands.UpdateIssueAction(self.issue.local_id) |
| 241 |
| 242 self.mox.StubOutWithMock(commitlogcommands, 'UpdateIssueAction') |
| 243 commitlogcommands.UpdateIssueAction(self.issue.local_id).AndReturn(mock_uia) |
| 244 |
| 245 self.mox.StubOutWithMock(mock_uia, 'Parse') |
| 246 mock_uia.Parse( |
| 247 self.cnxn, self.project.project_name, 111L, ['awesome!'], self.services, |
| 248 strip_quoted_lines=True) |
| 249 self.mox.StubOutWithMock(mock_uia, 'Run') |
| 250 # Allow edit is false here because the permission set does not contain |
| 251 # EDIT_ISSUE. |
| 252 mock_uia.Run(self.cnxn, self.services, allow_edit=False) |
| 253 |
| 254 self.mox.ReplayAll() |
| 255 ret = self.inbound.ProcessIssueReply( |
| 256 self.cnxn, self.project, self.issue.local_id, self.project_addr, |
| 257 'from_addr', 111L, [1, 2, 3], perms, 'awesome!') |
| 258 self.mox.VerifyAll() |
| 259 self.assertIsNone(ret) |
| 260 |
| 261 def testProcessIssueReply_Success(self): |
| 262 perms = permissions.COMMITTER_ACTIVE_PERMISSIONSET |
| 263 mock_uia = commitlogcommands.UpdateIssueAction(self.issue.local_id) |
| 264 |
| 265 self.mox.StubOutWithMock(commitlogcommands, 'UpdateIssueAction') |
| 266 commitlogcommands.UpdateIssueAction(self.issue.local_id).AndReturn(mock_uia) |
| 267 |
| 268 self.mox.StubOutWithMock(mock_uia, 'Parse') |
| 269 mock_uia.Parse( |
| 270 self.cnxn, self.project.project_name, 111L, ['awesome!'], self.services, |
| 271 strip_quoted_lines=True) |
| 272 self.mox.StubOutWithMock(mock_uia, 'Run') |
| 273 mock_uia.Run(self.cnxn, self.services, allow_edit=True) |
| 274 |
| 275 self.mox.ReplayAll() |
| 276 ret = self.inbound.ProcessIssueReply( |
| 277 self.cnxn, self.project, self.issue.local_id, self.project_addr, |
| 278 'from_addr', 111L, [1, 2, 3], perms, 'awesome!') |
| 279 self.mox.VerifyAll() |
| 280 self.assertIsNone(ret) |
OLD | NEW |