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 """Tests for the API v1.""" |
| 7 |
| 8 import endpoints |
| 9 import unittest |
| 10 import webtest |
| 11 from google.appengine.api import oauth |
| 12 from mock import Mock |
| 13 from protorpc import messages |
| 14 from protorpc import message_types |
| 15 |
| 16 from framework import monorailrequest |
| 17 from framework import permissions |
| 18 from framework import template_helpers |
| 19 from proto import project_pb2 |
| 20 from proto import tracker_pb2 |
| 21 from search import frontendsearchpipeline |
| 22 from services import api_svc_v1 |
| 23 from services import issue_svc |
| 24 from services import project_svc |
| 25 from services import service_manager |
| 26 from services import user_svc |
| 27 from testing import fake |
| 28 from testing_utils import testing |
| 29 from tracker import tracker_bizobj |
| 30 |
| 31 |
| 32 def MakeFakeServiceManager(): |
| 33 return service_manager.Services( |
| 34 user=fake.UserService(), |
| 35 usergroup=fake.UserGroupService(), |
| 36 project=fake.ProjectService(), |
| 37 config=fake.ConfigService(), |
| 38 issue=fake.IssueService(), |
| 39 issue_star=fake.IssueStarService(), |
| 40 features=fake.FeaturesService(), |
| 41 cache_manager=fake.CacheManager()) |
| 42 |
| 43 |
| 44 class FakeMonorailApiRequest(object): |
| 45 |
| 46 def __init__(self, request, services, perms=None): |
| 47 self.cnxn = None |
| 48 self.auth = monorailrequest.AuthData.FromEmail( |
| 49 self.cnxn, request['requester'], services) |
| 50 self.me_user_id = self.auth.user_id |
| 51 self.project_name = None |
| 52 self.project = None |
| 53 self.viewed_username = None |
| 54 self.viewed_user_auth = None |
| 55 self.config = None |
| 56 if 'userId' in request: |
| 57 self.viewed_username = request['userId'] |
| 58 self.viewed_user_auth = monorailrequest.AuthData.FromEmail( |
| 59 self.cnxn, self.viewed_username, services) |
| 60 elif 'groupName' in request: |
| 61 self.viewed_username = request['groupName'] |
| 62 try: |
| 63 self.viewed_user_auth = monorailrequest.AuthData.FromEmail( |
| 64 self.cnxn, self.viewed_username, services) |
| 65 except user_svc.NoSuchUserException: |
| 66 self.viewed_user_auth = None |
| 67 if 'projectId' in request: |
| 68 self.project_name = request['projectId'] |
| 69 self.project = services.project.GetProjectByName( |
| 70 self.cnxn, self.project_name) |
| 71 self.config = services.config.GetProjectConfig( |
| 72 self.cnxn, self.project_id) |
| 73 self.perms = perms or permissions.GetPermissions( |
| 74 self.auth.user_pb, self.auth.effective_ids, self.project) |
| 75 self.granted_perms = set() |
| 76 |
| 77 self.params = { |
| 78 'can': request.get('can', 1), |
| 79 'start': request.get('startIndex', 0), |
| 80 'num': request.get('maxResults', 100), |
| 81 'q': request.get('q', ''), |
| 82 'sort': request.get('sort', ''), |
| 83 'groupby': '', |
| 84 'projects': request.get('additionalProject', []) + [self.project_name]} |
| 85 self.use_cached_searches = True |
| 86 self.errors = template_helpers.EZTError() |
| 87 self.mode = None |
| 88 |
| 89 self.query_project_names = self.GetParam('projects') |
| 90 self.group_by_spec = self.GetParam('groupby') |
| 91 self.sort_spec = self.GetParam('sort') |
| 92 self.query = self.GetParam('q') |
| 93 self.can = self.GetParam('can') |
| 94 self.start = self.GetParam('start') |
| 95 self.num = self.GetParam('num') |
| 96 |
| 97 @property |
| 98 def project_id(self): |
| 99 return self.project.project_id if self.project else None |
| 100 |
| 101 def GetParam(self, query_param_name, default_value=None, |
| 102 _antitamper_re=None): |
| 103 return self.params.get(query_param_name, default_value) |
| 104 |
| 105 def GetPositiveIntParam(self, query_param_name, default_value=None): |
| 106 """Returns 0 if the user-provided value is less than 0.""" |
| 107 return max(self.GetParam(query_param_name, default_value=default_value), |
| 108 0) |
| 109 |
| 110 |
| 111 class FakeFrontendSearchPipeline(object): |
| 112 |
| 113 def __init__(self): |
| 114 issue1 = fake.MakeTestIssue( |
| 115 project_id=12345, local_id=1, owner_id=2, status='New', summary='sum') |
| 116 issue2 = fake.MakeTestIssue( |
| 117 project_id=12345, local_id=2, owner_id=2, status='New', summary='sum') |
| 118 self.allowed_results = [issue1, issue2] |
| 119 self.visible_results = [issue1] |
| 120 self.total_count = len(self.allowed_results) |
| 121 self.config = None |
| 122 self.projectId = 0 |
| 123 |
| 124 def SearchForIIDs(self): |
| 125 pass |
| 126 |
| 127 def MergeAndSortIssues(self): |
| 128 pass |
| 129 |
| 130 def Paginate(self): |
| 131 pass |
| 132 |
| 133 |
| 134 class MonorailApiTest(testing.EndpointsTestCase): |
| 135 |
| 136 api_service_cls = api_svc_v1.MonorailApi |
| 137 |
| 138 def makeMar(self, request): |
| 139 return FakeMonorailApiRequest(request, self.services) |
| 140 |
| 141 def setUp(self): |
| 142 super(MonorailApiTest, self).setUp() |
| 143 self.requester = RequesterMock(email='requester@example.com') |
| 144 self.mock(endpoints, 'get_current_user', lambda: self.requester) |
| 145 self.config = None |
| 146 self.services = MakeFakeServiceManager() |
| 147 self.mock(api_svc_v1.MonorailApi, '_services', self.services) |
| 148 self.services.user.TestAddUser('requester@example.com', 1) |
| 149 self.services.user.TestAddUser('user@example.com', 2) |
| 150 self.services.user.TestAddUser('group@example.com', 123) |
| 151 self.services.usergroup.TestAddGroupSettings(123, 'group@example.com') |
| 152 self.request = { |
| 153 'userId': 'user@example.com', |
| 154 'ownerProjectsOnly': False, |
| 155 'requester': 'requester@example.com', |
| 156 'projectId': 'test-project', |
| 157 'issueId': 1} |
| 158 self.mock(api_svc_v1.MonorailApi, 'mar_factory', |
| 159 lambda x, y: FakeMonorailApiRequest(self.request, self.services)) |
| 160 |
| 161 # api_base_checks is tested in AllBaseChecksTest, |
| 162 # so mock it to reduce noise. |
| 163 self.mock(api_svc_v1, 'api_base_checks', lambda x, y, z, u, v, w: None) |
| 164 |
| 165 def SetUpComponents( |
| 166 self, project_id, component_id, component_name, component_doc='doc', |
| 167 deprecated=False, admin_ids=None, cc_ids=None, created=100000, creator=1): |
| 168 admin_ids = admin_ids or [] |
| 169 cc_ids = cc_ids or [] |
| 170 self.config = self.services.config.GetProjectConfig( |
| 171 'fake cnxn', project_id) |
| 172 self.services.config.StoreConfig('fake cnxn', self.config) |
| 173 cd = tracker_bizobj.MakeComponentDef( |
| 174 component_id, project_id, component_name, component_doc, deprecated, |
| 175 admin_ids, cc_ids, created, creator) |
| 176 self.config.component_defs.append(cd) |
| 177 |
| 178 def SetUpFieldDefs( |
| 179 self, field_id, project_id, field_name, field_type_int, |
| 180 min_value=0, max_value=100, needs_member=False, docstring='doc'): |
| 181 self.config = self.services.config.GetProjectConfig( |
| 182 'fake cnxn', project_id) |
| 183 self.services.config.StoreConfig('fake cnxn', self.config) |
| 184 fd = tracker_bizobj.MakeFieldDef( |
| 185 field_id, project_id, field_name, field_type_int, '', |
| 186 '', False, False, min_value, max_value, None, needs_member, None, '', |
| 187 tracker_pb2.NotifyTriggers.NEVER, docstring, False) |
| 188 self.config.field_defs.append(fd) |
| 189 |
| 190 def testUsersGet_NoProject(self): |
| 191 """The viewed user has no projects.""" |
| 192 |
| 193 self.services.project.TestAddProject( |
| 194 'public-project', owner_ids=[1]) |
| 195 resp = self.call_api('users_get', self.request).json_body |
| 196 expected = { |
| 197 'id': '2', |
| 198 'kind': 'monorail#user'} |
| 199 self.assertEqual(expected, resp) |
| 200 |
| 201 def testUsersGet_PublicProject(self): |
| 202 """The viewed user has one public project.""" |
| 203 |
| 204 self.services.project.TestAddProject( |
| 205 'public-project', owner_ids=[2]) |
| 206 resp = self.call_api('users_get', self.request).json_body |
| 207 |
| 208 self.assertEqual(1, len(resp['projects'])) |
| 209 self.assertEqual('public-project', resp['projects'][0]['name']) |
| 210 |
| 211 def testUsersGet_PrivateProject(self): |
| 212 """The viewed user has one project but the requester cannot view.""" |
| 213 |
| 214 self.services.project.TestAddProject( |
| 215 'private-project', owner_ids=[2], |
| 216 access=project_pb2.ProjectAccess.MEMBERS_ONLY) |
| 217 resp = self.call_api('users_get', self.request).json_body |
| 218 self.assertNotIn('projects', resp) |
| 219 |
| 220 def testUsersGet_OwnerProjectOnly(self): |
| 221 """The viewed user has different roles of projects.""" |
| 222 |
| 223 self.services.project.TestAddProject( |
| 224 'owner-project', owner_ids=[2]) |
| 225 self.services.project.TestAddProject( |
| 226 'member-project', owner_ids=[1], committer_ids=[2]) |
| 227 resp = self.call_api('users_get', self.request).json_body |
| 228 self.assertEqual(2, len(resp['projects'])) |
| 229 |
| 230 self.request['ownerProjectsOnly'] = True |
| 231 resp = self.call_api('users_get', self.request).json_body |
| 232 self.assertEqual(1, len(resp['projects'])) |
| 233 self.assertEqual('owner-project', resp['projects'][0]['name']) |
| 234 |
| 235 def testIssuesGet_GetIssue(self): |
| 236 """Get the requested issue.""" |
| 237 |
| 238 self.services.project.TestAddProject( |
| 239 'test-project', owner_ids=[2], |
| 240 project_id=12345) |
| 241 self.SetUpComponents(12345, 1, 'API') |
| 242 self.SetUpFieldDefs(1, 12345, 'Field1', tracker_pb2.FieldTypes.INT_TYPE) |
| 243 |
| 244 fv = tracker_pb2.FieldValue( |
| 245 field_id=1, |
| 246 int_value=11) |
| 247 issue1 = fake.MakeTestIssue( |
| 248 project_id=12345, local_id=1, owner_id=2, reporter_id=1, status='New', |
| 249 summary='sum', component_ids=[1], field_values=[fv]) |
| 250 self.services.issue.TestAddIssue(issue1) |
| 251 |
| 252 resp = self.call_api('issues_get', self.request).json_body |
| 253 self.assertEqual(1, resp['id']) |
| 254 self.assertEqual('New', resp['status']) |
| 255 self.assertEqual('open', resp['state']) |
| 256 self.assertFalse(resp['canEdit']) |
| 257 self.assertTrue(resp['canComment']) |
| 258 self.assertEqual('requester@example.com', resp['author']['name']) |
| 259 self.assertEqual('user@example.com', resp['owner']['name']) |
| 260 self.assertEqual('API', resp['components'][0]) |
| 261 self.assertEqual('Field1', resp['fieldValues'][0]['fieldName']) |
| 262 self.assertEqual('11', resp['fieldValues'][0]['fieldValue']) |
| 263 |
| 264 def testIssuesInsert_BadRequest(self): |
| 265 """The request does not specify summary or status.""" |
| 266 |
| 267 with self.assertRaises(webtest.AppError): |
| 268 self.call_api('issues_insert', self.request) |
| 269 |
| 270 issue_dict = { |
| 271 'status': 'New', |
| 272 'summary': 'Test issue', |
| 273 'owner': {'name': 'notexist@example.com'}} |
| 274 self.request.update(issue_dict) |
| 275 self.services.project.TestAddProject( |
| 276 'test-project', owner_ids=[2], |
| 277 project_id=12345) |
| 278 with self.call_should_fail(400): |
| 279 self.call_api('issues_insert', self.request) |
| 280 |
| 281 # Invalid field value |
| 282 self.SetUpFieldDefs(1, 12345, 'Field1', tracker_pb2.FieldTypes.INT_TYPE) |
| 283 issue_dict = { |
| 284 'status': 'New', |
| 285 'summary': 'Test issue', |
| 286 'owner': {'name': 'requester@example.com'}, |
| 287 'fieldValues': [{'fieldName': 'Field1', 'fieldValue': '111'}]} |
| 288 self.request.update(issue_dict) |
| 289 with self.call_should_fail(400): |
| 290 self.call_api('issues_insert', self.request) |
| 291 |
| 292 def testIssuesInsert_NoPermission(self): |
| 293 """The requester has no permission to create issues.""" |
| 294 |
| 295 issue_dict = { |
| 296 'status': 'New', |
| 297 'summary': 'Test issue'} |
| 298 self.request.update(issue_dict) |
| 299 |
| 300 self.services.project.TestAddProject( |
| 301 'test-project', owner_ids=[2], |
| 302 access=project_pb2.ProjectAccess.MEMBERS_ONLY, |
| 303 project_id=12345) |
| 304 with self.call_should_fail(403): |
| 305 self.call_api('issues_insert', self.request) |
| 306 |
| 307 def testIssuesInsert_CreateIssue(self): |
| 308 """Create an issue as requested.""" |
| 309 |
| 310 self.services.project.TestAddProject( |
| 311 'test-project', owner_ids=[2], |
| 312 project_id=12345) |
| 313 self.SetUpFieldDefs(1, 12345, 'Field1', tracker_pb2.FieldTypes.INT_TYPE) |
| 314 |
| 315 issue1 = fake.MakeTestIssue( |
| 316 project_id=12345, local_id=1, owner_id=2, reporter_id=1, status='New', |
| 317 summary='Test issue') |
| 318 self.services.issue.TestAddIssue(issue1) |
| 319 |
| 320 issue_dict = { |
| 321 'blockedOn': [{'issueId': 1}], |
| 322 'cc': [{'name': 'user@example.com'}], |
| 323 'description': 'description', |
| 324 'labels': ['label1', 'label2'], |
| 325 'owner': {'name': 'requester@example.com'}, |
| 326 'status': 'New', |
| 327 'summary': 'Test issue', |
| 328 'fieldValues': [{'fieldName': 'Field1', 'fieldValue': '11'}]} |
| 329 self.request.update(issue_dict) |
| 330 |
| 331 resp = self.call_api('issues_insert', self.request).json_body |
| 332 self.assertEqual('New', resp['status']) |
| 333 self.assertEqual('requester@example.com', resp['author']['name']) |
| 334 self.assertEqual('requester@example.com', resp['owner']['name']) |
| 335 self.assertEqual('user@example.com', resp['cc'][0]['name']) |
| 336 self.assertEqual(1, resp['blockedOn'][0]['issueId']) |
| 337 self.assertEqual([u'label1', u'label2'], resp['labels']) |
| 338 self.assertEqual('Test issue', resp['summary']) |
| 339 self.assertEqual('Field1', resp['fieldValues'][0]['fieldName']) |
| 340 self.assertEqual('11', resp['fieldValues'][0]['fieldValue']) |
| 341 |
| 342 def testIssuesList_NoPermission(self): |
| 343 """No permission for additional projects.""" |
| 344 self.services.project.TestAddProject( |
| 345 'test-project', owner_ids=[2], |
| 346 project_id=12345) |
| 347 |
| 348 self.services.project.TestAddProject( |
| 349 'test-project2', owner_ids=[2], |
| 350 access=project_pb2.ProjectAccess.MEMBERS_ONLY, |
| 351 project_id=123456) |
| 352 self.request['additionalProject'] = ['test-project2'] |
| 353 with self.call_should_fail(403): |
| 354 self.call_api('issues_list', self.request) |
| 355 |
| 356 def testIssuesList_SearchIssues(self): |
| 357 """Find issues of one project.""" |
| 358 |
| 359 self.mock(frontendsearchpipeline, 'FrontendSearchPipeline', |
| 360 lambda x, y, z, w: FakeFrontendSearchPipeline()) |
| 361 |
| 362 self.services.project.TestAddProject( |
| 363 'test-project', owner_ids=[2], |
| 364 access=project_pb2.ProjectAccess.MEMBERS_ONLY, |
| 365 project_id=12345) |
| 366 resp = self.call_api('issues_list', self.request).json_body |
| 367 self.assertEqual(2, int(resp['totalResults'])) |
| 368 self.assertEqual(1, len(resp['items'])) |
| 369 self.assertEqual(1, resp['items'][0]['id']) |
| 370 |
| 371 def testIssuesCommentsList_GetComments(self): |
| 372 """Get comments of requested issue.""" |
| 373 |
| 374 self.services.project.TestAddProject( |
| 375 'test-project', owner_ids=[2], |
| 376 project_id=12345) |
| 377 |
| 378 issue1 = fake.MakeTestIssue( |
| 379 project_id=12345, local_id=1, summary='test summary', status='New', |
| 380 issue_id=10001, owner_id=2, reporter_id=1) |
| 381 self.services.issue.TestAddIssue(issue1) |
| 382 |
| 383 comment = tracker_pb2.IssueComment( |
| 384 id=123, issue_id=10001, |
| 385 project_id=12345, user_id=2, |
| 386 content='this is a comment', |
| 387 timestamp=1437700000) |
| 388 self.services.issue.TestAddComment(comment, 1) |
| 389 |
| 390 resp = self.call_api('issues_comments_list', self.request).json_body |
| 391 self.assertEqual(2, resp['totalResults']) |
| 392 comment1 = resp['items'][0] |
| 393 comment2 = resp['items'][1] |
| 394 self.assertEqual('requester@example.com', comment1['author']['name']) |
| 395 self.assertEqual('test summary', comment1['content']) |
| 396 self.assertEqual('user@example.com', comment2['author']['name']) |
| 397 self.assertEqual('this is a comment', comment2['content']) |
| 398 |
| 399 def testIssuesCommentsInsert_NoCommentPermission(self): |
| 400 """No permission to comment an issue.""" |
| 401 |
| 402 self.services.project.TestAddProject( |
| 403 'test-project', owner_ids=[2], |
| 404 access=project_pb2.ProjectAccess.MEMBERS_ONLY, |
| 405 project_id=12345) |
| 406 |
| 407 issue1 = fake.MakeTestIssue( |
| 408 12345, 1, 'Issue 1', 'New', 2) |
| 409 self.services.issue.TestAddIssue(issue1) |
| 410 |
| 411 with self.call_should_fail(403): |
| 412 self.call_api('issues_comments_insert', self.request) |
| 413 |
| 414 def testIssuesCommentsInsert_Amendments(self): |
| 415 """Insert comments with amendments.""" |
| 416 |
| 417 self.services.project.TestAddProject( |
| 418 'test-project', owner_ids=[2], |
| 419 project_id=12345) |
| 420 |
| 421 issue1 = fake.MakeTestIssue( |
| 422 12345, 1, 'Issue 1', 'New', 2) |
| 423 issue2 = fake.MakeTestIssue( |
| 424 12345, 2, 'Issue 2', 'New', 2) |
| 425 issue3 = fake.MakeTestIssue( |
| 426 12345, 3, 'Issue 3', 'New', 2) |
| 427 issue4 = fake.MakeTestIssue( |
| 428 12345, 4, 'Issue 4', 'New', 2) |
| 429 self.services.issue.TestAddIssue(issue1) |
| 430 self.services.issue.TestAddIssue(issue1) |
| 431 self.services.issue.TestAddIssue(issue2) |
| 432 self.services.issue.TestAddIssue(issue3) |
| 433 self.services.issue.TestAddIssue(issue4) |
| 434 |
| 435 self.request['updates'] = { |
| 436 'summary': 'new summary', |
| 437 'status': 'Duplicate', |
| 438 'owner': 'requester@example.com', |
| 439 'cc': ['user@example.com'], |
| 440 'labels': ['add_label', '-remove_label'], |
| 441 'blockedOn': ['2'], |
| 442 'blocking': ['3'], |
| 443 'merged_into': 4} |
| 444 resp = self.call_api('issues_comments_insert', self.request).json_body |
| 445 self.assertEqual('requester@example.com', resp['author']['name']) |
| 446 self.assertEqual('Updated', resp['updates']['status']) |
| 447 |
| 448 def testIssuesCommentInsert_CustomFields(self): |
| 449 """Update custom field values.""" |
| 450 self.services.project.TestAddProject( |
| 451 'test-project', owner_ids=[2], |
| 452 project_id=12345) |
| 453 issue1 = fake.MakeTestIssue( |
| 454 12345, 1, 'Issue 1', 'New', 2, |
| 455 project_name='test-project') |
| 456 self.services.issue.TestAddIssue(issue1) |
| 457 self.SetUpFieldDefs( |
| 458 1, 12345, 'Field_int', tracker_pb2.FieldTypes.INT_TYPE) |
| 459 self.SetUpFieldDefs( |
| 460 2, 12345, 'Field_enum', tracker_pb2.FieldTypes.ENUM_TYPE) |
| 461 |
| 462 self.request['updates'] = { |
| 463 'fieldValues': [{'fieldName': 'Field_int', 'fieldValue': '11'}, |
| 464 {'fieldName': 'Field_enum', 'fieldValue': 'str'}]} |
| 465 resp = self.call_api('issues_comments_insert', self.request).json_body |
| 466 self.assertEqual('Updated', resp['updates']['status']) |
| 467 |
| 468 def testIssuesCommentInsert_MoveToProject_Fail(self): |
| 469 """Move issue to a different project and failed.""" |
| 470 self.services.project.TestAddProject( |
| 471 'test-project', owner_ids=[2], |
| 472 project_id=12345) |
| 473 issue1 = fake.MakeTestIssue( |
| 474 12345, 1, 'Issue 1', 'New', 2, labels=['Restrict-View-Google'], |
| 475 project_name='test-project') |
| 476 self.services.issue.TestAddIssue(issue1) |
| 477 |
| 478 self.services.project.TestAddProject( |
| 479 'test-project2', owner_ids=[1], |
| 480 project_id=12346) |
| 481 issue2 = fake.MakeTestIssue( |
| 482 12346, 1, 'Issue 1', 'New', 2, project_name='test-project2') |
| 483 self.services.issue.TestAddIssue(issue2) |
| 484 |
| 485 # Project doesn't exist |
| 486 self.request['updates'] = { |
| 487 'moveToProject': 'not exist'} |
| 488 with self.call_should_fail(400): |
| 489 self.call_api('issues_comments_insert', self.request) |
| 490 |
| 491 # The issue is already in destination |
| 492 self.request['updates'] = { |
| 493 'moveToProject': 'test-project'} |
| 494 with self.call_should_fail(400): |
| 495 self.call_api('issues_comments_insert', self.request) |
| 496 |
| 497 # The user has no permission in test-project |
| 498 self.request['projectId'] = 'test-project2' |
| 499 self.request['updates'] = { |
| 500 'moveToProject': 'test-project'} |
| 501 with self.call_should_fail(400): |
| 502 self.call_api('issues_comments_insert', self.request) |
| 503 |
| 504 # Restrict labels |
| 505 self.request['projectId'] = 'test-project' |
| 506 self.request['updates'] = { |
| 507 'moveToProject': 'test-project2'} |
| 508 with self.call_should_fail(400): |
| 509 self.call_api('issues_comments_insert', self.request) |
| 510 |
| 511 def testIssuesCommentInsert_MoveToProject_Normal(self): |
| 512 """Move issue.""" |
| 513 self.services.project.TestAddProject( |
| 514 'test-project', owner_ids=[1, 2], |
| 515 project_id=12345) |
| 516 self.services.project.TestAddProject( |
| 517 'test-project2', owner_ids=[1, 2], |
| 518 project_id=12346) |
| 519 issue1 = fake.MakeTestIssue( |
| 520 12345, 1, 'Issue 1', 'New', 2, project_name='test-project') |
| 521 self.services.issue.TestAddIssue(issue1) |
| 522 issue2 = fake.MakeTestIssue( |
| 523 12346, 1, 'Issue 1', 'New', 2, project_name='test-project2') |
| 524 self.services.issue.TestAddIssue(issue2) |
| 525 |
| 526 self.request['updates'] = { |
| 527 'moveToProject': 'test-project2'} |
| 528 resp = self.call_api('issues_comments_insert', self.request).json_body |
| 529 |
| 530 self.assertEqual( |
| 531 'Moved issue test-project:1 to now be issue test-project:2.', |
| 532 resp['content']) |
| 533 |
| 534 def testIssuesCommentsDelete_NoComment(self): |
| 535 self.services.project.TestAddProject( |
| 536 'test-project', owner_ids=[2], |
| 537 project_id=12345) |
| 538 issue1 = fake.MakeTestIssue( |
| 539 project_id=12345, local_id=1, summary='test summary', |
| 540 issue_id=10001, status='New', owner_id=2, reporter_id=2) |
| 541 self.services.issue.TestAddIssue(issue1) |
| 542 self.request['commentId'] = 1 |
| 543 with self.call_should_fail(404): |
| 544 self.call_api('issues_comments_delete', self.request) |
| 545 |
| 546 def testIssuesCommentsDelete_NoDeletePermission(self): |
| 547 self.services.project.TestAddProject( |
| 548 'test-project', owner_ids=[2], |
| 549 project_id=12345) |
| 550 issue1 = fake.MakeTestIssue( |
| 551 project_id=12345, local_id=1, summary='test summary', |
| 552 issue_id=10001, status='New', owner_id=2, reporter_id=2) |
| 553 self.services.issue.TestAddIssue(issue1) |
| 554 self.request['commentId'] = 0 |
| 555 with self.call_should_fail(403): |
| 556 self.call_api('issues_comments_delete', self.request) |
| 557 |
| 558 def testIssuesCommentsDelete_DeleteUndelete(self): |
| 559 self.services.project.TestAddProject( |
| 560 'test-project', owner_ids=[2], |
| 561 project_id=12345) |
| 562 issue1 = fake.MakeTestIssue( |
| 563 project_id=12345, local_id=1, summary='test summary', |
| 564 issue_id=10001, status='New', owner_id=2, reporter_id=1) |
| 565 self.services.issue.TestAddIssue(issue1) |
| 566 comment = tracker_pb2.IssueComment( |
| 567 id=123, issue_id=10001, |
| 568 project_id=12345, user_id=1, |
| 569 content='this is a comment', |
| 570 timestamp=1437700000) |
| 571 self.services.issue.TestAddComment(comment, 1) |
| 572 self.request['commentId'] = 1 |
| 573 |
| 574 comments = self.services.issue.GetCommentsForIssue(None, 10001) |
| 575 |
| 576 self.call_api('issues_comments_delete', self.request) |
| 577 self.assertEqual(1, comments[1].deleted_by) |
| 578 |
| 579 self.call_api('issues_comments_undelete', self.request) |
| 580 self.assertIsNone(comments[1].deleted_by) |
| 581 |
| 582 def testGroupsSettingsList_AllSettings(self): |
| 583 resp = self.call_api('groups_settings_list', self.request).json_body |
| 584 all_settings = resp['groupSettings'] |
| 585 self.assertEqual(1, len(all_settings)) |
| 586 self.assertEqual('group@example.com', all_settings[0]['groupName']) |
| 587 |
| 588 def testGroupsSettingsList_ImportedSettings(self): |
| 589 self.services.user.TestAddUser('imported@example.com', 234) |
| 590 self.services.usergroup.TestAddGroupSettings( |
| 591 234, 'imported@example.com', external_group_type='mdb') |
| 592 self.request['importedGroupsOnly'] = True |
| 593 resp = self.call_api('groups_settings_list', self.request).json_body |
| 594 all_settings = resp['groupSettings'] |
| 595 self.assertEqual(1, len(all_settings)) |
| 596 self.assertEqual('imported@example.com', all_settings[0]['groupName']) |
| 597 |
| 598 def testGroupsCreate_NoPermission(self): |
| 599 self.request['groupName'] = 'group' |
| 600 with self.call_should_fail(403): |
| 601 self.call_api('groups_create', self.request) |
| 602 |
| 603 def SetUpGroupRequest(self, group_name, who_can_view_members='MEMBERS', |
| 604 ext_group_type=None, perms=None, |
| 605 requester='requester@example.com'): |
| 606 request = { |
| 607 'groupName': group_name, |
| 608 'requester': requester, |
| 609 'who_can_view_members': who_can_view_members, |
| 610 'ext_group_type': ext_group_type} |
| 611 self.request.pop("userId", None) |
| 612 self.mock(api_svc_v1.MonorailApi, 'mar_factory', |
| 613 lambda x, y: FakeMonorailApiRequest( |
| 614 request, self.services, perms)) |
| 615 return request |
| 616 |
| 617 def testGroupsCreate_Normal(self): |
| 618 request = self.SetUpGroupRequest('newgroup@example.com', 'MEMBERS', |
| 619 'MDB', permissions.ADMIN_PERMISSIONSET) |
| 620 |
| 621 resp = self.call_api('groups_create', request).json_body |
| 622 self.assertIn('groupID', resp) |
| 623 |
| 624 def testGroupsGet_NoPermission(self): |
| 625 request = self.SetUpGroupRequest('group@example.com') |
| 626 with self.call_should_fail(403): |
| 627 self.call_api('groups_get', request) |
| 628 |
| 629 def testGroupsGet_Normal(self): |
| 630 request = self.SetUpGroupRequest('group@example.com', |
| 631 perms=permissions.ADMIN_PERMISSIONSET) |
| 632 self.services.usergroup.TestAddMembers(123, [1], 'member') |
| 633 self.services.usergroup.TestAddMembers(123, [2], 'owner') |
| 634 resp = self.call_api('groups_get', request).json_body |
| 635 self.assertEqual(123, resp['groupID']) |
| 636 self.assertEqual(['requester@example.com'], resp['groupMembers']) |
| 637 self.assertEqual(['user@example.com'], resp['groupOwners']) |
| 638 self.assertEqual('group@example.com', resp['groupSettings']['groupName']) |
| 639 |
| 640 def testGroupsUpdate_NoPermission(self): |
| 641 request = self.SetUpGroupRequest('group@example.com') |
| 642 with self.call_should_fail(403): |
| 643 self.call_api('groups_update', request) |
| 644 |
| 645 def testGroupsUpdate_Normal(self): |
| 646 request = self.SetUpGroupRequest('group@example.com') |
| 647 request = self.SetUpGroupRequest('group@example.com', |
| 648 perms=permissions.ADMIN_PERMISSIONSET) |
| 649 request['last_sync_time'] = 123456789 |
| 650 request['groupOwners'] = ['requester@example.com'] |
| 651 request['groupMembers'] = ['user@example.com'] |
| 652 resp = self.call_api('groups_update', request).json_body |
| 653 self.assertFalse(resp.get('error')) |
| 654 |
| 655 def testComponentsList(self): |
| 656 """Get components for a project.""" |
| 657 self.services.project.TestAddProject( |
| 658 'test-project', owner_ids=[2], |
| 659 project_id=12345) |
| 660 self.SetUpComponents(12345, 1, 'API') |
| 661 resp = self.call_api('components_list', self.request).json_body |
| 662 |
| 663 self.assertEqual(1, len(resp['components'])) |
| 664 cd = resp['components'][0] |
| 665 self.assertEqual(1, cd['componentId']) |
| 666 self.assertEqual('API', cd['componentPath']) |
| 667 self.assertEqual(1, cd['componentId']) |
| 668 self.assertEqual('test-project', cd['projectName']) |
| 669 |
| 670 def testComponentsCreate_NoPermission(self): |
| 671 self.services.project.TestAddProject( |
| 672 'test-project', owner_ids=[2], |
| 673 project_id=12345) |
| 674 self.SetUpComponents(12345, 1, 'API') |
| 675 |
| 676 cd_dict = { |
| 677 'componentName': 'Test'} |
| 678 self.request.update(cd_dict) |
| 679 |
| 680 with self.call_should_fail(403): |
| 681 self.call_api('components_create', self.request) |
| 682 |
| 683 def testComponentsCreate_Invalid(self): |
| 684 self.services.project.TestAddProject( |
| 685 'test-project', owner_ids=[1], |
| 686 project_id=12345) |
| 687 self.SetUpComponents(12345, 1, 'API') |
| 688 |
| 689 # Component with invalid name |
| 690 cd_dict = { |
| 691 'componentName': 'c>d>e'} |
| 692 self.request.update(cd_dict) |
| 693 with self.call_should_fail(400): |
| 694 self.call_api('components_create', self.request) |
| 695 |
| 696 # Name already in use |
| 697 cd_dict = { |
| 698 'componentName': 'API'} |
| 699 self.request.update(cd_dict) |
| 700 with self.call_should_fail(400): |
| 701 self.call_api('components_create', self.request) |
| 702 |
| 703 # Parent component does not exist |
| 704 cd_dict = { |
| 705 'componentName': 'test', |
| 706 'parentPath': 'NotExist'} |
| 707 self.request.update(cd_dict) |
| 708 with self.call_should_fail(404): |
| 709 self.call_api('components_create', self.request) |
| 710 |
| 711 |
| 712 def testComponentsCreate_Normal(self): |
| 713 self.services.project.TestAddProject( |
| 714 'test-project', owner_ids=[1], |
| 715 project_id=12345) |
| 716 self.SetUpComponents(12345, 1, 'API') |
| 717 |
| 718 cd_dict = { |
| 719 'componentName': 'Test', |
| 720 'description':'test comp', |
| 721 'cc': ['requester@example.com']} |
| 722 self.request.update(cd_dict) |
| 723 |
| 724 resp = self.call_api('components_create', self.request).json_body |
| 725 self.assertEqual('test comp', resp['description']) |
| 726 self.assertEqual('requester@example.com', resp['creator']) |
| 727 self.assertEqual([u'requester@example.com'], resp['cc']) |
| 728 self.assertEqual('Test', resp['componentPath']) |
| 729 |
| 730 cd_dict = { |
| 731 'componentName': 'TestChild', |
| 732 'parentPath': 'API'} |
| 733 self.request.update(cd_dict) |
| 734 resp = self.call_api('components_create', self.request).json_body |
| 735 |
| 736 self.assertEqual('API>TestChild', resp['componentPath']) |
| 737 |
| 738 def testComponentsDelete_Invalid(self): |
| 739 self.services.project.TestAddProject( |
| 740 'test-project', owner_ids=[2], |
| 741 project_id=12345) |
| 742 self.SetUpComponents(12345, 1, 'API') |
| 743 |
| 744 # Fail to delete a non-existent component |
| 745 cd_dict = { |
| 746 'componentPath': 'NotExist'} |
| 747 self.request.update(cd_dict) |
| 748 with self.call_should_fail(404): |
| 749 self.call_api('components_delete', self.request) |
| 750 |
| 751 # The user has no permission to delete component |
| 752 cd_dict = { |
| 753 'componentPath': 'API'} |
| 754 self.request.update(cd_dict) |
| 755 with self.call_should_fail(403): |
| 756 self.call_api('components_delete', self.request) |
| 757 |
| 758 # The user tries to delete component that had subcomponents |
| 759 self.services.project.TestAddProject( |
| 760 'test-project2', owner_ids=[1], |
| 761 project_id=123456) |
| 762 self.SetUpComponents(123456, 1, 'Parent') |
| 763 self.SetUpComponents(123456, 2, 'Parent>Child') |
| 764 cd_dict = { |
| 765 'componentPath': 'Parent', |
| 766 'projectId': 'test-project2',} |
| 767 self.request.update(cd_dict) |
| 768 with self.call_should_fail(403): |
| 769 self.call_api('components_delete', self.request) |
| 770 |
| 771 def testComponentsDelete_Normal(self): |
| 772 self.services.project.TestAddProject( |
| 773 'test-project', owner_ids=[1], |
| 774 project_id=12345) |
| 775 self.SetUpComponents(12345, 1, 'API') |
| 776 |
| 777 cd_dict = { |
| 778 'componentPath': 'API'} |
| 779 self.request.update(cd_dict) |
| 780 _ = self.call_api('components_delete', self.request).json_body |
| 781 self.assertEqual(0, len(self.config.component_defs)) |
| 782 |
| 783 def testComponentsUpdate_Invalid(self): |
| 784 self.services.project.TestAddProject( |
| 785 'test-project', owner_ids=[2], |
| 786 project_id=12345) |
| 787 self.SetUpComponents(12345, 1, 'API') |
| 788 self.SetUpComponents(12345, 2, 'Test', admin_ids=[1]) |
| 789 |
| 790 # Fail to update a non-existent component |
| 791 cd_dict = { |
| 792 'componentPath': 'NotExist'} |
| 793 self.request.update(cd_dict) |
| 794 with self.call_should_fail(404): |
| 795 self.call_api('components_update', self.request) |
| 796 |
| 797 # The user has no permission to edit component |
| 798 cd_dict = { |
| 799 'componentPath': 'API'} |
| 800 self.request.update(cd_dict) |
| 801 with self.call_should_fail(403): |
| 802 self.call_api('components_update', self.request) |
| 803 |
| 804 # The user tries an invalid component name |
| 805 cd_dict = { |
| 806 'componentPath': 'Test', |
| 807 'updates': [{'field': 'LEAF_NAME', 'leafName': 'c>e'}]} |
| 808 self.request.update(cd_dict) |
| 809 with self.call_should_fail(400): |
| 810 self.call_api('components_update', self.request) |
| 811 |
| 812 # The user tries a name already in use |
| 813 cd_dict = { |
| 814 'componentPath': 'Test', |
| 815 'updates': [{'field': 'LEAF_NAME', 'leafName': 'API'}]} |
| 816 self.request.update(cd_dict) |
| 817 with self.call_should_fail(400): |
| 818 self.call_api('components_update', self.request) |
| 819 |
| 820 def testComponentsUpdate_Normal(self): |
| 821 self.services.project.TestAddProject( |
| 822 'test-project', owner_ids=[1], |
| 823 project_id=12345) |
| 824 self.SetUpComponents(12345, 1, 'API') |
| 825 self.SetUpComponents(12345, 2, 'Parent') |
| 826 self.SetUpComponents(12345, 3, 'Parent>Child') |
| 827 |
| 828 cd_dict = { |
| 829 'componentPath': 'API', |
| 830 'updates': [ |
| 831 {'field': 'DESCRIPTION', 'description': ''}, |
| 832 {'field': 'CC', 'cc': ['requester@example.com', 'user@example.com']}, |
| 833 {'field': 'DEPRECATED', 'deprecated': True}]} |
| 834 self.request.update(cd_dict) |
| 835 _ = self.call_api('components_update', self.request).json_body |
| 836 component_def = tracker_bizobj.FindComponentDef( |
| 837 'API', self.config) |
| 838 self.assertIsNotNone(component_def) |
| 839 self.assertEqual('', component_def.docstring) |
| 840 self.assertEqual([1L, 2L], component_def.cc_ids) |
| 841 self.assertTrue(component_def.deprecated) |
| 842 |
| 843 cd_dict = { |
| 844 'componentPath': 'Parent', |
| 845 'updates': [ |
| 846 {'field': 'LEAF_NAME', 'leafName': 'NewParent'}]} |
| 847 self.request.update(cd_dict) |
| 848 _ = self.call_api('components_update', self.request).json_body |
| 849 cd_parent = tracker_bizobj.FindComponentDef( |
| 850 'NewParent', self.config) |
| 851 cd_child = tracker_bizobj.FindComponentDef( |
| 852 'NewParent>Child', self.config) |
| 853 self.assertIsNotNone(cd_parent) |
| 854 self.assertIsNotNone(cd_child) |
| 855 |
| 856 |
| 857 class RequestMock(object): |
| 858 |
| 859 def __init__(self): |
| 860 self.projectId = None |
| 861 self.issueId = None |
| 862 |
| 863 |
| 864 class RequesterMock(object): |
| 865 |
| 866 def __init__(self, email=None): |
| 867 self._email = email |
| 868 |
| 869 def email(self): |
| 870 return self._email |
| 871 |
| 872 |
| 873 class AllBaseChecksTest(unittest.TestCase): |
| 874 |
| 875 def setUp(self): |
| 876 self.services = MakeFakeServiceManager() |
| 877 self.services.user.TestAddUser('test@example.com', 1) |
| 878 self.services.project.TestAddProject( |
| 879 'test-project', owner_ids=[1], project_id=123, |
| 880 access=project_pb2.ProjectAccess.MEMBERS_ONLY) |
| 881 self.auth_client_ids = ['123456789.apps.googleusercontent.com'] |
| 882 oauth.get_client_id = Mock(return_value=self.auth_client_ids[0]) |
| 883 oauth.get_current_user = Mock( |
| 884 return_value=RequesterMock(email='test@example.com')) |
| 885 |
| 886 def testUnauthorizedRequester(self): |
| 887 with self.assertRaises(endpoints.UnauthorizedException): |
| 888 api_svc_v1.api_base_checks(None, None, None, None, [], []) |
| 889 |
| 890 def testNoUser(self): |
| 891 requester = RequesterMock(email='notexist@example.com') |
| 892 with self.assertRaises(user_svc.NoSuchUserException): |
| 893 api_svc_v1.api_base_checks( |
| 894 None, requester, self.services, None, self.auth_client_ids, []) |
| 895 |
| 896 def testNoOauthUser(self): |
| 897 oauth.get_current_user.side_effect = oauth.Error() |
| 898 with self.assertRaises(endpoints.UnauthorizedException): |
| 899 api_svc_v1.api_base_checks( |
| 900 None, None, self.services, None, [], []) |
| 901 |
| 902 def testBannedUser(self): |
| 903 banned_email = 'banned@example.com' |
| 904 self.services.user.TestAddUser(banned_email, 2, banned=True) |
| 905 requester = RequesterMock(email=banned_email) |
| 906 with self.assertRaises(permissions.BannedUserException): |
| 907 api_svc_v1.api_base_checks( |
| 908 None, requester, self.services, None, self.auth_client_ids, []) |
| 909 |
| 910 def testNoProject(self): |
| 911 request = RequestMock() |
| 912 request.projectId = 'notexist-project' |
| 913 requester = RequesterMock(email='test@example.com') |
| 914 with self.assertRaises(project_svc.NoSuchProjectException): |
| 915 api_svc_v1.api_base_checks( |
| 916 request, requester, self.services, None, self.auth_client_ids, []) |
| 917 |
| 918 def testNonLiveProject(self): |
| 919 archived_project = 'archived-project' |
| 920 self.services.project.TestAddProject( |
| 921 archived_project, owner_ids=[1], |
| 922 state=project_pb2.ProjectState.ARCHIVED) |
| 923 request = RequestMock() |
| 924 request.projectId = archived_project |
| 925 requester = RequesterMock(email='test@example.com') |
| 926 with self.assertRaises(permissions.PermissionException): |
| 927 api_svc_v1.api_base_checks( |
| 928 request, requester, self.services, None, self.auth_client_ids, []) |
| 929 |
| 930 def testNoViewProjectPermission(self): |
| 931 nonmember_email = 'nonmember@example.com' |
| 932 self.services.user.TestAddUser(nonmember_email, 2) |
| 933 requester = RequesterMock(email=nonmember_email) |
| 934 request = RequestMock() |
| 935 request.projectId = 'test-project' |
| 936 with self.assertRaises(permissions.PermissionException): |
| 937 api_svc_v1.api_base_checks( |
| 938 request, requester, self.services, None, self.auth_client_ids, []) |
| 939 |
| 940 def testAllPass(self): |
| 941 requester = RequesterMock(email='test@example.com') |
| 942 request = RequestMock() |
| 943 request.projectId = 'test-project' |
| 944 try: |
| 945 api_svc_v1.api_base_checks( |
| 946 request, requester, self.services, None, self.auth_client_ids, []) |
| 947 except Exception as e: |
| 948 self.fail('Unexpected exception: %s' % str(e)) |
| 949 |
| 950 def testNoIssue(self): |
| 951 requester = RequesterMock(email='test@example.com') |
| 952 request = RequestMock() |
| 953 request.projectId = 'test-project' |
| 954 request.issueId = 12345 |
| 955 with self.assertRaises(issue_svc.NoSuchIssueException): |
| 956 api_svc_v1.api_base_checks( |
| 957 request, requester, self.services, None, self.auth_client_ids, []) |
| 958 |
| 959 def testNoViewIssuePermission(self): |
| 960 requester = RequesterMock(email='test@example.com') |
| 961 request = RequestMock() |
| 962 request.projectId = 'test-project' |
| 963 request.issueId = 1 |
| 964 issue1 = fake.MakeTestIssue( |
| 965 project_id=123, local_id=1, summary='test summary', |
| 966 status='New', owner_id=1, reporter_id=1) |
| 967 issue1.deleted = True |
| 968 self.services.issue.TestAddIssue(issue1) |
| 969 with self.assertRaises(permissions.PermissionException): |
| 970 api_svc_v1.api_base_checks( |
| 971 request, requester, self.services, None, self.auth_client_ids, []) |
| 972 |
| 973 def testAnonymousClients(self): |
| 974 oauth.get_client_id = Mock(return_value='anonymous') |
| 975 requester = RequesterMock(email='test@example.com') |
| 976 request = RequestMock() |
| 977 request.projectId = 'test-project' |
| 978 try: |
| 979 api_svc_v1.api_base_checks( |
| 980 request, requester, self.services, None, [], ['test@example.com']) |
| 981 except Exception as e: |
| 982 self.fail('Unexpected exception: %s' % str(e)) |
| 983 |
| 984 with self.assertRaises(endpoints.UnauthorizedException): |
| 985 api_svc_v1.api_base_checks( |
| 986 request, requester, self.services, None, [], []) |
OLD | NEW |