| 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.tracker.issuedetail.""" | 
 |    7  | 
 |    8 import logging | 
 |    9 import mox | 
 |   10 import time | 
 |   11 import unittest | 
 |   12  | 
 |   13 import settings | 
 |   14 from features import notify | 
 |   15 from framework import permissions | 
 |   16 from framework import template_helpers | 
 |   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 services import issue_svc | 
 |   22 from testing import fake | 
 |   23 from testing import testing_helpers | 
 |   24 from tracker import issuedetail | 
 |   25 from tracker import tracker_constants | 
 |   26 from tracker import tracker_helpers | 
 |   27  | 
 |   28  | 
 |   29 class IssueDetailTest(unittest.TestCase): | 
 |   30  | 
 |   31   def setUp(self): | 
 |   32     self.cnxn = 'fake cnxn' | 
 |   33     self.services = service_manager.Services( | 
 |   34         config=fake.ConfigService(), | 
 |   35         issue=fake.IssueService(), | 
 |   36         user=fake.UserService(), | 
 |   37         project=fake.ProjectService(), | 
 |   38         issue_star=fake.IssueStarService(), | 
 |   39         spam=fake.SpamService()) | 
 |   40     self.project = self.services.project.TestAddProject('proj', project_id=987) | 
 |   41     self.config = tracker_pb2.ProjectIssueConfig() | 
 |   42     self.config.statuses_offer_merge.append('Duplicate') | 
 |   43     self.services.config.StoreConfig(self.cnxn, self.config) | 
 |   44  | 
 |   45   def testChooseNextPage(self): | 
 |   46     mr = testing_helpers.MakeMonorailRequest( | 
 |   47         path='/p/proj/issues/detail?id=123&q=term') | 
 |   48     mr.col_spec = '' | 
 |   49     config = tracker_pb2.ProjectIssueConfig() | 
 |   50     issue = fake.MakeTestIssue(987, 123, 'summary', 'New', 111L) | 
 |   51  | 
 |   52     url = issuedetail._ChooseNextPage( | 
 |   53         mr, issue.local_id, config, None, None, | 
 |   54         user_pb2.IssueUpdateNav.UP_TO_LIST, '124') | 
 |   55     self.assertTrue(url.startswith( | 
 |   56         'http://127.0.0.1/p/proj/issues/list?cursor=proj%3A123&q=term')) | 
 |   57     self.assertTrue(url.endswith('&updated=123')) | 
 |   58  | 
 |   59     url = issuedetail._ChooseNextPage( | 
 |   60         mr, issue.local_id, config, None, None, | 
 |   61         user_pb2.IssueUpdateNav.STAY_SAME_ISSUE, '124') | 
 |   62     self.assertEqual('http://127.0.0.1/p/proj/issues/detail?id=123&q=term', | 
 |   63                      url) | 
 |   64  | 
 |   65     url = issuedetail._ChooseNextPage( | 
 |   66         mr, issue.local_id, config, None, None, | 
 |   67         user_pb2.IssueUpdateNav.NEXT_IN_LIST, '124') | 
 |   68     self.assertEqual('http://127.0.0.1/p/proj/issues/detail?id=124&q=term', | 
 |   69                      url) | 
 |   70  | 
 |   71     # If this is the last in the list, the next_id from the form will be ''. | 
 |   72     url = issuedetail._ChooseNextPage( | 
 |   73         mr, issue.local_id, config, None, None, | 
 |   74         user_pb2.IssueUpdateNav.NEXT_IN_LIST, '') | 
 |   75     self.assertTrue(url.startswith( | 
 |   76         'http://127.0.0.1/p/proj/issues/list?cursor=proj%3A123&q=term')) | 
 |   77     self.assertTrue(url.endswith('&updated=123')) | 
 |   78  | 
 |   79   def testChooseNextPage_ForMoveRequest(self): | 
 |   80     mr = testing_helpers.MakeMonorailRequest( | 
 |   81         path='/p/proj/issues/detail?id=123&q=term') | 
 |   82     mr.col_spec = '' | 
 |   83     config = tracker_pb2.ProjectIssueConfig() | 
 |   84     issue = fake.MakeTestIssue(987, 123, 'summary', 'New', 111L) | 
 |   85     moved_to_project_name = 'projB' | 
 |   86     moved_to_project_local_id = 543 | 
 |   87     moved_to_project_name_and_local_id = (moved_to_project_name, | 
 |   88                                           moved_to_project_local_id) | 
 |   89  | 
 |   90     url = issuedetail._ChooseNextPage( | 
 |   91         mr, issue.local_id, config, moved_to_project_name_and_local_id, None, | 
 |   92         user_pb2.IssueUpdateNav.UP_TO_LIST, '124') | 
 |   93     self.assertTrue(url.startswith( | 
 |   94         'http://127.0.0.1/p/proj/issues/list?cursor=proj%3A123&moved_to_id=' + | 
 |   95         str(moved_to_project_local_id) + '&moved_to_project=' + | 
 |   96         moved_to_project_name + '&q=term')) | 
 |   97  | 
 |   98     url = issuedetail._ChooseNextPage( | 
 |   99         mr, issue.local_id, config, moved_to_project_name_and_local_id, None, | 
 |  100         user_pb2.IssueUpdateNav.STAY_SAME_ISSUE, '124') | 
 |  101     self.assertEqual( | 
 |  102         'http://127.0.0.1/p/%s/issues/detail?id=123&q=term' % ( | 
 |  103             moved_to_project_name), | 
 |  104         url) | 
 |  105     mr.project_name = 'proj'  # reset project name back. | 
 |  106  | 
 |  107     url = issuedetail._ChooseNextPage( | 
 |  108         mr, issue.local_id, config, moved_to_project_name_and_local_id, None, | 
 |  109         user_pb2.IssueUpdateNav.NEXT_IN_LIST, '124') | 
 |  110     self.assertEqual('http://127.0.0.1/p/proj/issues/detail?id=124&q=term', | 
 |  111                      url) | 
 |  112  | 
 |  113     # If this is the last in the list, the next_id from the form will be ''. | 
 |  114     url = issuedetail._ChooseNextPage( | 
 |  115         mr, issue.local_id, config, moved_to_project_name_and_local_id, None, | 
 |  116         user_pb2.IssueUpdateNav.NEXT_IN_LIST, '') | 
 |  117     self.assertTrue(url.startswith( | 
 |  118         'http://127.0.0.1/p/proj/issues/list?cursor=proj%3A123&moved_to_id=' + | 
 |  119         str(moved_to_project_local_id) + '&moved_to_project=' + | 
 |  120         moved_to_project_name + '&q=term')) | 
 |  121  | 
 |  122   def testChooseNextPage_ForCopyRequest(self): | 
 |  123     mr = testing_helpers.MakeMonorailRequest( | 
 |  124         path='/p/proj/issues/detail?id=123&q=term') | 
 |  125     mr.col_spec = '' | 
 |  126     config = tracker_pb2.ProjectIssueConfig() | 
 |  127     issue = fake.MakeTestIssue(987, 123, 'summary', 'New', 111L) | 
 |  128     copied_to_project_name = 'projB' | 
 |  129     copied_to_project_local_id = 543 | 
 |  130     copied_to_project_name_and_local_id = (copied_to_project_name, | 
 |  131                                            copied_to_project_local_id) | 
 |  132  | 
 |  133     url = issuedetail._ChooseNextPage( | 
 |  134         mr, issue.local_id, config, None, copied_to_project_name_and_local_id, | 
 |  135         user_pb2.IssueUpdateNav.UP_TO_LIST, '124') | 
 |  136     self.assertTrue(url.startswith( | 
 |  137         'http://127.0.0.1/p/proj/issues/list?copied_from_id=123' | 
 |  138         '&copied_to_id=' + str(copied_to_project_local_id) + | 
 |  139         '&copied_to_project=' + copied_to_project_name + | 
 |  140         '&cursor=proj%3A123&q=term')) | 
 |  141  | 
 |  142     url = issuedetail._ChooseNextPage( | 
 |  143         mr, issue.local_id, config, None, copied_to_project_name_and_local_id, | 
 |  144         user_pb2.IssueUpdateNav.STAY_SAME_ISSUE, '124') | 
 |  145     self.assertEqual('http://127.0.0.1/p/proj/issues/detail?id=123&q=term', url) | 
 |  146     mr.project_name = 'proj'  # reset project name back. | 
 |  147  | 
 |  148     url = issuedetail._ChooseNextPage( | 
 |  149         mr, issue.local_id, config, None, copied_to_project_name_and_local_id, | 
 |  150         user_pb2.IssueUpdateNav.NEXT_IN_LIST, '124') | 
 |  151     self.assertEqual('http://127.0.0.1/p/proj/issues/detail?id=124&q=term', | 
 |  152                      url) | 
 |  153  | 
 |  154     # If this is the last in the list, the next_id from the form will be ''. | 
 |  155     url = issuedetail._ChooseNextPage( | 
 |  156         mr, issue.local_id, config, None, copied_to_project_name_and_local_id, | 
 |  157         user_pb2.IssueUpdateNav.NEXT_IN_LIST, '') | 
 |  158     self.assertTrue(url.startswith( | 
 |  159         'http://127.0.0.1/p/proj/issues/list?copied_from_id=123' | 
 |  160         '&copied_to_id=' + str(copied_to_project_local_id) + | 
 |  161         '&copied_to_project=' + copied_to_project_name + | 
 |  162         '&cursor=proj%3A123&q=term')) | 
 |  163  | 
 |  164   def testGatherHelpData(self): | 
 |  165     servlet = issuedetail.IssueDetail('req', 'res', services=self.services) | 
 |  166     mr = testing_helpers.MakeMonorailRequest() | 
 |  167  | 
 |  168     # User did not jump to an issue, no query at all. | 
 |  169     help_data = servlet.GatherHelpData(mr, {}) | 
 |  170     self.assertEqual(None, help_data['cue']) | 
 |  171  | 
 |  172     # User did not jump to an issue, query was not a local ID number. | 
 |  173     mr.query = 'memory leak' | 
 |  174     help_data = servlet.GatherHelpData(mr, {}) | 
 |  175     self.assertEqual(None, help_data['cue']) | 
 |  176  | 
 |  177     # User jumped directly to an issue, maybe they meant to search instead. | 
 |  178     mr.query = '123' | 
 |  179     help_data = servlet.GatherHelpData(mr, {}) | 
 |  180     self.assertEqual('search_for_numbers', help_data['cue']) | 
 |  181     self.assertEqual(123, help_data['jump_local_id']) | 
 |  182  | 
 |  183  | 
 |  184 class IssueDetailFunctionsTest(unittest.TestCase): | 
 |  185  | 
 |  186   def setUp(self): | 
 |  187     self.project_name = 'proj' | 
 |  188     self.project_id = 987 | 
 |  189     self.cnxn = 'fake cnxn' | 
 |  190     self.services = service_manager.Services( | 
 |  191         config=fake.ConfigService(), | 
 |  192         issue=fake.IssueService(), | 
 |  193         issue_star=fake.IssueStarService(), | 
 |  194         project=fake.ProjectService(), | 
 |  195         user=fake.UserService()) | 
 |  196     self.project = self.services.project.TestAddProject( | 
 |  197       'proj', project_id=987, committer_ids=[111L]) | 
 |  198     self.servlet = issuedetail.IssueDetail( | 
 |  199         'req', 'res', services=self.services) | 
 |  200     self.mox = mox.Mox() | 
 |  201  | 
 |  202   def tearDown(self): | 
 |  203     self.mox.UnsetStubs() | 
 |  204     self.mox.ResetAll() | 
 |  205  | 
 |  206   def VerifyShouldShowFlipper( | 
 |  207       self, expected, query, sort_spec, can, create_issues=0): | 
 |  208     """Instantiate a _Flipper and check if makes a pipeline or not.""" | 
 |  209     services = service_manager.Services( | 
 |  210         config=fake.ConfigService(), | 
 |  211         issue=fake.IssueService(), | 
 |  212         project=fake.ProjectService(), | 
 |  213         user=fake.UserService()) | 
 |  214     mr = testing_helpers.MakeMonorailRequest(project=self.project) | 
 |  215     mr.query = query | 
 |  216     mr.sort_spec = sort_spec | 
 |  217     mr.can = can | 
 |  218     mr.project_name = self.project.project_name | 
 |  219     mr.project = self.project | 
 |  220  | 
 |  221     for idx in range(create_issues): | 
 |  222       _local_id = services.issue.CreateIssue( | 
 |  223           self.cnxn, services, self.project.project_id, | 
 |  224           'summary_%d' % idx, 'status', 111L, [], [], [], [], 111L, | 
 |  225           'description_%d' % idx) | 
 |  226  | 
 |  227     self.assertEqual( | 
 |  228         expected, | 
 |  229         issuedetail._ShouldShowFlipper(mr, services)) | 
 |  230  | 
 |  231   def testShouldShowFlipper_RegularSizedProject(self): | 
 |  232     # If the user is looking for a specific issue, no flipper. | 
 |  233     self.VerifyShouldShowFlipper( | 
 |  234         False, '123', '', tracker_constants.OPEN_ISSUES_CAN) | 
 |  235     self.VerifyShouldShowFlipper(False, '123', '', 5) | 
 |  236     self.VerifyShouldShowFlipper( | 
 |  237         False, '123', 'priority', tracker_constants.OPEN_ISSUES_CAN) | 
 |  238  | 
 |  239     # If the user did a search or sort or all in a small can, show flipper. | 
 |  240     self.VerifyShouldShowFlipper( | 
 |  241         True, 'memory leak', '', tracker_constants.OPEN_ISSUES_CAN) | 
 |  242     self.VerifyShouldShowFlipper( | 
 |  243         True, 'id=1,2,3', '', tracker_constants.OPEN_ISSUES_CAN) | 
 |  244     # Any can other than 1 or 2 is doing a query and so it should have a | 
 |  245     # failry narrow result set size.  5 is issues starred by me. | 
 |  246     self.VerifyShouldShowFlipper(True, '', '', 5) | 
 |  247     self.VerifyShouldShowFlipper( | 
 |  248         True, '', 'status', tracker_constants.OPEN_ISSUES_CAN) | 
 |  249  | 
 |  250     # In a project without a huge number of issues, still show the flipper even | 
 |  251     # if there was no specific query. | 
 |  252     self.VerifyShouldShowFlipper( | 
 |  253         True, '', '', tracker_constants.OPEN_ISSUES_CAN) | 
 |  254  | 
 |  255   def testShouldShowFlipper_LargeSizedProject(self): | 
 |  256     settings.threshold_to_suppress_prev_next = 1 | 
 |  257  | 
 |  258     # In a project that has tons of issues, save time by not showing the | 
 |  259     # flipper unless there was a specific query, sort, or can. | 
 |  260     self.VerifyShouldShowFlipper( | 
 |  261         False, '', '', tracker_constants.ALL_ISSUES_CAN, create_issues=3) | 
 |  262     self.VerifyShouldShowFlipper( | 
 |  263         False, '', '', tracker_constants.OPEN_ISSUES_CAN, create_issues=3) | 
 |  264  | 
 |  265   def testFieldEditPermitted_NoEdit(self): | 
 |  266     page_perms = testing_helpers.Blank( | 
 |  267         EditIssueSummary=False, EditIssueStatus=False, EditIssueOwner=False, | 
 |  268         EditIssueCc=False)  # no perms are needed. | 
 |  269     self.assertTrue(issuedetail._FieldEditPermitted( | 
 |  270         [], '', '', '', '', 0, [], page_perms)) | 
 |  271  | 
 |  272   def testFieldEditPermitted_AllNeededPerms(self): | 
 |  273     page_perms = testing_helpers.Blank( | 
 |  274         EditIssueSummary=True, EditIssueStatus=True, EditIssueOwner=True, | 
 |  275         EditIssueCc=True) | 
 |  276     self.assertTrue(issuedetail._FieldEditPermitted( | 
 |  277         [], '', '', 'new sum', 'new status', 111L, [222L], page_perms)) | 
 |  278  | 
 |  279   def testFieldEditPermitted_MissingPerms(self): | 
 |  280     page_perms = testing_helpers.Blank( | 
 |  281         EditIssueSummary=False, EditIssueStatus=False, EditIssueOwner=False, | 
 |  282         EditIssueCc=False)  # no perms. | 
 |  283     self.assertFalse(issuedetail._FieldEditPermitted( | 
 |  284         [], '', '', 'new sum', '', 0, [], page_perms)) | 
 |  285     self.assertFalse(issuedetail._FieldEditPermitted( | 
 |  286         [], '', '', '', 'new status', 0, [], page_perms)) | 
 |  287     self.assertFalse(issuedetail._FieldEditPermitted( | 
 |  288         [], '', '', '', '', 111L, [], page_perms)) | 
 |  289     self.assertFalse(issuedetail._FieldEditPermitted( | 
 |  290         [], '', '', '', '', 0, [222L], page_perms)) | 
 |  291  | 
 |  292   def testFieldEditPermitted_NeededPermsNotOffered(self): | 
 |  293     """Even if user has all the field-level perms, they still can't do this.""" | 
 |  294     page_perms = testing_helpers.Blank( | 
 |  295         EditIssueSummary=True, EditIssueStatus=True, EditIssueOwner=True, | 
 |  296         EditIssueCc=True) | 
 |  297     self.assertFalse(issuedetail._FieldEditPermitted( | 
 |  298         ['NewLabel'], '', '', '', '', 0, [], page_perms)) | 
 |  299     self.assertFalse(issuedetail._FieldEditPermitted( | 
 |  300         [], 'new blocked on', '', '', '', 0, [], page_perms)) | 
 |  301     self.assertFalse(issuedetail._FieldEditPermitted( | 
 |  302         [], '', 'new blocking', '', '', 0, [], page_perms)) | 
 |  303  | 
 |  304   def testValidateOwner_ChangedToValidOwner(self): | 
 |  305     post_data_owner = 'superman@krypton.com' | 
 |  306     parsed_owner_id = 111 | 
 |  307     original_issue_owner_id = 111 | 
 |  308     mr = testing_helpers.MakeMonorailRequest(project=self.project) | 
 |  309  | 
 |  310     self.mox.StubOutWithMock(tracker_helpers, 'IsValidIssueOwner') | 
 |  311     tracker_helpers.IsValidIssueOwner( | 
 |  312         mr.cnxn, mr.project, parsed_owner_id, self.services).AndReturn( | 
 |  313             (True, '')) | 
 |  314     self.mox.ReplayAll() | 
 |  315  | 
 |  316     ret = self.servlet._ValidateOwner( | 
 |  317         mr, post_data_owner, parsed_owner_id, original_issue_owner_id) | 
 |  318     self.mox.VerifyAll() | 
 |  319     self.assertIsNone(ret) | 
 |  320  | 
 |  321   def testValidateOwner_UnchangedInvalidOwner(self): | 
 |  322     post_data_owner = 'superman@krypton.com' | 
 |  323     parsed_owner_id = 111 | 
 |  324     original_issue_owner_id = 111 | 
 |  325     mr = testing_helpers.MakeMonorailRequest(project=self.project) | 
 |  326     self.services.user.TestAddUser(post_data_owner, original_issue_owner_id) | 
 |  327  | 
 |  328     self.mox.StubOutWithMock(tracker_helpers, 'IsValidIssueOwner') | 
 |  329     tracker_helpers.IsValidIssueOwner( | 
 |  330         mr.cnxn, mr.project, parsed_owner_id, self.services).AndReturn( | 
 |  331             (False, 'invalid owner')) | 
 |  332     self.mox.ReplayAll() | 
 |  333  | 
 |  334     ret = self.servlet._ValidateOwner( | 
 |  335         mr, post_data_owner, parsed_owner_id, original_issue_owner_id) | 
 |  336     self.mox.VerifyAll() | 
 |  337     self.assertIsNone(ret) | 
 |  338  | 
 |  339   def testValidateOwner_ChangedFromValidToInvalidOwner(self): | 
 |  340     post_data_owner = 'lexluthor' | 
 |  341     parsed_owner_id = 111 | 
 |  342     original_issue_owner_id = 111 | 
 |  343     original_issue_owner = 'superman@krypton.com' | 
 |  344     mr = testing_helpers.MakeMonorailRequest(project=self.project) | 
 |  345     self.services.user.TestAddUser(original_issue_owner, | 
 |  346                                    original_issue_owner_id) | 
 |  347  | 
 |  348     self.mox.StubOutWithMock(tracker_helpers, 'IsValidIssueOwner') | 
 |  349     tracker_helpers.IsValidIssueOwner( | 
 |  350         mr.cnxn, mr.project, parsed_owner_id, self.services).AndReturn( | 
 |  351             (False, 'invalid owner')) | 
 |  352     self.mox.ReplayAll() | 
 |  353  | 
 |  354     ret = self.servlet._ValidateOwner( | 
 |  355         mr, post_data_owner, parsed_owner_id, original_issue_owner_id) | 
 |  356     self.mox.VerifyAll() | 
 |  357     self.assertEquals('invalid owner', ret) | 
 |  358  | 
 |  359   def testProcessFormData_NoPermission(self): | 
 |  360     """Anonymous users and users without ADD_ISSUE_COMMENT cannot comment.""" | 
 |  361     local_id_1 = self.services.issue.CreateIssue( | 
 |  362         self.cnxn, self.services, self.project.project_id, | 
 |  363         'summary_1', 'status', 111L, [], [], [], [], 111L, 'description_1') | 
 |  364     _, mr = testing_helpers.GetRequestObjects( | 
 |  365         project=self.project, | 
 |  366         perms=permissions.CONTRIBUTOR_INACTIVE_PERMISSIONSET) | 
 |  367     mr.auth.user_id = 0 | 
 |  368     mr.local_id = local_id_1 | 
 |  369     self.assertRaises(permissions.PermissionException, | 
 |  370                       self.servlet.ProcessFormData, mr, {}) | 
 |  371     mr.auth.user_id = 111L | 
 |  372     self.assertRaises(permissions.PermissionException, | 
 |  373                       self.servlet.ProcessFormData, mr, {}) | 
 |  374  | 
 |  375   def testProcessFormData_NonMembersCantEdit(self): | 
 |  376     """Non-members can comment, but never affect issue fields.""" | 
 |  377     orig_prepsend = notify.PrepareAndSendIssueChangeNotification | 
 |  378     notify.PrepareAndSendIssueChangeNotification = lambda *args, **kwargs: None | 
 |  379  | 
 |  380     local_id_1 = self.services.issue.CreateIssue( | 
 |  381         self.cnxn, self.services, self.project.project_id, | 
 |  382         'summary_1', 'status', 111L, [], [], [], [], 111L, 'description_1') | 
 |  383     local_id_2 = self.services.issue.CreateIssue( | 
 |  384         self.cnxn, self.services, self.project.project_id, | 
 |  385         'summary_2', 'status', 111L, [], [], [], [], 111L, 'description_2') | 
 |  386  | 
 |  387     _amendments, _cmnt_pb = self.services.issue.ApplyIssueComment( | 
 |  388         self.cnxn, self.services, 111L, | 
 |  389         self.project.project_id, local_id_2, 'summary', 'Duplicate', 111L, | 
 |  390         [], [], [], [], [], [], [], [], local_id_1, | 
 |  391         comment='closing as a dup of 1') | 
 |  392  | 
 |  393     non_member_user_id = 999L | 
 |  394     post_data = fake.PostData({ | 
 |  395         'merge_into': [''],  # non-member tries to remove merged_into | 
 |  396         'comment': ['thanks!'], | 
 |  397         'can': ['1'], | 
 |  398         'q': ['foo'], | 
 |  399         'colspec': ['bar'], | 
 |  400         'sort': 'baz', | 
 |  401         'groupby': 'qux', | 
 |  402         'start': ['0'], | 
 |  403         'num': ['100'], | 
 |  404         'pagegen': [str(int(time.time()) + 1)], | 
 |  405         }) | 
 |  406  | 
 |  407     _, mr = testing_helpers.GetRequestObjects( | 
 |  408         user_info={'user_id': non_member_user_id}, | 
 |  409         path='/p/proj/issues/detail.do?id=%d' % local_id_2, | 
 |  410         project=self.project, method='POST', | 
 |  411         perms=permissions.USER_PERMISSIONSET) | 
 |  412     mr.project_name = self.project.project_name | 
 |  413     mr.project = self.project | 
 |  414  | 
 |  415     # The form should be processed and redirect back to viewing the issue. | 
 |  416     redirect_url = self.servlet.ProcessFormData(mr, post_data) | 
 |  417     self.assertTrue(redirect_url.startswith( | 
 |  418         'http://127.0.0.1/p/proj/issues/detail?id=%d' % local_id_2)) | 
 |  419  | 
 |  420     # BUT, issue should not have been edited because user lacked permission. | 
 |  421     updated_issue_2 = self.services.issue.GetIssueByLocalID( | 
 |  422         self.cnxn, self.project.project_id, local_id_2) | 
 |  423     self.assertEqual(local_id_1, updated_issue_2.merged_into) | 
 |  424  | 
 |  425     notify.PrepareAndSendIssueChangeNotification = orig_prepsend | 
 |  426  | 
 |  427   def testProcessFormData_DuplicateAddsACommentToTarget(self): | 
 |  428     """Marking issue 2 as dup of 1 adds a comment to 1.""" | 
 |  429     orig_prepsend = notify.PrepareAndSendIssueChangeNotification | 
 |  430     notify.PrepareAndSendIssueChangeNotification = lambda *args, **kwargs: None | 
 |  431     orig_get_starrers = tracker_helpers.GetNewIssueStarrers | 
 |  432     tracker_helpers.GetNewIssueStarrers = lambda *args, **kwargs: [] | 
 |  433  | 
 |  434     local_id_1 = self.services.issue.CreateIssue( | 
 |  435         self.cnxn, self.services, self.project.project_id, | 
 |  436         'summary_1', 'New', 111L, [], [], [], [], 111L, 'description_1') | 
 |  437     issue_1 = self.services.issue.GetIssueByLocalID( | 
 |  438         self.cnxn, self.project.project_id, local_id_1) | 
 |  439     issue_1.project_name = 'proj' | 
 |  440     local_id_2 = self.services.issue.CreateIssue( | 
 |  441         self.cnxn, self.services, self.project.project_id, | 
 |  442         'summary_2', 'New', 111L, [], [], [], [], 111L, 'description_2') | 
 |  443     issue_2 = self.services.issue.GetIssueByLocalID( | 
 |  444         self.cnxn, self.project.project_id, local_id_2) | 
 |  445     issue_2.project_name = 'proj' | 
 |  446  | 
 |  447     post_data = fake.PostData({ | 
 |  448         'status': ['Duplicate'], | 
 |  449         'merge_into': [str(local_id_1)], | 
 |  450         'comment': ['marking as dup'], | 
 |  451         'can': ['1'], | 
 |  452         'q': ['foo'], | 
 |  453         'colspec': ['bar'], | 
 |  454         'sort': 'baz', | 
 |  455         'groupby': 'qux', | 
 |  456         'start': ['0'], | 
 |  457         'num': ['100'], | 
 |  458         'pagegen': [str(int(time.time()) + 1)], | 
 |  459         }) | 
 |  460  | 
 |  461     member_user_id = 111L | 
 |  462     _, mr = testing_helpers.GetRequestObjects( | 
 |  463         user_info={'user_id': member_user_id}, | 
 |  464         path='/p/proj/issues/detail.do?id=%d' % local_id_2, | 
 |  465         project=self.project, method='POST', | 
 |  466         perms=permissions.COMMITTER_ACTIVE_PERMISSIONSET) | 
 |  467     mr.project_name = self.project.project_name | 
 |  468     mr.project = self.project | 
 |  469  | 
 |  470     # The form should be processed and redirect back to viewing the issue. | 
 |  471     self.servlet.ProcessFormData(mr, post_data) | 
 |  472  | 
 |  473     self.assertEqual('Duplicate', issue_2.status) | 
 |  474     self.assertEqual(issue_1.issue_id, issue_2.merged_into) | 
 |  475     comments_1 = self.services.issue.GetCommentsForIssue( | 
 |  476         self.cnxn, issue_1.issue_id) | 
 |  477     self.assertEqual(2, len(comments_1)) | 
 |  478     self.assertEqual( | 
 |  479         'Issue 2 has been merged into this issue.', | 
 |  480         comments_1[1].content) | 
 |  481  | 
 |  482     # Making another comment on issue 2 does not affect issue 1. | 
 |  483     self.servlet.ProcessFormData(mr, post_data) | 
 |  484     comments_1 = self.services.issue.GetCommentsForIssue( | 
 |  485         self.cnxn, issue_1.issue_id) | 
 |  486     self.assertEqual(2, len(comments_1)) | 
 |  487  | 
 |  488     notify.PrepareAndSendIssueChangeNotification = orig_prepsend | 
 |  489     tracker_helpers.GetNewIssueStarrers = orig_get_starrers | 
 |  490  | 
 |  491     # TODO(jrobbins): add more unit tests for other aspects of ProcessForm. | 
 |  492  | 
 |  493  | 
 |  494 class SetStarFormTest(unittest.TestCase): | 
 |  495  | 
 |  496   def setUp(self): | 
 |  497     self.cnxn = 'fake cnxn' | 
 |  498     self.services = service_manager.Services( | 
 |  499         config=fake.ConfigService(), | 
 |  500         issue=fake.IssueService(), | 
 |  501         user=fake.UserService(), | 
 |  502         project=fake.ProjectService(), | 
 |  503         issue_star=fake.IssueStarService()) | 
 |  504     self.project = self.services.project.TestAddProject('proj', project_id=987) | 
 |  505     self.servlet = issuedetail.SetStarForm( | 
 |  506         'req', 'res', services=self.services) | 
 |  507  | 
 |  508   def testAssertBasePermission(self): | 
 |  509     """Only users with SET_STAR could set star.""" | 
 |  510     local_id_1 = self.services.issue.CreateIssue( | 
 |  511         self.cnxn, self.services, self.project.project_id, | 
 |  512         'summary_1', 'status', 111L, [], [], [], [], 111L, 'description_1') | 
 |  513     _, mr = testing_helpers.GetRequestObjects( | 
 |  514         project=self.project, | 
 |  515         perms=permissions.READ_ONLY_PERMISSIONSET) | 
 |  516     mr.local_id = local_id_1 | 
 |  517     self.assertRaises(permissions.PermissionException, | 
 |  518                       self.servlet.AssertBasePermission, mr) | 
 |  519     _, mr = testing_helpers.GetRequestObjects( | 
 |  520         project=self.project, | 
 |  521         perms=permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET) | 
 |  522     mr.local_id = local_id_1 | 
 |  523     self.servlet.AssertBasePermission(mr) | 
 |  524  | 
 |  525  | 
 |  526 class IssueCommentDeletionTest(unittest.TestCase): | 
 |  527  | 
 |  528   def setUp(self): | 
 |  529     self.cnxn = 'fake cnxn' | 
 |  530     self.services = service_manager.Services( | 
 |  531         config=fake.ConfigService(), | 
 |  532         issue=fake.IssueService(), | 
 |  533         user=fake.UserService(), | 
 |  534         project=fake.ProjectService(), | 
 |  535         issue_star=fake.IssueStarService()) | 
 |  536     self.project = self.services.project.TestAddProject('proj', project_id=987) | 
 |  537     self.servlet = issuedetail.IssueCommentDeletion( | 
 |  538         'req', 'res', services=self.services) | 
 |  539  | 
 |  540   def testProcessFormData_Permission(self): | 
 |  541     """Permit users who can delete.""" | 
 |  542     local_id_1 = self.services.issue.CreateIssue( | 
 |  543         self.cnxn, self.services, self.project.project_id, | 
 |  544         'summary_1', 'status', 111L, [], [], [], [], 111L, 'description_1') | 
 |  545     _, mr = testing_helpers.GetRequestObjects( | 
 |  546         project=self.project, | 
 |  547         perms=permissions.READ_ONLY_PERMISSIONSET) | 
 |  548     mr.local_id = local_id_1 | 
 |  549     mr.auth.user_id = 222L | 
 |  550     post_data = { | 
 |  551         'id': local_id_1, | 
 |  552         'sequence_num': 0, | 
 |  553         'mode': 0} | 
 |  554     self.assertRaises(permissions.PermissionException, | 
 |  555                       self.servlet.ProcessFormData, mr, post_data) | 
 |  556     _, mr = testing_helpers.GetRequestObjects( | 
 |  557         project=self.project, | 
 |  558         perms=permissions.OWNER_ACTIVE_PERMISSIONSET) | 
 |  559     mr.local_id = local_id_1 | 
 |  560     mr.auth.user_id = 222L | 
 |  561     self.servlet.ProcessFormData(mr, post_data) | 
 |  562  | 
 |  563  | 
 |  564 if __name__ == '__main__': | 
 |  565   unittest.main() | 
| OLD | NEW |