blob: 89d9bc30af451ff019bce14feeffba36cbf78742 [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"""Unittests for monorail.tracker.issuebulkedit."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import mock
12import os
13import unittest
14import webapp2
15
16from google.appengine.api import memcache
17from google.appengine.ext import testbed
18
19from framework import exceptions
20from framework import permissions
21from proto import tracker_pb2
22from services import service_manager
23from services import tracker_fulltext
24from testing import fake
25from testing import testing_helpers
26from tracker import issuebulkedit
27from tracker import tracker_bizobj
28from tracker import tracker_constants
29
30
31class Response(object):
32
33 def __init__(self):
34 self.status = None
35
36
37class IssueBulkEditTest(unittest.TestCase):
38
39 def setUp(self):
40 self.services = service_manager.Services(
41 features=fake.FeaturesService(),
42 project=fake.ProjectService(),
43 config=fake.ConfigService(),
44 issue=fake.IssueService(),
45 issue_star=fake.IssueStarService(),
46 user=fake.UserService(),
47 usergroup=fake.UserGroupService())
48 self.servlet = issuebulkedit.IssueBulkEdit(
49 'req', 'res', services=self.services)
50 self.mr = testing_helpers.MakeMonorailRequest(
51 perms=permissions.OWNER_ACTIVE_PERMISSIONSET)
52 self.project = self.services.project.TestAddProject(
53 name='proj', project_id=789, owner_ids=[111])
54 self.cnxn = 'fake connection'
55 self.config = self.services.config.GetProjectConfig(
56 self.cnxn, self.project.project_id)
57 self.services.config.StoreConfig(self.cnxn, self.config)
58 self.owner = self.services.user.TestAddUser('owner@example.com', 111)
59
60 self.testbed = testbed.Testbed()
61 self.testbed.activate()
62 self.testbed.init_memcache_stub()
63 self.testbed.init_datastore_v3_stub()
64
65 self.mocked_methods = {}
66
67 def tearDown(self):
68 """Restore mocked objects of other modules."""
69 self.testbed.deactivate()
70 for obj, items in self.mocked_methods.items():
71 for member, previous_value in items.items():
72 setattr(obj, member, previous_value)
73
74 def testAssertBasePermission(self):
75 """Permit users with EDIT_ISSUE and ADD_ISSUE_COMMENT permissions."""
76 mr = testing_helpers.MakeMonorailRequest(
77 perms=permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET)
78 self.assertRaises(permissions.PermissionException,
79 self.servlet.AssertBasePermission, mr)
80
81 self.servlet.AssertBasePermission(self.mr)
82
83 def testGatherPageData(self):
84 """Test GPD works in a normal no-corner-cases case."""
85 created_issue_1 = fake.MakeTestIssue(
86 789, 1, 'issue summary', 'New', 0, reporter_id=111)
87 self.services.issue.TestAddIssue(created_issue_1)
88 local_id_1 = created_issue_1.local_id
89 mr = testing_helpers.MakeMonorailRequest(
90 project=self.project)
91 mr.local_id_list = [local_id_1]
92
93 page_data = self.servlet.GatherPageData(mr)
94 self.assertEqual(1, page_data['num_issues'])
95
96 def testGatherPageData_CustomFieldEdition(self):
97 """Test GPD works in a normal no-corner-cases case."""
98 created_issue_1 = fake.MakeTestIssue(
99 789, 1, 'issue summary', 'New', 0, reporter_id=111)
100 self.services.issue.TestAddIssue(created_issue_1)
101 local_id_1 = created_issue_1.local_id
102 mr = testing_helpers.MakeMonorailRequest(
103 project=self.project, perms=permissions.PermissionSet([]))
104 mr.local_id_list = [local_id_1]
105 mr.auth.effective_ids = {222}
106
107 fd_not_restricted = tracker_bizobj.MakeFieldDef(
108 123,
109 789,
110 'CPU',
111 tracker_pb2.FieldTypes.INT_TYPE,
112 None,
113 '',
114 False,
115 False,
116 False,
117 None,
118 None,
119 '',
120 False,
121 '',
122 '',
123 tracker_pb2.NotifyTriggers.NEVER,
124 'no_action',
125 'doc',
126 False,
127 is_restricted_field=False)
128 self.config.field_defs.append(fd_not_restricted)
129
130 fd_restricted = tracker_bizobj.MakeFieldDef(
131 124,
132 789,
133 'CPU',
134 tracker_pb2.FieldTypes.INT_TYPE,
135 None,
136 '',
137 False,
138 False,
139 False,
140 None,
141 None,
142 '',
143 False,
144 '',
145 '',
146 tracker_pb2.NotifyTriggers.NEVER,
147 'no_action',
148 'doc',
149 False,
150 is_restricted_field=True)
151 self.config.field_defs.append(fd_restricted)
152
153 page_data = self.servlet.GatherPageData(mr)
154 self.assertTrue(page_data['fields'][0].is_editable)
155 self.assertFalse(page_data['fields'][1].is_editable)
156
157 def testGatherPageData_NoIssues(self):
158 """Test GPD when no issues are specified in the mr."""
159 mr = testing_helpers.MakeMonorailRequest(
160 project=self.project)
161 self.assertRaises(exceptions.InputException,
162 self.servlet.GatherPageData, mr)
163
164 def testGatherPageData_FilteredIssues(self):
165 """Test GPD when all specified issues get filtered out."""
166 created_issue_1 = fake.MakeTestIssue(
167 789,
168 1,
169 'issue summary',
170 'New',
171 0,
172 reporter_id=111,
173 labels=['restrict-view-Googler'])
174 self.services.issue.TestAddIssue(created_issue_1)
175 local_id_1 = created_issue_1.local_id
176 mr = testing_helpers.MakeMonorailRequest(
177 project=self.project)
178 mr.local_id_list = [local_id_1]
179
180 self.assertRaises(webapp2.HTTPException,
181 self.servlet.GatherPageData, mr)
182
183 def testGatherPageData_TypeLabels(self):
184 """Test that GPD displays a custom field for appropriate issues."""
185 created_issue_1 = fake.MakeTestIssue(
186 789,
187 1,
188 'issue summary',
189 'New',
190 0,
191 reporter_id=111,
192 labels=['type-customlabels'])
193 self.services.issue.TestAddIssue(created_issue_1)
194 local_id_1 = created_issue_1.local_id
195 mr = testing_helpers.MakeMonorailRequest(
196 project=self.project)
197 mr.local_id_list = [local_id_1]
198
199 fd = tracker_bizobj.MakeFieldDef(
200 123, 789, 'CPU', tracker_pb2.FieldTypes.INT_TYPE, None,
201 '', False, False, False, None, None, '', False, '', '',
202 tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False)
203 self.config.field_defs.append(fd)
204
205 page_data = self.servlet.GatherPageData(mr)
206 self.assertEqual(1, len(page_data['fields']))
207
208 @mock.patch('framework.cloud_tasks_helpers.create_task')
209 def testProcessFormData(self, _create_task_mock):
210 """Test that PFD works in a normal no-corner-cases case."""
211 created_issue_1 = fake.MakeTestIssue(
212 789, 1, 'issue summary', 'New', 111, reporter_id=111)
213 self.services.issue.TestAddIssue(created_issue_1)
214 local_id_1 = created_issue_1.local_id
215
216 mr = testing_helpers.MakeMonorailRequest(
217 project=self.project,
218 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
219 user_info={'user_id': 111})
220 mr.local_id_list = [local_id_1]
221
222 post_data = fake.PostData(
223 owner=['owner@example.com'], can=[1],
224 q=[''], colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
225 self._MockMethods()
226 url = self.servlet.ProcessFormData(mr, post_data)
227 self.assertTrue('list?can=1&q=&saved=1' in url)
228
229 def testProcessFormData_NoIssues(self):
230 """Test PFD when no issues are specified."""
231 mr = testing_helpers.MakeMonorailRequest(
232 project=self.project,
233 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
234 user_info={'user_id': 111})
235 post_data = fake.PostData()
236 self.servlet.response = Response()
237 self.servlet.ProcessFormData(mr, post_data)
238 # 400 == bad request
239 self.assertEqual(400, self.servlet.response.status)
240
241 def testProcessFormData_NoUser(self):
242 """Test PFD when the user is not logged in."""
243 mr = testing_helpers.MakeMonorailRequest(
244 project=self.project)
245 mr.local_id_list = [99999]
246 post_data = fake.PostData()
247 self.servlet.response = Response()
248 self.servlet.ProcessFormData(mr, post_data)
249 # 400 == bad request
250 self.assertEqual(400, self.servlet.response.status)
251
252 def testProcessFormData_CantComment(self):
253 """Test PFD when the user can't comment on any of the issues."""
254 mr = testing_helpers.MakeMonorailRequest(
255 project=self.project,
256 perms=permissions.EMPTY_PERMISSIONSET,
257 user_info={'user_id': 111})
258 mr.local_id_list = [99999]
259 post_data = fake.PostData()
260 self.servlet.response = Response()
261 self.servlet.ProcessFormData(mr, post_data)
262 # 400 == bad request
263 self.assertEqual(400, self.servlet.response.status)
264
265 def testProcessFormData_CantEdit(self):
266 """Test PFD when the user can't edit any issue metadata."""
267 mr = testing_helpers.MakeMonorailRequest(
268 project=self.project,
269 perms=permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET,
270 user_info={'user_id': 111})
271 mr.local_id_list = [99999]
272 post_data = fake.PostData()
273 self.servlet.response = Response()
274 self.servlet.ProcessFormData(mr, post_data)
275 # 400 == bad request
276 self.assertEqual(400, self.servlet.response.status)
277
278 def testProcessFormData_CantMove(self):
279 """Test PFD when the user can't move issues."""
280 mr = testing_helpers.MakeMonorailRequest(
281 project=self.project,
282 perms=permissions.COMMITTER_ACTIVE_PERMISSIONSET,
283 user_info={'user_id': 111})
284 mr.local_id_list = [99999]
285 post_data = fake.PostData(move_to=['proj'])
286 self.servlet.response = Response()
287 self.servlet.ProcessFormData(mr, post_data)
288 # 400 == bad request
289 self.assertEqual(400, self.servlet.response.status)
290
291 created_issue_1 = fake.MakeTestIssue(
292 789, 1, 'issue summary', 'New', 111, reporter_id=111)
293 self.services.issue.TestAddIssue(created_issue_1)
294 local_id_1 = created_issue_1.local_id
295 mr.perms = permissions.OWNER_ACTIVE_PERMISSIONSET
296 mr.local_id_list = [local_id_1]
297 mr.project_name = 'proj'
298 self._MockMethods()
299 self.servlet.ProcessFormData(mr, post_data)
300 self.assertEqual(
301 'The issues are already in project proj', mr.errors.move_to)
302
303 post_data = fake.PostData(move_to=['notexist'])
304 self.servlet.ProcessFormData(mr, post_data)
305 self.assertEqual('No such project: notexist', mr.errors.move_to)
306
307 def _MockMethods(self):
308 # Mock methods of other modules to avoid unnecessary testing
309 self.mocked_methods[tracker_fulltext] = {
310 'IndexIssues': tracker_fulltext.IndexIssues,
311 'UnindexIssues': tracker_fulltext.UnindexIssues}
312 def DoNothing(*_args, **_kwargs):
313 pass
314 self.servlet.PleaseCorrect = DoNothing
315 tracker_fulltext.IndexIssues = DoNothing
316 tracker_fulltext.UnindexIssues = DoNothing
317
318 def GetFirstAmendment(self, project_id, local_id):
319 issue = self.services.issue.GetIssueByLocalID(
320 self.cnxn, project_id, local_id)
321 issue_id = issue.issue_id
322 comments = self.services.issue.GetCommentsForIssue(self.cnxn, issue_id)
323 last_comment = comments[-1]
324 first_amendment = last_comment.amendments[0]
325 return first_amendment.field, first_amendment.newvalue
326
327 def testProcessFormData_BadUserField(self):
328 """Test PFD when a nonexistent user is added as a field value."""
329 created_issue_1 = fake.MakeTestIssue(
330 789, 1, 'issue summary', 'New', 111, reporter_id=111)
331 self.services.issue.TestAddIssue(created_issue_1)
332 local_id_1 = created_issue_1.local_id
333 mr = testing_helpers.MakeMonorailRequest(
334 project=self.project,
335 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
336 user_info={'user_id': 111})
337 mr.local_id_list = [local_id_1]
338
339 fd = tracker_bizobj.MakeFieldDef(
340 12345, 789, 'PM', tracker_pb2.FieldTypes.USER_TYPE, None,
341 '', False, False, False, None, None, '', False, '', '',
342 tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False)
343 self.config.field_defs.append(fd)
344
345 post_data = fake.PostData(
346 custom_12345=['ghost@gmail.com'], owner=['owner@example.com'], can=[1],
347 q=[''], colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
348 self._MockMethods()
349 self.servlet.ProcessFormData(mr, post_data)
350 self.assertEqual('User not found.', mr.errors.custom_fields[0].message)
351
352 @mock.patch('framework.cloud_tasks_helpers.create_task')
353 def testProcessFormData_CustomFields(self, _create_task_mock):
354 """Test PFD processes edits to custom fields."""
355 created_issue_1 = fake.MakeTestIssue(
356 789, 1, 'issue summary', 'New', 111, reporter_id=111)
357 self.services.issue.TestAddIssue(created_issue_1)
358 local_id_1 = created_issue_1.local_id
359 mr = testing_helpers.MakeMonorailRequest(
360 project=self.project,
361 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
362 user_info={'user_id': 111})
363 mr.local_id_list = [local_id_1]
364
365 fd = tracker_bizobj.MakeFieldDef(
366 12345, 789, 'CPU', tracker_pb2.FieldTypes.INT_TYPE, None,
367 '', False, False, False, None, None, '', False, '', '',
368 tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'doc', False)
369 self.config.field_defs.append(fd)
370
371 post_data = fake.PostData(
372 custom_12345=['10'],
373 owner=['owner@example.com'],
374 can=[1],
375 q=[''],
376 colspec=[''],
377 sort=[''],
378 groupby=[''],
379 start=[0],
380 num=[100])
381 self._MockMethods()
382 self.servlet.ProcessFormData(mr, post_data)
383 self.assertEqual(
384 (tracker_pb2.FieldID.CUSTOM, '10'),
385 self.GetFirstAmendment(789, local_id_1))
386
387 @mock.patch('framework.cloud_tasks_helpers.create_task')
388 def testProcessFormData_RestrictedCustomFieldsAccept(self, _create_task_mock):
389 """We accept edits to restricted fields by editors (or admins)."""
390 created_issue_1 = fake.MakeTestIssue(
391 789, 1, 'issue summary', 'New', 111, reporter_id=111)
392 self.services.issue.TestAddIssue(created_issue_1)
393 local_id_1 = created_issue_1.local_id
394 mr = testing_helpers.MakeMonorailRequest(
395 project=self.project,
396 perms=permissions.PermissionSet(
397 [
398 permissions.EDIT_ISSUE, permissions.ADD_ISSUE_COMMENT,
399 permissions.VIEW
400 ]),
401 user_info={'user_id': 111})
402 mr.local_id_list = [local_id_1]
403
404 fd = tracker_bizobj.MakeFieldDef(
405 12345,
406 789,
407 'CPU',
408 tracker_pb2.FieldTypes.INT_TYPE,
409 None,
410 '',
411 False,
412 False,
413 False,
414 None,
415 None,
416 '',
417 False,
418 '',
419 '',
420 tracker_pb2.NotifyTriggers.NEVER,
421 'no_action',
422 'doc',
423 False,
424 is_restricted_field=True)
425 fd.editor_ids = [111]
426 self.config.field_defs.append(fd)
427
428 post_data = fake.PostData(
429 custom_12345=['10'],
430 owner=['owner@example.com'],
431 can=[1],
432 q=[''],
433 colspec=[''],
434 sort=[''],
435 groupby=[''],
436 start=[0],
437 num=[100])
438 self._MockMethods()
439 self.servlet.ProcessFormData(mr, post_data)
440 self.assertEqual(
441 (tracker_pb2.FieldID.CUSTOM, '10'),
442 self.GetFirstAmendment(789, local_id_1))
443
444 def testProcessFormData_RestrictedCustomFieldsReject(self):
445 """We reject edits to restricted fields by non-editors (and non-admins)."""
446 created_issue_1 = fake.MakeTestIssue(
447 789, 1, 'issue summary', 'New', 111, reporter_id=111)
448 self.services.issue.TestAddIssue(created_issue_1)
449 local_id_1 = created_issue_1.local_id
450 mr = testing_helpers.MakeMonorailRequest(
451 project=self.project,
452 perms=permissions.PermissionSet(
453 [
454 permissions.EDIT_ISSUE, permissions.ADD_ISSUE_COMMENT,
455 permissions.VIEW
456 ]),
457 user_info={'user_id': 111})
458 mr.local_id_list = [local_id_1]
459
460 fd_int = tracker_bizobj.MakeFieldDef(
461 11111,
462 789,
463 'fd_int',
464 tracker_pb2.FieldTypes.INT_TYPE,
465 None,
466 '',
467 False,
468 False,
469 False,
470 None,
471 None,
472 '',
473 False,
474 '',
475 '',
476 tracker_pb2.NotifyTriggers.NEVER,
477 'no_action',
478 'doc',
479 False,
480 is_restricted_field=True)
481 fd_enum = tracker_bizobj.MakeFieldDef(
482 44444,
483 789,
484 'fdEnum',
485 tracker_pb2.FieldTypes.ENUM_TYPE,
486 None,
487 '',
488 False,
489 False,
490 False,
491 None,
492 None,
493 '',
494 False,
495 '',
496 '',
497 tracker_pb2.NotifyTriggers.NEVER,
498 'no_action',
499 'doc',
500 False,
501 is_restricted_field=True)
502 fd_int.admin_ids = [222]
503 fd_enum.editor_ids = [333]
504 self.config.field_defs = [fd_int, fd_enum]
505
506 post_data_add_fv = fake.PostData(
507 custom_11111=['10'],
508 owner=['owner@example.com'],
509 can=[1],
510 q=[''],
511 colspec=[''],
512 sort=[''],
513 groupby=[''],
514 start=[0],
515 num=[100])
516 post_data_rm_fv = fake.PostData(
517 op_custom_11111=['remove'],
518 custom_11111=['10'],
519 owner=['owner@example.com'],
520 can=[1],
521 q=[''],
522 colspec=[''],
523 sort=[''],
524 groupby=[''],
525 start=[0],
526 num=[100])
527 post_data_clear_fd = fake.PostData(
528 op_custom_11111=['clear'],
529 owner=['owner@example.com'],
530 can=[1],
531 q=[''],
532 colspec=[''],
533 sort=[''],
534 groupby=[''],
535 start=[0],
536 num=[100])
537 post_data_label_edits_enum = fake.PostData(
538 label=['fdEnum-a'],
539 owner=['owner@example.com'],
540 can=[1],
541 q=[''],
542 colspec=[''],
543 sort=[''],
544 groupby=[''],
545 start=[0],
546 num=[100])
547 post_data_label_rm_enum = fake.PostData(
548 label=['-fdEnum-b'],
549 owner=['owner@example.com'],
550 can=[1],
551 q=[''],
552 colspec=[''],
553 sort=[''],
554 groupby=[''],
555 start=[0],
556 num=[100])
557
558 self._MockMethods()
559 self.assertRaises(
560 AssertionError, self.servlet.ProcessFormData, mr, post_data_add_fv)
561 self.assertRaises(
562 AssertionError, self.servlet.ProcessFormData, mr, post_data_rm_fv)
563 self.assertRaises(
564 AssertionError, self.servlet.ProcessFormData, mr, post_data_clear_fd)
565 self.assertRaises(
566 AssertionError, self.servlet.ProcessFormData, mr,
567 post_data_label_edits_enum)
568 self.assertRaises(
569 AssertionError, self.servlet.ProcessFormData, mr,
570 post_data_label_rm_enum)
571
572 def testProcessFormData_DuplicateStatus_MergeSameIssue(self):
573 """Test PFD processes null/cleared status values."""
574 created_issue_1 = fake.MakeTestIssue(
575 789, 1, 'issue summary', 'New', 111, reporter_id=111)
576 self.services.issue.TestAddIssue(created_issue_1)
577 local_id_1 = created_issue_1.local_id
578
579 created_issue_2 = fake.MakeTestIssue(
580 789, 1, 'issue summary', 'New', 112, reporter_id=112)
581 self.services.issue.TestAddIssue(created_issue_2)
582 merge_into_local_id_2 = created_issue_2.local_id
583
584 mr = testing_helpers.MakeMonorailRequest(
585 project=self.project,
586 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
587 user_info={'user_id': 111})
588 mr.local_id_list = [local_id_1, merge_into_local_id_2]
589 mr.project_name = 'proj'
590
591 # Add required project_name to merge_into_issue.
592 merge_into_issue = self.services.issue.GetIssueByLocalID(
593 mr.cnxn, self.project.project_id, merge_into_local_id_2)
594 merge_into_issue.project_name = 'proj'
595
596 post_data = fake.PostData(status=['Duplicate'],
597 merge_into=[str(merge_into_local_id_2)], owner=['owner@example.com'],
598 can=[1], q=[''], colspec=[''], sort=[''], groupby=[''], start=[0],
599 num=[100])
600 self._MockMethods()
601 self.servlet.ProcessFormData(mr, post_data)
602 self.assertEqual('Cannot merge issue into itself', mr.errors.merge_into_id)
603
604 def testProcessFormData_DuplicateStatus_MergeMissingIssue(self):
605 """Test PFD processes null/cleared status values."""
606 created_issue_1 = fake.MakeTestIssue(
607 789, 1, 'issue summary', 'New', 111, reporter_id=111)
608 self.services.issue.TestAddIssue(created_issue_1)
609 local_id_1 = created_issue_1.local_id
610 created_issue_2 = fake.MakeTestIssue(
611 789, 1, 'issue summary2', 'New', 112, reporter_id=112)
612 self.services.issue.TestAddIssue(created_issue_2)
613 local_id_2 = created_issue_2.local_id
614 mr = testing_helpers.MakeMonorailRequest(
615 project=self.project,
616 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
617 user_info={'user_id': 111})
618 mr.local_id_list = [local_id_1, local_id_2]
619 mr.project_name = 'proj'
620
621 post_data = fake.PostData(status=['Duplicate'],
622 merge_into=['non existant id'], owner=['owner@example.com'],
623 can=[1], q=[''], colspec=[''], sort=[''], groupby=[''], start=[0],
624 num=[100])
625 self._MockMethods()
626 self.servlet.ProcessFormData(mr, post_data)
627 self.assertEqual('Please enter an issue ID', mr.errors.merge_into_id)
628
629 @mock.patch('framework.cloud_tasks_helpers.create_task')
630 def testProcessFormData_DuplicateStatus_Success(self, _create_task_mock):
631 """Test PFD processes null/cleared status values."""
632 created_issue_1 = fake.MakeTestIssue(
633 789, 1, 'issue summary', 'New', 111, reporter_id=111)
634 self.services.issue.TestAddIssue(created_issue_1)
635 local_id_1 = created_issue_1.local_id
636 created_issue_2 = fake.MakeTestIssue(
637 789, 2, 'issue summary2', 'New', 111, reporter_id=111)
638 self.services.issue.TestAddIssue(created_issue_2)
639 local_id_2 = created_issue_2.local_id
640 created_issue_3 = fake.MakeTestIssue(
641 789, 3, 'issue summary3', 'New', 112, reporter_id=112)
642 self.services.issue.TestAddIssue(created_issue_3)
643 merge_into_local_id_3 = created_issue_3.local_id
644 mr = testing_helpers.MakeMonorailRequest(
645 project=self.project,
646 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
647 user_info={'user_id': 111})
648 mr.local_id_list = [local_id_1, local_id_2]
649 mr.project_name = 'proj'
650
651 post_data = fake.PostData(status=['Duplicate'],
652 merge_into=[str(merge_into_local_id_3)], owner=['owner@example.com'],
653 can=[1], q=[''], colspec=[''], sort=[''], groupby=[''], start=[0],
654 num=[100])
655 self._MockMethods()
656
657 # Add project_name, CCs and starrers to the merge_into_issue.
658 merge_into_issue = self.services.issue.GetIssueByLocalID(
659 mr.cnxn, self.project.project_id, merge_into_local_id_3)
660 merge_into_issue.project_name = 'proj'
661 merge_into_issue.cc_ids = [113, 120]
662 self.services.issue_star.SetStar(
663 mr.cnxn, self.services, None, merge_into_issue.issue_id, 120, True)
664
665 # Add project_name, CCs and starrers to the source issues.
666 # Issue 1
667 issue_1 = self.services.issue.GetIssueByLocalID(
668 mr.cnxn, self.project.project_id, local_id_1)
669 issue_1.project_name = 'proj'
670 issue_1.cc_ids = [113, 114]
671 self.services.issue_star.SetStar(
672 mr.cnxn, self.services, None, issue_1.issue_id, 113, True)
673 # Issue 2
674 issue_2 = self.services.issue.GetIssueByLocalID(
675 mr.cnxn, self.project.project_id, local_id_2)
676 issue_2.project_name = 'proj'
677 issue_2.cc_ids = [113, 115, 118]
678 self.services.issue_star.SetStar(
679 mr.cnxn, self.services, None, issue_2.issue_id, 114, True)
680 self.services.issue_star.SetStar(
681 mr.cnxn, self.services, None, issue_2.issue_id, 115, True)
682
683 self.servlet.ProcessFormData(mr, post_data)
684
685 # Verify both source issues were updated.
686 self.assertEqual(
687 (tracker_pb2.FieldID.STATUS, 'Duplicate'),
688 self.GetFirstAmendment(self.project.project_id, local_id_1))
689 self.assertEqual(
690 (tracker_pb2.FieldID.STATUS, 'Duplicate'),
691 self.GetFirstAmendment(self.project.project_id, local_id_2))
692
693 # Verify that the merge into issue was updated with a comment.
694 comments = self.services.issue.GetCommentsForIssue(
695 self.cnxn, merge_into_issue.issue_id)
696 self.assertEqual(
697 'Issue 1 has been merged into this issue.\n'
698 'Issue 2 has been merged into this issue.', comments[-1].content)
699
700 # Verify CC lists and owner were merged to the merge_into issue.
701 self.assertEqual(
702 [113, 120, 114, 115, 118, 111], merge_into_issue.cc_ids)
703 # Verify new starrers were added to the merge_into issue.
704 self.assertEqual(4,
705 self.services.issue_star.CountItemStars(
706 self.cnxn, merge_into_issue.issue_id))
707 self.assertEqual([120, 113, 114, 115],
708 self.services.issue_star.LookupItemStarrers(
709 self.cnxn, merge_into_issue.issue_id))
710
711 @mock.patch('framework.cloud_tasks_helpers.create_task')
712 def testProcessFormData_ClearStatus(self, _create_task_mock):
713 """Test PFD processes null/cleared status values."""
714 created_issue_1 = fake.MakeTestIssue(
715 789, 1, 'issue summary', 'New', 111, reporter_id=111)
716 self.services.issue.TestAddIssue(created_issue_1)
717 local_id_1 = created_issue_1.local_id
718 mr = testing_helpers.MakeMonorailRequest(
719 project=self.project,
720 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
721 user_info={'user_id': 111})
722 mr.local_id_list = [local_id_1]
723
724 post_data = fake.PostData(
725 op_statusenter=['clear'], owner=['owner@example.com'], can=[1],
726 q=[''], colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
727 self._MockMethods()
728 self.servlet.ProcessFormData(mr, post_data)
729 self.assertEqual(
730 (tracker_pb2.FieldID.STATUS, ''), self.GetFirstAmendment(
731 789, local_id_1))
732
733 def testProcessFormData_InvalidOwner(self):
734 """Test PFD rejects invalid owner emails."""
735 created_issue_1 = fake.MakeTestIssue(
736 789, 1, 'issue summary', 'New', 0, reporter_id=111)
737 self.services.issue.TestAddIssue(created_issue_1)
738 local_id_1 = created_issue_1.local_id
739 mr = testing_helpers.MakeMonorailRequest(
740 project=self.project,
741 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
742 user_info={'user_id': 111})
743 mr.local_id_list = [local_id_1]
744 post_data = fake.PostData(
745 owner=['invalid'])
746 self.servlet.response = Response()
747 self._MockMethods()
748 self.servlet.ProcessFormData(mr, post_data)
749 self.assertTrue(mr.errors.AnyErrors())
750
751 @mock.patch('framework.cloud_tasks_helpers.create_task')
752 def testProcessFormData_MoveTo(self, _create_task_mock):
753 """Test PFD processes move_to values."""
754 created_issue_1 = fake.MakeTestIssue(
755 789, 1, 'issue summary', 'New', 111, reporter_id=111)
756 self.services.issue.TestAddIssue(created_issue_1)
757 local_id_1 = created_issue_1.local_id
758 move_to_project = self.services.project.TestAddProject(
759 name='proj2', project_id=790, owner_ids=[111])
760
761 mr = testing_helpers.MakeMonorailRequest(
762 project=self.project,
763 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
764 user_info={'user_id': 111})
765 mr.project_name = 'proj'
766 mr.local_id_list = [local_id_1]
767
768 self._MockMethods()
769 post_data = fake.PostData(
770 move_to=['proj2'], can=[1], q=[''],
771 colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
772 self.servlet.response = Response()
773 self.servlet.ProcessFormData(mr, post_data)
774
775 issue = self.services.issue.GetIssueByLocalID(
776 self.cnxn, move_to_project.project_id, local_id_1)
777 self.assertIsNotNone(issue)
778
779 def testProcessFormData_InvalidBlockIssues(self):
780 """Test PFD processes invalid blocked_on and blocking values."""
781 created_issue_1 = fake.MakeTestIssue(
782 789, 1, 'issue summary', 'New', 111, reporter_id=111)
783 self.services.issue.TestAddIssue(created_issue_1)
784 local_id_1 = created_issue_1.local_id
785 mr = testing_helpers.MakeMonorailRequest(
786 project=self.project,
787 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
788 user_info={'user_id': 111})
789 mr.project_name = 'proj'
790 mr.local_id_list = [local_id_1]
791
792 self._MockMethods()
793 post_data = fake.PostData(
794 op_blockedonenter=['append'], blocked_on=['12345'],
795 op_blockingenter=['append'], blocking=['54321'],
796 can=[1], q=[''],
797 colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
798 self.servlet.ProcessFormData(mr, post_data)
799
800 self.assertEqual('Invalid issue ID 12345', mr.errors.blocked_on)
801 self.assertEqual('Invalid issue ID 54321', mr.errors.blocking)
802
803 def testProcessFormData_BlockIssuesOnItself(self):
804 """Test PFD processes invalid blocked_on and blocking values."""
805 created_issue_1 = fake.MakeTestIssue(
806 789, 1, 'issue summary', 'New', 111, reporter_id=111)
807 self.services.issue.TestAddIssue(created_issue_1)
808 local_id_1 = created_issue_1.local_id
809 created_issue_2 = fake.MakeTestIssue(
810 789, 1, 'issue summary', 'New', 111, reporter_id=111)
811 self.services.issue.TestAddIssue(created_issue_2)
812 local_id_2 = created_issue_2.local_id
813 mr = testing_helpers.MakeMonorailRequest(
814 project=self.project,
815 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
816 user_info={'user_id': 111})
817 mr.project_name = 'proj'
818 mr.local_id_list = [local_id_1, local_id_2]
819
820 self._MockMethods()
821 post_data = fake.PostData(
822 op_blockedonenter=['append'], blocked_on=[str(local_id_1)],
823 op_blockingenter=['append'], blocking=[str(local_id_2)],
824 can=[1], q=[''],
825 colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
826 self.servlet.ProcessFormData(mr, post_data)
827
828 self.assertEqual('Cannot block an issue on itself.', mr.errors.blocked_on)
829 self.assertEqual('Cannot block an issue on itself.', mr.errors.blocking)
830
831 @mock.patch('framework.cloud_tasks_helpers.create_task')
832 def testProcessFormData_NormalBlockIssues(self, _create_task_mock):
833 """Test PFD processes blocked_on and blocking values."""
834 created_issue_1 = fake.MakeTestIssue(
835 789, 1, 'issue summary', 'New', 111, reporter_id=111)
836 self.services.issue.TestAddIssue(created_issue_1)
837 local_id_1 = created_issue_1.local_id
838
839 created_issueid = fake.MakeTestIssue(
840 789, 2, 'blocking', 'New', 111, reporter_id=111)
841 self.services.issue.TestAddIssue(created_issueid)
842 blocking_id = created_issueid.local_id
843
844 created_issueid = fake.MakeTestIssue(
845 789, 3, 'blocked on', 'New', 111, reporter_id=111)
846 self.services.issue.TestAddIssue(created_issueid)
847 blocked_on_id = created_issueid.local_id
848 mr = testing_helpers.MakeMonorailRequest(
849 project=self.project,
850 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
851 user_info={'user_id': 111})
852 mr.project_name = 'proj'
853 mr.local_id_list = [local_id_1]
854
855 self._MockMethods()
856 post_data = fake.PostData(
857 op_blockedonenter=['append'], blocked_on=[str(blocked_on_id)],
858 op_blockingenter=['append'], blocking=[str(blocking_id)],
859 can=[1], q=[''],
860 colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
861 self.servlet.ProcessFormData(mr, post_data)
862
863 self.assertIsNone(mr.errors.blocked_on)
864 self.assertIsNone(mr.errors.blocking)
865
866 def testProcessFormData_TooLongComment(self):
867 """Test PFD rejects comments that are too long."""
868 created_issue_1 = fake.MakeTestIssue(
869 789, 1, 'issue summary', 'New', 111, reporter_id=111)
870 self.services.issue.TestAddIssue(created_issue_1)
871 local_id_1 = created_issue_1.local_id
872
873 mr = testing_helpers.MakeMonorailRequest(
874 project=self.project,
875 perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
876 user_info={'user_id': 111})
877 mr.local_id_list = [local_id_1]
878
879 post_data = fake.PostData(
880 owner=['owner@example.com'],
881 can=[1],
882 q=[''],
883 colspec=[''],
884 sort=[''],
885 groupby=[''],
886 start=[0],
887 num=[100],
888 comment=[' ' + 'c' * tracker_constants.MAX_COMMENT_CHARS + ' '])
889 self._MockMethods()
890 self.servlet.ProcessFormData(mr, post_data)
891 self.assertTrue(mr.errors.AnyErrors())
892 self.assertEqual('Comment is too long.', mr.errors.comment)