blob: 683fa90cf72e12afc9328ab7dd37d26f2c4a1066 [file] [log] [blame]
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed 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 projects servicer."""
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import unittest
from mock import patch
from components.prpc import codes
from components.prpc import context
from api import projects_servicer
from api.api_proto import common_pb2
from api.api_proto import issue_objects_pb2
from api.api_proto import project_objects_pb2
from api.api_proto import projects_pb2
from framework import exceptions
from framework import monorailcontext
from framework import permissions
from proto import tracker_pb2
from proto import project_pb2
from tracker import tracker_bizobj
from tracker import tracker_constants
from testing import fake
from testing import testing_helpers
from services import service_manager
class ProjectsServicerTest(unittest.TestCase):
def setUp(self):
self.cnxn = fake.MonorailConnection()
self.services = service_manager.Services(
config=fake.ConfigService(),
issue=fake.IssueService(),
user=fake.UserService(),
usergroup=fake.UserGroupService(),
project=fake.ProjectService(),
project_star=fake.ProjectStarService(),
features=fake.FeaturesService())
self.admin = self.services.user.TestAddUser('admin@example.com', 123)
self.admin.is_site_admin = True
self.owner = self.services.user.TestAddUser('owner@example.com', 111)
self.services.user.TestAddUser('user_222@example.com', 222)
self.services.user.TestAddUser('user_333@example.com', 333)
self.services.user.TestAddUser('user_444@example.com', 444)
self.services.user.TestAddUser('user_666@example.com', 666)
# User group 888 has members: user_555 and proj@monorail.com
self.services.user.TestAddUser('group888@googlegroups.com', 888)
self.services.usergroup.TestAddGroupSettings(
888, 'group888@googlegroups.com')
self.services.usergroup.TestAddMembers(888, [555, 1001])
# User group 999 has members: user_111 and user_444
self.services.user.TestAddUser('group999@googlegroups.com', 999)
self.services.usergroup.TestAddGroupSettings(
999, 'group999@googlegroups.com')
self.services.usergroup.TestAddMembers(999, [111, 444])
# User group 777 has members: user_666 and group 999.
self.services.user.TestAddUser('group777@googlegroups.com', 777)
self.services.usergroup.TestAddGroupSettings(
777, 'group777@googlegroups.com')
self.services.usergroup.TestAddMembers(777, [666, 999])
self.project = self.services.project.TestAddProject(
'proj',
project_id=789,
owner_ids=[111],
committer_ids=[222],
contrib_ids=[333])
self.projects_svcr = projects_servicer.ProjectsServicer(
self.services, make_rate_limiter=False)
self.prpc_context = context.ServicerContext()
self.prpc_context.set_code(codes.StatusCode.OK)
def CallWrapped(self, wrapped_handler, *args, **kwargs):
return wrapped_handler.wrapped(self.projects_svcr, *args, **kwargs)
def testListProjects_Normal(self):
"""We can get a list of all projects on the site."""
request = projects_pb2.ListProjectsRequest()
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(self.projects_svcr.ListProjects, mc, request)
self.assertEqual(2, len(response.projects))
def testGetConfig_Normal(self):
"""We can get a project config."""
request = projects_pb2.GetConfigRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(self.projects_svcr.GetConfig, mc, request)
self.assertEqual('proj', response.project_name)
def testGetConfig_NoSuchProject(self):
"""We reject a request to get a config for a non-existent project."""
request = projects_pb2.GetConfigRequest(project_name='unknown-proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
with self.assertRaises(exceptions.NoSuchProjectException):
self.CallWrapped(self.projects_svcr.GetConfig, mc, request)
def testGetConfig_PermissionDenied(self):
"""We reject a request to get a config for a non-viewable project."""
self.project.access = project_pb2.ProjectAccess.MEMBERS_ONLY
request = projects_pb2.GetConfigRequest(project_name='proj')
# User is a member of the members-only project.
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(self.projects_svcr.GetConfig, mc, request)
self.assertEqual('proj', response.project_name)
# User is not a member of the members-only project.
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='nonmember@example.com')
with self.assertRaises(permissions.PermissionException):
self.CallWrapped(self.projects_svcr.GetConfig, mc, request)
@patch('businesslogic.work_env.WorkEnv.ListProjectTemplates')
def testListProjectTemplates_Normal(self, mockListProjectTemplates):
fd_1 = tracker_pb2.FieldDef(
field_name='FirstField', field_id=1,
field_type=tracker_pb2.FieldTypes.STR_TYPE)
fd_2 = tracker_pb2.FieldDef(
field_name='LegalApproval', field_id=2,
field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE)
component = tracker_pb2.ComponentDef(component_id=1, path='dude')
status_def = tracker_pb2.StatusDef(status='New', means_open=True)
config = tracker_pb2.ProjectIssueConfig(
project_id=789, field_defs=[fd_1, fd_2], component_defs=[component],
well_known_statuses=[status_def])
self.services.config.StoreConfig(self.cnxn, config)
admin1 = self.services.user.TestAddUser('admin@example.com', 222)
appr1 = self.services.user.TestAddUser('approver@example.com', 333)
setter = self.services.user.TestAddUser('setter@example.com', 444)
template = tracker_pb2.TemplateDef(
name='Chicken', content='description', summary='summary',
status='New', admin_ids=[admin1.user_id],
field_values=[tracker_bizobj.MakeFieldValue(
fd_1.field_id, None, 'Cow', None, None, None, False)],
component_ids=[component.component_id],
approval_values=[tracker_pb2.ApprovalValue(
approval_id=2, approver_ids=[appr1.user_id],
setter_id=setter.user_id)])
mockListProjectTemplates.return_value = [template]
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
request = projects_pb2.ListProjectTemplatesRequest(project_name='proj')
response = self.CallWrapped(
self.projects_svcr.ListProjectTemplates, mc, request)
self.assertEqual(
response,
projects_pb2.ListProjectTemplatesResponse(
templates=[project_objects_pb2.TemplateDef(
template_name='Chicken',
content='description',
summary='summary',
status_ref=common_pb2.StatusRef(
status='New',
is_derived=False,
means_open=True),
owner_defaults_to_member=True,
admin_refs=[
common_pb2.UserRef(
user_id=admin1.user_id,
display_name=testing_helpers.ObscuredEmail(admin1.email),
is_derived=False)],
field_values=[
issue_objects_pb2.FieldValue(
field_ref=common_pb2.FieldRef(
field_id=fd_1.field_id,
field_name=fd_1.field_name,
type=common_pb2.STR_TYPE),
value='Cow')],
component_refs=[
common_pb2.ComponentRef(
path=component.path, is_derived=False)],
approval_values=[
issue_objects_pb2.Approval(
field_ref=common_pb2.FieldRef(
field_id=fd_2.field_id,
field_name=fd_2.field_name,
type=common_pb2.APPROVAL_TYPE),
setter_ref=common_pb2.UserRef(
user_id=setter.user_id,
display_name=testing_helpers.ObscuredEmail(
setter.email)),
phase_ref=issue_objects_pb2.PhaseRef(),
approver_refs=[common_pb2.UserRef(
user_id=appr1.user_id,
display_name=testing_helpers.ObscuredEmail(appr1.email),
is_derived=False)])],
)]))
def testListProjectTemplates_NoProjectName(self):
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
request = projects_pb2.ListProjectTemplatesRequest()
with self.assertRaises(exceptions.InputException):
self.CallWrapped(self.projects_svcr.ListProjectTemplates, mc, request)
def testListProjectTemplates_NoSuchProject(self):
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
request = projects_pb2.ListProjectTemplatesRequest(project_name='ghost')
with self.assertRaises(exceptions.NoSuchProjectException):
self.CallWrapped(self.projects_svcr.ListProjectTemplates, mc, request)
def testListProjectTemplates_PermissionDenied(self):
self.project.access = project_pb2.ProjectAccess.MEMBERS_ONLY
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='nonmember@example.com')
request = projects_pb2.GetConfigRequest(project_name='proj')
with self.assertRaises(permissions.PermissionException):
self.CallWrapped(self.projects_svcr.ListProjectTemplates, mc, request)
def testGetPresentationConfig_Normal(self):
"""Test getting project summary, thumbnail url, custom issue entry, etc."""
config = tracker_pb2.ProjectIssueConfig(project_id=789)
self.project.summary = 'project summary'
config.custom_issue_entry_url = 'issue entry url'
config.member_default_query = 'default query'
config.default_col_spec = 'ID Summary'
config.default_sort_spec = 'Priority Status'
config.default_x_attr = 'Priority'
config.default_y_attr = 'Status'
self.project.revision_url_format = 'revision url format'
self.services.config.StoreConfig(self.cnxn, config)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
request = projects_pb2.GetPresentationConfigRequest(project_name='proj')
response = self.CallWrapped(
self.projects_svcr.GetPresentationConfig, mc, request)
self.assertEqual('project summary', response.project_summary)
self.assertEqual('issue entry url', response.custom_issue_entry_url)
self.assertEqual('default query', response.default_query)
self.assertEqual('ID Summary', response.default_col_spec)
self.assertEqual('Priority Status', response.default_sort_spec)
self.assertEqual('Priority', response.default_x_attr)
self.assertEqual('Status', response.default_y_attr)
self.assertEqual('revision url format', response.revision_url_format)
def testGetPresentationConfig_SavedQueriesAllowed(self):
"""Only project members or higher can see project saved queries."""
self.services.features.UpdateCannedQueries(self.cnxn, 789, [
tracker_pb2.SavedQuery(query_id=101, name='test', query='owner:me'),
tracker_pb2.SavedQuery(query_id=202, name='hello', query='world')
])
# User 333 is a contributor.
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='user_333@example.com')
request = projects_pb2.GetPresentationConfigRequest(project_name='proj')
response = self.CallWrapped(self.projects_svcr.GetPresentationConfig, mc,
request)
self.assertEqual(2, len(response.saved_queries))
self.assertEqual(101, response.saved_queries[0].query_id)
self.assertEqual('test', response.saved_queries[0].name)
self.assertEqual('owner:me', response.saved_queries[0].query)
self.assertEqual(202, response.saved_queries[1].query_id)
self.assertEqual('hello', response.saved_queries[1].name)
self.assertEqual('world', response.saved_queries[1].query)
def testGetPresentationConfig_SavedQueriesDenied(self):
"""Only project members or higher can see project saved queries."""
self.services.features.UpdateCannedQueries(self.cnxn, 789, [
tracker_pb2.SavedQuery(query_id=101, name='test', query='owner:me'),
tracker_pb2.SavedQuery(query_id=202, name='hello', query='world')
])
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='nonmember@example.com')
request = projects_pb2.GetPresentationConfigRequest(project_name='proj')
response = self.CallWrapped(self.projects_svcr.GetPresentationConfig, mc,
request)
self.assertEqual(0, len(response.saved_queries))
def testGetCustomPermissions_Normal(self):
self.project.extra_perms = [
project_pb2.Project.ExtraPerms(
member_id=111,
perms=['FooPerm', 'BarPerm'])]
request = projects_pb2.GetConfigRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='foo@example.org')
response = self.CallWrapped(
self.projects_svcr.GetCustomPermissions, mc, request)
self.assertEqual(['BarPerm', 'FooPerm'], response.permissions)
def testGetCustomPermissions_PermissionsAreDedupped(self):
self.project.extra_perms = [
project_pb2.Project.ExtraPerms(
member_id=111,
perms=['FooPerm', 'FooPerm']),
project_pb2.Project.ExtraPerms(
member_id=222,
perms=['FooPerm'])]
request = projects_pb2.GetConfigRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='foo@example.org')
response = self.CallWrapped(
self.projects_svcr.GetCustomPermissions, mc, request)
self.assertEqual(['FooPerm'], response.permissions)
def testGetCustomPermissions_PermissionsAreSorted(self):
self.project.extra_perms = [
project_pb2.Project.ExtraPerms(
member_id=111,
perms=['FooPerm', 'BarPerm']),
project_pb2.Project.ExtraPerms(
member_id=222,
perms=['BazPerm'])]
request = projects_pb2.GetConfigRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='foo@example.org')
response = self.CallWrapped(
self.projects_svcr.GetCustomPermissions, mc, request)
self.assertEqual(['BarPerm', 'BazPerm', 'FooPerm'], response.permissions)
def testGetCustomPermissions_IgnoreStandardPermissions(self):
self.project.extra_perms = [
project_pb2.Project.ExtraPerms(
member_id=111,
perms=permissions.STANDARD_PERMISSIONS + ['FooPerm'])]
request = projects_pb2.GetConfigRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='foo@example.org')
response = self.CallWrapped(
self.projects_svcr.GetCustomPermissions, mc, request)
self.assertEqual(['FooPerm'], response.permissions)
def testGetCustomPermissions_NoCustomPermissions(self):
self.project.extra_perms = []
request = projects_pb2.GetConfigRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='foo@example.org')
response = self.CallWrapped(
self.projects_svcr.GetCustomPermissions, mc, request)
self.assertEqual([], response.permissions)
def assertVisibleMembers(self, expected_user_ids, expected_group_ids,
requester=None):
request = projects_pb2.GetVisibleMembersRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=requester)
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.GetVisibleMembers, mc, request)
self.assertEqual(
expected_user_ids,
[user_ref.user_id for user_ref in response.user_refs])
# Assert that we get the full email address.
self.assertEqual(
[self.services.user.LookupUserEmail(self.cnxn, user_id)
for user_id in expected_user_ids],
[user_ref.display_name for user_ref in response.user_refs])
self.assertEqual(
expected_group_ids,
[group_ref.user_id for group_ref in response.group_refs])
# Assert that we get the full email address.
self.assertEqual(
[self.services.user.LookupUserEmail(self.cnxn, user_id)
for user_id in expected_group_ids],
[group_ref.display_name for group_ref in response.group_refs])
return response
def testGetVisibleMembers_Normal(self):
# Not logged in - Test users have their email addresses obscured to
# non-project members by default.
self.assertVisibleMembers([], [])
# Logged in as non project member
self.assertVisibleMembers([], [], requester='foo@example.com')
# Logged in as owner
self.assertVisibleMembers([111, 222, 333], [],
requester='owner@example.com')
# Logged in as committer
self.assertVisibleMembers([111, 222, 333], [],
requester='user_222@example.com')
# Logged in as contributor
self.assertVisibleMembers([111, 222, 333], [],
requester='user_333@example.com')
def testGetVisibleMembers_OnlyOwnersSeeContributors(self):
self.project.only_owners_see_contributors = True
# Not logged in
with self.assertRaises(permissions.PermissionException):
self.assertVisibleMembers([111, 222], [])
# Logged in with a non-member
with self.assertRaises(permissions.PermissionException):
self.assertVisibleMembers([111, 222], [], requester='foo@example.com')
# Logged in as owner
self.assertVisibleMembers([111, 222, 333], [],
requester='owner@example.com')
# Logged in as committer
self.assertVisibleMembers([111, 222, 333], [],
requester='user_222@example.com')
# Logged in as contributor
with self.assertRaises(permissions.PermissionException):
self.assertVisibleMembers(
[111, 222], [], requester='user_333@example.com')
def testGetVisibleMembers_MemberIsGroup(self):
self.project.contributor_ids.extend([999])
self.assertVisibleMembers([999, 111, 222, 333, 444], [999],
requester='owner@example.com')
def testGetVisibleMembers_AcExclusion(self):
self.services.project.ac_exclusion_ids[self.project.project_id] = [333]
self.assertVisibleMembers([111, 222], [], requester='owner@example.com')
def testGetVisibleMembers_NoExpand(self):
self.services.project.no_expand_ids[self.project.project_id] = [999]
self.project.contributor_ids.extend([999])
self.assertVisibleMembers([999, 111, 222, 333], [999],
requester='owner@example.com')
def testGetVisibleMembers_ObscuredEmails(self):
# Unobscure the owner's email. Non-project members can see.
self.services.user.UpdateUserSettings(
self.cnxn, 111, self.owner, obscure_email=False)
# Not logged in
self.assertVisibleMembers([111], [])
# Logged in as not a project member
self.assertVisibleMembers([111], [], requester='foo@example.com')
# Logged in as owner
self.assertVisibleMembers(
[111, 222, 333], [], requester='owner@example.com')
# Logged in as committer
self.assertVisibleMembers(
[111, 222, 333], [], requester='user_222@example.com')
# Logged in as contributor
self.assertVisibleMembers(
[111, 222, 333], [], requester='user_333@example.com')
def testListStatuses(self):
request = projects_pb2.ListStatusesRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListStatuses, mc, request)
self.assertFalse(response.restrict_to_known)
self.assertEqual(
[('New', True),
('Accepted', True),
('Started', True),
('Fixed', False),
('Verified', False),
('Invalid', False),
('Duplicate', False),
('WontFix', False),
('Done', False)],
[(status_def.status, status_def.means_open)
for status_def in response.status_defs])
self.assertEqual(
[('Duplicate', False)],
[(status_def.status, status_def.means_open)
for status_def in response.statuses_offer_merge])
def testListComponents(self):
self.services.config.CreateComponentDef(
self.cnxn, self.project.project_id, 'Foo', 'Foo Component', True, [],
[], True, 111, [])
self.services.config.CreateComponentDef(
self.cnxn, self.project.project_id, 'Bar', 'Bar Component', False, [],
[], True, 111, [])
self.services.config.CreateComponentDef(
self.cnxn, self.project.project_id, 'Bar>Baz', 'Baz Component',
False, [], [], True, 111, [])
request = projects_pb2.ListComponentsRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListComponents, mc, request)
self.assertEqual(
[project_objects_pb2.ComponentDef(
path='Foo',
docstring='Foo Component',
deprecated=True),
project_objects_pb2.ComponentDef(
path='Bar',
docstring='Bar Component',
deprecated=False),
project_objects_pb2.ComponentDef(
path='Bar>Baz',
docstring='Baz Component',
deprecated=False)],
list(response.component_defs))
def testListComponents_IncludeAdminInfo(self):
self.services.config.CreateComponentDef(
self.cnxn, self.project.project_id, 'Foo', 'Foo Component', True, [],
[], 1234567, 111, [])
self.services.config.CreateComponentDef(
self.cnxn, self.project.project_id, 'Bar', 'Bar Component', False, [],
[], 1234568, 111, [])
self.services.config.CreateComponentDef(
self.cnxn, self.project.project_id, 'Bar>Baz', 'Baz Component',
False, [], [], 1234569, 111, [])
creator_ref = common_pb2.UserRef(
user_id=111,
display_name='owner@example.com')
request = projects_pb2.ListComponentsRequest(
project_name='proj', include_admin_info=True)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListComponents, mc, request)
self.assertEqual(
[project_objects_pb2.ComponentDef(
path='Foo',
docstring='Foo Component',
deprecated=True,
created=1234567,
creator_ref=creator_ref),
project_objects_pb2.ComponentDef(
path='Bar',
docstring='Bar Component',
deprecated=False,
created=1234568,
creator_ref=creator_ref),
project_objects_pb2.ComponentDef(
path='Bar>Baz',
docstring='Baz Component',
deprecated=False,
created=1234569,
creator_ref=creator_ref),
],
list(response.component_defs))
def AddField(self, name, **kwargs):
if kwargs.get('needs_perm'):
kwargs['needs_member'] = True
kwargs.setdefault('cnxn', self.cnxn)
kwargs.setdefault('project_id', self.project.project_id)
kwargs.setdefault('field_name', name)
kwargs.setdefault('field_type_str', 'USER_TYPE')
for arg in ('applic_type', 'applic_pred', 'is_required', 'is_niche',
'is_multivalued', 'min_value', 'max_value', 'regex',
'needs_member', 'needs_perm', 'grants_perm', 'notify_on',
'date_action_str', 'docstring'):
kwargs.setdefault(arg, None)
for arg in ('admin_ids', 'editor_ids'):
kwargs.setdefault(arg, [])
self.services.config.CreateFieldDef(**kwargs)
def testListFields_Normal(self):
self.AddField('Foo Field', needs_perm=permissions.EDIT_ISSUE)
request = projects_pb2.ListFieldsRequest(
project_name='proj', include_user_choices=True)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListFields, mc, request)
self.assertEqual(1, len(response.field_defs))
field = response.field_defs[0]
self.assertEqual('Foo Field', field.field_ref.field_name)
self.assertEqual(
[111, 222],
sorted([user_ref.user_id for user_ref in field.user_choices]))
self.assertEqual(
['owner@example.com', 'user_222@example.com'],
sorted([user_ref.display_name for user_ref in field.user_choices]))
def testListFields_DontIncludeUserChoices(self):
self.AddField('Foo Field', needs_perm=permissions.EDIT_ISSUE)
request = projects_pb2.ListFieldsRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListFields, mc, request)
self.assertEqual(1, len(response.field_defs))
field = response.field_defs[0]
self.assertEqual(0, len(field.user_choices))
def testListFields_IncludeAdminInfo(self):
self.AddField('Foo Field', needs_perm=permissions.EDIT_ISSUE, is_niche=True,
applic_type='Foo Applic Type')
request = projects_pb2.ListFieldsRequest(
project_name='proj', include_admin_info=True)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListFields, mc, request)
self.assertEqual(1, len(response.field_defs))
field = response.field_defs[0]
self.assertEqual('Foo Field', field.field_ref.field_name)
self.assertEqual(True, field.is_niche)
self.assertEqual('Foo Applic Type', field.applicable_type)
def testListFields_EnumFieldChoices(self):
self.AddField('Type', field_type_str='ENUM_TYPE')
request = projects_pb2.ListFieldsRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListFields, mc, request)
self.assertEqual(1, len(response.field_defs))
field = response.field_defs[0]
self.assertEqual('Type', field.field_ref.field_name)
self.assertEqual(
['Defect', 'Enhancement', 'Task', 'Other'],
[label.label for label in field.enum_choices])
def testListFields_CustomPermission(self):
self.AddField('Foo Field', needs_perm='FooPerm')
self.project.extra_perms = [
project_pb2.Project.ExtraPerms(
member_id=111,
perms=['UnrelatedPerm']),
project_pb2.Project.ExtraPerms(
member_id=222,
perms=['FooPerm'])]
request = projects_pb2.ListFieldsRequest(
project_name='proj', include_user_choices=True)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListFields, mc, request)
self.assertEqual(1, len(response.field_defs))
field = response.field_defs[0]
self.assertEqual('Foo Field', field.field_ref.field_name)
self.assertEqual(
[222],
sorted([user_ref.user_id for user_ref in field.user_choices]))
self.assertEqual(
['user_222@example.com'],
sorted([user_ref.display_name for user_ref in field.user_choices]))
def testListFields_IndirectPermission(self):
"""Test that the permissions of effective ids are also considered."""
self.AddField('Foo Field', needs_perm='FooPerm')
self.project.contributor_ids.extend([999])
self.project.extra_perms = [
project_pb2.Project.ExtraPerms(
member_id=999,
perms=['FooPerm', 'BarPerm'])]
request = projects_pb2.ListFieldsRequest(
project_name='proj', include_user_choices=True)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.ListFields, mc, request)
self.assertEqual(1, len(response.field_defs))
field = response.field_defs[0]
self.assertEqual('Foo Field', field.field_ref.field_name)
# Users 111 and 444 are members of group 999, which has the needed
# permission.
self.assertEqual(
[111, 444, 999],
sorted([user_ref.user_id for user_ref in field.user_choices]))
self.assertEqual(
['group999@googlegroups.com', 'owner@example.com',
'user_444@example.com'],
sorted([user_ref.display_name for user_ref in field.user_choices]))
def testListFields_TwiceIndirectPermission(self):
"""Test that only direct memberships are considered."""
self.AddField('Foo Field', needs_perm='FooPerm')
# User group 777 has members: user_666 and group 999.
self.project.contributor_ids.extend([777])
self.project.contributor_ids.extend([999])
self.project.extra_perms = [
project_pb2.Project.ExtraPerms(
member_id=777, perms=['FooPerm', 'BarPerm'])
]
request = projects_pb2.ListFieldsRequest(
project_name='proj', include_user_choices=True)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(self.projects_svcr.ListFields, mc, request)
self.assertEqual(1, len(response.field_defs))
field = response.field_defs[0]
self.assertEqual('Foo Field', field.field_ref.field_name)
self.assertEqual(
[666, 777, 999],
sorted([user_ref.user_id for user_ref in field.user_choices]))
self.assertEqual(
[
'group777@googlegroups.com', 'group999@googlegroups.com',
'user_666@example.com'
], sorted([user_ref.display_name for user_ref in field.user_choices]))
def testListFields_NoPermissionsNeeded(self):
self.AddField('Foo Field')
request = projects_pb2.ListFieldsRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListFields, mc, request)
self.assertEqual(1, len(response.field_defs))
field = response.field_defs[0]
self.assertEqual('Foo Field', field.field_ref.field_name)
def testListFields_MultipleFields(self):
self.AddField('Bar Field', needs_perm=permissions.VIEW)
self.AddField('Foo Field', needs_perm=permissions.EDIT_ISSUE)
request = projects_pb2.ListFieldsRequest(
project_name='proj', include_user_choices=True)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListFields, mc, request)
self.assertEqual(2, len(response.field_defs))
field_defs = sorted(
response.field_defs, key=lambda field: field.field_ref.field_name)
self.assertEqual(
['Bar Field', 'Foo Field'],
[field.field_ref.field_name for field in field_defs])
self.assertEqual(
[[111, 222, 333],
[111, 222]],
[sorted(user_ref.user_id for user_ref in field.user_choices)
for field in field_defs])
self.assertEqual(
[['owner@example.com', 'user_222@example.com', 'user_333@example.com'],
['owner@example.com', 'user_222@example.com']],
[sorted(user_ref.display_name for user_ref in field.user_choices)
for field in field_defs])
def testListFields_NoFields(self):
request = projects_pb2.ListFieldsRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.ListFields, mc, request)
self.assertEqual(0, len(response.field_defs))
def testGetLabelOptions_Normal(self):
request = projects_pb2.GetLabelOptionsRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.GetLabelOptions, mc, request)
expected_label_names = [
label[0] for label in tracker_constants.DEFAULT_WELL_KNOWN_LABELS]
expected_label_names += [
'Restrict-View-EditIssue', 'Restrict-AddIssueComment-EditIssue',
'Restrict-View-CoreTeam']
self.assertEqual(
sorted(expected_label_names),
sorted(label.label for label in response.label_options))
def testGetLabelOptions_CustomPermissions(self):
self.project.extra_perms = [
project_pb2.Project.ExtraPerms(
member_id=222,
perms=['FooPerm', 'BarPerm'])]
request = projects_pb2.GetLabelOptionsRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.GetLabelOptions, mc, request)
expected_label_names = [
label[0] for label in tracker_constants.DEFAULT_WELL_KNOWN_LABELS]
expected_label_names += [
'Restrict-View-EditIssue', 'Restrict-AddIssueComment-EditIssue']
expected_label_names += [
'Restrict-%s-%s' % (std_perm, custom_perm)
for std_perm in permissions.STANDARD_ISSUE_PERMISSIONS
for custom_perm in ('BarPerm', 'FooPerm')]
self.assertEqual(
sorted(expected_label_names),
sorted(label.label for label in response.label_options))
def testGetLabelOptions_FieldMasksLabel(self):
self.AddField('Type', field_type_str='ENUM_TYPE')
request = projects_pb2.GetLabelOptionsRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.GetLabelOptions, mc, request)
expected_label_names = [
label[0] for label in tracker_constants.DEFAULT_WELL_KNOWN_LABELS
if not label[0].startswith('Type-')
]
expected_label_names += [
'Restrict-View-EditIssue', 'Restrict-AddIssueComment-EditIssue',
'Restrict-View-CoreTeam']
self.assertEqual(
sorted(expected_label_names),
sorted(label.label for label in response.label_options))
def CallGetStarCount(self):
request = projects_pb2.GetProjectStarCountRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
response = self.CallWrapped(
self.projects_svcr.GetProjectStarCount, mc, request)
return response.star_count
def CallStar(self, requester='owner@example.com', starred=True):
request = projects_pb2.StarProjectRequest(
project_name='proj', starred=starred)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=requester)
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.StarProject, mc, request)
return response.star_count
def testStarCount_Normal(self):
self.assertEqual(0, self.CallGetStarCount())
self.assertEqual(1, self.CallStar())
self.assertEqual(1, self.CallGetStarCount())
def testStarCount_StarTwiceSameUser(self):
self.assertEqual(1, self.CallStar())
self.assertEqual(1, self.CallStar())
self.assertEqual(1, self.CallGetStarCount())
def testStarCount_StarTwiceDifferentUser(self):
self.assertEqual(1, self.CallStar())
self.assertEqual(2, self.CallStar(requester='user_222@example.com'))
self.assertEqual(2, self.CallGetStarCount())
def testStarCount_RemoveStarTwiceSameUser(self):
self.assertEqual(1, self.CallStar())
self.assertEqual(1, self.CallGetStarCount())
self.assertEqual(0, self.CallStar(starred=False))
self.assertEqual(0, self.CallStar(starred=False))
self.assertEqual(0, self.CallGetStarCount())
def testStarCount_RemoveStarTwiceDifferentUser(self):
self.assertEqual(1, self.CallStar())
self.assertEqual(2, self.CallStar(requester='user_222@example.com'))
self.assertEqual(2, self.CallGetStarCount())
self.assertEqual(1, self.CallStar(starred=False))
self.assertEqual(
0, self.CallStar(requester='user_222@example.com', starred=False))
self.assertEqual(0, self.CallGetStarCount())
def testCheckProjectName_OK(self):
"""We can check a project name."""
request = projects_pb2.CheckProjectNameRequest(project_name='foo')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.CheckProjectName, mc, request)
self.assertEqual('', response.error)
def testCheckProjectName_InvalidProjectName(self):
"""We reject an invalid project name."""
request = projects_pb2.CheckProjectNameRequest(project_name='Foo')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.CheckProjectName, mc, request)
self.assertNotEqual('', response.error)
def testCheckProjectName_NotAllowed(self):
"""Users that can't create a project shouldn't get any information."""
request = projects_pb2.CheckProjectNameRequest(project_name='Foo')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='owner@example.com')
mc.LookupLoggedInUserPerms(self.project)
with self.assertRaises(permissions.PermissionException):
self.CallWrapped(self.projects_svcr.CheckProjectName, mc, request)
def testCheckProjectName_ProjectAlreadyExists(self):
"""There is already a project with that name."""
request = projects_pb2.CheckProjectNameRequest(project_name='proj')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.CheckProjectName, mc, request)
self.assertNotEqual('', response.error)
def testCheckComponentName_OK(self):
request = projects_pb2.CheckComponentNameRequest(
project_name='proj',
component_name='Component')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.CheckComponentName, mc, request)
self.assertEqual('', response.error)
def testCheckComponentName_ParentComponentOK(self):
self.services.config.CreateComponentDef(
self.cnxn, self.project.project_id, 'Component', 'Docstring',
False, [], [], 0, 111, [])
request = projects_pb2.CheckComponentNameRequest(
project_name='proj',
parent_path='Component',
component_name='Path')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.CheckComponentName, mc, request)
self.assertEqual('', response.error)
def testCheckComponentName_InvalidComponentName(self):
request = projects_pb2.CheckComponentNameRequest(
project_name='proj',
component_name='Component-')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.CheckComponentName, mc, request)
self.assertNotEqual('', response.error)
def testCheckComponentName_ComponentAlreadyExists(self):
self.services.config.CreateComponentDef(
self.cnxn, self.project.project_id, 'Component', 'Docstring',
False, [], [], 0, 111, [])
request = projects_pb2.CheckComponentNameRequest(
project_name='proj',
component_name='Component')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(
self.projects_svcr.CheckComponentName, mc, request)
self.assertNotEqual('', response.error)
def testCheckComponentName_NotAllowedToViewProject(self):
self.project.access = project_pb2.ProjectAccess.MEMBERS_ONLY
request = projects_pb2.CheckComponentNameRequest(
project_name='proj',
parent_path='Component',
component_name='Path')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='user_444@example.com')
mc.LookupLoggedInUserPerms(self.project)
with self.assertRaises(permissions.PermissionException):
self.CallWrapped(self.projects_svcr.CheckComponentName, mc, request)
def testCheckComponentName_ParentComponentDoesntExist(self):
request = projects_pb2.CheckComponentNameRequest(
project_name='proj',
parent_path='Component',
component_name='Path')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
with self.assertRaises(exceptions.NoSuchComponentException):
self.CallWrapped(self.projects_svcr.CheckComponentName, mc, request)
def testCheckFieldName_OK(self):
request = projects_pb2.CheckFieldNameRequest(
project_name='proj',
field_name='Foo')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(self.projects_svcr.CheckFieldName, mc, request)
self.assertEqual('', response.error)
def testCheckFieldName_InvalidFieldName(self):
request = projects_pb2.CheckFieldNameRequest(
project_name='proj',
field_name='**Foo**')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(self.projects_svcr.CheckFieldName, mc, request)
self.assertNotEqual('', response.error)
def testCheckFieldName_InvalidFieldName_ApproverSuffix(self):
request = projects_pb2.CheckFieldNameRequest(
project_name='proj',
field_name='Foo-aPprOver')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(self.projects_svcr.CheckFieldName, mc, request)
self.assertNotEqual('', response.error)
def testCheckFieldName_FieldAlreadyExists(self):
self.AddField('Foo')
request = projects_pb2.CheckFieldNameRequest(
project_name='proj',
field_name='Foo')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(self.projects_svcr.CheckFieldName, mc, request)
self.assertNotEqual('', response.error)
def testCheckFieldName_FieldIsPrefixOfAnother(self):
self.AddField('Foo-Bar')
request = projects_pb2.CheckFieldNameRequest(
project_name='proj',
field_name='Foo')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(self.projects_svcr.CheckFieldName, mc, request)
self.assertNotEqual('', response.error)
def testCheckFieldName_AnotherFieldIsPrefix(self):
self.AddField('Foo')
request = projects_pb2.CheckFieldNameRequest(
project_name='proj',
field_name='Foo-Bar')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='admin@example.com')
mc.LookupLoggedInUserPerms(self.project)
response = self.CallWrapped(self.projects_svcr.CheckFieldName, mc, request)
self.assertNotEqual('', response.error)
def testCheckFieldName_NotAllowedToViewProject(self):
self.project.access = project_pb2.ProjectAccess.MEMBERS_ONLY
request = projects_pb2.CheckFieldNameRequest(
project_name='proj',
field_name='Foo')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester='user_444@example.com')
mc.LookupLoggedInUserPerms(self.project)
with self.assertRaises(permissions.PermissionException):
self.CallWrapped(self.projects_svcr.CheckFieldName, mc, request)