Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(174)

Side by Side Diff: appengine/monorail/services/test/api_svc_v1_test.py

Issue 1868553004: Open Source Monorail (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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, [], [])
OLDNEW
« no previous file with comments | « appengine/monorail/services/test/api_pb2_v1_helpers_test.py ('k') | appengine/monorail/services/test/cachemanager_svc_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698