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