blob: 170cf2eef5cbd417067e12effe37b0db9a62015d [file] [log] [blame]
# Copyright 2020 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 hotlists servicer."""
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import unittest
from google.protobuf import empty_pb2
from google.protobuf import field_mask_pb2
from api import resource_name_converters as rnc
from api.v3 import hotlists_servicer
from api.v3 import converters
from api.v3.api_proto import hotlists_pb2
from api.v3.api_proto import feature_objects_pb2
from api.v3.api_proto import issue_objects_pb2
from api.v3.api_proto import user_objects_pb2
from framework import exceptions
from framework import monorailcontext
from framework import permissions
from framework import sorting
from features import features_constants
from testing import fake
from services import features_svc
from services import service_manager
class HotlistsServicerTest(unittest.TestCase):
def setUp(self):
self.cnxn = fake.MonorailConnection()
self.services = service_manager.Services(
features=fake.FeaturesService(),
issue=fake.IssueService(),
project=fake.ProjectService(),
config=fake.ConfigService(),
cache_manager=fake.CacheManager(),
user=fake.UserService(),
usergroup=fake.UserGroupService())
self.hotlists_svcr = hotlists_servicer.HotlistsServicer(
self.services, make_rate_limiter=False)
self.converter = None
self.PAST_TIME = 12345
self.user_1 = self.services.user.TestAddUser('user_111@example.com', 111)
self.user_2 = self.services.user.TestAddUser('user_222@example.com', 222)
self.user_3 = self.services.user.TestAddUser('user_333@example.com', 333)
user_ids = [self.user_1.user_id, self.user_2.user_id, self.user_3.user_id]
self.user_ids_to_name = rnc.ConvertUserNames(user_ids)
self.project_1 = self.services.project.TestAddProject(
'proj', project_id=789)
self.issue_1 = fake.MakeTestIssue(
self.project_1.project_id, 1, 'sum', 'New', 111,
project_name=self.project_1.project_name)
self.issue_2 = fake.MakeTestIssue(
self.project_1.project_id, 2, 'sum', 'New', 111,
project_name=self.project_1.project_name)
self.issue_3 = fake.MakeTestIssue(
self.project_1.project_id, 3, 'sum', 'New', 111,
project_name=self.project_1.project_name)
self.issue_4 = fake.MakeTestIssue(
self.project_1.project_id, 4, 'sum', 'New', 111,
project_name=self.project_1.project_name)
self.issue_5 = fake.MakeTestIssue(
self.project_1.project_id, 5, 'sum', 'New', 111,
project_name=self.project_1.project_name)
self.issue_6 = fake.MakeTestIssue(
self.project_1.project_id, 6, 'sum', 'New', 111,
project_name=self.project_1.project_name)
self.services.issue.TestAddIssue(self.issue_1)
self.services.issue.TestAddIssue(self.issue_2)
self.services.issue.TestAddIssue(self.issue_3)
self.services.issue.TestAddIssue(self.issue_4)
self.services.issue.TestAddIssue(self.issue_5)
self.services.issue.TestAddIssue(self.issue_6)
issue_ids = [
self.issue_1.issue_id, self.issue_2.issue_id, self.issue_3.issue_id,
self.issue_4.issue_id, self.issue_5.issue_id, self.issue_6.issue_id
]
self.issue_ids_to_name = rnc.ConvertIssueNames(
self.cnxn, issue_ids, self.services)
hotlist_items = [
(
self.issue_4.issue_id, 31, self.user_3.user_id, self.PAST_TIME,
'note5'),
(
self.issue_3.issue_id, 21, self.user_1.user_id, self.PAST_TIME,
'note1'),
(
self.issue_2.issue_id, 11, self.user_2.user_id, self.PAST_TIME,
'note2'),
(
self.issue_1.issue_id, 1, self.user_1.user_id, self.PAST_TIME,
'note4')
]
self.hotlist_1 = self.services.features.TestAddHotlist(
'HotlistName',
summary='summary',
description='description',
owner_ids=[self.user_1.user_id],
editor_ids=[self.user_2.user_id],
hotlist_item_fields=hotlist_items,
default_col_spec='',
is_private=True)
self.hotlist_resource_name = rnc.ConvertHotlistName(
self.hotlist_1.hotlist_id)
sorting.InitializeArtValues(self.services)
def CallWrapped(self, wrapped_handler, mc, *args, **kwargs):
self.converter = converters.Converter(mc, self.services)
self.hotlists_svcr.converter = self.converter
return wrapped_handler.wrapped(self.hotlists_svcr, mc, *args, **kwargs)
# TODO(crbug/monorail/7104): Add page_token tests when implemented.
def testListHotlistItems(self):
"""We can list a Hotlist's HotlistItems."""
request = hotlists_pb2.ListHotlistItemsRequest(
parent=self.hotlist_resource_name, page_size=2, order_by='note,stars')
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
response = self.CallWrapped(
self.hotlists_svcr.ListHotlistItems, mc, request)
expected_items = self.converter.ConvertHotlistItems(
self.hotlist_1.hotlist_id,
[self.hotlist_1.items[1], self.hotlist_1.items[2]])
self.assertEqual(
response, hotlists_pb2.ListHotlistItemsResponse(items=expected_items))
def testListHotlistItems_Empty(self):
"""We can return a response if the Hotlist has no items"""
empty_hotlist = self.services.features.TestAddHotlist(
'Empty',
owner_ids=[self.user_1.user_id],
editor_ids=[self.user_2.user_id],
hotlist_item_fields=[])
hotlist_resource_name = rnc.ConvertHotlistName(empty_hotlist.hotlist_id)
request = hotlists_pb2.ListHotlistItemsRequest(parent=hotlist_resource_name)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
response = self.CallWrapped(
self.hotlists_svcr.ListHotlistItems, mc, request)
self.assertEqual(response, hotlists_pb2.ListHotlistItemsResponse(items=[]))
def testListHotlistItems_InvalidPageSize(self):
"""We raise an exception if `page_size` is negative."""
request = hotlists_pb2.ListHotlistItemsRequest(
parent=self.hotlist_resource_name, page_size=-1)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
with self.assertRaises(exceptions.InputException):
self.CallWrapped(self.hotlists_svcr.ListHotlistItems, mc, request)
def testListHotlistItems_DefaultPageSize(self):
"""We use our default page size when no `page_size` is given."""
request = hotlists_pb2.ListHotlistItemsRequest(
parent=self.hotlist_resource_name)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
response = self.CallWrapped(
self.hotlists_svcr.ListHotlistItems, mc, request)
self.assertEqual(
len(response.items),
min(
features_constants.DEFAULT_RESULTS_PER_PAGE,
len(self.hotlist_1.items)))
def testRerankHotlistItems(self):
"""We can rerank a Hotlist."""
item_names_dict = rnc.ConvertHotlistItemNames(
self.cnxn, self.hotlist_1.hotlist_id,
[item.issue_id for item in self.hotlist_1.items], self.services)
request = hotlists_pb2.RerankHotlistItemsRequest(
name=self.hotlist_resource_name,
hotlist_items=[
item_names_dict[self.issue_4.issue_id],
item_names_dict[self.issue_3.issue_id]
],
target_position=0)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
self.CallWrapped(self.hotlists_svcr.RerankHotlistItems, mc, request)
updated_hotlist = self.services.features.GetHotlist(
self.cnxn, self.hotlist_1.hotlist_id)
self.assertEqual(
[item.issue_id for item in updated_hotlist.items],
[self.issue_4.issue_id, self.issue_3.issue_id,
self.issue_1.issue_id, self.issue_2.issue_id])
def testRemoveHotlistItems(self):
"""We can remove items from a Hotlist."""
issue_1_name = self.issue_ids_to_name[self.issue_1.issue_id]
issue_2_name = self.issue_ids_to_name[self.issue_2.issue_id]
request = hotlists_pb2.RemoveHotlistItemsRequest(
parent=self.hotlist_resource_name, issues=[issue_1_name, issue_2_name])
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
self.CallWrapped(self.hotlists_svcr.RemoveHotlistItems, mc, request)
updated_hotlist = self.services.features.GetHotlist(
self.cnxn, self.hotlist_1.hotlist_id)
# The hotlist used to have 4 items and we've removed two.
self.assertEqual(len(updated_hotlist.items), 2)
def testAddHotlistItems(self):
"""We can add items to a Hotlist."""
issue_5_name = self.issue_ids_to_name[self.issue_5.issue_id]
issue_6_name = self.issue_ids_to_name[self.issue_6.issue_id]
request = hotlists_pb2.AddHotlistItemsRequest(
parent=self.hotlist_resource_name, issues=[issue_5_name, issue_6_name])
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
self.CallWrapped(self.hotlists_svcr.AddHotlistItems, mc, request)
updated_hotlist = self.services.features.GetHotlist(
self.cnxn, self.hotlist_1.hotlist_id)
# The hotlist used to have 4 items and we've added two.
self.assertEqual(len(updated_hotlist.items), 6)
def testRemoveHotlistEditors(self):
"""We can remove editors from a Hotlist."""
user_2_name = self.user_ids_to_name[self.user_2.user_id]
request = hotlists_pb2.RemoveHotlistEditorsRequest(
name=self.hotlist_resource_name, editors=[user_2_name])
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
self.CallWrapped(self.hotlists_svcr.RemoveHotlistEditors, mc, request)
updated_hotlist = self.services.features.GetHotlist(
self.cnxn, self.hotlist_1.hotlist_id)
# User 2 was the only editor in the hotlist, and we removed them.
self.assertEqual(len(updated_hotlist.editor_ids), 0)
def testGetHotlist(self):
"""We can get a Hotlist."""
request = hotlists_pb2.GetHotlistRequest(name=self.hotlist_resource_name)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
api_hotlist = self.CallWrapped(self.hotlists_svcr.GetHotlist, mc, request)
self.assertEqual(api_hotlist, self.converter.ConvertHotlist(self.hotlist_1))
def testGatherHotlistsForUser(self):
"""We can get all visible hotlists of a user."""
request = hotlists_pb2.GatherHotlistsForUserRequest(
user=self.user_ids_to_name[self.user_2.user_id])
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
response = self.CallWrapped(
self.hotlists_svcr.GatherHotlistsForUser, mc, request)
user_names_by_id = rnc.ConvertUserNames(
[self.user_2.user_id, self.user_1.user_id])
expected_api_hotlists = [
feature_objects_pb2.Hotlist(
name=self.hotlist_resource_name,
display_name='HotlistName',
summary='summary',
description='description',
hotlist_privacy=feature_objects_pb2.Hotlist.HotlistPrivacy.Value(
'PRIVATE'),
owner=user_names_by_id[self.user_1.user_id],
editors=[user_names_by_id[self.user_2.user_id]])
]
self.assertEqual(
response,
hotlists_pb2.GatherHotlistsForUserResponse(
hotlists=expected_api_hotlists))
def testUpdateHotlist_AllFields(self):
"""We can update a Hotlist."""
request = hotlists_pb2.UpdateHotlistRequest(
update_mask=field_mask_pb2.FieldMask(
paths=[
'summary',
'description',
'default_columns',
'hotlist_privacy',
'display_name',
'owner',
'editors',
]),
hotlist=feature_objects_pb2.Hotlist(
name=self.hotlist_resource_name,
display_name='newName',
summary='new summary',
description='new description',
default_columns=[
issue_objects_pb2.IssuesListColumn(column='new-chicken-egg')
],
hotlist_privacy=feature_objects_pb2.Hotlist.HotlistPrivacy.Value(
'PUBLIC'),
owner=self.user_ids_to_name[self.user_2.user_id],
editors=[self.user_ids_to_name[self.user_3.user_id]]))
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
api_hotlist = self.CallWrapped(
self.hotlists_svcr.UpdateHotlist, mc, request)
user_names_by_id = rnc.ConvertUserNames(
[self.user_3.user_id, self.user_2.user_id, self.user_1.user_id])
expected_hotlist = feature_objects_pb2.Hotlist(
name=self.hotlist_resource_name,
display_name='newName',
summary='new summary',
description='new description',
default_columns=[
issue_objects_pb2.IssuesListColumn(column='new-chicken-egg')
],
hotlist_privacy=feature_objects_pb2.Hotlist.HotlistPrivacy.Value(
'PUBLIC'),
owner=user_names_by_id[self.user_2.user_id],
editors=[
user_names_by_id[self.user_2.user_id],
user_names_by_id[self.user_3.user_id]
])
self.assertEqual(api_hotlist, expected_hotlist)
def testUpdateHotlist_OneField(self):
request = hotlists_pb2.UpdateHotlistRequest(
update_mask=field_mask_pb2.FieldMask(paths=['summary']),
hotlist=feature_objects_pb2.Hotlist(
name=self.hotlist_resource_name,
display_name='newName',
summary='new summary',
description='new description',
default_columns=[
issue_objects_pb2.IssuesListColumn(column='new-chicken-egg')
],
hotlist_privacy=feature_objects_pb2.Hotlist.HotlistPrivacy.Value(
'PUBLIC')))
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
api_hotlist = self.CallWrapped(
self.hotlists_svcr.UpdateHotlist, mc, request)
user_names_by_id = rnc.ConvertUserNames(
[self.user_2.user_id, self.user_1.user_id])
expected_hotlist = feature_objects_pb2.Hotlist(
name=self.hotlist_resource_name,
display_name='HotlistName',
summary='new summary',
description='description',
default_columns=[],
hotlist_privacy=feature_objects_pb2.Hotlist.HotlistPrivacy.Value(
'PRIVATE'),
owner=user_names_by_id[self.user_1.user_id],
editors=[user_names_by_id[self.user_2.user_id]])
self.assertEqual(api_hotlist, expected_hotlist)
def testUpdateHotlist_EmptyFieldMask(self):
request = hotlists_pb2.UpdateHotlistRequest(
hotlist=feature_objects_pb2.Hotlist(summary='new'))
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
with self.assertRaises(exceptions.InputException):
self.CallWrapped(self.hotlists_svcr.UpdateHotlist, mc, request)
def testUpdateHotlist_EmptyHotlist(self):
request = hotlists_pb2.UpdateHotlistRequest(
update_mask=field_mask_pb2.FieldMask(paths=['summary']))
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
with self.assertRaises(exceptions.InputException):
self.CallWrapped(self.hotlists_svcr.UpdateHotlist, mc, request)
def testDeleteHotlist(self):
"""We can delete a Hotlist."""
request = hotlists_pb2.GetHotlistRequest(name=self.hotlist_resource_name)
mc = monorailcontext.MonorailContext(
self.services, cnxn=self.cnxn, requester=self.user_1.email)
mc.LookupLoggedInUserPerms(None)
api_response = self.CallWrapped(
self.hotlists_svcr.DeleteHotlist, mc, request)
self.assertEqual(api_response, empty_pb2.Empty())
with self.assertRaises(features_svc.NoSuchHotlistException):
self.services.features.GetHotlist(
self.cnxn, self.hotlist_1.hotlist_id)