| Index: appengine/monorail/services/test/api_svc_v1_test.py
|
| diff --git a/appengine/monorail/services/test/api_svc_v1_test.py b/appengine/monorail/services/test/api_svc_v1_test.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8eda278755f4a6d91e1c13c1fb9f8e9e67abf7e5
|
| --- /dev/null
|
| +++ b/appengine/monorail/services/test/api_svc_v1_test.py
|
| @@ -0,0 +1,986 @@
|
| +# Copyright 2016 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is govered by a BSD-style
|
| +# license that can be found in the LICENSE file or at
|
| +# https://developers.google.com/open-source/licenses/bsd
|
| +
|
| +"""Tests for the API v1."""
|
| +
|
| +import endpoints
|
| +import unittest
|
| +import webtest
|
| +from google.appengine.api import oauth
|
| +from mock import Mock
|
| +from protorpc import messages
|
| +from protorpc import message_types
|
| +
|
| +from framework import monorailrequest
|
| +from framework import permissions
|
| +from framework import template_helpers
|
| +from proto import project_pb2
|
| +from proto import tracker_pb2
|
| +from search import frontendsearchpipeline
|
| +from services import api_svc_v1
|
| +from services import issue_svc
|
| +from services import project_svc
|
| +from services import service_manager
|
| +from services import user_svc
|
| +from testing import fake
|
| +from testing_utils import testing
|
| +from tracker import tracker_bizobj
|
| +
|
| +
|
| +def MakeFakeServiceManager():
|
| + return service_manager.Services(
|
| + user=fake.UserService(),
|
| + usergroup=fake.UserGroupService(),
|
| + project=fake.ProjectService(),
|
| + config=fake.ConfigService(),
|
| + issue=fake.IssueService(),
|
| + issue_star=fake.IssueStarService(),
|
| + features=fake.FeaturesService(),
|
| + cache_manager=fake.CacheManager())
|
| +
|
| +
|
| +class FakeMonorailApiRequest(object):
|
| +
|
| + def __init__(self, request, services, perms=None):
|
| + self.cnxn = None
|
| + self.auth = monorailrequest.AuthData.FromEmail(
|
| + self.cnxn, request['requester'], services)
|
| + self.me_user_id = self.auth.user_id
|
| + self.project_name = None
|
| + self.project = None
|
| + self.viewed_username = None
|
| + self.viewed_user_auth = None
|
| + self.config = None
|
| + if 'userId' in request:
|
| + self.viewed_username = request['userId']
|
| + self.viewed_user_auth = monorailrequest.AuthData.FromEmail(
|
| + self.cnxn, self.viewed_username, services)
|
| + elif 'groupName' in request:
|
| + self.viewed_username = request['groupName']
|
| + try:
|
| + self.viewed_user_auth = monorailrequest.AuthData.FromEmail(
|
| + self.cnxn, self.viewed_username, services)
|
| + except user_svc.NoSuchUserException:
|
| + self.viewed_user_auth = None
|
| + if 'projectId' in request:
|
| + self.project_name = request['projectId']
|
| + self.project = services.project.GetProjectByName(
|
| + self.cnxn, self.project_name)
|
| + self.config = services.config.GetProjectConfig(
|
| + self.cnxn, self.project_id)
|
| + self.perms = perms or permissions.GetPermissions(
|
| + self.auth.user_pb, self.auth.effective_ids, self.project)
|
| + self.granted_perms = set()
|
| +
|
| + self.params = {
|
| + 'can': request.get('can', 1),
|
| + 'start': request.get('startIndex', 0),
|
| + 'num': request.get('maxResults', 100),
|
| + 'q': request.get('q', ''),
|
| + 'sort': request.get('sort', ''),
|
| + 'groupby': '',
|
| + 'projects': request.get('additionalProject', []) + [self.project_name]}
|
| + self.use_cached_searches = True
|
| + self.errors = template_helpers.EZTError()
|
| + self.mode = None
|
| +
|
| + self.query_project_names = self.GetParam('projects')
|
| + self.group_by_spec = self.GetParam('groupby')
|
| + self.sort_spec = self.GetParam('sort')
|
| + self.query = self.GetParam('q')
|
| + self.can = self.GetParam('can')
|
| + self.start = self.GetParam('start')
|
| + self.num = self.GetParam('num')
|
| +
|
| + @property
|
| + def project_id(self):
|
| + return self.project.project_id if self.project else None
|
| +
|
| + def GetParam(self, query_param_name, default_value=None,
|
| + _antitamper_re=None):
|
| + return self.params.get(query_param_name, default_value)
|
| +
|
| + def GetPositiveIntParam(self, query_param_name, default_value=None):
|
| + """Returns 0 if the user-provided value is less than 0."""
|
| + return max(self.GetParam(query_param_name, default_value=default_value),
|
| + 0)
|
| +
|
| +
|
| +class FakeFrontendSearchPipeline(object):
|
| +
|
| + def __init__(self):
|
| + issue1 = fake.MakeTestIssue(
|
| + project_id=12345, local_id=1, owner_id=2, status='New', summary='sum')
|
| + issue2 = fake.MakeTestIssue(
|
| + project_id=12345, local_id=2, owner_id=2, status='New', summary='sum')
|
| + self.allowed_results = [issue1, issue2]
|
| + self.visible_results = [issue1]
|
| + self.total_count = len(self.allowed_results)
|
| + self.config = None
|
| + self.projectId = 0
|
| +
|
| + def SearchForIIDs(self):
|
| + pass
|
| +
|
| + def MergeAndSortIssues(self):
|
| + pass
|
| +
|
| + def Paginate(self):
|
| + pass
|
| +
|
| +
|
| +class MonorailApiTest(testing.EndpointsTestCase):
|
| +
|
| + api_service_cls = api_svc_v1.MonorailApi
|
| +
|
| + def makeMar(self, request):
|
| + return FakeMonorailApiRequest(request, self.services)
|
| +
|
| + def setUp(self):
|
| + super(MonorailApiTest, self).setUp()
|
| + self.requester = RequesterMock(email='requester@example.com')
|
| + self.mock(endpoints, 'get_current_user', lambda: self.requester)
|
| + self.config = None
|
| + self.services = MakeFakeServiceManager()
|
| + self.mock(api_svc_v1.MonorailApi, '_services', self.services)
|
| + self.services.user.TestAddUser('requester@example.com', 1)
|
| + self.services.user.TestAddUser('user@example.com', 2)
|
| + self.services.user.TestAddUser('group@example.com', 123)
|
| + self.services.usergroup.TestAddGroupSettings(123, 'group@example.com')
|
| + self.request = {
|
| + 'userId': 'user@example.com',
|
| + 'ownerProjectsOnly': False,
|
| + 'requester': 'requester@example.com',
|
| + 'projectId': 'test-project',
|
| + 'issueId': 1}
|
| + self.mock(api_svc_v1.MonorailApi, 'mar_factory',
|
| + lambda x, y: FakeMonorailApiRequest(self.request, self.services))
|
| +
|
| + # api_base_checks is tested in AllBaseChecksTest,
|
| + # so mock it to reduce noise.
|
| + self.mock(api_svc_v1, 'api_base_checks', lambda x, y, z, u, v, w: None)
|
| +
|
| + def SetUpComponents(
|
| + self, project_id, component_id, component_name, component_doc='doc',
|
| + deprecated=False, admin_ids=None, cc_ids=None, created=100000, creator=1):
|
| + admin_ids = admin_ids or []
|
| + cc_ids = cc_ids or []
|
| + self.config = self.services.config.GetProjectConfig(
|
| + 'fake cnxn', project_id)
|
| + self.services.config.StoreConfig('fake cnxn', self.config)
|
| + cd = tracker_bizobj.MakeComponentDef(
|
| + component_id, project_id, component_name, component_doc, deprecated,
|
| + admin_ids, cc_ids, created, creator)
|
| + self.config.component_defs.append(cd)
|
| +
|
| + def SetUpFieldDefs(
|
| + self, field_id, project_id, field_name, field_type_int,
|
| + min_value=0, max_value=100, needs_member=False, docstring='doc'):
|
| + self.config = self.services.config.GetProjectConfig(
|
| + 'fake cnxn', project_id)
|
| + self.services.config.StoreConfig('fake cnxn', self.config)
|
| + fd = tracker_bizobj.MakeFieldDef(
|
| + field_id, project_id, field_name, field_type_int, '',
|
| + '', False, False, min_value, max_value, None, needs_member, None, '',
|
| + tracker_pb2.NotifyTriggers.NEVER, docstring, False)
|
| + self.config.field_defs.append(fd)
|
| +
|
| + def testUsersGet_NoProject(self):
|
| + """The viewed user has no projects."""
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'public-project', owner_ids=[1])
|
| + resp = self.call_api('users_get', self.request).json_body
|
| + expected = {
|
| + 'id': '2',
|
| + 'kind': 'monorail#user'}
|
| + self.assertEqual(expected, resp)
|
| +
|
| + def testUsersGet_PublicProject(self):
|
| + """The viewed user has one public project."""
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'public-project', owner_ids=[2])
|
| + resp = self.call_api('users_get', self.request).json_body
|
| +
|
| + self.assertEqual(1, len(resp['projects']))
|
| + self.assertEqual('public-project', resp['projects'][0]['name'])
|
| +
|
| + def testUsersGet_PrivateProject(self):
|
| + """The viewed user has one project but the requester cannot view."""
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'private-project', owner_ids=[2],
|
| + access=project_pb2.ProjectAccess.MEMBERS_ONLY)
|
| + resp = self.call_api('users_get', self.request).json_body
|
| + self.assertNotIn('projects', resp)
|
| +
|
| + def testUsersGet_OwnerProjectOnly(self):
|
| + """The viewed user has different roles of projects."""
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'owner-project', owner_ids=[2])
|
| + self.services.project.TestAddProject(
|
| + 'member-project', owner_ids=[1], committer_ids=[2])
|
| + resp = self.call_api('users_get', self.request).json_body
|
| + self.assertEqual(2, len(resp['projects']))
|
| +
|
| + self.request['ownerProjectsOnly'] = True
|
| + resp = self.call_api('users_get', self.request).json_body
|
| + self.assertEqual(1, len(resp['projects']))
|
| + self.assertEqual('owner-project', resp['projects'][0]['name'])
|
| +
|
| + def testIssuesGet_GetIssue(self):
|
| + """Get the requested issue."""
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + self.SetUpComponents(12345, 1, 'API')
|
| + self.SetUpFieldDefs(1, 12345, 'Field1', tracker_pb2.FieldTypes.INT_TYPE)
|
| +
|
| + fv = tracker_pb2.FieldValue(
|
| + field_id=1,
|
| + int_value=11)
|
| + issue1 = fake.MakeTestIssue(
|
| + project_id=12345, local_id=1, owner_id=2, reporter_id=1, status='New',
|
| + summary='sum', component_ids=[1], field_values=[fv])
|
| + self.services.issue.TestAddIssue(issue1)
|
| +
|
| + resp = self.call_api('issues_get', self.request).json_body
|
| + self.assertEqual(1, resp['id'])
|
| + self.assertEqual('New', resp['status'])
|
| + self.assertEqual('open', resp['state'])
|
| + self.assertFalse(resp['canEdit'])
|
| + self.assertTrue(resp['canComment'])
|
| + self.assertEqual('requester@example.com', resp['author']['name'])
|
| + self.assertEqual('user@example.com', resp['owner']['name'])
|
| + self.assertEqual('API', resp['components'][0])
|
| + self.assertEqual('Field1', resp['fieldValues'][0]['fieldName'])
|
| + self.assertEqual('11', resp['fieldValues'][0]['fieldValue'])
|
| +
|
| + def testIssuesInsert_BadRequest(self):
|
| + """The request does not specify summary or status."""
|
| +
|
| + with self.assertRaises(webtest.AppError):
|
| + self.call_api('issues_insert', self.request)
|
| +
|
| + issue_dict = {
|
| + 'status': 'New',
|
| + 'summary': 'Test issue',
|
| + 'owner': {'name': 'notexist@example.com'}}
|
| + self.request.update(issue_dict)
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + with self.call_should_fail(400):
|
| + self.call_api('issues_insert', self.request)
|
| +
|
| + # Invalid field value
|
| + self.SetUpFieldDefs(1, 12345, 'Field1', tracker_pb2.FieldTypes.INT_TYPE)
|
| + issue_dict = {
|
| + 'status': 'New',
|
| + 'summary': 'Test issue',
|
| + 'owner': {'name': 'requester@example.com'},
|
| + 'fieldValues': [{'fieldName': 'Field1', 'fieldValue': '111'}]}
|
| + self.request.update(issue_dict)
|
| + with self.call_should_fail(400):
|
| + self.call_api('issues_insert', self.request)
|
| +
|
| + def testIssuesInsert_NoPermission(self):
|
| + """The requester has no permission to create issues."""
|
| +
|
| + issue_dict = {
|
| + 'status': 'New',
|
| + 'summary': 'Test issue'}
|
| + self.request.update(issue_dict)
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + access=project_pb2.ProjectAccess.MEMBERS_ONLY,
|
| + project_id=12345)
|
| + with self.call_should_fail(403):
|
| + self.call_api('issues_insert', self.request)
|
| +
|
| + def testIssuesInsert_CreateIssue(self):
|
| + """Create an issue as requested."""
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + self.SetUpFieldDefs(1, 12345, 'Field1', tracker_pb2.FieldTypes.INT_TYPE)
|
| +
|
| + issue1 = fake.MakeTestIssue(
|
| + project_id=12345, local_id=1, owner_id=2, reporter_id=1, status='New',
|
| + summary='Test issue')
|
| + self.services.issue.TestAddIssue(issue1)
|
| +
|
| + issue_dict = {
|
| + 'blockedOn': [{'issueId': 1}],
|
| + 'cc': [{'name': 'user@example.com'}],
|
| + 'description': 'description',
|
| + 'labels': ['label1', 'label2'],
|
| + 'owner': {'name': 'requester@example.com'},
|
| + 'status': 'New',
|
| + 'summary': 'Test issue',
|
| + 'fieldValues': [{'fieldName': 'Field1', 'fieldValue': '11'}]}
|
| + self.request.update(issue_dict)
|
| +
|
| + resp = self.call_api('issues_insert', self.request).json_body
|
| + self.assertEqual('New', resp['status'])
|
| + self.assertEqual('requester@example.com', resp['author']['name'])
|
| + self.assertEqual('requester@example.com', resp['owner']['name'])
|
| + self.assertEqual('user@example.com', resp['cc'][0]['name'])
|
| + self.assertEqual(1, resp['blockedOn'][0]['issueId'])
|
| + self.assertEqual([u'label1', u'label2'], resp['labels'])
|
| + self.assertEqual('Test issue', resp['summary'])
|
| + self.assertEqual('Field1', resp['fieldValues'][0]['fieldName'])
|
| + self.assertEqual('11', resp['fieldValues'][0]['fieldValue'])
|
| +
|
| + def testIssuesList_NoPermission(self):
|
| + """No permission for additional projects."""
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'test-project2', owner_ids=[2],
|
| + access=project_pb2.ProjectAccess.MEMBERS_ONLY,
|
| + project_id=123456)
|
| + self.request['additionalProject'] = ['test-project2']
|
| + with self.call_should_fail(403):
|
| + self.call_api('issues_list', self.request)
|
| +
|
| + def testIssuesList_SearchIssues(self):
|
| + """Find issues of one project."""
|
| +
|
| + self.mock(frontendsearchpipeline, 'FrontendSearchPipeline',
|
| + lambda x, y, z, w: FakeFrontendSearchPipeline())
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + access=project_pb2.ProjectAccess.MEMBERS_ONLY,
|
| + project_id=12345)
|
| + resp = self.call_api('issues_list', self.request).json_body
|
| + self.assertEqual(2, int(resp['totalResults']))
|
| + self.assertEqual(1, len(resp['items']))
|
| + self.assertEqual(1, resp['items'][0]['id'])
|
| +
|
| + def testIssuesCommentsList_GetComments(self):
|
| + """Get comments of requested issue."""
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| +
|
| + issue1 = fake.MakeTestIssue(
|
| + project_id=12345, local_id=1, summary='test summary', status='New',
|
| + issue_id=10001, owner_id=2, reporter_id=1)
|
| + self.services.issue.TestAddIssue(issue1)
|
| +
|
| + comment = tracker_pb2.IssueComment(
|
| + id=123, issue_id=10001,
|
| + project_id=12345, user_id=2,
|
| + content='this is a comment',
|
| + timestamp=1437700000)
|
| + self.services.issue.TestAddComment(comment, 1)
|
| +
|
| + resp = self.call_api('issues_comments_list', self.request).json_body
|
| + self.assertEqual(2, resp['totalResults'])
|
| + comment1 = resp['items'][0]
|
| + comment2 = resp['items'][1]
|
| + self.assertEqual('requester@example.com', comment1['author']['name'])
|
| + self.assertEqual('test summary', comment1['content'])
|
| + self.assertEqual('user@example.com', comment2['author']['name'])
|
| + self.assertEqual('this is a comment', comment2['content'])
|
| +
|
| + def testIssuesCommentsInsert_NoCommentPermission(self):
|
| + """No permission to comment an issue."""
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + access=project_pb2.ProjectAccess.MEMBERS_ONLY,
|
| + project_id=12345)
|
| +
|
| + issue1 = fake.MakeTestIssue(
|
| + 12345, 1, 'Issue 1', 'New', 2)
|
| + self.services.issue.TestAddIssue(issue1)
|
| +
|
| + with self.call_should_fail(403):
|
| + self.call_api('issues_comments_insert', self.request)
|
| +
|
| + def testIssuesCommentsInsert_Amendments(self):
|
| + """Insert comments with amendments."""
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| +
|
| + issue1 = fake.MakeTestIssue(
|
| + 12345, 1, 'Issue 1', 'New', 2)
|
| + issue2 = fake.MakeTestIssue(
|
| + 12345, 2, 'Issue 2', 'New', 2)
|
| + issue3 = fake.MakeTestIssue(
|
| + 12345, 3, 'Issue 3', 'New', 2)
|
| + issue4 = fake.MakeTestIssue(
|
| + 12345, 4, 'Issue 4', 'New', 2)
|
| + self.services.issue.TestAddIssue(issue1)
|
| + self.services.issue.TestAddIssue(issue1)
|
| + self.services.issue.TestAddIssue(issue2)
|
| + self.services.issue.TestAddIssue(issue3)
|
| + self.services.issue.TestAddIssue(issue4)
|
| +
|
| + self.request['updates'] = {
|
| + 'summary': 'new summary',
|
| + 'status': 'Duplicate',
|
| + 'owner': 'requester@example.com',
|
| + 'cc': ['user@example.com'],
|
| + 'labels': ['add_label', '-remove_label'],
|
| + 'blockedOn': ['2'],
|
| + 'blocking': ['3'],
|
| + 'merged_into': 4}
|
| + resp = self.call_api('issues_comments_insert', self.request).json_body
|
| + self.assertEqual('requester@example.com', resp['author']['name'])
|
| + self.assertEqual('Updated', resp['updates']['status'])
|
| +
|
| + def testIssuesCommentInsert_CustomFields(self):
|
| + """Update custom field values."""
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + issue1 = fake.MakeTestIssue(
|
| + 12345, 1, 'Issue 1', 'New', 2,
|
| + project_name='test-project')
|
| + self.services.issue.TestAddIssue(issue1)
|
| + self.SetUpFieldDefs(
|
| + 1, 12345, 'Field_int', tracker_pb2.FieldTypes.INT_TYPE)
|
| + self.SetUpFieldDefs(
|
| + 2, 12345, 'Field_enum', tracker_pb2.FieldTypes.ENUM_TYPE)
|
| +
|
| + self.request['updates'] = {
|
| + 'fieldValues': [{'fieldName': 'Field_int', 'fieldValue': '11'},
|
| + {'fieldName': 'Field_enum', 'fieldValue': 'str'}]}
|
| + resp = self.call_api('issues_comments_insert', self.request).json_body
|
| + self.assertEqual('Updated', resp['updates']['status'])
|
| +
|
| + def testIssuesCommentInsert_MoveToProject_Fail(self):
|
| + """Move issue to a different project and failed."""
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + issue1 = fake.MakeTestIssue(
|
| + 12345, 1, 'Issue 1', 'New', 2, labels=['Restrict-View-Google'],
|
| + project_name='test-project')
|
| + self.services.issue.TestAddIssue(issue1)
|
| +
|
| + self.services.project.TestAddProject(
|
| + 'test-project2', owner_ids=[1],
|
| + project_id=12346)
|
| + issue2 = fake.MakeTestIssue(
|
| + 12346, 1, 'Issue 1', 'New', 2, project_name='test-project2')
|
| + self.services.issue.TestAddIssue(issue2)
|
| +
|
| + # Project doesn't exist
|
| + self.request['updates'] = {
|
| + 'moveToProject': 'not exist'}
|
| + with self.call_should_fail(400):
|
| + self.call_api('issues_comments_insert', self.request)
|
| +
|
| + # The issue is already in destination
|
| + self.request['updates'] = {
|
| + 'moveToProject': 'test-project'}
|
| + with self.call_should_fail(400):
|
| + self.call_api('issues_comments_insert', self.request)
|
| +
|
| + # The user has no permission in test-project
|
| + self.request['projectId'] = 'test-project2'
|
| + self.request['updates'] = {
|
| + 'moveToProject': 'test-project'}
|
| + with self.call_should_fail(400):
|
| + self.call_api('issues_comments_insert', self.request)
|
| +
|
| + # Restrict labels
|
| + self.request['projectId'] = 'test-project'
|
| + self.request['updates'] = {
|
| + 'moveToProject': 'test-project2'}
|
| + with self.call_should_fail(400):
|
| + self.call_api('issues_comments_insert', self.request)
|
| +
|
| + def testIssuesCommentInsert_MoveToProject_Normal(self):
|
| + """Move issue."""
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[1, 2],
|
| + project_id=12345)
|
| + self.services.project.TestAddProject(
|
| + 'test-project2', owner_ids=[1, 2],
|
| + project_id=12346)
|
| + issue1 = fake.MakeTestIssue(
|
| + 12345, 1, 'Issue 1', 'New', 2, project_name='test-project')
|
| + self.services.issue.TestAddIssue(issue1)
|
| + issue2 = fake.MakeTestIssue(
|
| + 12346, 1, 'Issue 1', 'New', 2, project_name='test-project2')
|
| + self.services.issue.TestAddIssue(issue2)
|
| +
|
| + self.request['updates'] = {
|
| + 'moveToProject': 'test-project2'}
|
| + resp = self.call_api('issues_comments_insert', self.request).json_body
|
| +
|
| + self.assertEqual(
|
| + 'Moved issue test-project:1 to now be issue test-project:2.',
|
| + resp['content'])
|
| +
|
| + def testIssuesCommentsDelete_NoComment(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + issue1 = fake.MakeTestIssue(
|
| + project_id=12345, local_id=1, summary='test summary',
|
| + issue_id=10001, status='New', owner_id=2, reporter_id=2)
|
| + self.services.issue.TestAddIssue(issue1)
|
| + self.request['commentId'] = 1
|
| + with self.call_should_fail(404):
|
| + self.call_api('issues_comments_delete', self.request)
|
| +
|
| + def testIssuesCommentsDelete_NoDeletePermission(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + issue1 = fake.MakeTestIssue(
|
| + project_id=12345, local_id=1, summary='test summary',
|
| + issue_id=10001, status='New', owner_id=2, reporter_id=2)
|
| + self.services.issue.TestAddIssue(issue1)
|
| + self.request['commentId'] = 0
|
| + with self.call_should_fail(403):
|
| + self.call_api('issues_comments_delete', self.request)
|
| +
|
| + def testIssuesCommentsDelete_DeleteUndelete(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + issue1 = fake.MakeTestIssue(
|
| + project_id=12345, local_id=1, summary='test summary',
|
| + issue_id=10001, status='New', owner_id=2, reporter_id=1)
|
| + self.services.issue.TestAddIssue(issue1)
|
| + comment = tracker_pb2.IssueComment(
|
| + id=123, issue_id=10001,
|
| + project_id=12345, user_id=1,
|
| + content='this is a comment',
|
| + timestamp=1437700000)
|
| + self.services.issue.TestAddComment(comment, 1)
|
| + self.request['commentId'] = 1
|
| +
|
| + comments = self.services.issue.GetCommentsForIssue(None, 10001)
|
| +
|
| + self.call_api('issues_comments_delete', self.request)
|
| + self.assertEqual(1, comments[1].deleted_by)
|
| +
|
| + self.call_api('issues_comments_undelete', self.request)
|
| + self.assertIsNone(comments[1].deleted_by)
|
| +
|
| + def testGroupsSettingsList_AllSettings(self):
|
| + resp = self.call_api('groups_settings_list', self.request).json_body
|
| + all_settings = resp['groupSettings']
|
| + self.assertEqual(1, len(all_settings))
|
| + self.assertEqual('group@example.com', all_settings[0]['groupName'])
|
| +
|
| + def testGroupsSettingsList_ImportedSettings(self):
|
| + self.services.user.TestAddUser('imported@example.com', 234)
|
| + self.services.usergroup.TestAddGroupSettings(
|
| + 234, 'imported@example.com', external_group_type='mdb')
|
| + self.request['importedGroupsOnly'] = True
|
| + resp = self.call_api('groups_settings_list', self.request).json_body
|
| + all_settings = resp['groupSettings']
|
| + self.assertEqual(1, len(all_settings))
|
| + self.assertEqual('imported@example.com', all_settings[0]['groupName'])
|
| +
|
| + def testGroupsCreate_NoPermission(self):
|
| + self.request['groupName'] = 'group'
|
| + with self.call_should_fail(403):
|
| + self.call_api('groups_create', self.request)
|
| +
|
| + def SetUpGroupRequest(self, group_name, who_can_view_members='MEMBERS',
|
| + ext_group_type=None, perms=None,
|
| + requester='requester@example.com'):
|
| + request = {
|
| + 'groupName': group_name,
|
| + 'requester': requester,
|
| + 'who_can_view_members': who_can_view_members,
|
| + 'ext_group_type': ext_group_type}
|
| + self.request.pop("userId", None)
|
| + self.mock(api_svc_v1.MonorailApi, 'mar_factory',
|
| + lambda x, y: FakeMonorailApiRequest(
|
| + request, self.services, perms))
|
| + return request
|
| +
|
| + def testGroupsCreate_Normal(self):
|
| + request = self.SetUpGroupRequest('newgroup@example.com', 'MEMBERS',
|
| + 'MDB', permissions.ADMIN_PERMISSIONSET)
|
| +
|
| + resp = self.call_api('groups_create', request).json_body
|
| + self.assertIn('groupID', resp)
|
| +
|
| + def testGroupsGet_NoPermission(self):
|
| + request = self.SetUpGroupRequest('group@example.com')
|
| + with self.call_should_fail(403):
|
| + self.call_api('groups_get', request)
|
| +
|
| + def testGroupsGet_Normal(self):
|
| + request = self.SetUpGroupRequest('group@example.com',
|
| + perms=permissions.ADMIN_PERMISSIONSET)
|
| + self.services.usergroup.TestAddMembers(123, [1], 'member')
|
| + self.services.usergroup.TestAddMembers(123, [2], 'owner')
|
| + resp = self.call_api('groups_get', request).json_body
|
| + self.assertEqual(123, resp['groupID'])
|
| + self.assertEqual(['requester@example.com'], resp['groupMembers'])
|
| + self.assertEqual(['user@example.com'], resp['groupOwners'])
|
| + self.assertEqual('group@example.com', resp['groupSettings']['groupName'])
|
| +
|
| + def testGroupsUpdate_NoPermission(self):
|
| + request = self.SetUpGroupRequest('group@example.com')
|
| + with self.call_should_fail(403):
|
| + self.call_api('groups_update', request)
|
| +
|
| + def testGroupsUpdate_Normal(self):
|
| + request = self.SetUpGroupRequest('group@example.com')
|
| + request = self.SetUpGroupRequest('group@example.com',
|
| + perms=permissions.ADMIN_PERMISSIONSET)
|
| + request['last_sync_time'] = 123456789
|
| + request['groupOwners'] = ['requester@example.com']
|
| + request['groupMembers'] = ['user@example.com']
|
| + resp = self.call_api('groups_update', request).json_body
|
| + self.assertFalse(resp.get('error'))
|
| +
|
| + def testComponentsList(self):
|
| + """Get components for a project."""
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + self.SetUpComponents(12345, 1, 'API')
|
| + resp = self.call_api('components_list', self.request).json_body
|
| +
|
| + self.assertEqual(1, len(resp['components']))
|
| + cd = resp['components'][0]
|
| + self.assertEqual(1, cd['componentId'])
|
| + self.assertEqual('API', cd['componentPath'])
|
| + self.assertEqual(1, cd['componentId'])
|
| + self.assertEqual('test-project', cd['projectName'])
|
| +
|
| + def testComponentsCreate_NoPermission(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + self.SetUpComponents(12345, 1, 'API')
|
| +
|
| + cd_dict = {
|
| + 'componentName': 'Test'}
|
| + self.request.update(cd_dict)
|
| +
|
| + with self.call_should_fail(403):
|
| + self.call_api('components_create', self.request)
|
| +
|
| + def testComponentsCreate_Invalid(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[1],
|
| + project_id=12345)
|
| + self.SetUpComponents(12345, 1, 'API')
|
| +
|
| + # Component with invalid name
|
| + cd_dict = {
|
| + 'componentName': 'c>d>e'}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(400):
|
| + self.call_api('components_create', self.request)
|
| +
|
| + # Name already in use
|
| + cd_dict = {
|
| + 'componentName': 'API'}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(400):
|
| + self.call_api('components_create', self.request)
|
| +
|
| + # Parent component does not exist
|
| + cd_dict = {
|
| + 'componentName': 'test',
|
| + 'parentPath': 'NotExist'}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(404):
|
| + self.call_api('components_create', self.request)
|
| +
|
| +
|
| + def testComponentsCreate_Normal(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[1],
|
| + project_id=12345)
|
| + self.SetUpComponents(12345, 1, 'API')
|
| +
|
| + cd_dict = {
|
| + 'componentName': 'Test',
|
| + 'description':'test comp',
|
| + 'cc': ['requester@example.com']}
|
| + self.request.update(cd_dict)
|
| +
|
| + resp = self.call_api('components_create', self.request).json_body
|
| + self.assertEqual('test comp', resp['description'])
|
| + self.assertEqual('requester@example.com', resp['creator'])
|
| + self.assertEqual([u'requester@example.com'], resp['cc'])
|
| + self.assertEqual('Test', resp['componentPath'])
|
| +
|
| + cd_dict = {
|
| + 'componentName': 'TestChild',
|
| + 'parentPath': 'API'}
|
| + self.request.update(cd_dict)
|
| + resp = self.call_api('components_create', self.request).json_body
|
| +
|
| + self.assertEqual('API>TestChild', resp['componentPath'])
|
| +
|
| + def testComponentsDelete_Invalid(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + self.SetUpComponents(12345, 1, 'API')
|
| +
|
| + # Fail to delete a non-existent component
|
| + cd_dict = {
|
| + 'componentPath': 'NotExist'}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(404):
|
| + self.call_api('components_delete', self.request)
|
| +
|
| + # The user has no permission to delete component
|
| + cd_dict = {
|
| + 'componentPath': 'API'}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(403):
|
| + self.call_api('components_delete', self.request)
|
| +
|
| + # The user tries to delete component that had subcomponents
|
| + self.services.project.TestAddProject(
|
| + 'test-project2', owner_ids=[1],
|
| + project_id=123456)
|
| + self.SetUpComponents(123456, 1, 'Parent')
|
| + self.SetUpComponents(123456, 2, 'Parent>Child')
|
| + cd_dict = {
|
| + 'componentPath': 'Parent',
|
| + 'projectId': 'test-project2',}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(403):
|
| + self.call_api('components_delete', self.request)
|
| +
|
| + def testComponentsDelete_Normal(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[1],
|
| + project_id=12345)
|
| + self.SetUpComponents(12345, 1, 'API')
|
| +
|
| + cd_dict = {
|
| + 'componentPath': 'API'}
|
| + self.request.update(cd_dict)
|
| + _ = self.call_api('components_delete', self.request).json_body
|
| + self.assertEqual(0, len(self.config.component_defs))
|
| +
|
| + def testComponentsUpdate_Invalid(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[2],
|
| + project_id=12345)
|
| + self.SetUpComponents(12345, 1, 'API')
|
| + self.SetUpComponents(12345, 2, 'Test', admin_ids=[1])
|
| +
|
| + # Fail to update a non-existent component
|
| + cd_dict = {
|
| + 'componentPath': 'NotExist'}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(404):
|
| + self.call_api('components_update', self.request)
|
| +
|
| + # The user has no permission to edit component
|
| + cd_dict = {
|
| + 'componentPath': 'API'}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(403):
|
| + self.call_api('components_update', self.request)
|
| +
|
| + # The user tries an invalid component name
|
| + cd_dict = {
|
| + 'componentPath': 'Test',
|
| + 'updates': [{'field': 'LEAF_NAME', 'leafName': 'c>e'}]}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(400):
|
| + self.call_api('components_update', self.request)
|
| +
|
| + # The user tries a name already in use
|
| + cd_dict = {
|
| + 'componentPath': 'Test',
|
| + 'updates': [{'field': 'LEAF_NAME', 'leafName': 'API'}]}
|
| + self.request.update(cd_dict)
|
| + with self.call_should_fail(400):
|
| + self.call_api('components_update', self.request)
|
| +
|
| + def testComponentsUpdate_Normal(self):
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[1],
|
| + project_id=12345)
|
| + self.SetUpComponents(12345, 1, 'API')
|
| + self.SetUpComponents(12345, 2, 'Parent')
|
| + self.SetUpComponents(12345, 3, 'Parent>Child')
|
| +
|
| + cd_dict = {
|
| + 'componentPath': 'API',
|
| + 'updates': [
|
| + {'field': 'DESCRIPTION', 'description': ''},
|
| + {'field': 'CC', 'cc': ['requester@example.com', 'user@example.com']},
|
| + {'field': 'DEPRECATED', 'deprecated': True}]}
|
| + self.request.update(cd_dict)
|
| + _ = self.call_api('components_update', self.request).json_body
|
| + component_def = tracker_bizobj.FindComponentDef(
|
| + 'API', self.config)
|
| + self.assertIsNotNone(component_def)
|
| + self.assertEqual('', component_def.docstring)
|
| + self.assertEqual([1L, 2L], component_def.cc_ids)
|
| + self.assertTrue(component_def.deprecated)
|
| +
|
| + cd_dict = {
|
| + 'componentPath': 'Parent',
|
| + 'updates': [
|
| + {'field': 'LEAF_NAME', 'leafName': 'NewParent'}]}
|
| + self.request.update(cd_dict)
|
| + _ = self.call_api('components_update', self.request).json_body
|
| + cd_parent = tracker_bizobj.FindComponentDef(
|
| + 'NewParent', self.config)
|
| + cd_child = tracker_bizobj.FindComponentDef(
|
| + 'NewParent>Child', self.config)
|
| + self.assertIsNotNone(cd_parent)
|
| + self.assertIsNotNone(cd_child)
|
| +
|
| +
|
| +class RequestMock(object):
|
| +
|
| + def __init__(self):
|
| + self.projectId = None
|
| + self.issueId = None
|
| +
|
| +
|
| +class RequesterMock(object):
|
| +
|
| + def __init__(self, email=None):
|
| + self._email = email
|
| +
|
| + def email(self):
|
| + return self._email
|
| +
|
| +
|
| +class AllBaseChecksTest(unittest.TestCase):
|
| +
|
| + def setUp(self):
|
| + self.services = MakeFakeServiceManager()
|
| + self.services.user.TestAddUser('test@example.com', 1)
|
| + self.services.project.TestAddProject(
|
| + 'test-project', owner_ids=[1], project_id=123,
|
| + access=project_pb2.ProjectAccess.MEMBERS_ONLY)
|
| + self.auth_client_ids = ['123456789.apps.googleusercontent.com']
|
| + oauth.get_client_id = Mock(return_value=self.auth_client_ids[0])
|
| + oauth.get_current_user = Mock(
|
| + return_value=RequesterMock(email='test@example.com'))
|
| +
|
| + def testUnauthorizedRequester(self):
|
| + with self.assertRaises(endpoints.UnauthorizedException):
|
| + api_svc_v1.api_base_checks(None, None, None, None, [], [])
|
| +
|
| + def testNoUser(self):
|
| + requester = RequesterMock(email='notexist@example.com')
|
| + with self.assertRaises(user_svc.NoSuchUserException):
|
| + api_svc_v1.api_base_checks(
|
| + None, requester, self.services, None, self.auth_client_ids, [])
|
| +
|
| + def testNoOauthUser(self):
|
| + oauth.get_current_user.side_effect = oauth.Error()
|
| + with self.assertRaises(endpoints.UnauthorizedException):
|
| + api_svc_v1.api_base_checks(
|
| + None, None, self.services, None, [], [])
|
| +
|
| + def testBannedUser(self):
|
| + banned_email = 'banned@example.com'
|
| + self.services.user.TestAddUser(banned_email, 2, banned=True)
|
| + requester = RequesterMock(email=banned_email)
|
| + with self.assertRaises(permissions.BannedUserException):
|
| + api_svc_v1.api_base_checks(
|
| + None, requester, self.services, None, self.auth_client_ids, [])
|
| +
|
| + def testNoProject(self):
|
| + request = RequestMock()
|
| + request.projectId = 'notexist-project'
|
| + requester = RequesterMock(email='test@example.com')
|
| + with self.assertRaises(project_svc.NoSuchProjectException):
|
| + api_svc_v1.api_base_checks(
|
| + request, requester, self.services, None, self.auth_client_ids, [])
|
| +
|
| + def testNonLiveProject(self):
|
| + archived_project = 'archived-project'
|
| + self.services.project.TestAddProject(
|
| + archived_project, owner_ids=[1],
|
| + state=project_pb2.ProjectState.ARCHIVED)
|
| + request = RequestMock()
|
| + request.projectId = archived_project
|
| + requester = RequesterMock(email='test@example.com')
|
| + with self.assertRaises(permissions.PermissionException):
|
| + api_svc_v1.api_base_checks(
|
| + request, requester, self.services, None, self.auth_client_ids, [])
|
| +
|
| + def testNoViewProjectPermission(self):
|
| + nonmember_email = 'nonmember@example.com'
|
| + self.services.user.TestAddUser(nonmember_email, 2)
|
| + requester = RequesterMock(email=nonmember_email)
|
| + request = RequestMock()
|
| + request.projectId = 'test-project'
|
| + with self.assertRaises(permissions.PermissionException):
|
| + api_svc_v1.api_base_checks(
|
| + request, requester, self.services, None, self.auth_client_ids, [])
|
| +
|
| + def testAllPass(self):
|
| + requester = RequesterMock(email='test@example.com')
|
| + request = RequestMock()
|
| + request.projectId = 'test-project'
|
| + try:
|
| + api_svc_v1.api_base_checks(
|
| + request, requester, self.services, None, self.auth_client_ids, [])
|
| + except Exception as e:
|
| + self.fail('Unexpected exception: %s' % str(e))
|
| +
|
| + def testNoIssue(self):
|
| + requester = RequesterMock(email='test@example.com')
|
| + request = RequestMock()
|
| + request.projectId = 'test-project'
|
| + request.issueId = 12345
|
| + with self.assertRaises(issue_svc.NoSuchIssueException):
|
| + api_svc_v1.api_base_checks(
|
| + request, requester, self.services, None, self.auth_client_ids, [])
|
| +
|
| + def testNoViewIssuePermission(self):
|
| + requester = RequesterMock(email='test@example.com')
|
| + request = RequestMock()
|
| + request.projectId = 'test-project'
|
| + request.issueId = 1
|
| + issue1 = fake.MakeTestIssue(
|
| + project_id=123, local_id=1, summary='test summary',
|
| + status='New', owner_id=1, reporter_id=1)
|
| + issue1.deleted = True
|
| + self.services.issue.TestAddIssue(issue1)
|
| + with self.assertRaises(permissions.PermissionException):
|
| + api_svc_v1.api_base_checks(
|
| + request, requester, self.services, None, self.auth_client_ids, [])
|
| +
|
| + def testAnonymousClients(self):
|
| + oauth.get_client_id = Mock(return_value='anonymous')
|
| + requester = RequesterMock(email='test@example.com')
|
| + request = RequestMock()
|
| + request.projectId = 'test-project'
|
| + try:
|
| + api_svc_v1.api_base_checks(
|
| + request, requester, self.services, None, [], ['test@example.com'])
|
| + except Exception as e:
|
| + self.fail('Unexpected exception: %s' % str(e))
|
| +
|
| + with self.assertRaises(endpoints.UnauthorizedException):
|
| + api_svc_v1.api_base_checks(
|
| + request, requester, self.services, None, [], [])
|
|
|