Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame^] | 1 | # Copyright 2020 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 | """Tests for the hotlists servicer.""" |
| 6 | from __future__ import print_function |
| 7 | from __future__ import division |
| 8 | from __future__ import absolute_import |
| 9 | |
| 10 | import unittest |
| 11 | |
| 12 | from google.protobuf import timestamp_pb2 |
| 13 | from mock import patch |
| 14 | |
| 15 | from api import resource_name_converters as rnc |
| 16 | from api.v3 import converters |
| 17 | from api.v3 import frontend_servicer |
| 18 | from api.v3.api_proto import frontend_pb2 |
| 19 | from api.v3.api_proto import project_objects_pb2 |
| 20 | from framework import exceptions |
| 21 | from framework import monorailcontext |
| 22 | from proto import tracker_pb2 |
| 23 | from services import service_manager |
| 24 | from testing import fake |
| 25 | from tracker import tracker_constants |
| 26 | |
| 27 | |
| 28 | class FrontendServicerTest(unittest.TestCase): |
| 29 | |
| 30 | def setUp(self): |
| 31 | self.cnxn = fake.MonorailConnection() |
| 32 | self.services = service_manager.Services( |
| 33 | features=fake.FeaturesService(), |
| 34 | issue=fake.IssueService(), |
| 35 | project=fake.ProjectService(), |
| 36 | config=fake.ConfigService(), |
| 37 | user=fake.UserService(), |
| 38 | template=fake.TemplateService(), |
| 39 | usergroup=fake.UserGroupService()) |
| 40 | self.frontend_svcr = frontend_servicer.FrontendServicer( |
| 41 | self.services, make_rate_limiter=False) |
| 42 | |
| 43 | self.user_1 = self.services.user.TestAddUser('user_111@example.com', 111) |
| 44 | self.user_1_resource_name = 'users/111' |
| 45 | self.project_1_resource_name = 'projects/proj' |
| 46 | self.project_1 = self.services.project.TestAddProject( |
| 47 | 'proj', project_id=789) |
| 48 | self.template_0 = self.services.template.TestAddIssueTemplateDef( |
| 49 | 11110, self.project_1.project_id, 'template0') |
| 50 | self.PAST_TIME = 12345 |
| 51 | self.component_def_1_path = 'foo' |
| 52 | self.component_def_1_id = self.services.config.CreateComponentDef( |
| 53 | self.cnxn, self.project_1.project_id, self.component_def_1_path, |
| 54 | 'cd1_docstring', False, [self.user_1.user_id], [], self.PAST_TIME, |
| 55 | self.user_1.user_id, []) |
| 56 | self.field_def_1_name = 'test_field_1' |
| 57 | self.field_def_1 = self._CreateFieldDef( |
| 58 | self.project_1.project_id, |
| 59 | self.field_def_1_name, |
| 60 | 'STR_TYPE', |
| 61 | admin_ids=[self.user_1.user_id], |
| 62 | is_required=True, |
| 63 | is_multivalued=True, |
| 64 | is_phase_field=True, |
| 65 | regex='abc') |
| 66 | self.approval_def_1_name = 'approval_field_1' |
| 67 | self.approval_def_1_id = self._CreateFieldDef( |
| 68 | self.project_1.project_id, |
| 69 | self.approval_def_1_name, |
| 70 | 'APPROVAL_TYPE', |
| 71 | docstring='ad_1_docstring', |
| 72 | admin_ids=[self.user_1.user_id]) |
| 73 | self.approval_def_1 = tracker_pb2.ApprovalDef( |
| 74 | approval_id=self.approval_def_1_id, |
| 75 | approver_ids=[self.user_1.user_id], |
| 76 | survey='approval_def_1 survey') |
| 77 | self.services.config.UpdateConfig( |
| 78 | self.cnxn, |
| 79 | self.project_1, |
| 80 | # UpdateConfig accepts tuples rather than protorpc *Defs |
| 81 | approval_defs=[ |
| 82 | (ad.approval_id, ad.approver_ids, ad.survey) |
| 83 | for ad in [self.approval_def_1] |
| 84 | ]) |
| 85 | |
| 86 | def _CreateFieldDef( |
| 87 | self, |
| 88 | project_id, |
| 89 | field_name, |
| 90 | field_type_str, |
| 91 | docstring=None, |
| 92 | min_value=None, |
| 93 | max_value=None, |
| 94 | regex=None, |
| 95 | needs_member=None, |
| 96 | needs_perm=None, |
| 97 | grants_perm=None, |
| 98 | notify_on=None, |
| 99 | date_action_str=None, |
| 100 | admin_ids=None, |
| 101 | editor_ids=None, |
| 102 | is_required=False, |
| 103 | is_niche=False, |
| 104 | is_multivalued=False, |
| 105 | is_phase_field=False, |
| 106 | approval_id=None, |
| 107 | is_restricted_field=False): |
| 108 | """Calls CreateFieldDef with reasonable defaults, returns the ID.""" |
| 109 | if admin_ids is None: |
| 110 | admin_ids = [] |
| 111 | if editor_ids is None: |
| 112 | editor_ids = [] |
| 113 | return self.services.config.CreateFieldDef( |
| 114 | self.cnxn, |
| 115 | project_id, |
| 116 | field_name, |
| 117 | field_type_str, |
| 118 | None, |
| 119 | None, |
| 120 | is_required, |
| 121 | is_niche, |
| 122 | is_multivalued, |
| 123 | min_value, |
| 124 | max_value, |
| 125 | regex, |
| 126 | needs_member, |
| 127 | needs_perm, |
| 128 | grants_perm, |
| 129 | notify_on, |
| 130 | date_action_str, |
| 131 | docstring, |
| 132 | admin_ids, |
| 133 | editor_ids, |
| 134 | is_phase_field=is_phase_field, |
| 135 | approval_id=approval_id, |
| 136 | is_restricted_field=is_restricted_field) |
| 137 | |
| 138 | def CallWrapped(self, wrapped_handler, mc, *args, **kwargs): |
| 139 | self.frontend_svcr.converter = converters.Converter(mc, self.services) |
| 140 | return wrapped_handler.wrapped(self.frontend_svcr, mc, *args, **kwargs) |
| 141 | |
| 142 | @patch('project.project_helpers.GetThumbnailUrl') |
| 143 | def testGatherProjectEnvironment(self, mock_GetThumbnailUrl): |
| 144 | """We can fetch all project related parameters for web frontend.""" |
| 145 | mock_GetThumbnailUrl.return_value = 'xyz' |
| 146 | |
| 147 | request = frontend_pb2.GatherProjectEnvironmentRequest( |
| 148 | parent=self.project_1_resource_name) |
| 149 | mc = monorailcontext.MonorailContext( |
| 150 | self.services, cnxn=self.cnxn, requester=self.user_1.email) |
| 151 | response = self.CallWrapped( |
| 152 | self.frontend_svcr.GatherProjectEnvironment, mc, request) |
| 153 | project_config = self.services.config.GetProjectConfig( |
| 154 | self.cnxn, self.project_1.project_id) |
| 155 | |
| 156 | self.assertEqual( |
| 157 | response.project, |
| 158 | self.frontend_svcr.converter.ConvertProject(self.project_1)) |
| 159 | self.assertEqual( |
| 160 | response.project_config, |
| 161 | self.frontend_svcr.converter.ConvertProjectConfig(project_config)) |
| 162 | |
| 163 | self.assertEqual( |
| 164 | len(response.statuses), |
| 165 | len(tracker_constants.DEFAULT_WELL_KNOWN_STATUSES)) |
| 166 | self.assertEqual( |
| 167 | response.statuses[0], |
| 168 | project_objects_pb2.StatusDef( |
| 169 | name='projects/{project_name}/statusDefs/{status}'.format( |
| 170 | project_name=self.project_1.project_name, |
| 171 | status=tracker_constants.DEFAULT_WELL_KNOWN_STATUSES[0][0]), |
| 172 | value=tracker_constants.DEFAULT_WELL_KNOWN_STATUSES[0][0], |
| 173 | type=project_objects_pb2.StatusDef.StatusDefType.Value('OPEN'), |
| 174 | rank=0, |
| 175 | docstring=tracker_constants.DEFAULT_WELL_KNOWN_STATUSES[0][1], |
| 176 | state=project_objects_pb2.StatusDef.StatusDefState.Value('ACTIVE'), |
| 177 | )) |
| 178 | |
| 179 | self.assertEqual( |
| 180 | len(response.well_known_labels), |
| 181 | len(tracker_constants.DEFAULT_WELL_KNOWN_LABELS)) |
| 182 | self.assertEqual( |
| 183 | response.well_known_labels[0], |
| 184 | project_objects_pb2.LabelDef( |
| 185 | name='projects/{project_name}/labelDefs/{label}'.format( |
| 186 | project_name=self.project_1.project_name, |
| 187 | label=tracker_constants.DEFAULT_WELL_KNOWN_LABELS[0][0]), |
| 188 | value=tracker_constants.DEFAULT_WELL_KNOWN_LABELS[0][0], |
| 189 | docstring=tracker_constants.DEFAULT_WELL_KNOWN_LABELS[0][1], |
| 190 | state=project_objects_pb2.LabelDef.LabelDefState.Value('ACTIVE'), |
| 191 | )) |
| 192 | |
| 193 | expected = self.frontend_svcr.converter.ConvertComponentDefs( |
| 194 | project_config.component_defs, self.project_1.project_id) |
| 195 | # Have to use list comprehension to break response sub field into list |
| 196 | self.assertEqual([api_cd for api_cd in response.components], expected) |
| 197 | |
| 198 | expected = self.frontend_svcr.converter.ConvertFieldDefs( |
| 199 | project_config.field_defs, self.project_1.project_id) |
| 200 | self.assertEqual([api_fd for api_fd in response.fields], expected) |
| 201 | |
| 202 | expected = self.frontend_svcr.converter.ConvertApprovalDefs( |
| 203 | project_config.approval_defs, self.project_1.project_id) |
| 204 | self.assertEqual([api_ad for api_ad in response.approval_fields], expected) |
| 205 | |
| 206 | def testGatherProjectMembershipsForUser(self): |
| 207 | """We can list a user's project memberships.""" |
| 208 | self.services.project.TestAddProject( |
| 209 | 'owner_proj', project_id=777, owner_ids=[111]) |
| 210 | self.services.project.TestAddProject( |
| 211 | 'committer_proj', project_id=888, committer_ids=[111]) |
| 212 | contributor_proj = self.services.project.TestAddProject( |
| 213 | 'contributor_proj', project_id=999) |
| 214 | contributor_proj.contributor_ids = [111] |
| 215 | |
| 216 | request = frontend_pb2.GatherProjectMembershipsForUserRequest( |
| 217 | user=self.user_1_resource_name) |
| 218 | mc = monorailcontext.MonorailContext( |
| 219 | self.services, cnxn=self.cnxn, requester=self.user_1.email) |
| 220 | response = self.CallWrapped( |
| 221 | self.frontend_svcr.GatherProjectMembershipsForUser, mc, request) |
| 222 | |
| 223 | owner_membership = project_objects_pb2.ProjectMember( |
| 224 | name='projects/{}/members/{}'.format('owner_proj', '111'), |
| 225 | role=project_objects_pb2.ProjectMember.ProjectRole.Value('OWNER')) |
| 226 | committer_membership = project_objects_pb2.ProjectMember( |
| 227 | name='projects/{}/members/{}'.format('committer_proj', '111'), |
| 228 | role=project_objects_pb2.ProjectMember.ProjectRole.Value('COMMITTER')) |
| 229 | contributor_membership = project_objects_pb2.ProjectMember( |
| 230 | name='projects/{}/members/{}'.format('contributor_proj', '111'), |
| 231 | role=project_objects_pb2.ProjectMember.ProjectRole.Value('CONTRIBUTOR')) |
| 232 | self.assertEqual( |
| 233 | response, |
| 234 | frontend_pb2.GatherProjectMembershipsForUserResponse( |
| 235 | project_memberships=[ |
| 236 | owner_membership, committer_membership, contributor_membership |
| 237 | ])) |