| Index: appengine/monorail/tracker/test/issuebulkedit_test.py
|
| diff --git a/appengine/monorail/tracker/test/issuebulkedit_test.py b/appengine/monorail/tracker/test/issuebulkedit_test.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..aa722deba9e23f2f627ed3038049edc5f05a8d60
|
| --- /dev/null
|
| +++ b/appengine/monorail/tracker/test/issuebulkedit_test.py
|
| @@ -0,0 +1,462 @@
|
| +# 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
|
| +
|
| +"""Unittests for monorail.tracker.issuebulkedit."""
|
| +
|
| +import os
|
| +import unittest
|
| +import webapp2
|
| +
|
| +from google.appengine.api import memcache
|
| +from google.appengine.api import taskqueue
|
| +from google.appengine.ext import testbed
|
| +
|
| +from framework import monorailrequest
|
| +from framework import permissions
|
| +from proto import tracker_pb2
|
| +from services import service_manager
|
| +from services import tracker_fulltext
|
| +from testing import fake
|
| +from testing import testing_helpers
|
| +from tracker import issuebulkedit
|
| +from tracker import tracker_bizobj
|
| +
|
| +
|
| +class Response(object):
|
| +
|
| + def __init__(self):
|
| + self.status = None
|
| +
|
| +
|
| +class IssueBulkEditTest(unittest.TestCase):
|
| +
|
| + def setUp(self):
|
| + self.services = service_manager.Services(
|
| + features=fake.FeaturesService(),
|
| + project=fake.ProjectService(),
|
| + config=fake.ConfigService(),
|
| + issue=fake.IssueService(),
|
| + issue_star=fake.IssueStarService(),
|
| + user=fake.UserService(),
|
| + usergroup=fake.UserGroupService())
|
| + self.servlet = issuebulkedit.IssueBulkEdit(
|
| + 'req', 'res', services=self.services)
|
| + self.mr = testing_helpers.MakeMonorailRequest(
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET)
|
| + self.project = self.services.project.TestAddProject(
|
| + name='proj', project_id=789, owner_ids=[111])
|
| + self.cnxn = 'fake connection'
|
| + self.config = self.services.config.GetProjectConfig(
|
| + self.cnxn, self.project.project_id)
|
| + self.services.config.StoreConfig(self.cnxn, self.config)
|
| + self.owner = self.services.user.TestAddUser('owner@example.com', 111)
|
| +
|
| + self.testbed = testbed.Testbed()
|
| + self.testbed.activate()
|
| + self.testbed.init_taskqueue_stub()
|
| + self.testbed.init_memcache_stub()
|
| + self.testbed.init_datastore_v3_stub()
|
| + self.taskqueue_stub = self.testbed.get_stub(testbed.TASKQUEUE_SERVICE_NAME)
|
| + self.taskqueue_stub._root_path = os.path.dirname(
|
| + os.path.dirname(os.path.dirname( __file__ )))
|
| +
|
| + self.mocked_methods = {}
|
| +
|
| + def tearDown(self):
|
| + """Restore mocked objects of other modules."""
|
| + for obj, items in self.mocked_methods.iteritems():
|
| + for member, previous_value in items.iteritems():
|
| + setattr(obj, member, previous_value)
|
| +
|
| + def testAssertBasePermission(self):
|
| + """Permit users with EDIT_ISSUE and ADD_ISSUE_COMMENT permissions."""
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + perms=permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET)
|
| + self.assertRaises(permissions.PermissionException,
|
| + self.servlet.AssertBasePermission, mr)
|
| +
|
| + self.servlet.AssertBasePermission(self.mr)
|
| +
|
| + def testGatherPageData(self):
|
| + """Test GPD works in a normal no-corner-cases case."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, 789, 'issue summary', 'New', None,
|
| + [], [], [], [], 111L, 'test issue')
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project)
|
| + mr.local_id_list = [local_id_1]
|
| +
|
| + page_data = self.servlet.GatherPageData(mr)
|
| + self.assertEqual(1, page_data['num_issues'])
|
| +
|
| + def testGatherPageData_NoIssues(self):
|
| + """Test GPD when no issues are specified in the mr."""
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project)
|
| + self.assertRaises(monorailrequest.InputException,
|
| + self.servlet.GatherPageData, mr)
|
| +
|
| + def testGatherPageData_FilteredIssues(self):
|
| + """Test GPD when all specified issues get filtered out."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, 789, 'issue summary', 'New', None, [],
|
| + ['restrict-view-Googler'], [], [],
|
| + 111L, 'test issue')
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project)
|
| + mr.local_id_list = [local_id_1]
|
| +
|
| + self.assertRaises(webapp2.HTTPException,
|
| + self.servlet.GatherPageData, mr)
|
| +
|
| + def testGatherPageData_TypeLabels(self):
|
| + """Test that GPD displays a custom field for appropriate issues."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, 789, 'issue summary', 'New', None, [],
|
| + ['type-customlabels'], [], [],
|
| + 111L, 'test issue')
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project)
|
| + mr.local_id_list = [local_id_1]
|
| +
|
| + fd = tracker_bizobj.MakeFieldDef(
|
| + 123, 789, 'CPU', tracker_pb2.FieldTypes.INT_TYPE, None,
|
| + '', False, False, None, None, '', False, '', '',
|
| + tracker_pb2.NotifyTriggers.NEVER, 'doc', False)
|
| + self.config.field_defs.append(fd)
|
| +
|
| + page_data = self.servlet.GatherPageData(mr)
|
| + self.assertEqual(1, len(page_data['fields']))
|
| +
|
| + def testProcessFormData(self):
|
| + """Test that PFD works in a normal no-corner-cases case."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, 789, 'issue summary', 'New', 111L,
|
| + [], [], [], [], 111L, 'test issue')
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [local_id_1]
|
| +
|
| + post_data = fake.PostData(
|
| + owner=['owner@example.com'], can=[1],
|
| + q=[''], colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
|
| + self._MockMethods()
|
| + url = self.servlet.ProcessFormData(mr, post_data)
|
| + self.assertTrue('list?can=1&saved=1' in url)
|
| +
|
| + def testProcessFormData_NoIssues(self):
|
| + """Test PFD when no issues are specified."""
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + post_data = fake.PostData()
|
| + self.servlet.response = Response()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + # 400 == bad request
|
| + self.assertEqual(400, self.servlet.response.status)
|
| +
|
| + def testProcessFormData_NoUser(self):
|
| + """Test PDF when the user is not logged in."""
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project)
|
| + mr.local_id_list = [99999]
|
| + post_data = fake.PostData()
|
| + self.servlet.response = Response()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + # 400 == bad request
|
| + self.assertEqual(400, self.servlet.response.status)
|
| +
|
| + def testProcessFormData_CantComment(self):
|
| + """Test PFD when the user can't comment on any of the issues."""
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.EMPTY_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [99999]
|
| + post_data = fake.PostData()
|
| + self.servlet.response = Response()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + # 400 == bad request
|
| + self.assertEqual(400, self.servlet.response.status)
|
| +
|
| + def testProcessFormData_CantEdit(self):
|
| + """Test PFD when the user can't edit any issue metadata."""
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [99999]
|
| + post_data = fake.PostData()
|
| + self.servlet.response = Response()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + # 400 == bad request
|
| + self.assertEqual(400, self.servlet.response.status)
|
| +
|
| + def testProcessFormData_CantMove(self):
|
| + """Test PFD when the user can't move issues."""
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.COMMITTER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [99999]
|
| + post_data = fake.PostData(move_to=['proj'])
|
| + self.servlet.response = Response()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + # 400 == bad request
|
| + self.assertEqual(400, self.servlet.response.status)
|
| +
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, 789, 'issue summary', 'New', 111L,
|
| + [], [], [], [], 111L, 'test issue')
|
| + mr.perms = permissions.OWNER_ACTIVE_PERMISSIONSET
|
| + mr.local_id_list = [local_id_1]
|
| + mr.project_name = 'proj'
|
| + self._MockMethods()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + self.assertEqual(
|
| + 'The issues are already in project proj', mr.errors.move_to)
|
| +
|
| + post_data = fake.PostData(move_to=['notexist'])
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + self.assertEqual('No such project: notexist', mr.errors.move_to)
|
| +
|
| + def _MockMethods(self):
|
| + # Mock methods of other modules to avoid unnecessary testing
|
| + self.mocked_methods[tracker_fulltext] = {
|
| + 'IndexIssues': tracker_fulltext.IndexIssues,
|
| + 'UnindexIssues': tracker_fulltext.UnindexIssues}
|
| + def DoNothing(*_args, **_kwargs):
|
| + pass
|
| + self.servlet.PleaseCorrect = DoNothing
|
| + tracker_fulltext.IndexIssues = DoNothing
|
| + tracker_fulltext.UnindexIssues = DoNothing
|
| +
|
| + def VerifyIssueUpdated(self, project_id, local_id):
|
| + issue = self.services.issue.GetIssueByLocalID(
|
| + self.cnxn, project_id, local_id)
|
| + issue_id = issue.issue_id
|
| + comments = self.services.issue.GetCommentsForIssue(self.cnxn, issue_id)
|
| + last_comment = comments[-1]
|
| + if last_comment.amendments[0].newvalue == 'Updated':
|
| + return True
|
| + else:
|
| + return False
|
| +
|
| + def testProcessFormData_CustomFields(self):
|
| + """Test PFD processes edits to custom fields."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, 789, 'issue summary', 'New', 111L,
|
| + [], [], [], [], 111L, 'test issue')
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [local_id_1]
|
| +
|
| + fd = tracker_bizobj.MakeFieldDef(
|
| + 12345, 789, 'CPU', tracker_pb2.FieldTypes.INT_TYPE, None,
|
| + '', False, False, None, None, '', False, '', '',
|
| + tracker_pb2.NotifyTriggers.NEVER, 'doc', False)
|
| + self.config.field_defs.append(fd)
|
| +
|
| + post_data = fake.PostData(
|
| + custom_12345=['111'], owner=['owner@example.com'], can=[1],
|
| + q=[''], colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
|
| + self._MockMethods()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + self.assertTrue(self.VerifyIssueUpdated(789, local_id_1))
|
| +
|
| + def testProcessFormData_DuplicateStatus_MergeSameIssue(self):
|
| + """Test PFD processes null/cleared status values."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, self.project.project_id, 'issue summary',
|
| + 'New', 111L, [], [], [], [], 111L, 'test issue')
|
| + merge_into_local_id_2 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, self.project.project_id, 'issue summary2',
|
| + 'New', 112L, [], [], [], [], 112L, 'test issue2')
|
| +
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [local_id_1, merge_into_local_id_2]
|
| + mr.project_name = 'proj'
|
| +
|
| + # Add required project_name to merge_into_issue.
|
| + merge_into_issue = self.services.issue.GetIssueByLocalID(
|
| + mr.cnxn, self.project.project_id, merge_into_local_id_2)
|
| + merge_into_issue.project_name = 'proj'
|
| +
|
| + post_data = fake.PostData(status=['Duplicate'],
|
| + merge_into=[str(merge_into_local_id_2)], owner=['owner@example.com'],
|
| + can=[1], q=[''], colspec=[''], sort=[''], groupby=[''], start=[0],
|
| + num=[100])
|
| + self._MockMethods()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + self.assertEquals('Cannot merge issue into itself', mr.errors.merge_into_id)
|
| +
|
| + def testProcessFormData_DuplicateStatus_MergeMissingIssue(self):
|
| + """Test PFD processes null/cleared status values."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, self.project.project_id, 'issue summary',
|
| + 'New', 111L, [], [], [], [], 111L, 'test issue')
|
| + local_id_2 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, self.project.project_id, 'issue summary2',
|
| + 'New', 112L, [], [], [], [], 112L, 'test issue2')
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [local_id_1, local_id_2]
|
| + mr.project_name = 'proj'
|
| +
|
| + post_data = fake.PostData(status=['Duplicate'],
|
| + merge_into=['non existant id'], owner=['owner@example.com'],
|
| + can=[1], q=[''], colspec=[''], sort=[''], groupby=[''], start=[0],
|
| + num=[100])
|
| + self._MockMethods()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + self.assertEquals('Please enter an issue ID',
|
| + mr.errors.merge_into_id)
|
| +
|
| + def testProcessFormData_DuplicateStatus_Success(self):
|
| + """Test PFD processes null/cleared status values."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, self.project.project_id, 'issue summary',
|
| + 'New', 111L, [], [], [], [], 111L, 'test issue')
|
| + local_id_2 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, self.project.project_id, 'issue summary2',
|
| + 'New', 111L, [], [], [], [], 111L, 'test issue2')
|
| + merge_into_local_id_3 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, self.project.project_id, 'issue summary3',
|
| + 'New', 112L, [], [], [], [], 112L, 'test issue3')
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [local_id_1, local_id_2]
|
| + mr.project_name = 'proj'
|
| +
|
| + post_data = fake.PostData(status=['Duplicate'],
|
| + merge_into=[str(merge_into_local_id_3)], owner=['owner@example.com'],
|
| + can=[1], q=[''], colspec=[''], sort=[''], groupby=[''], start=[0],
|
| + num=[100])
|
| + self._MockMethods()
|
| +
|
| + # Add project_name, CCs and starrers to the merge_into_issue.
|
| + merge_into_issue = self.services.issue.GetIssueByLocalID(
|
| + mr.cnxn, self.project.project_id, merge_into_local_id_3)
|
| + merge_into_issue.project_name = 'proj'
|
| + merge_into_issue.cc_ids = [113L, 120L]
|
| + self.services.issue_star.SetStar(
|
| + mr.cnxn, None, None, merge_into_issue.issue_id, 120L, True)
|
| +
|
| + # Add project_name, CCs and starrers to the source issues.
|
| + # Issue 1
|
| + issue_1 = self.services.issue.GetIssueByLocalID(
|
| + mr.cnxn, self.project.project_id, local_id_1)
|
| + issue_1.project_name = 'proj'
|
| + issue_1.cc_ids = [113L, 114L]
|
| + self.services.issue_star.SetStar(
|
| + mr.cnxn, None, None, issue_1.issue_id, 113L, True)
|
| + # Issue 2
|
| + issue_2 = self.services.issue.GetIssueByLocalID(
|
| + mr.cnxn, self.project.project_id, local_id_2)
|
| + issue_2.project_name = 'proj'
|
| + issue_2.cc_ids = [113L, 115L, 118L]
|
| + self.services.issue_star.SetStar(
|
| + mr.cnxn, None, None, issue_2.issue_id, 114L, True)
|
| + self.services.issue_star.SetStar(
|
| + mr.cnxn, None, None, issue_2.issue_id, 115L, True)
|
| +
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| +
|
| + # Verify both source issues were updated.
|
| + self.assertTrue(
|
| + self.VerifyIssueUpdated(self.project.project_id, local_id_1))
|
| + self.assertTrue(
|
| + self.VerifyIssueUpdated(self.project.project_id, local_id_2))
|
| +
|
| + # Verify that the merge into issue was updated with a comment.
|
| + comments = self.services.issue.GetCommentsForIssue(
|
| + self.cnxn, merge_into_issue.issue_id)
|
| + self.assertEquals(
|
| + 'Issue 1 has been merged into this issue.\n'
|
| + 'Issue 2 has been merged into this issue.', comments[-1].content)
|
| +
|
| + # Verify CC lists and owner were merged to the merge_into issue.
|
| + self.assertEquals(
|
| + [113L, 120L, 114L, 115L, 118L, 111L], merge_into_issue.cc_ids)
|
| + # Verify new starrers were added to the merge_into issue.
|
| + self.assertEquals(4,
|
| + self.services.issue_star.CountItemStars(
|
| + self.cnxn, merge_into_issue.issue_id))
|
| + self.assertEquals([120L, 113L, 114L, 115L],
|
| + self.services.issue_star.LookupItemStarrers(
|
| + self.cnxn, merge_into_issue.issue_id))
|
| +
|
| + def testProcessFormData_ClearStatus(self):
|
| + """Test PFD processes null/cleared status values."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, 789, 'issue summary', 'New', 111L,
|
| + [], [], [], [], 111L, 'test issue')
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [local_id_1]
|
| +
|
| + post_data = fake.PostData(
|
| + op_statusenter=['clear'], owner=['owner@example.com'], can=[1],
|
| + q=[''], colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
|
| + self._MockMethods()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + self.assertTrue(self.VerifyIssueUpdated(789, local_id_1))
|
| +
|
| + def testProcessFormData_InvalidOwner(self):
|
| + """Test PFD rejects invalid owner emails."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, 789, 'issue summary', 'New', None,
|
| + [], [], [], [], 111L, 'test issue')
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.local_id_list = [local_id_1]
|
| + post_data = fake.PostData(
|
| + owner=['invalid'])
|
| + self.servlet.response = Response()
|
| + self._MockMethods()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| + self.assertTrue(mr.errors.AnyErrors())
|
| +
|
| + def testProcessFormData_MoveTo(self):
|
| + """Test PFD processes move_to values."""
|
| + local_id_1 = self.services.issue.CreateIssue(
|
| + self.cnxn, self.services, 789, 'issue to move', 'New', 111L,
|
| + [], [], [], [], 111L, 'test issue')
|
| + move_to_project = self.services.project.TestAddProject(
|
| + name='proj2', project_id=790, owner_ids=[111])
|
| +
|
| + mr = testing_helpers.MakeMonorailRequest(
|
| + project=self.project,
|
| + perms=permissions.OWNER_ACTIVE_PERMISSIONSET,
|
| + user_info={'user_id': 111})
|
| + mr.project_name = 'proj'
|
| + mr.local_id_list = [local_id_1]
|
| +
|
| + self._MockMethods()
|
| + post_data = fake.PostData(
|
| + move_to=['proj2'], can=[1], q=[''],
|
| + colspec=[''], sort=[''], groupby=[''], start=[0], num=[100])
|
| + self.servlet.response = Response()
|
| + self.servlet.ProcessFormData(mr, post_data)
|
| +
|
| + issue = self.services.issue.GetIssueByLocalID(
|
| + self.cnxn, move_to_project.project_id, local_id_1)
|
| + self.assertIsNotNone(issue)
|
|
|