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