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