blob: 7a7180bc77a32060e54451fb2209993fb4d112c5 [file] [log] [blame]
Copybara854996b2021-09-07 19:36:02 +00001# Copyright 2018 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file or at
4# https://developers.google.com/open-source/licenses/bsd
5
6"""Tests for the projects servicer."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import json
12import mock
13import unittest
14import mox
15
16from google.protobuf import wrappers_pb2
17
18from components.prpc import codes
19from components.prpc import context
20from components.prpc import server
21
22from api import converters
23from api.api_proto import common_pb2
24from api.api_proto import features_pb2
25from api.api_proto import features_objects_pb2
26from api.api_proto import issue_objects_pb2
27from framework import authdata
28from framework import exceptions
29from framework import monorailcontext
30from framework import permissions
31from framework import sorting
32from testing import fake
33from testing import testing_helpers
34from tracker import tracker_bizobj
35from services import features_svc
36from services import service_manager
37
38# Import component_helpers_test to mock cloudstorage before it is imported by
39# component_helpers via features servicer.
40from features.test import component_helpers_test
41from api import features_servicer # pylint: disable=ungrouped-imports
42
43
44class FeaturesServicerTest(unittest.TestCase):
45
46 def setUp(self):
47 self.mox = mox.Mox()
48 self.cnxn = fake.MonorailConnection()
49 self.services = service_manager.Services(
50 cache_manager=fake.CacheManager(),
51 config=fake.ConfigService(),
52 issue=fake.IssueService(),
53 user=fake.UserService(),
54 usergroup=fake.UserGroupService(),
55 project=fake.ProjectService(),
56 features=fake.FeaturesService(),
57 hotlist_star=fake.HotlistStarService())
58 sorting.InitializeArtValues(self.services)
59
60 self.project = self.services.project.TestAddProject(
61 'proj', project_id=789, owner_ids=[111], contrib_ids=[222, 333])
62 self.config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
63 self.user1 = self.services.user.TestAddUser('owner@example.com', 111)
64 self.user2 = self.services.user.TestAddUser('editor@example.com', 222)
65 self.user3 = self.services.user.TestAddUser('foo@example.com', 333)
66 self.user4 = self.services.user.TestAddUser('bar@example.com', 444)
67 self.features_svcr = features_servicer.FeaturesServicer(
68 self.services, make_rate_limiter=False)
69 self.prpc_context = context.ServicerContext()
70 self.prpc_context.set_code(codes.StatusCode.OK)
71 self.issue_1 = fake.MakeTestIssue(
72 789, 1, 'sum', 'New', 111, project_name='proj', issue_id=78901)
73 self.issue_2 = fake.MakeTestIssue(
74 789, 2, 'sum', 'Fixed', 111, project_name='proj', issue_id=78902,
75 closed_timestamp=112223344)
76 self.issue_3 = fake.MakeTestIssue(
77 789, 3, 'sum', 'New', 111, project_name='proj', issue_id=78903)
78
79 self.services.issue.TestAddIssue(self.issue_1)
80 self.services.issue.TestAddIssue(self.issue_2)
81 self.services.issue.TestAddIssue(self.issue_3)
82
83 self.project_2 = self.services.project.TestAddProject(
84 'proj2', project_id=788, owner_ids=[111], contrib_ids=[222, 333])
85 self.config_2 = tracker_bizobj.MakeDefaultProjectIssueConfig(788)
86 self.issue_21 = fake.MakeTestIssue(
87 788, 1, 'sum', 'New', 111, project_name='proj2', issue_id=78801)
88 self.issue_22 = fake.MakeTestIssue(
89 788, 2, 'sum', 'New', 111, project_name='proj2', issue_id=78802)
90 self.issue_23 = fake.MakeTestIssue(
91 788, 3, 'sum', 'New', 111, project_name='proj2', issue_id=78803)
92 self.services.issue.TestAddIssue(self.issue_21)
93 self.services.issue.TestAddIssue(self.issue_22)
94 self.services.issue.TestAddIssue(self.issue_23)
95
96 self.PAST_TIME = 123456
97
98 # For testing PredictComponent
99 self._ml_engine = component_helpers_test.FakeMLEngine(self)
100 self._top_words = None
101 self._components_by_index = None
102
103 mock.patch(
104 'services.ml_helpers.setup_ml_engine', lambda: self._ml_engine).start()
105 mock.patch(
106 'features.component_helpers._GetTopWords',
107 lambda _: self._top_words).start()
108 mock.patch('cloudstorage.open', self.cloudstorageOpen).start()
109 mock.patch('settings.component_features', 5).start()
110
111 self.addCleanup(mock.patch.stopall)
112
113 def cloudstorageOpen(self, name, mode):
114 """Create a file mock that returns self._components_by_index when read."""
115 open_fn = mock.mock_open(read_data=json.dumps(self._components_by_index))
116 return open_fn(name, mode)
117
118 def tearDown(self):
119 self.mox.UnsetStubs()
120 self.mox.ResetAll()
121
122 def CallWrapped(self, wrapped_handler, *args, **kwargs):
123 return wrapped_handler.wrapped(self.features_svcr, *args, **kwargs)
124
125 def testListHotlistsByUser_SearchByEmail(self):
126 """We can get a list of hotlists for a given email."""
127 # Public hostlist owned by 'owner@example.com'
128 self.services.features.CreateHotlist(
129 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
130 owner_ids=[111], editor_ids=[222])
131
132 # Query for issues for 'owner@example.com'
133 user_ref = common_pb2.UserRef(display_name='owner@example.com')
134 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
135
136 # We're not authenticated
137 mc = monorailcontext.MonorailContext(self.services, cnxn=self.cnxn)
138
139 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
140 request)
141 self.assertEqual(1, len(response.hotlists))
142 hotlist = response.hotlists[0]
143 self.assertEqual(111, hotlist.owner_ref.user_id)
144 self.assertEqual('ow...@example.com', hotlist.owner_ref.display_name)
145 self.assertEqual('Fake-Hotlist', hotlist.name)
146 self.assertEqual('Summary', hotlist.summary)
147 self.assertEqual('Description', hotlist.description)
148
149 def testListHotlistsByUser_SearchByOwner(self):
150 """We can get a list of hotlists for a given user."""
151 # Public hostlist owned by 'owner@example.com'
152 self.services.features.CreateHotlist(
153 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
154 owner_ids=[111], editor_ids=[222])
155
156 # Query for issues for 'owner@example.com'
157 user_ref = common_pb2.UserRef(user_id=111)
158 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
159
160 # We're authenticated as 'foo@example.com'
161 mc = monorailcontext.MonorailContext(
162 self.services, cnxn=self.cnxn, requester='foo@example.com')
163
164 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
165 request)
166 self.assertEqual(1, len(response.hotlists))
167 hotlist = response.hotlists[0]
168 self.assertEqual(111, hotlist.owner_ref.user_id)
169 # User1 and user3 share self.project.
170 self.assertEqual('owner@example.com', hotlist.owner_ref.display_name)
171 self.assertEqual('Fake-Hotlist', hotlist.name)
172 self.assertEqual('Summary', hotlist.summary)
173 self.assertEqual('Description', hotlist.description)
174
175 def testListHotlistsByUser_SearchByEditor(self):
176 """We can get a list of hotlists for a given user."""
177 # Public hostlist owned by 'owner@example.com'
178 self.services.features.CreateHotlist(
179 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
180 owner_ids=[111], editor_ids=[222])
181
182 # Query for issues for 'editor@example.com'
183 user_ref = common_pb2.UserRef(user_id=222)
184 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
185
186 # We're authenticated as 'foo@example.com'
187 mc = monorailcontext.MonorailContext(
188 self.services, cnxn=self.cnxn, requester='foo@example.com')
189
190 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
191 request)
192 self.assertEqual(1, len(response.hotlists))
193 hotlist = response.hotlists[0]
194 self.assertEqual(111, hotlist.owner_ref.user_id)
195 # User1 and user3 share self.project.
196 self.assertEqual('owner@example.com', hotlist.owner_ref.display_name)
197 self.assertEqual('Fake-Hotlist', hotlist.name)
198 self.assertEqual('Summary', hotlist.summary)
199 self.assertEqual('Description', hotlist.description)
200
201 def testListHotlistsByUser_NotSignedIn(self):
202 # Public hostlist owned by 'owner@example.com'
203 self.services.features.CreateHotlist(
204 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
205 owner_ids=[111], editor_ids=[222])
206
207 # Query for issues for 'owner@example.com'
208 user_ref = common_pb2.UserRef(user_id=111)
209 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
210
211 # We're not authenticated
212 mc = monorailcontext.MonorailContext(self.services, cnxn=self.cnxn)
213 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
214 request)
215
216 self.assertEqual(1, len(response.hotlists))
217 hotlist = response.hotlists[0]
218 self.assertEqual(111, hotlist.owner_ref.user_id)
219
220 def testListHotlistsByUser_Empty(self):
221 """There are no hotlists for the given user."""
222 # Public hostlist owned by 'owner@example.com'
223 self.services.features.CreateHotlist(
224 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
225 owner_ids=[111], editor_ids=[222])
226
227 # Query for issues for 'bar@example.com'
228 user_ref = common_pb2.UserRef(user_id=444)
229 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
230
231 # We're authenticated as 'foo@example.com'
232 mc = monorailcontext.MonorailContext(
233 self.services, cnxn=self.cnxn, requester='foo@example.com')
234 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
235 request)
236
237 self.assertEqual(0, len(response.hotlists))
238
239 def testListHotlistsByUser_NoHotlists(self):
240 """There are no hotlists."""
241 # No hotlists
242 # Query for issues for 'owner@example.com'
243 user_ref = common_pb2.UserRef(user_id=111)
244 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
245
246 # We're authenticated as 'foo@example.com'
247 mc = monorailcontext.MonorailContext(
248 self.services, cnxn=self.cnxn, requester='foo@example.com')
249 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
250 request)
251 self.assertEqual(0, len(response.hotlists))
252
253 def testListHotlistsByUser_PrivateIssueAsOwner(self):
254 # Private hostlist owned by 'owner@example.com'
255 self.services.features.CreateHotlist(
256 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
257 owner_ids=[111], editor_ids=[222], is_private=True)
258
259 # Query for issues for 'owner@example.com'
260 user_ref = common_pb2.UserRef(user_id=111)
261 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
262
263 # We're authenticated as 'owner@example.com'
264 mc = monorailcontext.MonorailContext(
265 self.services, cnxn=self.cnxn, requester='owner@example.com')
266 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
267 request)
268
269 self.assertEqual(1, len(response.hotlists))
270 hotlist = response.hotlists[0]
271 self.assertEqual(111, hotlist.owner_ref.user_id)
272
273 def testListHotlistsByUser_PrivateIssueAsEditor(self):
274 # Private hostlist owned by 'owner@example.com'
275 self.services.features.CreateHotlist(
276 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
277 owner_ids=[111], editor_ids=[222], is_private=True)
278
279 # Query for issues for 'owner@example.com'
280 user_ref = common_pb2.UserRef(user_id=111)
281 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
282
283 # We're authenticated as 'editor@example.com'
284 mc = monorailcontext.MonorailContext(
285 self.services, cnxn=self.cnxn, requester='editor@example.com')
286 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
287 request)
288
289 self.assertEqual(1, len(response.hotlists))
290 hotlist = response.hotlists[0]
291 self.assertEqual(111, hotlist.owner_ref.user_id)
292
293 def testListHotlistsByUser_PrivateIssueNoAccess(self):
294 # Private hostlist owned by 'owner@example.com'
295 self.services.features.CreateHotlist(
296 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
297 owner_ids=[111], editor_ids=[222], is_private=True)
298
299 # Query for issues for 'owner@example.com'
300 user_ref = common_pb2.UserRef(user_id=111)
301 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
302
303 # We're authenticated as 'foo@example.com'
304 mc = monorailcontext.MonorailContext(
305 self.services, cnxn=self.cnxn, requester='foo@example.com')
306 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
307 request)
308
309 self.assertEqual(0, len(response.hotlists))
310
311 def testListHotlistsByUser_PrivateIssueNotSignedIn(self):
312 # Private hostlist owned by 'owner@example.com'
313 self.services.features.CreateHotlist(
314 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
315 owner_ids=[111], editor_ids=[222], is_private=True)
316
317 # Query for issues for 'owner@example.com'
318 user_ref = common_pb2.UserRef(user_id=111)
319 request = features_pb2.ListHotlistsByUserRequest(user=user_ref)
320
321 # We're not authenticated
322 mc = monorailcontext.MonorailContext(self.services, cnxn=self.cnxn)
323 response = self.CallWrapped(self.features_svcr.ListHotlistsByUser, mc,
324 request)
325
326 self.assertEqual(0, len(response.hotlists))
327
328 def AddIssueToHotlist(self, hotlist_id, issue_id=78901, adder_id=111):
329 self.services.features.AddIssuesToHotlists(
330 self.cnxn, [hotlist_id], [(issue_id, adder_id, 0, '')],
331 None, None, None)
332
333 def testListHotlistsByIssue_Normal(self):
334 hotlist = self.services.features.CreateHotlist(
335 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
336 owner_ids=[111], editor_ids=[222])
337 self.AddIssueToHotlist(hotlist.hotlist_id)
338
339 issue_ref = common_pb2.IssueRef(project_name='proj', local_id=1)
340 request = features_pb2.ListHotlistsByIssueRequest(issue=issue_ref)
341
342 mc = monorailcontext.MonorailContext(
343 self.services, cnxn=self.cnxn, requester='foo@example.com')
344 response = self.CallWrapped(self.features_svcr.ListHotlistsByIssue, mc,
345 request)
346
347 self.assertEqual(1, len(response.hotlists))
348 hotlist = response.hotlists[0]
349 self.assertEqual('Fake-Hotlist', hotlist.name)
350
351 def testListHotlistsByIssue_NotSignedIn(self):
352 # Public hostlist owned by 'owner@example.com'
353 hotlist = self.services.features.CreateHotlist(
354 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
355 owner_ids=[111], editor_ids=[222])
356 self.AddIssueToHotlist(hotlist.hotlist_id)
357
358 issue_ref = common_pb2.IssueRef(project_name='proj', local_id=1)
359 request = features_pb2.ListHotlistsByIssueRequest(issue=issue_ref)
360
361 # We're not authenticated
362 mc = monorailcontext.MonorailContext(self.services, cnxn=self.cnxn)
363 response = self.CallWrapped(self.features_svcr.ListHotlistsByIssue, mc,
364 request)
365
366 self.assertEqual(1, len(response.hotlists))
367 hotlist = response.hotlists[0]
368 self.assertEqual('Fake-Hotlist', hotlist.name)
369
370 def testListHotlistsByIssue_Empty(self):
371 """There are no hotlists with the given issue."""
372 # Public hostlist owned by 'owner@example.com'
373 self.services.features.CreateHotlist(
374 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
375 owner_ids=[111], editor_ids=[222])
376
377 issue_ref = common_pb2.IssueRef(project_name='proj', local_id=1)
378 request = features_pb2.ListHotlistsByIssueRequest(issue=issue_ref)
379
380 # We're authenticated as 'foo@example.com'
381 mc = monorailcontext.MonorailContext(
382 self.services, cnxn=self.cnxn, requester='foo@example.com')
383 response = self.CallWrapped(self.features_svcr.ListHotlistsByIssue, mc,
384 request)
385
386 self.assertEqual(0, len(response.hotlists))
387
388 def testListHotlistsByIssue_NoHotlists(self):
389 issue_ref = common_pb2.IssueRef(project_name='proj', local_id=1)
390 request = features_pb2.ListHotlistsByIssueRequest(issue=issue_ref)
391
392 # We're authenticated as 'foo@example.com'
393 mc = monorailcontext.MonorailContext(
394 self.services, cnxn=self.cnxn, requester='foo@example.com')
395 response = self.CallWrapped(self.features_svcr.ListHotlistsByIssue, mc,
396 request)
397 self.assertEqual(0, len(response.hotlists))
398
399 def testListHotlistsByIssue_PrivateHotlistAsOwner(self):
400 """An owner can view their private issues."""
401 # Private hostlist owned by 'owner@example.com'
402 hotlist = self.services.features.CreateHotlist(
403 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
404 owner_ids=[111], editor_ids=[222], is_private=True)
405 self.AddIssueToHotlist(hotlist.hotlist_id)
406
407 issue_ref = common_pb2.IssueRef(project_name='proj', local_id=1)
408 request = features_pb2.ListHotlistsByIssueRequest(issue=issue_ref)
409
410 # We're authenticated as 'owner@example.com'
411 mc = monorailcontext.MonorailContext(
412 self.services, cnxn=self.cnxn, requester='owner@example.com')
413 response = self.CallWrapped(self.features_svcr.ListHotlistsByIssue, mc,
414 request)
415
416 self.assertEqual(1, len(response.hotlists))
417 hotlist = response.hotlists[0]
418 self.assertEqual('Fake-Hotlist', hotlist.name)
419
420 def testListHotlistsByIssue_PrivateHotlistNoAccess(self):
421 # Private hostlist owned by 'owner@example.com'
422 hotlist = self.services.features.CreateHotlist(
423 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
424 owner_ids=[111], editor_ids=[222], is_private=True)
425 self.AddIssueToHotlist(hotlist.hotlist_id)
426
427 issue_ref = common_pb2.IssueRef(project_name='proj', local_id=1)
428 request = features_pb2.ListHotlistsByIssueRequest(issue=issue_ref)
429
430 # We're authenticated as 'foo@example.com'
431 mc = monorailcontext.MonorailContext(
432 self.services, cnxn=self.cnxn, requester='foo@example.com')
433 response = self.CallWrapped(self.features_svcr.ListHotlistsByIssue, mc,
434 request)
435
436 self.assertEqual(0, len(response.hotlists))
437
438 def testListHotlistsByIssue_NonProjectHotlists(self):
439 hotlist = self.services.features.CreateHotlist(
440 self.cnxn,
441 'Fake-Hotlist',
442 'Summary',
443 'Description',
444 owner_ids=[111],
445 editor_ids=[222])
446 spam_hotlist = self.services.features.CreateHotlist(
447 self.cnxn,
448 'Spam-Hotlist',
449 'Summary',
450 'Description',
451 owner_ids=[444],
452 editor_ids=[])
453 another_hotlist = self.services.features.CreateHotlist(
454 self.cnxn,
455 'Another-Hotlist',
456 'Summary',
457 'Description',
458 owner_ids=[111],
459 editor_ids=[])
460 self.AddIssueToHotlist(hotlist.hotlist_id)
461 self.AddIssueToHotlist(spam_hotlist.hotlist_id)
462 self.AddIssueToHotlist(another_hotlist.hotlist_id)
463
464 issue_ref = common_pb2.IssueRef(project_name='proj', local_id=1)
465 request = features_pb2.ListHotlistsByIssueRequest(issue=issue_ref)
466
467 mc = monorailcontext.MonorailContext(
468 self.services, cnxn=self.cnxn, requester='foo@example.com')
469 response = self.CallWrapped(
470 self.features_svcr.ListHotlistsByIssue, mc, request)
471
472 self.assertEqual(2, len(response.hotlists))
473 self.assertEqual('Fake-Hotlist', response.hotlists[0].name)
474 self.assertEqual('Another-Hotlist', response.hotlists[1].name)
475
476 def testListRecentlyVisitedHotlists(self):
477 hotlists = [
478 self.services.features.CreateHotlist(
479 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
480 owner_ids=[self.user2.user_id], editor_ids=[self.user1.user_id],
481 default_col_spec='chicken'),
482 self.services.features.CreateHotlist(
483 self.cnxn, 'Fake-Hotlist-2', 'Summary', 'Description',
484 owner_ids=[self.user1.user_id], editor_ids=[self.user2.user_id],
485 default_col_spec='honk'),
486 self.services.features.CreateHotlist(
487 self.cnxn, 'Private-Hotlist', 'Summary', 'Description',
488 owner_ids=[self.user3.user_id], editor_ids=[self.user2.user_id],
489 is_private=True)]
490
491 for hotlist in hotlists:
492 self.services.user.AddVisitedHotlist(
493 self.cnxn, self.user1.user_id, hotlist.hotlist_id)
494
495 request = features_pb2.ListRecentlyVisitedHotlistsRequest()
496 mc = monorailcontext.MonorailContext(
497 self.services, cnxn=self.cnxn, requester=self.user1.email)
498 response = self.CallWrapped(
499 self.features_svcr.ListRecentlyVisitedHotlists, mc, request)
500
501 expected_hotlists = [
502 features_objects_pb2.Hotlist(
503 owner_ref=common_pb2.UserRef(
504 user_id=self.user2.user_id, display_name=self.user2.email),
505 editor_refs=[
506 common_pb2.UserRef(
507 user_id=self.user1.user_id, display_name=self.user1.email)
508 ],
509 name='Fake-Hotlist',
510 summary='Summary',
511 description='Description',
512 is_private=False,
513 default_col_spec='chicken'),
514 features_objects_pb2.Hotlist(
515 owner_ref=common_pb2.UserRef(
516 user_id=self.user1.user_id, display_name=self.user1.email),
517 editor_refs=[
518 common_pb2.UserRef(
519 user_id=self.user2.user_id, display_name=self.user2.email)
520 ],
521 name='Fake-Hotlist-2',
522 summary='Summary',
523 description='Description',
524 is_private=False,
525 default_col_spec='honk')
526 ]
527
528 # We don't have permission to see the last issue, because it is marked as
529 # private and we're not owners or editors.
530 self.assertEqual(expected_hotlists, list(response.hotlists))
531
532 def testListRecentlyVisitedHotlists_Anon(self):
533 request = features_pb2.ListRecentlyVisitedHotlistsRequest()
534 mc = monorailcontext.MonorailContext(self.services, cnxn=self.cnxn)
535 response = self.CallWrapped(
536 self.features_svcr.ListRecentlyVisitedHotlists, mc, request)
537 self.assertEqual(0, len(response.hotlists))
538
539 def testListStarredHotlists(self):
540 hotlists = [
541 self.services.features.CreateHotlist(
542 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
543 owner_ids=[self.user2.user_id], editor_ids=[self.user1.user_id],
544 default_col_spec='cow chicken'),
545 self.services.features.CreateHotlist(
546 self.cnxn, 'Fake-Hotlist-2', 'Summary', 'Description',
547 owner_ids=[self.user1.user_id],
548 editor_ids=[self.user2.user_id, self.user3.user_id],
549 default_col_spec=''),
550 self.services.features.CreateHotlist(
551 self.cnxn, 'Private-Hotlist', 'Summary', 'Description',
552 owner_ids=[self.user3.user_id], editor_ids=[self.user2.user_id],
553 is_private=True, default_col_spec='chicken')]
554
555 for hotlist in hotlists:
556 self.services.hotlist_star.SetStar(
557 self.cnxn, hotlist.hotlist_id, self.user1.user_id, True)
558
559 request = features_pb2.ListStarredHotlistsRequest()
560 mc = monorailcontext.MonorailContext(
561 self.services, cnxn=self.cnxn, requester='owner@example.com')
562 response = self.CallWrapped(
563 self.features_svcr.ListStarredHotlists, mc, request)
564
565 expected_hotlists = [
566 features_objects_pb2.Hotlist(
567 owner_ref=common_pb2.UserRef(
568 user_id=self.user2.user_id, display_name=self.user2.email),
569 editor_refs=[
570 common_pb2.UserRef(
571 user_id=self.user1.user_id, display_name=self.user1.email)
572 ],
573 name='Fake-Hotlist',
574 summary='Summary',
575 description='Description',
576 is_private=False,
577 default_col_spec='cow chicken'),
578 features_objects_pb2.Hotlist(
579 owner_ref=common_pb2.UserRef(
580 user_id=self.user1.user_id, display_name=self.user1.email),
581 editor_refs=[
582 common_pb2.UserRef(
583 user_id=self.user2.user_id, display_name=self.user2.email),
584 common_pb2.UserRef(
585 user_id=self.user3.user_id, display_name=self.user3.email)
586 ],
587 name='Fake-Hotlist-2',
588 summary='Summary',
589 description='Description',
590 is_private=False)
591 ]
592
593 # We don't have permission to see the last issue, because it is marked as
594 # private and we're not owners or editors.
595 self.assertEqual(expected_hotlists, list(response.hotlists))
596
597 def testListStarredHotlists_Anon(self):
598 request = features_pb2.ListStarredHotlistsRequest()
599 mc = monorailcontext.MonorailContext(self.services, cnxn=self.cnxn)
600 response = self.CallWrapped(
601 self.features_svcr.ListStarredHotlists, mc, request)
602 self.assertEqual(0, len(response.hotlists))
603
604 def CallGetStarCount(self):
605 # Query for hotlists for 'owner@example.com'
606 owner_ref = common_pb2.UserRef(user_id=111)
607 hotlist_ref = common_pb2.HotlistRef(name='Fake-Hotlist', owner=owner_ref)
608 request = features_pb2.GetHotlistStarCountRequest(hotlist_ref=hotlist_ref)
609 mc = monorailcontext.MonorailContext(
610 self.services, cnxn=self.cnxn, requester='owner@example.com')
611 response = self.CallWrapped(
612 self.features_svcr.GetHotlistStarCount, mc, request)
613 return response.star_count
614
615 def CallStar(self, requester='owner@example.com', starred=True):
616 # Query for hotlists for 'owner@example.com'
617 owner_ref = common_pb2.UserRef(user_id=111)
618 hotlist_ref = common_pb2.HotlistRef(name='Fake-Hotlist', owner=owner_ref)
619 request = features_pb2.StarHotlistRequest(
620 hotlist_ref=hotlist_ref, starred=starred)
621 mc = monorailcontext.MonorailContext(
622 self.services, cnxn=self.cnxn, requester=requester)
623 response = self.CallWrapped(
624 self.features_svcr.StarHotlist, mc, request)
625 return response.star_count
626
627 def testStarCount_Normal(self):
628 self.services.features.CreateHotlist(
629 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
630 owner_ids=[111], editor_ids=[222])
631 self.assertEqual(0, self.CallGetStarCount())
632 self.assertEqual(1, self.CallStar())
633 self.assertEqual(1, self.CallGetStarCount())
634
635 def testStarCount_StarTwiceSameUser(self):
636 self.services.features.CreateHotlist(
637 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
638 owner_ids=[111], editor_ids=[222])
639 self.assertEqual(1, self.CallStar())
640 self.assertEqual(1, self.CallStar())
641 self.assertEqual(1, self.CallGetStarCount())
642
643 def testStarCount_StarTwiceDifferentUser(self):
644 self.services.features.CreateHotlist(
645 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
646 owner_ids=[111], editor_ids=[222])
647 self.assertEqual(1, self.CallStar())
648 self.assertEqual(2, self.CallStar(requester='user_222@example.com'))
649 self.assertEqual(2, self.CallGetStarCount())
650
651 def testStarCount_RemoveStarTwiceSameUser(self):
652 self.services.features.CreateHotlist(
653 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
654 owner_ids=[111], editor_ids=[222])
655 self.assertEqual(1, self.CallStar())
656 self.assertEqual(1, self.CallGetStarCount())
657
658 self.assertEqual(0, self.CallStar(starred=False))
659 self.assertEqual(0, self.CallStar(starred=False))
660 self.assertEqual(0, self.CallGetStarCount())
661
662 def testStarCount_RemoveStarTwiceDifferentUser(self):
663 self.services.features.CreateHotlist(
664 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
665 owner_ids=[111], editor_ids=[222])
666 self.assertEqual(1, self.CallStar())
667 self.assertEqual(2, self.CallStar(requester='user_222@example.com'))
668 self.assertEqual(2, self.CallGetStarCount())
669
670 self.assertEqual(1, self.CallStar(starred=False))
671 self.assertEqual(
672 0, self.CallStar(requester='user_222@example.com', starred=False))
673 self.assertEqual(0, self.CallGetStarCount())
674
675 def testGetHotlist(self):
676 hotlist = self.services.features.CreateHotlist(
677 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
678 owner_ids=[self.user3.user_id], editor_ids=[self.user4.user_id],
679 is_private=True, default_col_spec='corgi butts')
680
681 owner_ref = common_pb2.UserRef(user_id=self.user3.user_id)
682 hotlist_ref = common_pb2.HotlistRef(
683 name=hotlist.name, owner=owner_ref)
684 request = features_pb2.GetHotlistRequest(hotlist_ref=hotlist_ref)
685
686 mc = monorailcontext.MonorailContext(
687 self.services, cnxn=self.cnxn, requester=self.user4.email)
688 mc.LookupLoggedInUserPerms(None)
689 response = self.CallWrapped(
690 self.features_svcr.GetHotlist, mc, request)
691
692 self.assertEqual(
693 response.hotlist,
694 features_objects_pb2.Hotlist(
695 owner_ref=common_pb2.UserRef(
696 user_id=self.user3.user_id,
697 display_name=testing_helpers.ObscuredEmail(self.user3.email)),
698 editor_refs=[common_pb2.UserRef(
699 user_id=self.user4.user_id,
700 display_name=self.user4.email)],
701 name=hotlist.name,
702 summary=hotlist.summary,
703 description=hotlist.description,
704 default_col_spec='corgi butts',
705 is_private=True))
706
707 def testGetHotlist_BadInput(self):
708 hotlist_ref = common_pb2.HotlistRef()
709 request = features_pb2.GetHotlistRequest(hotlist_ref=hotlist_ref)
710
711 mc = monorailcontext.MonorailContext(
712 self.services, cnxn=self.cnxn, requester='foo@example.com')
713 with self.assertRaises(features_svc.NoSuchHotlistException):
714 self.CallWrapped(self.features_svcr.GetHotlist, mc, request)
715
716 def testCreateHotlist_Normal(self):
717 request = features_pb2.CreateHotlistRequest(
718 name='Fake-Hotlist',
719 summary='Summary',
720 description='Description',
721 editor_refs=[
722 common_pb2.UserRef(user_id=222),
723 common_pb2.UserRef(display_name='foo@example.com')],
724 issue_refs=[
725 common_pb2.IssueRef(project_name='proj', local_id=1),
726 common_pb2.IssueRef(project_name='proj', local_id=2)],
727 is_private=True)
728 mc = monorailcontext.MonorailContext(
729 self.services, cnxn=self.cnxn, requester='owner@example.com')
730 self.CallWrapped(self.features_svcr.CreateHotlist, mc, request)
731
732 # Check that the hotlist was successfuly added.
733 hotlist_id = self.services.features.LookupHotlistIDs(
734 self.cnxn, ['Fake-Hotlist'], [111]).get(('fake-hotlist', 111))
735 hotlist = self.services.features.GetHotlist(self.cnxn, hotlist_id)
736 self.assertEqual('Summary', hotlist.summary)
737 self.assertEqual('Description', hotlist.description)
738 self.assertEqual([111], hotlist.owner_ids)
739 self.assertEqual([222, 333], hotlist.editor_ids)
740 self.assertEqual(
741 [self.issue_1.issue_id, self.issue_2.issue_id],
742 [item.issue_id for item in hotlist.items])
743 self.assertTrue(hotlist.is_private)
744
745 def testCreateHotlist_Simple(self):
746 request = features_pb2.CreateHotlistRequest(
747 name='Fake-Hotlist',
748 summary='Summary',
749 description='Description')
750 mc = monorailcontext.MonorailContext(
751 self.services, cnxn=self.cnxn, requester='owner@example.com')
752 self.CallWrapped(self.features_svcr.CreateHotlist, mc, request)
753
754 # Check that the hotlist was successfuly added.
755 hotlist_id = self.services.features.LookupHotlistIDs(
756 self.cnxn, ['Fake-Hotlist'], [111]).get(('fake-hotlist', 111))
757 hotlist = self.services.features.GetHotlist(self.cnxn, hotlist_id)
758 self.assertEqual('Summary', hotlist.summary)
759 self.assertEqual('Description', hotlist.description)
760 self.assertEqual([111], hotlist.owner_ids)
761 self.assertEqual([], hotlist.editor_ids)
762 self.assertEqual(0, len(hotlist.items))
763 self.assertFalse(hotlist.is_private)
764
765 def testCheckHotlistName_OK(self):
766 request = features_pb2.CheckHotlistNameRequest(name='Fake-Hotlist')
767 mc = monorailcontext.MonorailContext(
768 self.services, cnxn=self.cnxn, requester='owner@example.com')
769 result = self.CallWrapped(self.features_svcr.CheckHotlistName, mc, request)
770 self.assertEqual('', result.error)
771
772 def testCheckHotlistName_Anon(self):
773 request = features_pb2.CheckHotlistNameRequest(name='Fake-Hotlist')
774 mc = monorailcontext.MonorailContext(self.services, cnxn=self.cnxn)
775
776 with self.assertRaises(exceptions.InputException):
777 self.CallWrapped(self.features_svcr.CheckHotlistName, mc, request)
778
779 def testCheckHotlistName_InvalidName(self):
780 request = features_pb2.CheckHotlistNameRequest(name='**Invalid**')
781 mc = monorailcontext.MonorailContext(
782 self.services, cnxn=self.cnxn, requester='owner@example.com')
783
784 result = self.CallWrapped(self.features_svcr.CheckHotlistName, mc, request)
785 self.assertNotEqual('', result.error)
786
787 def testCheckHotlistName_AlreadyExists(self):
788 self.services.features.CreateHotlist(
789 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
790 owner_ids=[111], editor_ids=[])
791
792 request = features_pb2.CheckHotlistNameRequest(name='Fake-Hotlist')
793 mc = monorailcontext.MonorailContext(
794 self.services, cnxn=self.cnxn, requester='owner@example.com')
795
796 result = self.CallWrapped(self.features_svcr.CheckHotlistName, mc, request)
797 self.assertNotEqual('', result.error)
798
799 def testRemoveIssuesFromHotlists(self):
800 # Create two hotlists with issues 1 and 2.
801 hotlist_1 = self.services.features.CreateHotlist(
802 self.cnxn, 'Hotlist-1', 'Summary', 'Description', owner_ids=[111],
803 editor_ids=[])
804 hotlist_2 = self.services.features.CreateHotlist(
805 self.cnxn, 'Hotlist-2', 'Summary', 'Description', owner_ids=[111],
806 editor_ids=[])
807 self.services.features.AddIssuesToHotlists(
808 self.cnxn,
809 [hotlist_1.hotlist_id, hotlist_2.hotlist_id],
810 [(self.issue_1.issue_id, 111, 0, ''),
811 (self.issue_2.issue_id, 111, 0, '')],
812 None, None, None)
813
814 # Remove Issue 1 from both hotlists.
815 request = features_pb2.RemoveIssuesFromHotlistsRequest(
816 hotlist_refs=[
817 common_pb2.HotlistRef(
818 name='Hotlist-1',
819 owner=common_pb2.UserRef(user_id=111)),
820 common_pb2.HotlistRef(
821 name='Hotlist-2',
822 owner=common_pb2.UserRef(user_id=111))],
823 issue_refs=[
824 common_pb2.IssueRef(project_name='proj', local_id=1)])
825
826 mc = monorailcontext.MonorailContext(
827 self.services, cnxn=self.cnxn, requester='owner@example.com')
828 self.CallWrapped(self.features_svcr.RemoveIssuesFromHotlists, mc, request)
829
830 # Only Issue 2 should remain in both lists.
831 self.assertEqual(
832 [self.issue_2.issue_id],
833 [item.issue_id for item in hotlist_1.items])
834 self.assertEqual(
835 [self.issue_2.issue_id],
836 [item.issue_id for item in hotlist_2.items])
837
838 def testAddIssuesToHotlists(self):
839 # Create two hotlists
840 hotlist_1 = self.services.features.CreateHotlist(
841 self.cnxn, 'Hotlist-1', 'Summary', 'Description', owner_ids=[111],
842 editor_ids=[])
843 hotlist_2 = self.services.features.CreateHotlist(
844 self.cnxn, 'Hotlist-2', 'Summary', 'Description', owner_ids=[111],
845 editor_ids=[])
846
847 # Add Issue 1 to both hotlists
848 request = features_pb2.AddIssuesToHotlistsRequest(
849 note='Foo',
850 hotlist_refs=[
851 common_pb2.HotlistRef(
852 name='Hotlist-1',
853 owner=common_pb2.UserRef(user_id=111)),
854 common_pb2.HotlistRef(
855 name='Hotlist-2',
856 owner=common_pb2.UserRef(user_id=111))],
857 issue_refs=[
858 common_pb2.IssueRef(project_name='proj', local_id=1)])
859
860 mc = monorailcontext.MonorailContext(
861 self.services, cnxn=self.cnxn, requester='owner@example.com')
862 self.CallWrapped(self.features_svcr.AddIssuesToHotlists, mc, request)
863
864 self.assertEqual(
865 [self.issue_1.issue_id],
866 [item.issue_id for item in hotlist_1.items])
867 self.assertEqual(
868 [self.issue_1.issue_id],
869 [item.issue_id for item in hotlist_2.items])
870
871 self.assertEqual('Foo', hotlist_1.items[0].note)
872 self.assertEqual('Foo', hotlist_2.items[0].note)
873
874 def testRerankHotlistIssues(self):
875 """Rerank a hotlist."""
876 issue_3 = fake.MakeTestIssue(
877 789, 3, 'sum', 'New', 111, project_name='proj', issue_id=78903)
878 issue_4 = fake.MakeTestIssue(
879 789, 4, 'sum', 'New', 111, project_name='proj', issue_id=78904)
880 self.services.issue.TestAddIssue(issue_3)
881 self.services.issue.TestAddIssue(issue_4)
882
883 owner_ids = [self.user1.user_id]
884 follower_ids = [self.user2.user_id]
885 editor_ids = [self.user3.user_id]
886 hotlist_items = [
887 (78904, 31, self.user2.user_id, self.PAST_TIME, 'note'),
888 (78903, 21, self.user2.user_id, self.PAST_TIME, 'note'),
889 (78902, 11, self.user2.user_id, self.PAST_TIME, 'note'),
890 (78901, 1, self.user2.user_id, self.PAST_TIME, 'note')]
891 hotlist = self.services.features.TestAddHotlist(
892 'RerankHotlistName', summary='summary', owner_ids=owner_ids,
893 editor_ids=editor_ids, follower_ids=follower_ids,
894 hotlist_id=1236, hotlist_item_fields=hotlist_items)
895
896 request = features_pb2.RerankHotlistIssuesRequest(
897 hotlist_ref=common_pb2.HotlistRef(
898 name='RerankHotlistName',
899 owner=common_pb2.UserRef(user_id=self.user1.user_id)),
900 moved_refs=[common_pb2.IssueRef(
901 project_name='proj', local_id=2)],
902 target_ref=common_pb2.IssueRef(project_name='proj', local_id=4),
903 split_above=True)
904
905 mc = monorailcontext.MonorailContext(
906 self.services, cnxn=self.cnxn, requester=self.user1.email)
907 mc.LookupLoggedInUserPerms(self.project)
908 self.CallWrapped(self.features_svcr.RerankHotlistIssues, mc, request)
909
910 self.assertEqual(
911 [item.issue_id for item in hotlist.items],
912 [78901, 78903, 78902, 78904])
913
914 def testUpdateHotlistIssueNote(self):
915 hotlist = self.services.features.CreateHotlist(
916 self.cnxn, 'Hotlist-1', 'Summary', 'Description', owner_ids=[111],
917 editor_ids=[])
918 self.services.features.AddIssuesToHotlists(
919 self.cnxn,
920 [hotlist.hotlist_id], [(self.issue_1.issue_id, 111, 0, '')],
921 None, None, None)
922
923 request = features_pb2.UpdateHotlistIssueNoteRequest(
924 hotlist_ref=common_pb2.HotlistRef(
925 name='Hotlist-1',
926 owner=common_pb2.UserRef(user_id=111)),
927 issue_ref=common_pb2.IssueRef(
928 project_name='proj',
929 local_id=1),
930 note='Note')
931
932 mc = monorailcontext.MonorailContext(
933 self.services, cnxn=self.cnxn, requester='owner@example.com')
934 self.CallWrapped(self.features_svcr.UpdateHotlistIssueNote, mc, request)
935
936 self.assertEqual('Note', hotlist.items[0].note)
937
938 def testUpdateHotlistIssueNote_NotAllowed(self):
939 hotlist = self.services.features.CreateHotlist(
940 self.cnxn, 'Hotlist-1', 'Summary', 'Description', owner_ids=[222],
941 editor_ids=[])
942 self.services.features.AddIssuesToHotlists(
943 self.cnxn,
944 [hotlist.hotlist_id], [(self.issue_1.issue_id, 222, 0, '')],
945 None, None, None)
946
947 request = features_pb2.UpdateHotlistIssueNoteRequest(
948 hotlist_ref=common_pb2.HotlistRef(
949 name='Hotlist-1',
950 owner=common_pb2.UserRef(user_id=222)),
951 issue_ref=common_pb2.IssueRef(
952 project_name='proj',
953 local_id=1),
954 note='Note')
955
956 mc = monorailcontext.MonorailContext(
957 self.services, cnxn=self.cnxn, requester='owner@example.com')
958 with self.assertRaises(permissions.PermissionException):
959 self.CallWrapped(self.features_svcr.UpdateHotlistIssueNote, mc, request)
960
961 mc = monorailcontext.MonorailContext(
962 self.services, cnxn=self.cnxn, requester=self.user2.email)
963 mc.LookupLoggedInUserPerms(None)
964
965 mc = monorailcontext.MonorailContext(
966 self.services, cnxn=self.cnxn, requester=self.user2.email)
967 mc.LookupLoggedInUserPerms(None)
968
969 def testDeleteHotlist(self):
970 """Test we can delete a hotlist via the API."""
971 owner_ids = [self.user2.user_id]
972 editor_ids = []
973 hotlist = self.services.features.TestAddHotlist(
974 name='Hotlist-1', summary='summary', description='description',
975 owner_ids=owner_ids, editor_ids=editor_ids, hotlist_id=1235)
976 request = features_pb2.DeleteHotlistRequest(
977 hotlist_ref=common_pb2.HotlistRef(
978 name=hotlist.name,
979 owner=common_pb2.UserRef(user_id=self.user2.user_id)))
980
981 mc = monorailcontext.MonorailContext(
982 self.services, cnxn=self.cnxn, requester=self.user2.email)
983 mc.LookupLoggedInUserPerms(None)
984 self.CallWrapped(self.features_svcr.DeleteHotlist, mc, request)
985
986 self.assertTrue(
987 hotlist.hotlist_id in self.services.features.expunged_hotlist_ids)
988
989 def testPredictComponent_Normal(self):
990 """Test normal case when predicted component exists."""
991 component_id = self.services.config.CreateComponentDef(
992 cnxn=None, project_id=self.project.project_id, path='Ruta>Baga',
993 docstring='', deprecated=False, admin_ids=[], cc_ids=[], created=None,
994 creator_id=None, label_ids=[])
995
996 self._top_words = {
997 'foo': 0,
998 'bar': 1,
999 'baz': 2}
1000 self._components_by_index = {
1001 '0': '123',
1002 '1': str(component_id),
1003 '2': '789'}
1004 self._ml_engine.expected_features = [3, 0, 1, 0, 0]
1005 self._ml_engine.scores = [5, 10, 3]
1006
1007 request = features_pb2.PredictComponentRequest(
1008 project_name='proj',
1009 text='foo baz foo foo')
1010 mc = monorailcontext.MonorailContext(
1011 self.services, cnxn=self.cnxn, requester='owner@example.com')
1012 result = self.CallWrapped(self.features_svcr.PredictComponent, mc, request)
1013
1014 self.assertEqual(
1015 common_pb2.ComponentRef(
1016 path='Ruta>Baga'),
1017 result.component_ref)
1018
1019 def testPredictComponent_NoPrediction(self):
1020 """Test case when no component id was predicted."""
1021 self._top_words = {
1022 'foo': 0,
1023 'bar': 1,
1024 'baz': 2}
1025 self._components_by_index = {
1026 '0': '123',
1027 '1': '456',
1028 '2': '789'}
1029 self._ml_engine.expected_features = [3, 0, 1, 0, 0]
1030 self._ml_engine.scores = [5, 10, 3]
1031
1032 request = features_pb2.PredictComponentRequest(
1033 project_name='proj',
1034 text='foo baz foo foo')
1035 mc = monorailcontext.MonorailContext(
1036 self.services, cnxn=self.cnxn, requester='owner@example.com')
1037 result = self.CallWrapped(self.features_svcr.PredictComponent, mc, request)
1038
1039 self.assertEqual(common_pb2.ComponentRef(), result.component_ref)