blob: ee060bd5f82d79fbb4d74b220c4c7679209f6875 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2018 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5"""Tests for converting internal protorpc to external protoc."""
6from __future__ import print_function
7from __future__ import division
8from __future__ import absolute_import
9
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010010from mock import patch
11import six
Copybara854996b2021-09-07 19:36:02 +000012import unittest
13
14from google.protobuf import wrappers_pb2
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010015import pytest
Copybara854996b2021-09-07 19:36:02 +000016
17import settings
18from api import converters
19from api.api_proto import common_pb2
20from api.api_proto import features_objects_pb2
21from api.api_proto import issue_objects_pb2
22from api.api_proto import project_objects_pb2
23from api.api_proto import user_objects_pb2
24from framework import exceptions
25from framework import permissions
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010026from mrproto import tracker_pb2
27from mrproto import user_pb2
Copybara854996b2021-09-07 19:36:02 +000028from testing import fake
29from testing import testing_helpers
30from tracker import tracker_bizobj
31from services import features_svc
32from services import service_manager
33
34
35class ConverterFunctionsTest(unittest.TestCase):
36
37 NOW = 1234567890
38
39 def setUp(self):
40 self.users_by_id = {
41 111: testing_helpers.Blank(
42 display_name='one@example.com', email='one@example.com',
43 banned=False),
44 222: testing_helpers.Blank(
45 display_name='two@example.com', email='two@example.com',
46 banned=False),
47 333: testing_helpers.Blank(
48 display_name='ban...@example.com', email='banned@example.com',
49 banned=True),
50 }
51
52 self.services = service_manager.Services(
53 issue=fake.IssueService(),
54 project=fake.ProjectService(),
55 user=fake.UserService(),
56 features=fake.FeaturesService())
57 self.cnxn = fake.MonorailConnection()
58 self.project = self.services.project.TestAddProject(
59 'proj', project_id=789)
60 self.config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
61
62 self.fd_1 = tracker_pb2.FieldDef(
63 field_name='FirstField', field_id=1,
64 field_type=tracker_pb2.FieldTypes.STR_TYPE,
65 applicable_type='')
66 self.fd_2 = tracker_pb2.FieldDef(
67 field_name='SecField', field_id=2,
68 field_type=tracker_pb2.FieldTypes.INT_TYPE,
69 applicable_type='')
70 self.fd_3 = tracker_pb2.FieldDef(
71 field_name='LegalApproval', field_id=3,
72 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE,
73 applicable_type='')
74 self.fd_4 = tracker_pb2.FieldDef(
75 field_name='UserField', field_id=4,
76 field_type=tracker_pb2.FieldTypes.USER_TYPE,
77 applicable_type='')
78 self.fd_5 = tracker_pb2.FieldDef(
79 field_name='Pre', field_id=5,
80 field_type=tracker_pb2.FieldTypes.ENUM_TYPE,
81 applicable_type='')
82 self.fd_6 = tracker_pb2.FieldDef(
83 field_name='PhaseField', field_id=6,
84 field_type=tracker_pb2.FieldTypes.INT_TYPE,
85 applicable_type='', is_phase_field=True)
86 self.fd_7 = tracker_pb2.FieldDef(
87 field_name='ApprovalEnum', field_id=7,
88 field_type=tracker_pb2.FieldTypes.ENUM_TYPE,
89 applicable_type='', approval_id=self.fd_3.field_id)
90
91 self.user_1 = self.services.user.TestAddUser('one@example.com', 111)
92 self.user_2 = self.services.user.TestAddUser('two@example.com', 222)
93 self.user_3 = self.services.user.TestAddUser('banned@example.com', 333)
94 self.issue_1 = fake.MakeTestIssue(
95 789, 1, 'sum', 'New', 111, project_name='proj')
96 self.issue_2 = fake.MakeTestIssue(
97 789, 2, 'sum', 'New', 111, project_name='proj')
98 self.services.issue.TestAddIssue(self.issue_1)
99 self.services.issue.TestAddIssue(self.issue_2)
100
101 def testConvertApprovalValues_Empty(self):
102 """We handle the case where an issue has no approval values."""
103 actual = converters.ConvertApprovalValues([], [], {}, self.config)
104 self.assertEqual([], actual)
105
106 def testConvertApprovalValues_Normal(self):
107 """We can convert a list of approval values."""
108 now = 1234567890
109 self.config.field_defs.append(tracker_pb2.FieldDef(
110 field_id=1, project_id=789, field_name='EstDays',
111 field_type=tracker_pb2.FieldTypes.INT_TYPE,
112 applicable_type=''))
113 self.config.field_defs.append(tracker_pb2.FieldDef(
114 field_id=11, project_id=789, field_name='Accessibility',
115 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE,
116 applicable_type='Launch'))
117 self.config.approval_defs.append(tracker_pb2.ApprovalDef(
118 approval_id=11, approver_ids=[111], survey='survey 1'))
119 self.config.approval_defs.append(tracker_pb2.ApprovalDef(
120 approval_id=12, approver_ids=[111], survey='survey 2'))
121 av_11 = tracker_pb2.ApprovalValue(
122 approval_id=11, status=tracker_pb2.ApprovalStatus.NEED_INFO,
123 setter_id=111, set_on=now, approver_ids=[111, 222],
124 phase_id=21)
125 # Note: no approval def, no phase, so it won't be returned.
126 # TODO(ehmaldonado): Figure out support for "foreign" fields.
127 av_12 = tracker_pb2.ApprovalValue(
128 approval_id=12, status=tracker_pb2.ApprovalStatus.NOT_SET,
129 setter_id=111, set_on=now, approver_ids=[111])
130 phase_21 = tracker_pb2.Phase(phase_id=21, name='Stable', rank=1)
131 actual = converters.ConvertApprovalValues(
132 [av_11, av_12], [phase_21], self.users_by_id, self.config)
133
134 expected_av_1 = issue_objects_pb2.Approval(
135 field_ref=common_pb2.FieldRef(
136 field_id=11,
137 field_name='Accessibility',
138 type=common_pb2.APPROVAL_TYPE),
139 approver_refs=[
140 common_pb2.UserRef(user_id=111, display_name='one@example.com'),
141 common_pb2.UserRef(user_id=222, display_name='two@example.com'),
142 ],
143 status=issue_objects_pb2.NEED_INFO,
144 set_on=now,
145 setter_ref=common_pb2.UserRef(
146 user_id=111, display_name='one@example.com'),
147 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Stable'))
148
149 self.assertEqual([expected_av_1], actual)
150
151 def testConvertApproval(self):
152 """We can convert ApprovalValues to protoc Approvals."""
153 approval_value = tracker_pb2.ApprovalValue(
154 approval_id=3,
155 status=tracker_pb2.ApprovalStatus.NEED_INFO,
156 setter_id=222,
157 set_on=2345,
158 approver_ids=[111],
159 phase_id=1
160 )
161
162 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_3]
163
164 phase = tracker_pb2.Phase(phase_id=1, name='Canary')
165
166 actual = converters.ConvertApproval(
167 approval_value, self.users_by_id, self.config, phase=phase)
168 expected = issue_objects_pb2.Approval(
169 field_ref=common_pb2.FieldRef(
170 field_id=3,
171 field_name='LegalApproval',
172 type=common_pb2.APPROVAL_TYPE),
173 approver_refs=[common_pb2.UserRef(
174 user_id=111, display_name='one@example.com', is_derived=False)
175 ],
176 status=5,
177 set_on=2345,
178 setter_ref=common_pb2.UserRef(
179 user_id=222, display_name='two@example.com', is_derived=False
180 ),
181 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Canary')
182 )
183
184 self.assertEqual(expected, actual)
185
186 def testConvertApproval_NonExistentApproval(self):
187 approval_value = tracker_pb2.ApprovalValue(
188 approval_id=3,
189 status=tracker_pb2.ApprovalStatus.NEED_INFO,
190 setter_id=222,
191 set_on=2345,
192 approver_ids=[111],
193 phase_id=1
194 )
195 phase = tracker_pb2.Phase(phase_id=1, name='Canary')
196 self.assertIsNone(converters.ConvertApproval(
197 approval_value, self.users_by_id, self.config, phase=phase))
198
199
200 def testConvertApprovalStatus(self):
201 """We can convert a protorpc ApprovalStatus to a protoc ApprovalStatus."""
202 actual = converters.ConvertApprovalStatus(
203 tracker_pb2.ApprovalStatus.REVIEW_REQUESTED)
204 self.assertEqual(actual, issue_objects_pb2.REVIEW_REQUESTED)
205
206 actual = converters.ConvertApprovalStatus(
207 tracker_pb2.ApprovalStatus.NOT_SET)
208 self.assertEqual(actual, issue_objects_pb2.NOT_SET)
209
210 def testConvertUserRef(self):
211 """We can convert user IDs to a UserRef."""
212 # No specified user
213 actual = converters.ConvertUserRef(None, None, self.users_by_id)
214 expected = None
215 self.assertEqual(expected, actual)
216
217 # Explicitly specified user
218 actual = converters.ConvertUserRef(111, None, self.users_by_id)
219 expected = common_pb2.UserRef(
220 user_id=111, is_derived=False, display_name='one@example.com')
221 self.assertEqual(expected, actual)
222
223 # Derived user
224 actual = converters.ConvertUserRef(None, 111, self.users_by_id)
225 expected = common_pb2.UserRef(
226 user_id=111, is_derived=True, display_name='one@example.com')
227 self.assertEqual(expected, actual)
228
229 def testConvertUserRefs(self):
230 """We can convert lists of user_ids into UserRefs."""
231 # No specified users
232 actual = converters.ConvertUserRefs(
233 [], [], self.users_by_id, False)
234 expected = []
235 self.assertEqual(expected, actual)
236
237 # A mix of explicit and derived users
238 actual = converters.ConvertUserRefs(
239 [111], [222], self.users_by_id, False)
240 expected = [
241 common_pb2.UserRef(
242 user_id=111, is_derived=False, display_name='one@example.com'),
243 common_pb2.UserRef(
244 user_id=222, is_derived=True, display_name='two@example.com'),
245 ]
246 self.assertEqual(expected, actual)
247
248 # Use display name
249 actual = converters.ConvertUserRefs([333], [], self.users_by_id, False)
250 self.assertEqual(
251 [common_pb2.UserRef(
252 user_id=333, is_derived=False, display_name='ban...@example.com')],
253 actual)
254
255 # Use email
256 actual = converters.ConvertUserRefs([333], [], self.users_by_id, True)
257 self.assertEqual(
258 [common_pb2.UserRef(
259 user_id=333, is_derived=False, display_name='banned@example.com')],
260 actual)
261
262 @patch('time.time')
263 def testConvertUsers(self, mock_time):
264 """We can convert lists of protorpc Users to protoc Users."""
265 mock_time.return_value = self.NOW
266 user1 = user_pb2.User(
267 user_id=1, email='user1@example.com', last_visit_timestamp=self.NOW)
268 user2 = user_pb2.User(
269 user_id=2, email='user2@example.com', is_site_admin=True,
270 last_visit_timestamp=self.NOW)
271 user3 = user_pb2.User(
272 user_id=3, email='user3@example.com',
273 linked_child_ids=[4])
274 user4 = user_pb2.User(
275 user_id=4, email='user4@example.com', last_visit_timestamp=1,
276 linked_parent_id=3)
277 users_by_id = {
278 3: testing_helpers.Blank(
279 display_name='user3@example.com', email='user3@example.com',
280 banned=False),
281 4: testing_helpers.Blank(
282 display_name='user4@example.com', email='user4@example.com',
283 banned=False),
284 }
285
286 actual = converters.ConvertUsers(
287 [user1, user2, user3, user4], users_by_id)
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100288 six.assertCountEqual(
289 self, actual, [
290 user_objects_pb2.User(user_id=1, display_name='user1@example.com'),
291 user_objects_pb2.User(
292 user_id=2, display_name='user2@example.com',
293 is_site_admin=True),
294 user_objects_pb2.User(
295 user_id=3,
296 display_name='user3@example.com',
297 availability='User never visited',
298 linked_child_refs=[
299 common_pb2.UserRef(
300 user_id=4, display_name='user4@example.com')
301 ]),
302 user_objects_pb2.User(
303 user_id=4,
304 display_name='user4@example.com',
305 availability='Last visit > 30 days ago',
306 linked_parent_ref=common_pb2.UserRef(
307 user_id=3, display_name='user3@example.com')),
308 ])
Copybara854996b2021-09-07 19:36:02 +0000309
310 def testConvetPrefValues(self):
311 """We can convert a list of UserPrefValues from protorpc to protoc."""
312 self.assertEqual(
313 [],
314 converters.ConvertPrefValues([]))
315
316 userprefvalues = [
317 user_pb2.UserPrefValue(name='foo_1', value='bar_1'),
318 user_pb2.UserPrefValue(name='foo_2', value='bar_2')]
319 actual = converters.ConvertPrefValues(userprefvalues)
320 expected = [
321 user_objects_pb2.UserPrefValue(name='foo_1', value='bar_1'),
322 user_objects_pb2.UserPrefValue(name='foo_2', value='bar_2')]
323 self.assertEqual(expected, actual)
324
325 def testConvertLabels(self):
326 """We can convert labels."""
327 # No labels specified
328 actual = converters.ConvertLabels([], [])
329 self.assertEqual([], actual)
330
331 # A mix of explicit and derived labels
332 actual = converters.ConvertLabels(
333 ['Milestone-66'], ['Restrict-View-CoreTeam'])
334 expected = [
335 common_pb2.LabelRef(label='Milestone-66', is_derived=False),
336 common_pb2.LabelRef(label='Restrict-View-CoreTeam', is_derived=True),
337 ]
338 self.assertEqual(expected, actual)
339
340 def testConvertComponentRef(self):
341 """We can convert a component ref."""
342 self.config.component_defs = [
343 tracker_pb2.ComponentDef(component_id=1, path='UI'),
344 tracker_pb2.ComponentDef(component_id=2, path='DB')]
345
346 self.assertEqual(
347 common_pb2.ComponentRef(
348 path='UI',
349 is_derived=False),
350 converters.ConvertComponentRef(1, self.config))
351
352 self.assertEqual(
353 common_pb2.ComponentRef(
354 path='DB',
355 is_derived=True),
356 converters.ConvertComponentRef(2, self.config, True))
357
358 self.assertIsNone(
359 converters.ConvertComponentRef(3, self.config, True))
360
361 def testConvertComponents(self):
362 """We can convert a list of components."""
363 self.config.component_defs = [
364 tracker_pb2.ComponentDef(component_id=1, path='UI'),
365 tracker_pb2.ComponentDef(component_id=2, path='DB'),
366 ]
367
368 # No components specified
369 actual = converters.ConvertComponents([], [], self.config)
370 self.assertEqual([], actual)
371
372 # A mix of explicit, derived, and non-existing components
373 actual = converters.ConvertComponents([1, 4], [2, 3], self.config)
374 expected = [
375 common_pb2.ComponentRef(path='UI', is_derived=False),
376 common_pb2.ComponentRef(path='DB', is_derived=True),
377 ]
378 self.assertEqual(expected, actual)
379
380 def testConvertIssueRef(self):
381 """We can convert a pair (project_name, local_id) to an IssueRef."""
382 actual = converters.ConvertIssueRef(('proj', 1))
383 self.assertEqual(
384 common_pb2.IssueRef(project_name='proj', local_id=1),
385 actual)
386
387 def testConvertIssueRef_ExtIssue(self):
388 """ConvertIssueRef successfully converts an external issue."""
389 actual = converters.ConvertIssueRef(('', 0), ext_id='b/1234567')
390 self.assertEqual(
391 common_pb2.IssueRef(project_name='', local_id=0,
392 ext_identifier='b/1234567'),
393 actual)
394
395 def testConvertIssueRefs(self):
396 """We can convert issue_ids to IssueRefs."""
397 related_refs_dict = {
398 78901: ('proj', 1),
399 78902: ('proj', 2),
400 }
401 actual = converters.ConvertIssueRefs([78901, 78902], related_refs_dict)
402 self.assertEqual(
403 [common_pb2.IssueRef(project_name='proj', local_id=1),
404 common_pb2.IssueRef(project_name='proj', local_id=2)],
405 actual)
406
407 def testConvertFieldType(self):
408 self.assertEqual(
409 common_pb2.STR_TYPE,
410 converters.ConvertFieldType(tracker_pb2.FieldTypes.STR_TYPE))
411
412 self.assertEqual(
413 common_pb2.URL_TYPE,
414 converters.ConvertFieldType(tracker_pb2.FieldTypes.URL_TYPE))
415
416 def testConvertFieldRef(self):
417 actual = converters.ConvertFieldRef(
418 1, 'SomeName', tracker_pb2.FieldTypes.ENUM_TYPE, None)
419 self.assertEqual(
420 actual,
421 common_pb2.FieldRef(
422 field_id=1,
423 field_name='SomeName',
424 type=common_pb2.ENUM_TYPE))
425
426 def testConvertFieldValue(self):
427 """We can convert one FieldValueView item to a protoc FieldValue."""
428 actual = converters.ConvertFieldValue(
429 1, 'Size', 123, tracker_pb2.FieldTypes.INT_TYPE, phase_name='Canary')
430 expected = issue_objects_pb2.FieldValue(
431 field_ref=common_pb2.FieldRef(
432 field_id=1,
433 field_name='Size',
434 type=common_pb2.INT_TYPE),
435 value='123',
436 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Canary'))
437 self.assertEqual(expected, actual)
438
439 actual = converters.ConvertFieldValue(
440 1, 'Size', 123, tracker_pb2.FieldTypes.INT_TYPE, 'Legal', '',
441 is_derived=True)
442 expected = issue_objects_pb2.FieldValue(
443 field_ref=common_pb2.FieldRef(
444 field_id=1,
445 field_name='Size',
446 type=common_pb2.INT_TYPE,
447 approval_name='Legal'),
448 value='123',
449 is_derived=True)
450 self.assertEqual(expected, actual)
451
452 def testConvertFieldValue_Unicode(self):
453 """We can convert one FieldValueView unicode item to a protoc FieldValue."""
454 actual = converters.ConvertFieldValue(
455 1, 'Size', u'\xe2\x9d\xa4\xef\xb8\x8f',
456 tracker_pb2.FieldTypes.STR_TYPE, phase_name='Canary')
457 expected = issue_objects_pb2.FieldValue(
458 field_ref=common_pb2.FieldRef(
459 field_id=1,
460 field_name='Size',
461 type=common_pb2.STR_TYPE),
462 value=u'\xe2\x9d\xa4\xef\xb8\x8f',
463 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Canary'))
464 self.assertEqual(expected, actual)
465
466 def testConvertFieldValues(self):
467 self.fd_2.approval_id = 3
468 self.config.field_defs = [
469 self.fd_1, self.fd_2, self.fd_3, self.fd_4, self.fd_5]
470 fv_1 = tracker_bizobj.MakeFieldValue(
471 1, None, 'string', None, None, None, False)
472 fv_2 = tracker_bizobj.MakeFieldValue(
473 2, 34, None, None, None, None, False)
474 fv_3 = tracker_bizobj.MakeFieldValue(
475 111, None, 'value', None, None, None, False)
476 labels = ['Pre-label', 'not-label-enum', 'prenot-label']
477 der_labels = ['Pre-label2']
478 phases = [tracker_pb2.Phase(name='Canary', phase_id=17)]
479 fv_1.phase_id=17
480
481 actual = converters.ConvertFieldValues(
482 self.config, labels, der_labels, [fv_1, fv_2, fv_3], {}, phases=phases)
483
484 self.maxDiff = None
485 expected = [
486 issue_objects_pb2.FieldValue(
487 field_ref=common_pb2.FieldRef(
488 field_id=1,
489 field_name='FirstField',
490 type=common_pb2.STR_TYPE),
491 value='string',
492 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Canary')),
493 issue_objects_pb2.FieldValue(
494 field_ref=common_pb2.FieldRef(
495 field_id=2,
496 field_name='SecField',
497 type=common_pb2.INT_TYPE,
498 approval_name='LegalApproval'),
499 value='34'),
500 issue_objects_pb2.FieldValue(
501 field_ref=common_pb2.FieldRef(
502 field_id=5, field_name='Pre', type=common_pb2.ENUM_TYPE),
503 value='label'),
504 issue_objects_pb2.FieldValue(
505 field_ref=common_pb2.FieldRef(
506 field_id=5, field_name='Pre', type=common_pb2.ENUM_TYPE),
507 value='label2', is_derived=True),
508 ]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100509 six.assertCountEqual(self, expected, actual)
Copybara854996b2021-09-07 19:36:02 +0000510
511 def testConvertIssue(self):
512 """We can convert a protorpc Issue to a protoc Issue."""
513 related_refs_dict = {
514 78901: ('proj', 1),
515 78902: ('proj', 2),
516 }
517 now = 12345678
518 self.config.component_defs = [
519 tracker_pb2.ComponentDef(component_id=1, path='UI'),
520 tracker_pb2.ComponentDef(component_id=2, path='DB'),
521 ]
522 issue = fake.MakeTestIssue(
523 789, 3, 'sum', 'New', 111, labels=['Hot'],
524 derived_labels=['Scalability'], star_count=12, reporter_id=222,
525 opened_timestamp=now, component_ids=[1], project_name='proj',
526 cc_ids=[111], derived_cc_ids=[222])
527 issue.phases = [
528 tracker_pb2.Phase(phase_id=1, name='Dev', rank=1),
529 tracker_pb2.Phase(phase_id=2, name='Beta', rank=2),
530 ]
531 issue.dangling_blocked_on_refs = [
532 tracker_pb2.DanglingIssueRef(project='dangling_proj', issue_id=1234)]
533 issue.dangling_blocking_refs = [
534 tracker_pb2.DanglingIssueRef(project='dangling_proj', issue_id=5678)]
535
536 actual = converters.ConvertIssue(
537 issue, self.users_by_id, related_refs_dict, self.config)
538
539 expected = issue_objects_pb2.Issue(
540 project_name='proj',
541 local_id=3,
542 summary='sum',
543 status_ref=common_pb2.StatusRef(
544 status='New',
545 is_derived=False,
546 means_open=True),
547 owner_ref=common_pb2.UserRef(
548 user_id=111,
549 display_name='one@example.com',
550 is_derived=False),
551 cc_refs=[
552 common_pb2.UserRef(
553 user_id=111,
554 display_name='one@example.com',
555 is_derived=False),
556 common_pb2.UserRef(
557 user_id=222,
558 display_name='two@example.com',
559 is_derived=True)],
560 label_refs=[
561 common_pb2.LabelRef(label='Hot', is_derived=False),
562 common_pb2.LabelRef(label='Scalability', is_derived=True)],
563 component_refs=[common_pb2.ComponentRef(path='UI', is_derived=False)],
564 is_deleted=False,
565 reporter_ref=common_pb2.UserRef(
566 user_id=222, display_name='two@example.com', is_derived=False),
567 opened_timestamp=now,
568 component_modified_timestamp=now,
569 status_modified_timestamp=now,
570 owner_modified_timestamp=now,
571 star_count=12,
572 is_spam=False,
573 attachment_count=0,
574 dangling_blocked_on_refs=[
575 common_pb2.IssueRef(project_name='dangling_proj', local_id=1234)],
576 dangling_blocking_refs=[
577 common_pb2.IssueRef(project_name='dangling_proj', local_id=5678)],
578 phases=[
579 issue_objects_pb2.PhaseDef(
580 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Dev'),
581 rank=1),
582 issue_objects_pb2.PhaseDef(
583 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Beta'),
584 rank=2)])
585 self.assertEqual(expected, actual)
586
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100587 def testConvertIssue_WithMigratedID(self):
588 """We can convert a protorpc Issue to a protoc Issue."""
589 related_refs_dict = {
590 78901: ('proj', 1),
591 78902: ('proj', 2),
592 }
593 now = 12345678
594 self.config.component_defs = [
595 tracker_pb2.ComponentDef(component_id=1, path='UI'),
596 tracker_pb2.ComponentDef(component_id=2, path='DB'),
597 ]
598 issue = fake.MakeTestIssue(
599 789, 3, 'sum', 'New', 111, labels=['Hot'],
600 derived_labels=['Scalability'], star_count=12, reporter_id=222,
601 opened_timestamp=now, component_ids=[1], project_name='proj',
602 cc_ids=[111], derived_cc_ids=[222])
603 issue.phases = [
604 tracker_pb2.Phase(phase_id=1, name='Dev', rank=1),
605 tracker_pb2.Phase(phase_id=2, name='Beta', rank=2),
606 ]
607 issue.dangling_blocked_on_refs = [
608 tracker_pb2.DanglingIssueRef(project='dangling_proj', issue_id=1234)]
609 issue.dangling_blocking_refs = [
610 tracker_pb2.DanglingIssueRef(project='dangling_proj', issue_id=5678)]
611
612 actual = converters.ConvertIssue(
613 issue, self.users_by_id, related_refs_dict, self.config, '123')
614
615 expected = issue_objects_pb2.Issue(
616 project_name='proj',
617 local_id=3,
618 summary='sum',
619 status_ref=common_pb2.StatusRef(
620 status='New',
621 is_derived=False,
622 means_open=True),
623 owner_ref=common_pb2.UserRef(
624 user_id=111,
625 display_name='one@example.com',
626 is_derived=False),
627 cc_refs=[
628 common_pb2.UserRef(
629 user_id=111,
630 display_name='one@example.com',
631 is_derived=False),
632 common_pb2.UserRef(
633 user_id=222,
634 display_name='two@example.com',
635 is_derived=True)],
636 label_refs=[
637 common_pb2.LabelRef(label='Hot', is_derived=False),
638 common_pb2.LabelRef(label='Scalability', is_derived=True)],
639 component_refs=[common_pb2.ComponentRef(path='UI', is_derived=False)],
640 is_deleted=False,
641 reporter_ref=common_pb2.UserRef(
642 user_id=222, display_name='two@example.com', is_derived=False),
643 opened_timestamp=now,
644 component_modified_timestamp=now,
645 status_modified_timestamp=now,
646 owner_modified_timestamp=now,
647 star_count=12,
648 is_spam=False,
649 attachment_count=0,
650 dangling_blocked_on_refs=[
651 common_pb2.IssueRef(project_name='dangling_proj', local_id=1234)],
652 dangling_blocking_refs=[
653 common_pb2.IssueRef(project_name='dangling_proj', local_id=5678)],
654 phases=[
655 issue_objects_pb2.PhaseDef(
656 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Dev'),
657 rank=1),
658 issue_objects_pb2.PhaseDef(
659 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Beta'),
660 rank=2)],
661 migrated_id='123',)
662 self.assertEqual(expected, actual)
663
Copybara854996b2021-09-07 19:36:02 +0000664 def testConvertIssue_NegativeAttachmentCount(self):
665 """We can convert a protorpc Issue to a protoc Issue."""
666 related_refs_dict = {
667 78901: ('proj', 1),
668 78902: ('proj', 2),
669 }
670 now = 12345678
671 self.config.component_defs = [
672 tracker_pb2.ComponentDef(component_id=1, path='UI'),
673 tracker_pb2.ComponentDef(component_id=2, path='DB'),
674 ]
675 issue = fake.MakeTestIssue(
676 789, 3, 'sum', 'New', 111, labels=['Hot'],
677 derived_labels=['Scalability'], star_count=12, reporter_id=222,
678 opened_timestamp=now, component_ids=[1], project_name='proj',
679 cc_ids=[111], derived_cc_ids=[222], attachment_count=-10)
680 issue.phases = [
681 tracker_pb2.Phase(phase_id=1, name='Dev', rank=1),
682 tracker_pb2.Phase(phase_id=2, name='Beta', rank=2),
683 ]
684 issue.dangling_blocked_on_refs = [
685 tracker_pb2.DanglingIssueRef(project='dangling_proj', issue_id=1234)]
686 issue.dangling_blocking_refs = [
687 tracker_pb2.DanglingIssueRef(project='dangling_proj', issue_id=5678)]
688
689 actual = converters.ConvertIssue(
690 issue, self.users_by_id, related_refs_dict, self.config)
691
692 expected = issue_objects_pb2.Issue(
693 project_name='proj',
694 local_id=3,
695 summary='sum',
696 status_ref=common_pb2.StatusRef(
697 status='New',
698 is_derived=False,
699 means_open=True),
700 owner_ref=common_pb2.UserRef(
701 user_id=111,
702 display_name='one@example.com',
703 is_derived=False),
704 cc_refs=[
705 common_pb2.UserRef(
706 user_id=111,
707 display_name='one@example.com',
708 is_derived=False),
709 common_pb2.UserRef(
710 user_id=222,
711 display_name='two@example.com',
712 is_derived=True)],
713 label_refs=[
714 common_pb2.LabelRef(label='Hot', is_derived=False),
715 common_pb2.LabelRef(label='Scalability', is_derived=True)],
716 component_refs=[common_pb2.ComponentRef(path='UI', is_derived=False)],
717 is_deleted=False,
718 reporter_ref=common_pb2.UserRef(
719 user_id=222, display_name='two@example.com', is_derived=False),
720 opened_timestamp=now,
721 component_modified_timestamp=now,
722 status_modified_timestamp=now,
723 owner_modified_timestamp=now,
724 star_count=12,
725 is_spam=False,
726 dangling_blocked_on_refs=[
727 common_pb2.IssueRef(project_name='dangling_proj', local_id=1234)],
728 dangling_blocking_refs=[
729 common_pb2.IssueRef(project_name='dangling_proj', local_id=5678)],
730 phases=[
731 issue_objects_pb2.PhaseDef(
732 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Dev'),
733 rank=1),
734 issue_objects_pb2.PhaseDef(
735 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Beta'),
736 rank=2)])
737 self.assertEqual(expected, actual)
738
739 def testConvertIssue_ExternalMergedInto(self):
740 """ConvertIssue works on issues with external mergedinto values."""
741 issue = fake.MakeTestIssue(789, 3, 'sum', 'New', 111, project_name='proj',
742 merged_into_external='b/5678')
743 actual = converters.ConvertIssue(issue, self.users_by_id, {}, self.config)
744 expected = issue_objects_pb2.Issue(
745 project_name='proj',
746 local_id=3,
747 summary='sum',
748 merged_into_issue_ref=common_pb2.IssueRef(ext_identifier='b/5678'),
749 status_ref=common_pb2.StatusRef(
750 status='New',
751 is_derived=False,
752 means_open=True),
753 owner_ref=common_pb2.UserRef(
754 user_id=111,
755 display_name='one@example.com',
756 is_derived=False),
757 reporter_ref=common_pb2.UserRef(
758 user_id=111, display_name='one@example.com', is_derived=False))
759
760 self.assertEqual(expected, actual)
761
762 def testConvertPhaseDef(self):
763 """We can convert a prototpc Phase to a protoc PhaseDef. """
764 phase = tracker_pb2.Phase(phase_id=1, name='phase', rank=2)
765 actual = converters.ConvertPhaseDef(phase)
766 expected = issue_objects_pb2.PhaseDef(
767 phase_ref=issue_objects_pb2.PhaseRef(phase_name='phase'),
768 rank=2
769 )
770 self.assertEqual(expected, actual)
771
772 def testConvertAmendment(self):
773 """We can convert various kinds of Amendments."""
774 amend = tracker_pb2.Amendment(
775 field=tracker_pb2.FieldID.SUMMARY, newvalue='new', oldvalue='old')
776 actual = converters.ConvertAmendment(amend, self.users_by_id)
777 self.assertEqual('Summary', actual.field_name)
778 self.assertEqual('new', actual.new_or_delta_value)
779 self.assertEqual('old', actual.old_value)
780
781 amend = tracker_pb2.Amendment(
782 field=tracker_pb2.FieldID.OWNER, added_user_ids=[111])
783 actual = converters.ConvertAmendment(amend, self.users_by_id)
784 self.assertEqual('Owner', actual.field_name)
785 self.assertEqual('one@example.com', actual.new_or_delta_value)
786 self.assertEqual('', actual.old_value)
787
788 amend = tracker_pb2.Amendment(
789 field=tracker_pb2.FieldID.CC,
790 added_user_ids=[111], removed_user_ids=[222])
791 actual = converters.ConvertAmendment(amend, self.users_by_id)
792 self.assertEqual('Cc', actual.field_name)
793 self.assertEqual(
794 '-two@example.com one@example.com', actual.new_or_delta_value)
795 self.assertEqual('', actual.old_value)
796
797 amend = tracker_pb2.Amendment(
798 field=tracker_pb2.FieldID.CUSTOM, custom_field_name='EstDays',
799 newvalue='12')
800 actual = converters.ConvertAmendment(amend, self.users_by_id)
801 self.assertEqual('EstDays', actual.field_name)
802 self.assertEqual('12', actual.new_or_delta_value)
803 self.assertEqual('', actual.old_value)
804
805 @patch('tracker.attachment_helpers.SignAttachmentID')
806 def testConvertAttachment(self, mock_SignAttachmentID):
807 mock_SignAttachmentID.return_value = 2
808 attach = tracker_pb2.Attachment(
809 attachment_id=1, mimetype='image/png', filename='example.png',
810 filesize=12345)
811
812 actual = converters.ConvertAttachment(attach, 'proj')
813
814 expected = issue_objects_pb2.Attachment(
815 attachment_id=1, filename='example.png',
816 size=12345, content_type='image/png',
817 thumbnail_url='attachment?aid=1&signed_aid=2&inline=1&thumb=1',
818 view_url='attachment?aid=1&signed_aid=2&inline=1',
819 download_url='attachment?aid=1&signed_aid=2')
820 self.assertEqual(expected, actual)
821
822 def testConvertComment_Normal(self):
823 """We can convert a protorpc IssueComment to a protoc Comment."""
824 now = 1234567890
825 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
826 comment = tracker_pb2.IssueComment(
827 id=101, project_id=789, user_id=111, timestamp=now,
828 content='a comment', sequence=12)
829
830 actual = converters.ConvertComment(
831 issue, comment, self.config, self.users_by_id, [], {}, 111,
832 permissions.PermissionSet([]))
833 expected = issue_objects_pb2.Comment(
834 project_name='proj', local_id=1, sequence_num=12, is_deleted=False,
835 commenter=common_pb2.UserRef(
836 user_id=111, display_name='one@example.com'),
837 timestamp=now, content='a comment', is_spam=False)
838 self.assertEqual(expected, actual)
839
840 def testConvertComment_CanReportComment(self):
841 now = 1234567890
842 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
843 comment = tracker_pb2.IssueComment(
844 id=101, project_id=789, user_id=111, timestamp=now,
845 content='a comment', sequence=12)
846
847 actual = converters.ConvertComment(
848 issue, comment, self.config, self.users_by_id, [], {}, 111,
849 permissions.PermissionSet([permissions.FLAG_SPAM]))
850 expected = issue_objects_pb2.Comment(
851 project_name='proj', local_id=1, sequence_num=12,
852 commenter=common_pb2.UserRef(
853 user_id=111, display_name='one@example.com'),
854 timestamp=now, content='a comment', can_flag=True)
855 self.assertEqual(expected, actual)
856
857 def testConvertComment_CanUnReportComment(self):
858 now = 1234567890
859 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
860 comment = tracker_pb2.IssueComment(
861 id=101, project_id=789, user_id=111, timestamp=now,
862 content='a comment', sequence=12)
863
864 actual = converters.ConvertComment(
865 issue, comment, self.config, self.users_by_id, [111], {}, 111,
866 permissions.PermissionSet([permissions.FLAG_SPAM]))
867 expected = issue_objects_pb2.Comment(
868 project_name='proj', local_id=1, sequence_num=12,
869 commenter=common_pb2.UserRef(
870 user_id=111, display_name='one@example.com'),
871 timestamp=now, content='a comment', is_spam=True, is_deleted=True,
872 can_flag=True)
873 self.assertEqual(expected, actual)
874
875 def testConvertComment_CantUnFlagCommentWithoutVerdictSpam(self):
876 now = 1234567890
877 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
878 comment = tracker_pb2.IssueComment(
879 id=101, project_id=789, user_id=111, timestamp=now,
880 content='a comment', sequence=12, is_spam=True)
881
882 actual = converters.ConvertComment(
883 issue, comment, self.config, self.users_by_id, [111], {}, 111,
884 permissions.PermissionSet([permissions.FLAG_SPAM]))
885 expected = issue_objects_pb2.Comment(
886 project_name='proj', local_id=1, sequence_num=12,
887 timestamp=now, is_spam=True, is_deleted=True)
888 self.assertEqual(expected, actual)
889
890 def testConvertComment_CanFlagSpamComment(self):
891 now = 1234567890
892 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
893 comment = tracker_pb2.IssueComment(
894 id=101, project_id=789, user_id=111, timestamp=now,
895 content='a comment', sequence=12)
896
897 actual = converters.ConvertComment(
898 issue, comment, self.config, self.users_by_id, [], {}, 111,
899 permissions.PermissionSet([permissions.VERDICT_SPAM]))
900 expected = issue_objects_pb2.Comment(
901 project_name='proj', local_id=1, sequence_num=12,
902 commenter=common_pb2.UserRef(
903 user_id=111, display_name='one@example.com'),
904 timestamp=now, content='a comment', can_flag=True)
905 self.assertEqual(expected, actual)
906
907 def testConvertComment_CanUnFlagSpamComment(self):
908 now = 1234567890
909 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
910 comment = tracker_pb2.IssueComment(
911 id=101, project_id=789, user_id=111, timestamp=now,
912 content='a comment', sequence=12, is_spam=True)
913
914 actual = converters.ConvertComment(
915 issue, comment, self.config, self.users_by_id, [222], {}, 111,
916 permissions.PermissionSet([permissions.VERDICT_SPAM]))
917 expected = issue_objects_pb2.Comment(
918 project_name='proj', local_id=1, sequence_num=12,
919 commenter=common_pb2.UserRef(
920 user_id=111, display_name='one@example.com'),
921 timestamp=now, content='a comment', is_spam=True, is_deleted=True,
922 can_flag=True)
923 self.assertEqual(expected, actual)
924
925 def testConvertComment_DeletedComment(self):
926 """We can convert a protorpc IssueComment to a protoc Comment."""
927 now = 1234567890
928 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
929 comment = tracker_pb2.IssueComment(
930 id=101, project_id=789, user_id=111, timestamp=now,
931 content='a comment', sequence=12, deleted_by=111)
932 actual = converters.ConvertComment(
933 issue, comment, self.config, self.users_by_id, [], {}, 111,
934 permissions.PermissionSet([permissions.DELETE_OWN]))
935 expected = issue_objects_pb2.Comment(
936 project_name='proj', local_id=1, sequence_num=12, is_deleted=True,
937 commenter=common_pb2.UserRef(
938 user_id=111, display_name='one@example.com'),
939 timestamp=now, content='a comment', can_delete=True)
940 self.assertEqual(expected, actual)
941
942 def testConvertComment_DeletedCommentCantView(self):
943 """We can convert a protorpc IssueComment to a protoc Comment."""
944 now = 1234567890
945 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
946 comment = tracker_pb2.IssueComment(
947 id=101, project_id=789, user_id=111, timestamp=now,
948 content='a comment', sequence=12, deleted_by=111)
949 actual = converters.ConvertComment(
950 issue, comment, self.config, self.users_by_id, [], {}, 111,
951 permissions.PermissionSet([]))
952 expected = issue_objects_pb2.Comment(
953 project_name='proj', local_id=1, sequence_num=12, is_deleted=True,
954 timestamp=now)
955 self.assertEqual(expected, actual)
956
957 def testConvertComment_CommentByBannedUser(self):
958 """We can convert a protorpc IssueComment to a protoc Comment."""
959 now = 1234567890
960 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
961 comment = tracker_pb2.IssueComment(
962 id=101, project_id=789, user_id=333, timestamp=now,
963 content='a comment', sequence=12)
964 actual = converters.ConvertComment(
965 issue, comment, self.config, self.users_by_id, [], {}, 111,
966 permissions.PermissionSet([]))
967 expected = issue_objects_pb2.Comment(
968 project_name='proj', local_id=1, sequence_num=12, is_deleted=True,
969 timestamp=now)
970 self.assertEqual(expected, actual)
971
972 def testConvertComment_Description(self):
973 """We can convert a protorpc IssueComment to a protoc Comment."""
974 now = 1234567890
975 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
976 comment = tracker_pb2.IssueComment(
977 id=101, project_id=789, user_id=111, timestamp=now,
978 content='a comment', sequence=12, is_description=True)
979 actual = converters.ConvertComment(
980 issue, comment, self.config, self.users_by_id, [], {101: 1}, 111,
981 permissions.PermissionSet([]))
982 expected = issue_objects_pb2.Comment(
983 project_name='proj', local_id=1, sequence_num=12, is_deleted=False,
984 commenter=common_pb2.UserRef(
985 user_id=111, display_name='one@example.com'),
986 timestamp=now, content='a comment', is_spam=False, description_num=1)
987 self.assertEqual(expected, actual)
988 comment.is_description = False
989
990 def testConvertComment_Approval(self):
991 """We can convert a protorpc IssueComment to a protoc Comment."""
992 now = 1234567890
993 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
994 comment = tracker_pb2.IssueComment(
995 id=101, project_id=789, user_id=111, timestamp=now,
996 content='a comment', sequence=12, approval_id=11)
997 # Comment on an approval.
998 self.config.field_defs.append(tracker_pb2.FieldDef(
999 field_id=11, project_id=789, field_name='Accessibility',
1000 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE,
1001 applicable_type='Launch'))
1002 self.config.approval_defs.append(tracker_pb2.ApprovalDef(
1003 approval_id=11, approver_ids=[111], survey='survey 1'))
1004
1005 actual = converters.ConvertComment(
1006 issue, comment, self.config, self.users_by_id, [], {}, 111,
1007 permissions.PermissionSet([]))
1008 expected = issue_objects_pb2.Comment(
1009 project_name='proj', local_id=1, sequence_num=12, is_deleted=False,
1010 commenter=common_pb2.UserRef(
1011 user_id=111, display_name='one@example.com'),
1012 timestamp=now, content='a comment', is_spam=False,
1013 approval_ref=common_pb2.FieldRef(field_name='Accessibility'))
1014 self.assertEqual(expected, actual)
1015
1016 def testConvertComment_ViewOwnInboundMessage(self):
1017 """Users can view their own inbound messages."""
1018 now = 1234567890
1019 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
1020 comment = tracker_pb2.IssueComment(
1021 id=101, project_id=789, user_id=111, timestamp=now,
1022 content='a comment', sequence=12, inbound_message='inbound message')
1023
1024 actual = converters.ConvertComment(
1025 issue, comment, self.config, self.users_by_id, [], {}, 111,
1026 permissions.PermissionSet([]))
1027 expected = issue_objects_pb2.Comment(
1028 project_name='proj', local_id=1, sequence_num=12, is_deleted=False,
1029 commenter=common_pb2.UserRef(
1030 user_id=111, display_name='one@example.com'),
1031 timestamp=now, content='a comment', inbound_message='inbound message')
1032 self.assertEqual(expected, actual)
1033
1034 def testConvertComment_ViewInboundMessageWithPermission(self):
1035 """Users can view their own inbound messages."""
1036 now = 1234567890
1037 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
1038 comment = tracker_pb2.IssueComment(
1039 id=101, project_id=789, user_id=111, timestamp=now,
1040 content='a comment', sequence=12, inbound_message='inbound message')
1041
1042 actual = converters.ConvertComment(
1043 issue, comment, self.config, self.users_by_id, [], {}, 222,
1044 permissions.PermissionSet([permissions.VIEW_INBOUND_MESSAGES]))
1045 expected = issue_objects_pb2.Comment(
1046 project_name='proj', local_id=1, sequence_num=12, is_deleted=False,
1047 commenter=common_pb2.UserRef(
1048 user_id=111, display_name='one@example.com'),
1049 timestamp=now, content='a comment', inbound_message='inbound message')
1050 self.assertEqual(expected, actual)
1051
1052 def testConvertComment_NotAllowedToViewInboundMessage(self):
1053 """Users can view their own inbound messages."""
1054 now = 1234567890
1055 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
1056 comment = tracker_pb2.IssueComment(
1057 id=101, project_id=789, user_id=111, timestamp=now,
1058 content='a comment', sequence=12, inbound_message='inbound message')
1059
1060 actual = converters.ConvertComment(
1061 issue, comment, self.config, self.users_by_id, [], {}, 222,
1062 permissions.PermissionSet([]))
1063 expected = issue_objects_pb2.Comment(
1064 project_name='proj', local_id=1, sequence_num=12, is_deleted=False,
1065 commenter=common_pb2.UserRef(
1066 user_id=111, display_name='one@example.com'),
1067 timestamp=now, content='a comment')
1068 self.assertEqual(expected, actual)
1069
1070 def testConvertCommentList(self):
1071 """We can convert a list of comments."""
1072 now = 1234567890
1073 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
1074 comment_0 = tracker_pb2.IssueComment(
1075 id=100, project_id=789, user_id=111, timestamp=now,
1076 content='a description', sequence=0, is_description=True)
1077 comment_1 = tracker_pb2.IssueComment(
1078 id=101, project_id=789, user_id=222, timestamp=now,
1079 content='a comment', sequence=1)
1080 comment_2 = tracker_pb2.IssueComment(
1081 id=102, project_id=789, user_id=222, timestamp=now,
1082 content='deleted comment', sequence=2, deleted_by=111)
1083 comment_3 = tracker_pb2.IssueComment(
1084 id=103, project_id=789, user_id=111, timestamp=now,
1085 content='another desc', sequence=3, is_description=True)
1086
1087 actual = converters.ConvertCommentList(
1088 issue, [comment_0, comment_1, comment_2, comment_3], self.config,
1089 self.users_by_id, {}, 222,
1090 permissions.PermissionSet([permissions.DELETE_OWN]))
1091
1092 expected_0 = issue_objects_pb2.Comment(
1093 project_name='proj', local_id=1, sequence_num=0, is_deleted=False,
1094 commenter=common_pb2.UserRef(
1095 user_id=111, display_name='one@example.com'),
1096 timestamp=now, content='a description', is_spam=False,
1097 description_num=1)
1098 expected_1 = issue_objects_pb2.Comment(
1099 project_name='proj', local_id=1, sequence_num=1, is_deleted=False,
1100 commenter=common_pb2.UserRef(
1101 user_id=222, display_name='two@example.com'),
1102 timestamp=now, content='a comment', is_spam=False, can_delete=True)
1103 expected_2 = issue_objects_pb2.Comment(
1104 project_name='proj', local_id=1, sequence_num=2, is_deleted=True,
1105 timestamp=now)
1106 expected_3 = issue_objects_pb2.Comment(
1107 project_name='proj', local_id=1, sequence_num=3, is_deleted=False,
1108 commenter=common_pb2.UserRef(
1109 user_id=111, display_name='one@example.com'),
1110 timestamp=now, content='another desc', is_spam=False,
1111 description_num=2)
1112 self.assertEqual(expected_0, actual[0])
1113 self.assertEqual(expected_1, actual[1])
1114 self.assertEqual(expected_2, actual[2])
1115 self.assertEqual(expected_3, actual[3])
1116
1117 def testConvertCommentList_DontUseDeletedOrSpamDescriptions(self):
1118 """When converting comments, deleted or spam are not descriptions."""
1119 now = 1234567890
1120 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111, project_name='proj')
1121 comment_0 = tracker_pb2.IssueComment(
1122 id=100, project_id=789, user_id=111, timestamp=now,
1123 content='a description', sequence=0, is_description=True)
1124 comment_1 = tracker_pb2.IssueComment(
1125 id=101, project_id=789, user_id=222, timestamp=now,
1126 content='a spam description', sequence=1, is_description=True,
1127 is_spam=True)
1128 comment_2 = tracker_pb2.IssueComment(
1129 id=102, project_id=789, user_id=222, timestamp=now,
1130 content='a deleted description', sequence=2, is_description=True,
1131 deleted_by=111)
1132 comment_3 = tracker_pb2.IssueComment(
1133 id=103, project_id=789, user_id=111, timestamp=now,
1134 content='another good desc', sequence=3, is_description=True)
1135 comment_4 = tracker_pb2.IssueComment(
1136 id=104, project_id=789, user_id=333, timestamp=now,
1137 content='desc from banned', sequence=4, is_description=True)
1138
1139 actual = converters.ConvertCommentList(
1140 issue, [comment_0, comment_1, comment_2, comment_3, comment_4],
1141 self.config, self.users_by_id, {}, 222,
1142 permissions.PermissionSet([permissions.DELETE_OWN]))
1143
1144 expected_0 = issue_objects_pb2.Comment(
1145 project_name='proj', local_id=1, sequence_num=0, is_deleted=False,
1146 commenter=common_pb2.UserRef(
1147 user_id=111, display_name='one@example.com'),
1148 timestamp=now, content='a description', is_spam=False,
1149 description_num=1)
1150 expected_1 = issue_objects_pb2.Comment(
1151 project_name='proj', local_id=1, sequence_num=1, is_deleted=True,
1152 timestamp=now, is_spam=True, can_delete=False)
1153 expected_2 = issue_objects_pb2.Comment(
1154 project_name='proj', local_id=1, sequence_num=2, is_deleted=True,
1155 timestamp=now)
1156 expected_3 = issue_objects_pb2.Comment(
1157 project_name='proj', local_id=1, sequence_num=3, is_deleted=False,
1158 commenter=common_pb2.UserRef(
1159 user_id=111, display_name='one@example.com'),
1160 timestamp=now, content='another good desc', is_spam=False,
1161 description_num=2)
1162 expected_4 = issue_objects_pb2.Comment(
1163 project_name='proj', local_id=1, sequence_num=4, is_deleted=True,
1164 timestamp=now, is_spam=False)
1165 self.assertEqual(expected_0, actual[0])
1166 self.assertEqual(expected_1, actual[1])
1167 self.assertEqual(expected_2, actual[2])
1168 self.assertEqual(expected_3, actual[3])
1169 self.assertEqual(expected_4, actual[4])
1170
1171 def testIngestUserRef(self):
1172 """We can look up a single user ID for a protoc UserRef."""
1173 self.services.user.TestAddUser('user1@example.com', 111)
1174 ref = common_pb2.UserRef(display_name='user1@example.com')
1175 actual = converters.IngestUserRef(self.cnxn, ref, self.services.user)
1176 self.assertEqual(111, actual)
1177
1178 def testIngestUserRef_NoSuchUser(self):
1179 """We reject a malformed UserRef.display_name."""
1180 ref = common_pb2.UserRef(display_name='Bob@gmail.com')
1181 with self.assertRaises(exceptions.NoSuchUserException):
1182 converters.IngestUserRef(self.cnxn, ref, self.services.user)
1183
1184 def testIngestUserRefs_ClearTheOwnerField(self):
1185 """We can look up user IDs for protoc UserRefs."""
1186 ref = common_pb2.UserRef(user_id=0)
1187 actual = converters.IngestUserRefs(self.cnxn, [ref], self.services.user)
1188 self.assertEqual([0], actual)
1189
1190 def testIngestUserRefs_ByExistingID(self):
1191 """Users can be specified by user_id."""
1192 self.services.user.TestAddUser('user1@example.com', 111)
1193 ref = common_pb2.UserRef(user_id=111)
1194 actual = converters.IngestUserRefs(self.cnxn, [ref], self.services.user)
1195 self.assertEqual([111], actual)
1196
1197 def testIngestUserRefs_ByNonExistingID(self):
1198 """We reject references to non-existing user IDs."""
1199 ref = common_pb2.UserRef(user_id=999)
1200 with self.assertRaises(exceptions.NoSuchUserException):
1201 converters.IngestUserRefs(self.cnxn, [ref], self.services.user)
1202
1203 def testIngestUserRefs_ByExistingEmail(self):
1204 """Existing users can be specified by email address."""
1205 self.services.user.TestAddUser('user1@example.com', 111)
1206 ref = common_pb2.UserRef(display_name='user1@example.com')
1207 actual = converters.IngestUserRefs(self.cnxn, [ref], self.services.user)
1208 self.assertEqual([111], actual)
1209
1210 def testIngestUserRefs_ByNonExistingEmail(self):
1211 """New users can be specified by email address."""
1212 # Case where autocreate=False
1213 ref = common_pb2.UserRef(display_name='new@example.com')
1214 with self.assertRaises(exceptions.NoSuchUserException):
1215 converters.IngestUserRefs(
1216 self.cnxn, [ref], self.services.user, autocreate=False)
1217
1218 # Case where autocreate=True
1219 actual = converters.IngestUserRefs(
1220 self.cnxn, [ref], self.services.user, autocreate=True)
1221 user_id = self.services.user.LookupUserID(self.cnxn, 'new@example.com')
1222 self.assertEqual([user_id], actual)
1223
1224 def testIngestUserRefs_ByMalformedEmail(self):
1225 """We ignore malformed user emails."""
1226 self.services.user.TestAddUser('user1@example.com', 111)
1227 self.services.user.TestAddUser('user3@example.com', 333)
1228 refs = [
1229 common_pb2.UserRef(user_id=0),
1230 common_pb2.UserRef(display_name='not-a-valid-email'),
1231 common_pb2.UserRef(user_id=333),
1232 common_pb2.UserRef(display_name='user1@example.com')
1233 ]
1234 actual = converters.IngestUserRefs(
1235 self.cnxn, refs, self.services.user, autocreate=True)
1236 self.assertEqual(actual, [0, 333, 111])
1237
1238 def testIngestUserRefs_MixOfIDAndEmail(self):
1239 """Requests can specify some users by ID and others by email."""
1240 self.services.user.TestAddUser('user1@example.com', 111)
1241 self.services.user.TestAddUser('user2@example.com', 222)
1242 self.services.user.TestAddUser('user3@example.com', 333)
1243 ref1 = common_pb2.UserRef(display_name='user1@example.com')
1244 ref2 = common_pb2.UserRef(display_name='user2@example.com')
1245 ref3 = common_pb2.UserRef(user_id=333)
1246 actual = converters.IngestUserRefs(
1247 self.cnxn, [ref1, ref2, ref3], self.services.user)
1248 self.assertEqual([111, 222, 333], actual)
1249
1250 def testIngestUserRefs_UppercaseEmail(self):
1251 """Request can include uppercase letters in email"""
1252 self.services.user.TestAddUser('user1@example.com', 111)
1253 ref = common_pb2.UserRef(display_name='USER1@example.com')
1254 actual = converters.IngestUserRefs(self.cnxn, [ref], self.services.user)
1255 self.assertEqual([111], actual)
1256
1257 def testIngestPrefValues(self):
1258 """We can convert a list of UserPrefValues from protoc to protorpc."""
1259 self.assertEqual(
1260 [],
1261 converters.IngestPrefValues([]))
1262
1263 userprefvalues = [
1264 user_objects_pb2.UserPrefValue(name='foo_1', value='bar_1'),
1265 user_objects_pb2.UserPrefValue(name='foo_2', value='bar_2')]
1266 actual = converters.IngestPrefValues(userprefvalues)
1267 expected = [
1268 user_pb2.UserPrefValue(name='foo_1', value='bar_1'),
1269 user_pb2.UserPrefValue(name='foo_2', value='bar_2')]
1270 self.assertEqual(expected, actual)
1271
1272 def testIngestComponentRefs(self):
1273 """We can look up component IDs for a list of protoc UserRefs."""
1274 self.assertEqual([], converters.IngestComponentRefs([], self.config))
1275
1276 self.config.component_defs = [
1277 tracker_pb2.ComponentDef(component_id=1, path='UI'),
1278 tracker_pb2.ComponentDef(component_id=2, path='DB')]
1279 refs = [common_pb2.ComponentRef(path='UI'),
1280 common_pb2.ComponentRef(path='DB')]
1281 self.assertEqual(
1282 [1, 2], converters.IngestComponentRefs(refs, self.config))
1283
1284 def testIngestIssueRefs_ValidatesExternalRefs(self):
1285 """IngestIssueRefs requires external refs have at least one slash."""
1286 ref = common_pb2.IssueRef(ext_identifier='b123456')
1287 with self.assertRaises(exceptions.InvalidExternalIssueReference):
1288 converters.IngestIssueRefs(self.cnxn, [ref], self.services)
1289
1290 def testIngestIssueRefs_SkipsExternalRefs(self):
1291 """IngestIssueRefs skips external refs."""
1292 ref = common_pb2.IssueRef(ext_identifier='b/123456')
1293 actual = converters.IngestIssueRefs(
1294 self.cnxn, [ref], self.services)
1295 self.assertEqual([], actual)
1296
1297 def testIngestExtIssueRefs_Normal(self):
1298 """IngestExtIssueRefs returns all valid external refs."""
1299 refs = [
1300 common_pb2.IssueRef(project_name='rutabaga', local_id=1234),
1301 common_pb2.IssueRef(ext_identifier='b123456'),
1302 common_pb2.IssueRef(ext_identifier='b/123456'), # <- Valid ref 1.
1303 common_pb2.IssueRef(ext_identifier='rutabaga/123456'),
1304 common_pb2.IssueRef(ext_identifier='123456'),
1305 common_pb2.IssueRef(ext_identifier='b/56789'), # <- Valid ref 2.
1306 common_pb2.IssueRef(ext_identifier='b//123456')]
1307
1308 actual = converters.IngestExtIssueRefs(refs)
1309 self.assertEqual(['b/123456', 'b/56789'], actual)
1310
1311 def testIngestIssueDelta_Empty(self):
1312 """An empty protorpc IssueDelta makes an empty protoc IssueDelta."""
1313 delta = issue_objects_pb2.IssueDelta()
1314 actual = converters.IngestIssueDelta(
1315 self.cnxn, self.services, delta, self.config, [])
1316 expected = tracker_pb2.IssueDelta()
1317 self.assertEqual(expected, actual)
1318
1319 def testIngestIssueDelta_BuiltInFields(self):
1320 """We can create a protorpc IssueDelta from a protoc IssueDelta."""
1321 self.services.user.TestAddUser('user1@example.com', 111)
1322 self.services.user.TestAddUser('user2@example.com', 222)
1323 self.services.user.TestAddUser('user3@example.com', 333)
1324 self.config.component_defs = [
1325 tracker_pb2.ComponentDef(component_id=1, path='UI')]
1326 delta = issue_objects_pb2.IssueDelta(
1327 status=wrappers_pb2.StringValue(value='Fixed'),
1328 owner_ref=common_pb2.UserRef(user_id=222),
1329 summary=wrappers_pb2.StringValue(value='New summary'),
1330 cc_refs_add=[common_pb2.UserRef(user_id=333)],
1331 comp_refs_add=[common_pb2.ComponentRef(path='UI')],
1332 label_refs_add=[common_pb2.LabelRef(label='Hot')])
1333 actual = converters.IngestIssueDelta(
1334 self.cnxn, self.services, delta, self.config, [])
1335 expected = tracker_pb2.IssueDelta(
1336 status='Fixed', owner_id=222, summary='New summary',
1337 cc_ids_add=[333], comp_ids_add=[1],
1338 labels_add=['Hot'])
1339 self.assertEqual(expected, actual)
1340
1341 def testIngestIssueDelta_ClearMergedInto(self):
1342 """We can clear merged into from the current issue."""
1343 delta = issue_objects_pb2.IssueDelta(merged_into_ref=common_pb2.IssueRef())
1344 actual = converters.IngestIssueDelta(
1345 self.cnxn, self.services, delta, self.config, [])
1346 expected = tracker_pb2.IssueDelta(merged_into=0)
1347 self.assertEqual(expected, actual)
1348
1349 def testIngestIssueDelta_BadOwner(self):
1350 """We reject a specified owner that does not exist."""
1351 delta = issue_objects_pb2.IssueDelta(
1352 owner_ref=common_pb2.UserRef(display_name='user@exa'))
1353 with self.assertRaises(exceptions.NoSuchUserException):
1354 converters.IngestIssueDelta(
1355 self.cnxn, self.services, delta, self.config, [])
1356
1357 def testIngestIssueDelta_BadOwnerIgnored(self):
1358 """We can ignore an incomplete owner email for presubmit."""
1359 delta = issue_objects_pb2.IssueDelta(
1360 owner_ref=common_pb2.UserRef(display_name='user@exa'))
1361 actual = converters.IngestIssueDelta(
1362 self.cnxn, self.services, delta, self.config, [],
1363 ignore_missing_objects=True)
1364 expected = tracker_pb2.IssueDelta()
1365 self.assertEqual(expected, actual)
1366
1367 def testIngestIssueDelta_InvalidComponent(self):
1368 """We reject a protorpc IssueDelta that has an invalid component."""
1369 self.config.component_defs = [
1370 tracker_pb2.ComponentDef(component_id=1, path='UI')]
1371 delta = issue_objects_pb2.IssueDelta(
1372 comp_refs_add=[common_pb2.ComponentRef(path='XYZ')])
1373 with self.assertRaises(exceptions.NoSuchComponentException):
1374 converters.IngestIssueDelta(
1375 self.cnxn, self.services, delta, self.config, [])
1376
1377 def testIngestIssueDelta_InvalidComponentIgnored(self):
1378 """We can ignore invalid components for presubmits."""
1379 self.config.component_defs = [
1380 tracker_pb2.ComponentDef(component_id=1, path='UI')]
1381 delta = issue_objects_pb2.IssueDelta(
1382 comp_refs_add=[common_pb2.ComponentRef(path='UI'),
1383 common_pb2.ComponentRef(path='XYZ')])
1384 actual = converters.IngestIssueDelta(
1385 self.cnxn, self.services, delta, self.config, [],
1386 ignore_missing_objects=True)
1387 self.assertEqual([1], actual.comp_ids_add)
1388
1389 def testIngestIssueDelta_CustomFields(self):
1390 """We can create a protorpc IssueDelta from a protoc IssueDelta."""
1391 self.config.field_defs = [
1392 self.fd_1, self.fd_2, self.fd_3, self.fd_4, self.fd_6]
1393 phases = [tracker_pb2.Phase(phase_id=1, name="Beta")]
1394 delta = issue_objects_pb2.IssueDelta(
1395 field_vals_add=[
1396 issue_objects_pb2.FieldValue(
1397 value='string',
1398 field_ref=common_pb2.FieldRef(field_name='FirstField')
1399 ),
1400 issue_objects_pb2.FieldValue(
1401 value='1',
1402 field_ref=common_pb2.FieldRef(field_name='PhaseField'),
1403 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Beta')
1404 )],
1405 field_vals_remove=[
1406 issue_objects_pb2.FieldValue(
1407 value='34', field_ref=common_pb2.FieldRef(
1408 field_name='SecField'))],
1409 fields_clear=[common_pb2.FieldRef(field_name='FirstField')])
1410 actual = converters.IngestIssueDelta(
1411 self.cnxn, self.services, delta, self.config, phases)
1412 self.assertEqual(actual.field_vals_add,
1413 [tracker_pb2.FieldValue(
1414 str_value='string', field_id=1, derived=False),
1415 tracker_pb2.FieldValue(
1416 int_value=1, field_id=6, phase_id=1, derived=False)
1417 ])
1418 self.assertEqual(actual.field_vals_remove, [tracker_pb2.FieldValue(
1419 int_value=34, field_id=2, derived=False)])
1420 self.assertEqual(actual.fields_clear, [1])
1421
1422 def testIngestIssueDelta_InvalidCustomFields(self):
1423 """We can create a protorpc IssueDelta from a protoc IssueDelta."""
1424 # TODO(jrobbins): add and remove.
1425 delta = issue_objects_pb2.IssueDelta(
1426 fields_clear=[common_pb2.FieldRef(field_name='FirstField')])
1427 with self.assertRaises(exceptions.NoSuchFieldDefException):
1428 converters.IngestIssueDelta(
1429 self.cnxn, self.services, delta, self.config, [])
1430
1431 def testIngestIssueDelta_ShiftFieldsIntoLabels(self):
1432 """Test that enum fields are shifted into labels."""
1433 self.config.field_defs = [self.fd_5]
1434 delta = issue_objects_pb2.IssueDelta(
1435 field_vals_add=[
1436 issue_objects_pb2.FieldValue(
1437 value='Foo',
1438 field_ref=common_pb2.FieldRef(field_name='Pre', field_id=5)
1439 )],
1440 field_vals_remove=[
1441 issue_objects_pb2.FieldValue(
1442 value='Bar',
1443 field_ref=common_pb2.FieldRef(field_name='Pre', field_id=5),
1444 )])
1445 actual = converters.IngestIssueDelta(
1446 self.cnxn, self.services, delta, self.config, [])
1447 self.assertEqual(actual.field_vals_add, [])
1448 self.assertEqual(actual.field_vals_remove, [])
1449 self.assertEqual(actual.labels_add, ['Pre-Foo'])
1450 self.assertEqual(actual.labels_remove, ['Pre-Bar'])
1451
1452 def testIngestIssueDelta_RelatedIssues(self):
1453 """We can create a protorpc IssueDelta that references related issues."""
1454 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111)
1455 self.services.issue.TestAddIssue(issue)
1456 delta = issue_objects_pb2.IssueDelta(
1457 blocked_on_refs_add=[common_pb2.IssueRef(
1458 project_name='proj', local_id=issue.local_id)],
1459 merged_into_ref=common_pb2.IssueRef(
1460 project_name='proj', local_id=issue.local_id))
1461 actual = converters.IngestIssueDelta(
1462 self.cnxn, self.services, delta, self.config, [])
1463 self.assertEqual([issue.issue_id], actual.blocked_on_add)
1464 self.assertEqual([], actual.blocking_add)
1465 self.assertEqual(issue.issue_id, actual.merged_into)
1466
1467 def testIngestIssueDelta_InvalidRelatedIssues(self):
1468 """We reject references to related issues that do not exist."""
1469 delta = issue_objects_pb2.IssueDelta(
1470 merged_into_ref=common_pb2.IssueRef(
1471 project_name='not-a-proj', local_id=8))
1472 with self.assertRaises(exceptions.NoSuchProjectException):
1473 converters.IngestIssueDelta(
1474 self.cnxn, self.services, delta, self.config, [])
1475
1476 delta = issue_objects_pb2.IssueDelta(
1477 merged_into_ref=common_pb2.IssueRef(
1478 project_name='proj', local_id=999))
1479 with self.assertRaises(exceptions.NoSuchIssueException):
1480 converters.IngestIssueDelta(
1481 self.cnxn, self.services, delta, self.config, [])
1482
1483 def testIngestIssueDelta_ExternalMergedInto(self):
1484 """IngestIssueDelta properly handles external mergedinto refs."""
1485 issue = fake.MakeTestIssue(789, 1, 'sum', 'New', 111)
1486 self.services.issue.TestAddIssue(issue)
1487 delta = issue_objects_pb2.IssueDelta(
1488 merged_into_ref=common_pb2.IssueRef(ext_identifier='b/5678'))
1489 actual = converters.IngestIssueDelta(
1490 self.cnxn, self.services, delta, self.config, [])
1491
1492 self.assertIsNone(actual.merged_into)
1493 self.assertEqual('b/5678', actual.merged_into_external)
1494
1495 def testIngestAttachmentUploads_Empty(self):
1496 """Uploading zero files results in an empty list of attachments."""
1497 self.assertEqual([], converters.IngestAttachmentUploads([]))
1498
1499 def testIngestAttachmentUploads_Normal(self):
1500 """Uploading files results in a list of attachments."""
1501 uploads = [
1502 issue_objects_pb2.AttachmentUpload(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001503 filename='hello.c', content=b'int main() {}'),
Copybara854996b2021-09-07 19:36:02 +00001504 issue_objects_pb2.AttachmentUpload(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001505 filename='README.md', content=b'readme content'),
1506 ]
Copybara854996b2021-09-07 19:36:02 +00001507 actual = converters.IngestAttachmentUploads(uploads)
1508 self.assertEqual(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001509 [
1510 ('hello.c', b'int main() {}', 'text/plain'),
1511 ('README.md', b'readme content', 'text/plain')
1512 ], actual)
Copybara854996b2021-09-07 19:36:02 +00001513
1514 def testIngestAttachmentUploads_Invalid(self):
1515 """We reject uploaded files that lack a name or content."""
1516 with self.assertRaises(exceptions.InputException):
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001517 converters.IngestAttachmentUploads(
1518 [issue_objects_pb2.AttachmentUpload(content=b'name is mssing')])
Copybara854996b2021-09-07 19:36:02 +00001519
1520 with self.assertRaises(exceptions.InputException):
1521 converters.IngestAttachmentUploads([
1522 issue_objects_pb2.AttachmentUpload(filename='content is mssing')])
1523
1524 def testIngestApprovalDelta(self):
1525 self.services.user.TestAddUser('user1@example.com', 111)
1526 self.services.user.TestAddUser('user2@example.com', 222)
1527
1528 self.config.field_defs = [
1529 self.fd_1, self.fd_2, self.fd_3, self.fd_4, self.fd_7]
1530
1531 approval_delta = issue_objects_pb2.ApprovalDelta(
1532 status=issue_objects_pb2.APPROVED,
1533 approver_refs_add=[common_pb2.UserRef(user_id=111)],
1534 approver_refs_remove=[common_pb2.UserRef(user_id=222)],
1535 field_vals_add=[
1536 issue_objects_pb2.FieldValue(
1537 value='string', field_ref=common_pb2.FieldRef(
1538 field_id=1, field_name='FirstField')),
1539 issue_objects_pb2.FieldValue(
1540 value='choice1', field_ref=common_pb2.FieldRef(
1541 field_id=7, field_name='ApprovalEnum')),
1542 ],
1543 field_vals_remove=[
1544 issue_objects_pb2.FieldValue(
1545 value='34', field_ref=common_pb2.FieldRef(
1546 field_id=2, field_name='SecField')),
1547 issue_objects_pb2.FieldValue(
1548 value='choice2', field_ref=common_pb2.FieldRef(
1549 field_id=7, field_name='ApprovalEnum')),
1550 ],
1551 fields_clear=[common_pb2.FieldRef(field_name='FirstField')])
1552
1553 actual = converters.IngestApprovalDelta(
1554 self.cnxn, self.services.user, approval_delta, 333, self.config)
1555 self.assertEqual(
1556 actual.status, tracker_pb2.ApprovalStatus.APPROVED,)
1557 self.assertEqual(actual.setter_id, 333)
1558 self.assertEqual(actual.approver_ids_add, [111])
1559 self.assertEqual(actual.approver_ids_remove, [222])
1560 self.assertEqual(actual.subfield_vals_add, [tracker_pb2.FieldValue(
1561 str_value='string', field_id=1, derived=False)])
1562 self.assertEqual(actual.subfield_vals_remove, [tracker_pb2.FieldValue(
1563 int_value=34, field_id=2, derived=False)])
1564 self.assertEqual(actual.subfields_clear, [1])
1565 self.assertEqual(actual.labels_add, ['ApprovalEnum-choice1'])
1566 self.assertEqual(actual.labels_remove, ['ApprovalEnum-choice2'])
1567
1568 # test a NOT_SET status is registered as None.
1569 approval_delta.status = issue_objects_pb2.NOT_SET
1570 actual = converters.IngestApprovalDelta(
1571 self.cnxn, self.services.user, approval_delta, 333, self.config)
1572 self.assertIsNone(actual.status)
1573
1574 def testIngestApprovalStatus(self):
1575 actual = converters.IngestApprovalStatus(issue_objects_pb2.NOT_SET)
1576 self.assertEqual(actual, tracker_pb2.ApprovalStatus.NOT_SET)
1577
1578 actual = converters.IngestApprovalStatus(issue_objects_pb2.NOT_APPROVED)
1579 self.assertEqual(actual, tracker_pb2.ApprovalStatus.NOT_APPROVED)
1580
1581 def testIngestFieldValues(self):
1582 self.services.user.TestAddUser('user1@example.com', 111)
1583 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_4, self.fd_6]
1584 phases = [
1585 tracker_pb2.Phase(phase_id=3, name="Dev"),
1586 tracker_pb2.Phase(phase_id=1, name="Beta")
1587 ]
1588
1589 field_values = [
1590 issue_objects_pb2.FieldValue(
1591 value='string',
1592 field_ref=common_pb2.FieldRef(field_name='FirstField')
1593 ),
1594 issue_objects_pb2.FieldValue(
1595 value='34',
1596 field_ref=common_pb2.FieldRef(field_name='SecField')
1597 ),
1598 issue_objects_pb2.FieldValue(
1599 value='user1@example.com',
1600 field_ref=common_pb2.FieldRef(field_name='UserField'),
1601 # phase_ref for non-phase fields should be ignored.
1602 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Dev')
1603 ),
1604 issue_objects_pb2.FieldValue(
1605 value='2',
1606 field_ref=common_pb2.FieldRef(field_name='PhaseField'),
1607 phase_ref=issue_objects_pb2.PhaseRef(phase_name='Beta'))
1608 ]
1609
1610 actual = converters.IngestFieldValues(
1611 self.cnxn, self.services.user, field_values, self.config, phases)
1612 self.assertEqual(
1613 actual,
1614 [
1615 tracker_pb2.FieldValue(
1616 str_value='string', field_id=1, derived=False),
1617 tracker_pb2.FieldValue(int_value=34, field_id=2, derived=False),
1618 tracker_pb2.FieldValue(user_id=111, field_id=4, derived=False),
1619 tracker_pb2.FieldValue(
1620 int_value=2, field_id=6, phase_id=1, derived=False)
1621 ]
1622 )
1623
1624 def testIngestFieldValues_EmptyUser(self):
1625 """We ignore empty user email strings."""
1626 self.services.user.TestAddUser('user1@example.com', 111)
1627 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_4, self.fd_6]
1628 field_values = [
1629 issue_objects_pb2.FieldValue(
1630 value='user1@example.com',
1631 field_ref=common_pb2.FieldRef(field_name='UserField')),
1632 issue_objects_pb2.FieldValue(
1633 value='',
1634 field_ref=common_pb2.FieldRef(field_name='UserField'))
1635 ]
1636
1637 actual = converters.IngestFieldValues(
1638 self.cnxn, self.services.user, field_values, self.config, [])
1639 self.assertEqual(
1640 actual,
1641 [tracker_pb2.FieldValue(user_id=111, field_id=4, derived=False)])
1642
1643 def testIngestFieldValues_Unicode(self):
1644 """We can ingest unicode strings."""
1645 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_4, self.fd_6]
1646 field_values = [
1647 issue_objects_pb2.FieldValue(
1648 value=u'\xe2\x9d\xa4\xef\xb8\x8f',
1649 field_ref=common_pb2.FieldRef(field_name='FirstField')
1650 ),
1651 ]
1652
1653 actual = converters.IngestFieldValues(
1654 self.cnxn, self.services.user, field_values, self.config, [])
1655 self.assertEqual(
1656 actual,
1657 [
1658 tracker_pb2.FieldValue(
1659 str_value=u'\xe2\x9d\xa4\xef\xb8\x8f', field_id=1,
1660 derived=False),
1661 ]
1662 )
1663
1664 def testIngestFieldValues_InvalidUser(self):
1665 """We reject invalid user email strings."""
1666 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_4, self.fd_6]
1667 field_values = [
1668 issue_objects_pb2.FieldValue(
1669 value='bad value',
1670 field_ref=common_pb2.FieldRef(field_name='UserField'))]
1671
1672 with self.assertRaises(exceptions.NoSuchUserException):
1673 converters.IngestFieldValues(
1674 self.cnxn, self.services.user, field_values, self.config, [])
1675
1676 def testIngestFieldValues_InvalidInt(self):
1677 """We reject invalid int-field strings."""
1678 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_4, self.fd_6]
1679 field_values = [
1680 issue_objects_pb2.FieldValue(
1681 value='Not a number',
1682 field_ref=common_pb2.FieldRef(field_name='SecField'))]
1683
1684 with self.assertRaises(exceptions.InputException) as cm:
1685 converters.IngestFieldValues(
1686 self.cnxn, self.services.user, field_values, self.config, [])
1687
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001688 self.assertEqual('Unparsable value for field SecField', str(cm.exception))
Copybara854996b2021-09-07 19:36:02 +00001689
1690 def testIngestSavedQueries(self):
1691 self.services.project.TestAddProject('chromium', project_id=1)
1692 self.services.project.TestAddProject('fakeproject', project_id=2)
1693
1694 saved_queries = [
1695 tracker_pb2.SavedQuery(
1696 query_id=101,
1697 name='test query',
1698 query='owner:me',
1699 executes_in_project_ids=[1, 2]),
1700 tracker_pb2.SavedQuery(
1701 query_id=202,
1702 name='another query',
1703 query='-component:Test',
1704 executes_in_project_ids=[1])
1705 ]
1706
1707 converted_queries = converters.IngestSavedQueries(self.cnxn,
1708 self.services.project, saved_queries)
1709
1710 self.assertEqual(converted_queries[0].query_id, 101)
1711 self.assertEqual(converted_queries[0].name, 'test query')
1712 self.assertEqual(converted_queries[0].query, 'owner:me')
1713 self.assertEqual(converted_queries[0].project_names,
1714 ['chromium', 'fakeproject'])
1715
1716 self.assertEqual(converted_queries[1].query_id, 202)
1717 self.assertEqual(converted_queries[1].name, 'another query')
1718 self.assertEqual(converted_queries[1].query, '-component:Test')
1719 self.assertEqual(converted_queries[1].project_names, ['chromium'])
1720
1721
1722 def testIngestHotlistRef(self):
1723 self.services.user.TestAddUser('user1@example.com', 111)
1724 hotlist = self.services.features.CreateHotlist(
1725 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
1726 owner_ids=[111], editor_ids=[222])
1727
1728 owner_ref = common_pb2.UserRef(user_id=111)
1729 hotlist_ref = common_pb2.HotlistRef(name='Fake-Hotlist', owner=owner_ref)
1730
1731 actual_hotlist_id = converters.IngestHotlistRef(
1732 self.cnxn, self.services.user, self.services.features, hotlist_ref)
1733 self.assertEqual(actual_hotlist_id, hotlist.hotlist_id)
1734
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001735 @pytest.mark.skip(reason='Test is flaky (https://crbug.com/monorail/12052)')
Copybara854996b2021-09-07 19:36:02 +00001736 def testIngestHotlistRef_HotlistID(self):
1737 self.services.user.TestAddUser('user1@example.com', 111)
1738 hotlist = self.services.features.CreateHotlist(
1739 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
1740 owner_ids=[111], editor_ids=[222])
1741
1742 hotlist_ref = common_pb2.HotlistRef(hotlist_id=hotlist.hotlist_id)
1743
1744 actual_hotlist_id = converters.IngestHotlistRef(
1745 self.cnxn, self.services.user, self.services.features, hotlist_ref)
1746 self.assertEqual(actual_hotlist_id, hotlist.hotlist_id)
1747
1748 def testIngestHotlistRef_NotEnoughInformation(self):
1749 hotlist_ref = common_pb2.HotlistRef(name='Some-Hotlist')
1750 with self.assertRaises(features_svc.NoSuchHotlistException):
1751 converters.IngestHotlistRef(
1752 self.cnxn, self.services.user, self.services.features, hotlist_ref)
1753
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001754 @pytest.mark.skip(reason='Test is flaky (https://crbug.com/monorail/12052)')
Copybara854996b2021-09-07 19:36:02 +00001755 def testIngestHotlistRef_InconsistentRequest(self):
1756 self.services.user.TestAddUser('user1@example.com', 111)
1757 hotlist1 = self.services.features.CreateHotlist(
1758 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
1759 owner_ids=[111], editor_ids=[222])
1760 self.services.features.CreateHotlist(
1761 self.cnxn, 'Fake-Hotlist-2', 'Summary', 'Description',
1762 owner_ids=[111], editor_ids=[222])
1763
1764 hotlist_ref = common_pb2.HotlistRef(
1765 hotlist_id=hotlist1.hotlist_id,
1766 name='Fake-Hotlist-2',
1767 owner=common_pb2.UserRef(user_id=111))
1768 with self.assertRaises(features_svc.NoSuchHotlistException):
1769 converters.IngestHotlistRef(
1770 self.cnxn, self.services.user, self.services.features, hotlist_ref)
1771
1772 def testIngestHotlistRef_NonExistentHotlistID(self):
1773 hotlist_ref = common_pb2.HotlistRef(hotlist_id=1234)
1774 with self.assertRaises(features_svc.NoSuchHotlistException):
1775 converters.IngestHotlistRef(
1776 self.cnxn, self.services.user, self.services.features, hotlist_ref)
1777
1778 def testIngestHotlistRef_NoSuchHotlist(self):
1779 self.services.user.TestAddUser('user1@example.com', 111)
1780
1781 owner_ref = common_pb2.UserRef(user_id=111)
1782 hotlist_ref = common_pb2.HotlistRef(name='Fake-Hotlist', owner=owner_ref)
1783
1784 with self.assertRaises(features_svc.NoSuchHotlistException):
1785 converters.IngestHotlistRef(
1786 self.cnxn, self.services.user, self.services.features, hotlist_ref)
1787
1788 def testIngestHotlistRefs(self):
1789 self.services.user.TestAddUser('user1@example.com', 111)
1790 hotlist_1 = self.services.features.CreateHotlist(
1791 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
1792 owner_ids=[111], editor_ids=[222])
1793 hotlist_2 = self.services.features.CreateHotlist(
1794 self.cnxn, 'Fake-Hotlist-2', 'Summary', 'Description',
1795 owner_ids=[111], editor_ids=[222])
1796
1797 owner_ref = common_pb2.UserRef(user_id=111)
1798 hotlist_refs = [
1799 common_pb2.HotlistRef(name='Fake-Hotlist', owner=owner_ref),
1800 common_pb2.HotlistRef(hotlist_id=hotlist_2.hotlist_id)]
1801
1802 actual_hotlist_ids = converters.IngestHotlistRefs(
1803 self.cnxn, self.services.user, self.services.features, hotlist_refs)
1804 self.assertEqual(
1805 actual_hotlist_ids, [hotlist_1.hotlist_id, hotlist_2.hotlist_id])
1806
1807 def testIngestPagination(self):
1808 # Use settings.max_project_search_results_per_page if max_items is not
1809 # present.
1810 pagination = common_pb2.Pagination(start=1234)
1811 self.assertEqual(
1812 (1234, settings.max_artifact_search_results_per_page),
1813 converters.IngestPagination(pagination))
1814 # Otherwise, use the minimum between what was requested and
1815 # settings.max_project_search_results_per_page
1816 pagination = common_pb2.Pagination(start=1234, max_items=56)
1817 self.assertEqual(
1818 (1234, 56),
1819 converters.IngestPagination(pagination))
1820 pagination = common_pb2.Pagination(start=1234, max_items=5678)
1821 self.assertEqual(
1822 (1234, settings.max_artifact_search_results_per_page),
1823 converters.IngestPagination(pagination))
1824
1825 # TODO(jojwang): add testConvertStatusRef
1826
1827 def testConvertStatusDef(self):
1828 """We can convert a status definition to protoc."""
1829 status_def = tracker_pb2.StatusDef(status='Started')
1830 actual = converters.ConvertStatusDef(status_def)
1831 self.assertEqual('Started', actual.status)
1832 self.assertFalse(actual.means_open)
1833 self.assertEqual('', actual.docstring)
1834 self.assertFalse(actual.deprecated)
1835 # rank is not set on output, only used when setting a new rank.
1836 self.assertEqual(0, actual.rank)
1837
1838 status_def = tracker_pb2.StatusDef(
1839 status='New', means_open=True, status_docstring='doc', deprecated=True)
1840 actual = converters.ConvertStatusDef(status_def)
1841 self.assertEqual('New', actual.status)
1842 self.assertTrue(actual.means_open)
1843 self.assertEqual('doc', actual.docstring)
1844 self.assertTrue(actual.deprecated)
1845 self.assertEqual(0, actual.rank)
1846
1847 def testConvertLabelDef(self):
1848 """We can convert a label definition to protoc."""
1849 label_def = tracker_pb2.LabelDef(label='Security')
1850 actual = converters.ConvertLabelDef(label_def)
1851 self.assertEqual('Security', actual.label)
1852 self.assertEqual('', actual.docstring)
1853 self.assertFalse(actual.deprecated)
1854
1855 label_def = tracker_pb2.LabelDef(
1856 label='UI', label_docstring='doc', deprecated=True)
1857 actual = converters.ConvertLabelDef(label_def)
1858 self.assertEqual('UI', actual.label)
1859 self.assertEqual('doc', actual.docstring)
1860 self.assertTrue(actual.deprecated)
1861
1862 def testConvertComponentDef_Simple(self):
1863 """We can convert a minimal component definition to protoc."""
1864 now = 1234567890
1865 component_def = tracker_pb2.ComponentDef(
1866 path='Frontend', docstring='doc', created=now, creator_id=111,
1867 modified=now + 1, modifier_id=111)
1868 actual = converters.ConvertComponentDef(
1869 component_def, self.users_by_id, {}, True)
1870 self.assertEqual('Frontend', actual.path)
1871 self.assertEqual('doc', actual.docstring)
1872 self.assertFalse(actual.deprecated)
1873 self.assertEqual(now, actual.created)
1874 self.assertEqual(111, actual.creator_ref.user_id)
1875 self.assertEqual(now + 1, actual.modified)
1876 self.assertEqual(111, actual.modifier_ref.user_id)
1877 self.assertEqual('one@example.com', actual.creator_ref.display_name)
1878
1879 def testConvertComponentDef_Normal(self):
1880 """We can convert a component def that has CC'd users and adds labels."""
1881 labels_by_id = {1: 'Security', 2: 'Usability'}
1882 component_def = tracker_pb2.ComponentDef(
1883 path='Frontend', admin_ids=[111], cc_ids=[222], label_ids=[1, 2],
1884 docstring='doc')
1885 actual = converters.ConvertComponentDef(
1886 component_def, self.users_by_id, labels_by_id, True)
1887 self.assertEqual('Frontend', actual.path)
1888 self.assertEqual('doc', actual.docstring)
1889 self.assertEqual(1, len(actual.admin_refs))
1890 self.assertEqual(111, actual.admin_refs[0].user_id)
1891 self.assertEqual(1, len(actual.cc_refs))
1892 self.assertFalse(actual.deprecated)
1893 self.assertEqual(222, actual.cc_refs[0].user_id)
1894 self.assertEqual(2, len(actual.label_refs))
1895 self.assertEqual('Security', actual.label_refs[0].label)
1896 self.assertEqual('Usability', actual.label_refs[1].label)
1897
1898 # Without include_admin_info, some fields are not set.
1899 actual = converters.ConvertComponentDef(
1900 component_def, self.users_by_id, labels_by_id, False)
1901 self.assertEqual('Frontend', actual.path)
1902 self.assertEqual('doc', actual.docstring)
1903 self.assertEqual(0, len(actual.admin_refs))
1904 self.assertEqual(0, len(actual.cc_refs))
1905 self.assertFalse(actual.deprecated)
1906 self.assertEqual(0, len(actual.label_refs))
1907
1908 def testConvertFieldDef_Simple(self):
1909 """We can convert a minimal field definition to protoc."""
1910 field_def = tracker_pb2.FieldDef(
1911 field_name='EstDays', field_type=tracker_pb2.FieldTypes.INT_TYPE)
1912 actual = converters.ConvertFieldDef(
1913 field_def, [], self.users_by_id, self.config, True)
1914 self.assertEqual('EstDays', actual.field_ref.field_name)
1915 self.assertEqual(common_pb2.INT_TYPE, actual.field_ref.type)
1916 self.assertEqual('', actual.field_ref.approval_name)
1917 self.assertEqual('', actual.applicable_type)
1918 self.assertEqual('', actual.docstring)
1919 self.assertEqual(0, len(actual.admin_refs))
1920 self.assertFalse(actual.is_required)
1921 self.assertFalse(actual.is_niche)
1922 self.assertFalse(actual.is_multivalued)
1923 self.assertFalse(actual.is_phase_field)
1924
1925 field_def = tracker_pb2.FieldDef(
1926 field_name='DesignDocs', field_type=tracker_pb2.FieldTypes.URL_TYPE,
1927 applicable_type='Enhancement', is_required=True, is_niche=True,
1928 is_multivalued=True, docstring='doc', admin_ids=[111],
1929 is_phase_field=True)
1930 actual = converters.ConvertFieldDef(
1931 field_def, [], self.users_by_id, self.config, True)
1932 self.assertEqual('DesignDocs', actual.field_ref.field_name)
1933 self.assertEqual(common_pb2.URL_TYPE, actual.field_ref.type)
1934 self.assertEqual('', actual.field_ref.approval_name)
1935 self.assertEqual('Enhancement', actual.applicable_type)
1936 self.assertEqual('doc', actual.docstring)
1937 self.assertEqual(1, len(actual.admin_refs))
1938 self.assertEqual(111, actual.admin_refs[0].user_id)
1939 self.assertTrue(actual.is_required)
1940 self.assertTrue(actual.is_niche)
1941 self.assertTrue(actual.is_multivalued)
1942 self.assertTrue(actual.is_phase_field)
1943
1944 # Without include_admin_info, some fields are not set.
1945 actual = converters.ConvertFieldDef(
1946 field_def, [], self.users_by_id, self.config, False)
1947 self.assertEqual('DesignDocs', actual.field_ref.field_name)
1948 self.assertEqual(common_pb2.URL_TYPE, actual.field_ref.type)
1949 self.assertEqual('', actual.field_ref.approval_name)
1950 self.assertEqual('', actual.applicable_type)
1951 self.assertEqual('doc', actual.docstring)
1952 self.assertEqual(0, len(actual.admin_refs))
1953 self.assertFalse(actual.is_required)
1954 self.assertFalse(actual.is_niche)
1955 self.assertFalse(actual.is_multivalued)
1956 self.assertFalse(actual.is_phase_field)
1957
1958 def testConvertFieldDef_FieldOfAnApproval(self):
1959 """We can convert a field that is part of an approval."""
1960 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_3]
1961 field_def = tracker_pb2.FieldDef(
1962 field_name='Waiver', field_type=tracker_pb2.FieldTypes.URL_TYPE,
1963 approval_id=self.fd_3.field_id)
1964 actual = converters.ConvertFieldDef(
1965 field_def, [], self.users_by_id, self.config, True)
1966 self.assertEqual('Waiver', actual.field_ref.field_name)
1967 self.assertEqual('LegalApproval', actual.field_ref.approval_name)
1968
1969 def testConvertFieldDef_UserChoices(self):
1970 """We can convert an user type field that need special permissions."""
1971 field_def = tracker_pb2.FieldDef(
1972 field_name='PM', field_type=tracker_pb2.FieldTypes.USER_TYPE)
1973 actual = converters.ConvertFieldDef(
1974 field_def, [111, 333], self.users_by_id, self.config, False)
1975 self.assertEqual('PM', actual.field_ref.field_name)
1976 self.assertEqual(
1977 [111, 333],
1978 [user_ref.user_id for user_ref in actual.user_choices])
1979 self.assertEqual(
1980 ['one@example.com', 'banned@example.com'],
1981 [user_ref.display_name for user_ref in actual.user_choices])
1982
1983 def testConvertFieldDef_EnumChoices(self):
1984 """We can convert an enum type field."""
1985 field_def = tracker_pb2.FieldDef(
1986 field_name='Type', field_type=tracker_pb2.FieldTypes.ENUM_TYPE)
1987 actual = converters.ConvertFieldDef(
1988 field_def, [], self.users_by_id, self.config, False)
1989 self.assertEqual('Type', actual.field_ref.field_name)
1990 self.assertEqual(
1991 ['Defect', 'Enhancement', 'Task', 'Other'],
1992 [label_def.label for label_def in actual.enum_choices])
1993
1994 def testConvertApprovalDef(self):
1995 """We can convert an ApprovalDef to protoc."""
1996 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_3]
1997 approval_def = tracker_pb2.ApprovalDef(approval_id=3)
1998 actual = converters.ConvertApprovalDef(
1999 approval_def, self.users_by_id, self.config, True)
2000 self.assertEqual('LegalApproval', actual.field_ref.field_name)
2001 self.assertEqual(common_pb2.APPROVAL_TYPE, actual.field_ref.type)
2002 self.assertEqual(0, len(actual.approver_refs))
2003 self.assertEqual('', actual.survey)
2004
2005 approval_def = tracker_pb2.ApprovalDef(
2006 approval_id=3, approver_ids=[111], survey='What?')
2007 actual = converters.ConvertApprovalDef(
2008 approval_def, self.users_by_id, self.config, True)
2009 self.assertEqual('LegalApproval', actual.field_ref.field_name)
2010 self.assertEqual(common_pb2.APPROVAL_TYPE, actual.field_ref.type)
2011 self.assertEqual(1, len(actual.approver_refs))
2012 self.assertEqual(111, actual.approver_refs[0].user_id)
2013 self.assertEqual('What?', actual.survey)
2014
2015 # Without include_admin_info, some fields are not set.
2016 actual = converters.ConvertApprovalDef(
2017 approval_def, self.users_by_id, self.config, False)
2018 self.assertEqual('LegalApproval', actual.field_ref.field_name)
2019 self.assertEqual(common_pb2.APPROVAL_TYPE, actual.field_ref.type)
2020 self.assertEqual(0, len(actual.approver_refs))
2021 self.assertEqual('', actual.survey)
2022
2023 def testConvertConfig_Simple(self):
2024 """We can convert a simple config to protoc."""
2025 actual = converters.ConvertConfig(
2026 self.project, self.config, self.users_by_id, {})
2027 self.assertEqual('proj', actual.project_name)
2028 self.assertEqual(9, len(actual.status_defs))
2029 self.assertEqual('New', actual.status_defs[0].status)
2030 self.assertEqual(17, len(actual.label_defs))
2031 self.assertEqual('Type-Defect', actual.label_defs[0].label)
2032 self.assertEqual(
2033 ['Type', 'Priority', 'Milestone'], actual.exclusive_label_prefixes)
2034 self.assertEqual(0, len(actual.component_defs))
2035 self.assertEqual(0, len(actual.field_defs))
2036 self.assertEqual(0, len(actual.approval_defs))
2037 self.assertEqual(False, actual.restrict_to_known)
2038 self.assertEqual(
2039 ['Duplicate'], [s.status for s in actual.statuses_offer_merge])
2040
2041 def testConvertConfig_Normal(self):
2042 """We can convert a config with fields and components to protoc."""
2043 labels_by_id = {1: 'Security', 2: 'Usability'}
2044 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_3]
2045 self.config.component_defs = [
2046 tracker_pb2.ComponentDef(component_id=1, path='UI', label_ids=[2])]
2047 self.config.approval_defs.append(tracker_pb2.ApprovalDef(
2048 approval_id=3, approver_ids=[111], survey='What?'))
2049 self.config.restrict_to_known = True
2050 self.config.statuses_offer_merge = ['Duplicate', 'New']
2051 actual = converters.ConvertConfig(
2052 self.project, self.config, self.users_by_id, labels_by_id)
2053 self.assertEqual(1, len(actual.component_defs))
2054 self.assertEqual(3, len(actual.field_defs))
2055 self.assertEqual(1, len(actual.approval_defs))
2056 self.assertEqual('proj', actual.project_name)
2057 self.assertEqual(True, actual.restrict_to_known)
2058 self.assertEqual(
2059 ['Duplicate', 'New'],
2060 sorted(s.status for s in actual.statuses_offer_merge))
2061
2062 def testConvertConfig_FiltersDeletedFieldDefs(self):
2063 """Deleted fieldDefs don't make it into the config response."""
2064 labels_by_id = {1: 'Security', 2: 'Usability'}
2065 deleted_fd1 = tracker_pb2.FieldDef(
2066 field_name='DeletedField', field_id=100,
2067 field_type=tracker_pb2.FieldTypes.STR_TYPE,
2068 applicable_type='',
2069 is_deleted=True)
2070 deleted_fd2 = tracker_pb2.FieldDef(
2071 field_name='RemovedField', field_id=101,
2072 field_type=tracker_pb2.FieldTypes.ENUM_TYPE,
2073 applicable_type='',
2074 is_deleted=True)
2075 self.config.field_defs = [self.fd_1, self.fd_2, self.fd_3, deleted_fd1,
2076 deleted_fd2]
2077 actual = converters.ConvertConfig(
2078 self.project, self.config, self.users_by_id, labels_by_id)
2079 self.assertEqual(3, len(actual.field_defs))
2080
2081 def testConvertProjectTemplateDefs_Normal(self):
2082 """We can convert protoc TemplateDefs."""
2083 self.config.component_defs = [
2084 tracker_pb2.ComponentDef(component_id=1, path="dude"),
2085 ]
2086 status_def_1 = tracker_pb2.StatusDef(status='New', means_open=True)
2087 status_def_2 = tracker_pb2.StatusDef(status='Old', means_open=False)
2088 self.config.well_known_statuses.extend([status_def_1, status_def_2])
2089 owner = self.services.user.TestAddUser('owner@example.com', 111)
2090 admin1 = self.services.user.TestAddUser('admin1@example.com', 222)
2091 admin2 = self.services.user.TestAddUser('admin2@example.com', 333)
2092 appr1 = self.services.user.TestAddUser('approver1@example.com', 444)
2093 self.config.field_defs = [
2094 self.fd_1, # STR_TYPE
2095 self.fd_3, # APPROVAl_TYPE
2096 self.fd_5, # ENUM_TYPE
2097 self.fd_6, # INT_TYPE PHASE
2098 self.fd_7, # ENUM_TYPE APPROVAL
2099 ]
2100 field_values = [
2101 tracker_bizobj.MakeFieldValue(
2102 self.fd_1.field_id, None, 'honk', None, None, None, False),
2103 tracker_bizobj.MakeFieldValue(
2104 self.fd_6.field_id, 78, None, None, None, None, False, phase_id=3)]
2105 phases = [tracker_pb2.Phase(phase_id=3, name='phaseName')]
2106 approval_values = [tracker_pb2.ApprovalValue(
2107 approval_id=3, approver_ids=[appr1.user_id], phase_id=3)]
2108 labels = ['ApprovalEnum-choice1', 'label-2', 'chicken']
2109 templates = [
2110 tracker_pb2.TemplateDef(
2111 name='Chicken', content='description', summary='summary',
2112 summary_must_be_edited=True, owner_id=111, status='New',
2113 labels=labels, members_only=True,
2114 owner_defaults_to_member=True,
2115 admin_ids=[admin1.user_id, admin2.user_id],
2116 field_values=field_values, component_ids=[1],
2117 component_required=True, phases=phases,
2118 approval_values=approval_values),
2119 tracker_pb2.TemplateDef(name='Kale')]
2120 users_by_id = {
2121 owner.user_id: testing_helpers.Blank(
2122 display_name=owner.email, email=owner.email, banned=False),
2123 admin1.user_id: testing_helpers.Blank(
2124 display_name=admin1.email, email=admin1.email, banned=False),
2125 admin2.user_id: testing_helpers.Blank(
2126 display_name=admin2.email, email=admin2.email, banned=True),
2127 appr1.user_id: testing_helpers.Blank(
2128 display_name=appr1.email, email=appr1.email, banned=False),
2129 }
2130 actual = converters.ConvertProjectTemplateDefs(
2131 templates, users_by_id, self.config)
2132 expected = [
2133 project_objects_pb2.TemplateDef(
2134 template_name='Chicken',
2135 content='description',
2136 summary='summary',
2137 summary_must_be_edited=True,
2138 owner_ref=common_pb2.UserRef(
2139 user_id=owner.user_id,
2140 display_name=owner.email,
2141 is_derived=False),
2142 status_ref=common_pb2.StatusRef(
2143 status='New',
2144 is_derived=False,
2145 means_open=True),
2146 label_refs=[
2147 common_pb2.LabelRef(label='label-2', is_derived=False),
2148 common_pb2.LabelRef(label='chicken', is_derived=False)],
2149 members_only=True,
2150 owner_defaults_to_member=True,
2151 admin_refs=[
2152 common_pb2.UserRef(
2153 user_id=admin1.user_id,
2154 display_name=admin1.email,
2155 is_derived=False),
2156 common_pb2.UserRef(
2157 user_id=admin2.user_id,
2158 display_name=admin2.email,
2159 is_derived=False)],
2160 field_values=[
2161 issue_objects_pb2.FieldValue(
2162 field_ref=common_pb2.FieldRef(
2163 field_id=self.fd_7.field_id,
2164 field_name=self.fd_7.field_name,
2165 type=common_pb2.ENUM_TYPE),
2166 value='choice1'),
2167 issue_objects_pb2.FieldValue(
2168 field_ref=common_pb2.FieldRef(
2169 field_id=self.fd_1.field_id,
2170 field_name=self.fd_1.field_name,
2171 type=common_pb2.STR_TYPE),
2172 value='honk'),
2173 issue_objects_pb2.FieldValue(
2174 field_ref=common_pb2.FieldRef(
2175 field_id=self.fd_6.field_id,
2176 field_name=self.fd_6.field_name,
2177 type=common_pb2.INT_TYPE),
2178 value='78',
2179 phase_ref=issue_objects_pb2.PhaseRef(
2180 phase_name='phaseName'))],
2181 component_refs=[
2182 common_pb2.ComponentRef(path='dude', is_derived=False)],
2183 component_required=True,
2184 phases=[issue_objects_pb2.PhaseDef(
2185 phase_ref=issue_objects_pb2.PhaseRef(phase_name='phaseName'))],
2186 approval_values=[
2187 issue_objects_pb2.Approval(
2188 field_ref=common_pb2.FieldRef(
2189 field_id=self.fd_3.field_id,
2190 field_name=self.fd_3.field_name,
2191 type=common_pb2.APPROVAL_TYPE),
2192 phase_ref=issue_objects_pb2.PhaseRef(phase_name='phaseName'),
2193 approver_refs=[common_pb2.UserRef(
2194 user_id=appr1.user_id,
2195 display_name=appr1.email,
2196 is_derived=False)])],
2197 ),
2198 project_objects_pb2.TemplateDef(
2199 template_name='Kale',
2200 status_ref=common_pb2.StatusRef(
2201 status='----',
2202 means_open=True),
2203 owner_defaults_to_member=True)]
2204 self.assertEqual(actual, expected)
2205
2206 def testConvertTemplateDefs_Empty(self):
2207 """We can convert an empty list of protoc TemplateDefs."""
2208 actual = converters.ConvertProjectTemplateDefs([], {}, self.config)
2209 self.assertEqual(actual, [])
2210
2211 def testConvertHotlist(self):
2212 """We can convert a hotlist to protoc."""
2213 hotlist = fake.Hotlist(
2214 'Fake-hotlist', 123, is_private=True,
2215 owner_ids=[self.user_1.user_id], editor_ids=[self.user_2.user_id],
2216 follower_ids=[self.user_3.user_id])
2217 hotlist.summary = 'A fake hotlist.'
2218 hotlist.description = 'Detailed description of the fake hotlist.'
2219 hotlist.default_col_spec = 'cows tho'
2220 actual = converters.ConvertHotlist(hotlist, self.users_by_id)
2221 self.assertEqual(actual,
2222 features_objects_pb2.Hotlist(
2223 name=hotlist.name,
2224 summary=hotlist.summary,
2225 description=hotlist.description,
2226 default_col_spec=hotlist.default_col_spec,
2227 is_private=hotlist.is_private,
2228 owner_ref=common_pb2.UserRef(
2229 display_name=self.user_1.email,
2230 user_id=self.user_1.user_id),
2231 editor_refs=[common_pb2.UserRef(
2232 display_name=self.user_2.email,
2233 user_id=self.user_2.user_id)],
2234 follower_refs=[common_pb2.UserRef(
2235 display_name=testing_helpers.ObscuredEmail(
2236 self.user_3.email),
2237 user_id=self.user_3.user_id)]))
2238
2239
2240 def testConvertHotlistItem(self):
2241 """We can convert a HotlistItem to protoc."""
2242 project_2 = self.services.project.TestAddProject(
2243 'proj2', project_id=788)
2244 config_2 = tracker_bizobj.MakeDefaultProjectIssueConfig(
2245 project_2.project_id)
2246 config_2.field_defs = [self.fd_2]
2247 self.config.field_defs = [self.fd_1]
2248
2249 hotlist = self.services.features.CreateHotlist(
2250 self.cnxn, 'Fake-Hotlist', 'Summary', 'Description',
2251 owner_ids=[111], editor_ids=[])
2252 self.services.features.UpdateHotlistItems(
2253 self.cnxn, hotlist.hotlist_id, [],
2254 [(self.issue_1.issue_id, 222, 12345, 'Note')])
2255 issues_by_id = {self.issue_1.issue_id: self.issue_1}
2256 related_refs = {}
2257 harmonized_config = tracker_bizobj.HarmonizeConfigs([self.config, config_2])
2258
2259 actual = converters.ConvertHotlistItems(
2260 hotlist.items, issues_by_id, self.users_by_id, related_refs,
2261 harmonized_config)
2262
2263 expected_issue = converters.ConvertIssue(
2264 self.issue_1, self.users_by_id, related_refs, harmonized_config)
2265 self.assertEqual(
2266 [features_objects_pb2.HotlistItem(
2267 issue=expected_issue,
2268 rank=1,
2269 adder_ref=common_pb2.UserRef(
2270 user_id=222,
2271 display_name='two@example.com'),
2272 added_timestamp=12345,
2273 note='Note')],
2274 actual)
2275
2276 def testConvertValueAndWhy(self):
2277 """We can covert a dict wth 'why' and 'value' fields to a ValueAndWhy PB."""
2278 actual = converters.ConvertValueAndWhy({'value': 'Foo', 'why': 'Because'})
2279 self.assertEqual(
2280 common_pb2.ValueAndWhy(value='Foo', why='Because'),
2281 actual)
2282
2283 def testConvertValueAndWhyList(self):
2284 """We can convert a list of value and why dicts."""
2285 actual = converters.ConvertValueAndWhyList([
2286 {'value': 'A', 'why': 'Because A'},
2287 {'value': 'B'},
2288 {'why': 'Why what?'},
2289 {}])
2290 self.assertEqual(
2291 [common_pb2.ValueAndWhy(value='A', why='Because A'),
2292 common_pb2.ValueAndWhy(value='B'),
2293 common_pb2.ValueAndWhy(why='Why what?'),
2294 common_pb2.ValueAndWhy()],
2295 actual)
2296
2297 def testRedistributeEnumFieldsIntoLabels(self):
2298 # function called and tests covered by
2299 # IngestIssueDelta and IngestApprovalDelta
2300 pass