blob: be66ad2a34281526a8f236548f4ab4f6ce39559c [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"""Unittests for the flt launch issues conversion task."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10import copy
11import unittest
12import settings
13import mock
14
15from businesslogic import work_env
16from framework import exceptions
17from framework import permissions
18from services import service_manager
19from services import template_svc
20from tracker import fltconversion
21from tracker import tracker_bizobj
22from testing import fake
23from testing import testing_helpers
24from proto import tracker_pb2
25
26class FLTConvertTask(unittest.TestCase):
27
28 def setUp(self):
29 self.services = service_manager.Services(
30 issue=fake.IssueService(),
31 user=fake.UserService(),
32 project=fake.ProjectService(),
33 config=fake.ConfigService(),
34 template=mock.Mock(spec=template_svc.TemplateService),)
35 self.mr = testing_helpers.MakeMonorailRequest()
Adrià Vilanova Martínez9f9ade52022-10-10 23:20:11 +020036 self.task = fltconversion.FLTConvertTask(services=self.services)
Copybara854996b2021-09-07 19:36:02 +000037 self.task.mr = self.mr
38 self.issue = fake.MakeTestIssue(
39 789, 1, 'summary', 'New', 111, issue_id=78901)
40 self.config = tracker_bizobj.MakeDefaultProjectIssueConfig(789)
41 self.work_env = work_env.WorkEnv(
42 self.mr, self.services, 'Testing')
43 self.issue1 = fake.MakeTestIssue(
44 789, 1, 'sum', 'New', 111, issue_id=78901,
45 labels=[
46 'Launch-M-Approved-71-Stable', 'Launch-M-Target-70-Beta',
47 'Launch-UI-Yes', 'Launch-Privacy-NeedInfo',
48 'pm-jojwang', 'tl-annajo', 'ux-shiba', 'Type-Launch'])
49 self.issue2 = fake.MakeTestIssue(
50 789, 2, 'sum', 'New', 111, issue_id=78902,
51 labels=[
52 'Launch-M-Target-71-Stable', 'Launch-M-Approved-70-Beta',
53 'pm-jojwang', 'tl-annajo', 'OS-Chrome', 'OS-Android',
54 'Type-Launch'])
55 self.issue3 = fake.MakeTestIssue(
56 789, 3, 'sum', 'New', 111, issue_id=78903,
57 labels=['Launch-M-Approved-71-Stable',
58 'Launch-M-Approved-79-Stable-Exp', 'Launch-M-Target-70-Beta',
59 'Launch-M-Target-70-Stable', 'Launch-UI-Yes',
60 'Launch-Exp-Leadership-Yes', 'pm-annajo', 'tl-jojwang',
61 'OS-Chrome', 'Type-Launch'])
62 self.issue4 = fake.MakeTestIssue(
63 789, 4, 'sum', 'New', 111, issue_id=78904,
64 labels=['Launch-UI-Yes', 'OS-Chrome', 'Type-Launch'])
65 self.issue5 = fake.MakeTestIssue(
66 789, 5, 'sum', 'New', 111, issue_id=78905,
67 labels=['Launch-M-Approved-71-Stable',
68 'Launch-M-Approved-79-Stable-Exp', 'Launch-M-Target-70-Beta',
69 'Launch-M-Target-70-Stable', 'Launch-UI-Yes',
70 'Launch-Privacy-NeedInfo', 'Launch-Exp-Leadership-Yes',
71 'pm-annajo', 'tl-jojwang', 'OS-Chrome', 'Type-Launch'])
72
73 self.approval_values = [
74 tracker_pb2.ApprovalValue(
75 approval_id=7, status=tracker_pb2.ApprovalStatus.NOT_SET),
76 tracker_pb2.ApprovalValue(
77 approval_id=8, status=tracker_pb2.ApprovalStatus.NEEDS_REVIEW)]
78 self.phases = [tracker_pb2.Phase(name='Stable', phase_id=88),
79 tracker_pb2.Phase(name='Beta', phase_id=89)]
80
81 def testAssertBasePermission(self):
82 self.mr.auth.user_pb.is_site_admin = True
83 settings.app_id = 'monorail-staging'
84 self.task.AssertBasePermission(self.mr)
85
86 settings.app_id = 'monorail-prod'
87 self.task.AssertBasePermission(self.mr)
88
89 self.mr.auth.user_pb.is_site_admin = False
90 self.assertRaises(permissions.PermissionException,
91 self.task.AssertBasePermission, self.mr)
92
93 def testHandleRequest(self):
94 # Set up Objects
95 project_info = fltconversion.ProjectInfo(
96 self.config, 'q=query', self.approval_values, self.phases,
97 11, 12, 13, 16, 14, 15, fltconversion.BROWSER_PHASE_MAP,
98 fltconversion.BROWSER_APPROVALS_TO_LABELS,
99 fltconversion.BROWSER_M_LABELS_RE)
100
101 self.config.field_defs = [
102 tracker_pb2.FieldDef(field_id=7, field_name='Chrome-UX',
103 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE),
104 tracker_pb2.FieldDef(field_id=8, field_name='Chrome-Privacy',
105 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE)
106 ]
107
108 # Set up mocks
109 patcher = mock.patch(
110 'search.frontendsearchpipeline.FrontendSearchPipeline',
111 spec=True, visible_results=[self.issue1, self.issue2])
112 mockPipeline = patcher.start()
113
114 self.task.services.issue.GetIssue = mock.Mock(
115 side_effect=[self.issue1, self.issue2])
116
117 self.task.FetchAndAssertProjectInfo = mock.Mock(return_value=project_info)
118
119 with self.work_env as we:
120 we.ListIssues = mock.Mock(return_value=mockPipeline)
121
122 def side_effect(_cnxn, email):
123 if email == 'jojwang@chromium.org':
124 return 111
125 if email == 'annajo@google.com':
126 return 222
127 if email == 'shiba@google.com':
128 return 333
129 raise exceptions.NoSuchUserException
130 self.task.services.user.LookupUserID = mock.Mock(side_effect=side_effect)
131
132 self.task.ExecuteIssueChanges = mock.Mock(return_value=[])
133
134 # Call
135 json = self.task.HandleRequest(self.mr)
136
137 # assert
138 self.assertEqual(json['converted_issues'], [1, 2])
139
140 new_approvals1 = [
141 tracker_pb2.ApprovalValue(
142 approval_id=7, status=tracker_pb2.ApprovalStatus.APPROVED),
143 tracker_pb2.ApprovalValue(
144 approval_id=8, status=tracker_pb2.ApprovalStatus.NEED_INFO)]
145 new_fvs1 = [
146 # M-Approved Stable
147 tracker_bizobj.MakeFieldValue(
148 15, 71, None, None, None, None, False, phase_id=88),
149 # M-Target Beta
150 tracker_bizobj.MakeFieldValue(
151 14, 70, None, None, None, None, False, phase_id=89),
152 # PM field
153 tracker_bizobj.MakeFieldValue(
154 11, None, None, 111, None, None, False),
155 # TL field
156 tracker_bizobj.MakeFieldValue(
157 12, None, None, 222, None, None, False),
158 # UX field
159 tracker_bizobj.MakeFieldValue(
160 16, None, None, 333, None, None, False)
161 ]
162
163
164 new_approvals2 = [
165 tracker_pb2.ApprovalValue(
166 approval_id=7, status=tracker_pb2.ApprovalStatus.NOT_SET),
167 tracker_pb2.ApprovalValue(
168 approval_id=8, status=tracker_pb2.ApprovalStatus.NEEDS_REVIEW)
169 ]
170 new_fvs2 = [
171 tracker_bizobj.MakeFieldValue(
172 14, 71, None, None, None, None, False, phase_id=88),
173 tracker_bizobj.MakeFieldValue(
174 15, 70, None, None, None, None, False, phase_id=89),
175 # PM field
176 tracker_bizobj.MakeFieldValue(
177 11, None, None, 111, None, None, False),
178 # TL field
179 tracker_bizobj.MakeFieldValue(
180 12, None, None, 222, None, None, False)]
181
182 execute_calls = [
183 mock.call(
184 self.config, self.issue1, new_approvals1, self.phases, new_fvs1),
185 mock.call(
186 self.config, self.issue2, new_approvals2, self.phases, new_fvs2)]
187 self.task.ExecuteIssueChanges.assert_has_calls(execute_calls)
188
189 patcher.stop()
190
191 def testHandleRequest_UndoConversion(self):
192 # test Delete() is actually called
193 mr = testing_helpers.MakeMonorailRequest(path='url/url?launch=delete')
194 self.task.UndoConversion = mock.Mock(return_value={'deleteing': [1, 2]})
195 actualReturn = self.task.HandleRequest(mr)
196 self.assertEqual({'deleteing': [1, 2]}, actualReturn)
197
198 def testUndoConversion(self):
199 # Set up objects
200 self.issue1.field_values = [
201 # Test non phase and TL/PM/TE field_values are not deleted
202 tracker_bizobj.MakeFieldValue(
203 17, None, 'strvalue', None, None, None, False)]
204 issue1 = copy.deepcopy(self.issue1)
205 issue2 = copy.deepcopy(self.issue2)
206 fvs = [
207 tracker_bizobj.MakeFieldValue(
208 11, None, None, 222, None, None, False),
209 tracker_bizobj.MakeFieldValue(
210 12, None, None, 111, None, None, False),
211 tracker_bizobj.MakeFieldValue(
212 16, None, None, 111, None, None, False)]
213 self.config.field_defs = [
214 tracker_pb2.FieldDef(field_id=11, field_name='PM'),
215 tracker_pb2.FieldDef(field_id=12, field_name='TL'),
216 tracker_pb2.FieldDef(field_id=13, field_name='TE'),
217 tracker_pb2.FieldDef(field_id=16, field_name='UX')]
218 # Make element edits made during conversion that should be undone.
219 issue1.labels.extend(['Type-FLT-Launch', 'FLT-Conversion'])
220 issue1.labels.remove('Type-Launch')
221 issue2.labels.extend(['Type-FLT-Launch', 'FLT-Conversion'])
222 issue2.labels.remove('Type-Launch')
223 issue1.approval_values = self.approval_values
224 issue2.approval_values = self.approval_values
225 issue1.phases = self.phases
226 issue2.phases = self.phases
227 issue1.field_values.extend(fvs)
228
229 # Set up mocks
230 patcher = mock.patch(
231 'search.frontendsearchpipeline.FrontendSearchPipeline',
232 spec=True, visible_results=[issue1, issue2]) # converted issues
233 mockPipeline = patcher.start()
234 self.task.services.project.GetProjectByName = mock.Mock()
235 self.task.services.config.GetProjectConfig = mock.Mock(
236 return_value=self.config)
237 self.task.services.issue.GetIssue = mock.Mock(
238 side_effect=[issue1, issue2])
239 self.task.services.issue._UpdateIssuesApprovals = mock.Mock()
240 self.task.services.issue.UpdateIssue = mock.Mock()
241
242 with self.work_env as we:
243 we.ListIssues = mock.Mock(return_value=mockPipeline)
244
245 json = self.task.UndoConversion(self.mr)
246 self.assertEqual(json['deleting'], [1, 2])
247 # assert convert issue1 is back to the pre-conversion state, self.issue1.
248 self.assertEqual(issue1, self.issue1)
249 self.assertEqual(issue2, self.issue2)
250
251 # assert UpdateIssue calls were made with pre-conversion state issues.
252 update_calls = [
253 mock.call(self.mr.cnxn, self.issue1),
254 mock.call(self.mr.cnxn, self.issue2)]
255 self.task.services.issue._UpdateIssuesApprovals.assert_has_calls(
256 update_calls)
257 self.task.services.issue.UpdateIssue.assert_has_calls(update_calls)
258 patcher.stop()
259
260 def testVerifyConversion(self):
261 # Set up objects
262 self.issue1.labels.extend(
263 # Launch-M-Target-70-Stable-Exp should be ignored
264 ['Rollout-Type-Default', 'Launch-M-Target-70-Stable-Exp'])
265 self.issue1.phases = [tracker_pb2.Phase(name='Beta', phase_id=1),
266 tracker_pb2.Phase(name='Stable', phase_id=2)]
267 self.issue1.approval_values = [
268 tracker_pb2.ApprovalValue(
269 approval_id=1, status=tracker_pb2.ApprovalStatus.NOT_SET),
270 tracker_pb2.ApprovalValue(
271 approval_id=2, status=tracker_pb2.ApprovalStatus.APPROVED),
272 tracker_pb2.ApprovalValue(
273 approval_id=3, status=tracker_pb2.ApprovalStatus.NEED_INFO),
274 ]
275 self.issue1.field_values = [
276 # problem = expected field for TL
277 tracker_bizobj.MakeFieldValue(4, None, None, 111, None, None, False),
278 tracker_pb2.FieldValue(field_id=7, int_value=70, phase_id=1),
279 tracker_pb2.FieldValue(field_id=8, int_value=71, phase_id=2),
280 ]
281
282 self.issue2.labels.extend(['Rollout-Type-Finch'])
283 self.issue2.phases = [tracker_pb2.Phase(name='Beta', phase_id=1),
284 tracker_pb2.Phase(name='Stable-Full', phase_id=2),
285 tracker_pb2.Phase(name='Stable-Exp', phase_id=3)]
286 self.issue2.approval_values = [
287 tracker_pb2.ApprovalValue(
288 approval_id=1, status=tracker_pb2.ApprovalStatus.NOT_SET),
289 tracker_pb2.ApprovalValue(
290 approval_id=2, status=tracker_pb2.ApprovalStatus.NOT_SET),
291 tracker_pb2.ApprovalValue(
292 # problem = approval Chrome-Privacy has status approved for None
293 approval_id=3, status=tracker_pb2.ApprovalStatus.APPROVED),
294 ]
295 self.issue2.field_values = [
296 # problem = no phase field for label 'Launch-M-Approved-70-Beta'
297 tracker_pb2.FieldValue(field_id=7, int_value=71, phase_id=2),
298 tracker_bizobj.MakeFieldValue(4, None, None, 111, None, None, False),
299 tracker_bizobj.MakeFieldValue(5, None, None, 111, None, None, False),
300 ]
301
302 self.issue3.labels.extend(['Rollout-Type-Default'])
303 self.issue3.phases = [tracker_pb2.Phase(name='Feature Freeze', phase_id=4),
304 tracker_pb2.Phase(name='Branch', phase_id=5),
305 tracker_pb2.Phase(name='Stable', phase_id=6)]
306 self.issue3.approval_values = [
307 tracker_pb2.ApprovalValue(
308 approval_id=9, status=tracker_pb2.ApprovalStatus.APPROVED),
309 tracker_pb2.ApprovalValue(
310 approval_id=10, status=tracker_pb2.ApprovalStatus.NEEDS_REVIEW)]
311 # problem = no phase field label Launch-M-Target-70-Stable
312 # problem = missing a field for TL
313 self.issue3.field_values = [
314 tracker_pb2.FieldValue(field_id=8, int_value=71, phase_id=6),
315 tracker_bizobj.MakeFieldValue(4, None, None, 111, None, None, False)
316 ]
317
318 self.issue4.labels.extend(['Rollout-Type-Default'])
319 # problem = incorrect phases for OS default launch
320 self.issue4.phases = [tracker_pb2.Phase(name='Branch', phase_id=5),
321 tracker_pb2.Phase(name='Stable-Exp', phase_id=7)]
322 # problem = approval ChromeOS-UX has status 'NEEDS_REVIEW'
323 # for label value Yes
324 self.issue4.approval_values = [
325 tracker_pb2.ApprovalValue(
326 approval_id=9, status=tracker_pb2.ApprovalStatus.NEEDS_REVIEW)]
327
328 self.issue5.labels.extend(['Rollout-Type-Finch'])
329 self.issue5.phases = [tracker_pb2.Phase(name='Branch', phase_id=5),
330 tracker_pb2.Phase(name='Feature Freeze', phase_id=4),
331 tracker_pb2.Phase(name='Stable-Exp', phase_id=7),
332 tracker_pb2.Phase(name='Stable-Full', phase_id=8)]
333 self.issue5.approval_values = [
334 tracker_pb2.ApprovalValue(
335 approval_id=9, status=tracker_pb2.ApprovalStatus.APPROVED),
336 # problem = approval ChromeOS-Privacy has status 'REVIEW_REQUESTED'
337 # for label value NeedInfo
338 tracker_pb2.ApprovalValue(
339 approval_id=11, status=tracker_pb2.ApprovalStatus.REVIEW_REQUESTED),
340 # problem = approval ChromeOS-Leadership-Exp has status 'NA' for label
341 # value Yes.
342 tracker_pb2.ApprovalValue(
343 approval_id=13, status=tracker_pb2.ApprovalStatus.NA)
344 ]
345
346 # problem = no phase field for label Launch-M-Approved-79-Stable-Exp
347 # problem = no phase field for label Launch-M-Target-70-Stable
348 self.issue5.field_values = [
349 tracker_pb2.FieldValue(field_id=8, int_value=71, phase_id=8),
350 tracker_bizobj.MakeFieldValue(4, None, None, 111, None, None, False),
351 tracker_bizobj.MakeFieldValue(5, None, None, 111, None, None, False)]
352
353 self.config.field_defs = [
354 tracker_pb2.FieldDef(field_id=1, field_name='Chrome-Test'),
355 tracker_pb2.FieldDef(field_id=2, field_name='Chrome-UX'),
356 tracker_pb2.FieldDef(field_id=3, field_name='Chrome-Privacy'),
357 tracker_pb2.FieldDef(field_id=4, field_name='PM'),
358 tracker_pb2.FieldDef(field_id=5, field_name='TL'),
359 tracker_pb2.FieldDef(field_id=6, field_name='TE'),
360 tracker_pb2.FieldDef(field_id=12, field_name='UX'),
361 tracker_pb2.FieldDef(field_id=7, field_name='M-Target'),
362 tracker_pb2.FieldDef(field_id=8, field_name='M-Approved'),
363 tracker_pb2.FieldDef(field_id=9, field_name='ChromeOS-UX'),
364 tracker_pb2.FieldDef(field_id=10, field_name='ChromeOS-Enterprise'),
365 tracker_pb2.FieldDef(field_id=11, field_name='ChromeOS-Privacy'),
366 tracker_pb2.FieldDef(field_id=13, field_name='ChromeOS-Leadership-Exp')
367 ]
368
369 # Set up mocks
370 patcher = mock.patch(
371 'search.frontendsearchpipeline.FrontendSearchPipeline',
372 spec=True, allowed_results=[
373 self.issue1, self.issue2, self.issue3, self.issue4, self.issue5])
374 mockPipeline = patcher.start()
375 self.task.services.project.GetProjectByName = mock.Mock()
376 self.task.services.config.GetProjectConfig = mock.Mock(
377 return_value=self.config)
378 self.task.services.issue.GetIssue = mock.Mock(
379 side_effect=[
380 self.issue1, self.issue2, self.issue3, self.issue4, self.issue5])
381 self.task.services.user.LookupUserID = mock.Mock(return_value=111)
382 with self.work_env as we:
383 we.ListIssues = mock.Mock(return_value=mockPipeline)
384
385 # Assert
386 json = self.task.VerifyConversion(self.mr)
387 self.assertEqual(json['issues verified'],
388 ['issue 1', 'issue 2', 'issue 3', 'issue 4', 'issue 5'])
389 problems = json['problems found']
390 expected_problems = [
391 'issue 1: missing a field for TL',
392 'issue 1: missing a field for UX',
393 'issue 2: approval Chrome-Privacy has status \'APPROVED\' for '
394 'label value None',
395 'issue 2: no phase field for label Launch-M-Approved-70-Beta',
396 'issue 3: missing a field for TL',
397 'issue 3: no phase field for label Launch-M-Target-70-Stable',
398 'issue 4: incorrect phases for OS default launch.',
399 'issue 4: approval ChromeOS-UX has status \'NEEDS_REVIEW\' for '
400 'label value Yes',
401 'issue 5: approval ChromeOS-Privacy has status \'REVIEW_REQUESTED\' '
402 'for label value NeedInfo',
403 'issue 5: approval ChromeOS-Leadership-Exp has status \'NA\' for label '
404 'value Yes',
405 'issue 5: no phase field for label Launch-M-Approved-79-Stable-Exp',
406 'issue 5: no phase field for label Launch-M-Target-70-Stable',
407 ]
408 self.assertEqual(problems, expected_problems)
409 patcher.stop()
410
411 def testFetchAndAssertProjectInfo(self):
412
413 # test no 'launch' in request
414 self.assertRaisesRegexp(
415 AssertionError, r'bad launch type:',
416 self.task.FetchAndAssertProjectInfo, self.mr)
417
418 # test bad 'launch' in request
419 mr = testing_helpers.MakeMonorailRequest(path='url/url?launch=bad')
420 self.assertRaisesRegexp(
421 AssertionError, r'bad launch type: bad',
422 self.task.FetchAndAssertProjectInfo, mr)
423
424 self.task.services.project.GetProjectByName = mock.Mock()
425 self.task.services.config.GetProjectConfig = mock.Mock(
426 return_value=self.config)
427
428 mr = testing_helpers.MakeMonorailRequest(path='url/url?launch=default')
429 # test no template
430 self.task.services.template.GetTemplateByName = mock.Mock(
431 return_value=None)
432 self.assertRaisesRegexp(
433 AssertionError, r'not found in chromium project',
434 self.task.FetchAndAssertProjectInfo, mr)
435
436 # test template has no phases/approvals
437 template = tracker_bizobj.MakeIssueTemplate(
438 'template', 'sum', 'New', 111, 'content', [], [], [], [])
439 self.task.services.template.GetTemplateByName = mock.Mock(
440 return_value=template)
441 self.assertRaisesRegexp(
442 AssertionError, 'no approvals or phases in',
443 self.task.FetchAndAssertProjectInfo, mr)
444
445 # test phases not recognized
446 template.phases = [tracker_pb2.Phase(name='WeirdPhase')]
447 template.approval_values = [tracker_pb2.ApprovalValue()]
448 self.assertRaisesRegexp(
449 AssertionError, 'one or more phases not recognized',
450 self.task.FetchAndAssertProjectInfo, mr)
451
452 template.phases = [tracker_pb2.Phase(name='Stable'),
453 tracker_pb2.Phase(name='Stable-Exp')]
454 template.approval_values = [
455 tracker_pb2.ApprovalValue(approval_id=1),
456 tracker_pb2.ApprovalValue(approval_id=2),
457 tracker_pb2.ApprovalValue(approval_id=3)]
458
459 # test approvals not recognized
460 self.assertRaisesRegexp(
461 AssertionError, 'one or more approvals not recognized',
462 self.task.FetchAndAssertProjectInfo, mr)
463
464 self.config.field_defs = [
465 tracker_pb2.FieldDef(field_id=1, field_name='ChromeOS-Enterprise',
466 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE),
467 tracker_pb2.FieldDef(field_id=2, field_name='Chrome-UX',
468 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE),
469 tracker_pb2.FieldDef(field_id=3, field_name='Chrome-Privacy',
470 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE)
471 ]
472
473 # test approvals not in config's approval_defs
474 self.assertRaisesRegexp(
475 AssertionError, 'one or more approvals not in config.approval_defs',
476 self.task.FetchAndAssertProjectInfo, mr)
477
478 self.config.approval_defs = [
479 tracker_pb2.ApprovalDef(approval_id=1),
480 tracker_pb2.ApprovalDef(approval_id=2),
481 tracker_pb2.ApprovalDef(approval_id=3)]
482
483 # test no pm field exists in project
484 self.assertRaisesRegexp(
485 AssertionError, 'project has no FieldDef %s' % fltconversion.PM_FIELD,
486 self.task.FetchAndAssertProjectInfo, mr)
487
488 self.config.field_defs.extend([
489 tracker_pb2.FieldDef(field_id=4, field_name='PM',
490 field_type=tracker_pb2.FieldTypes.USER_TYPE),
491 tracker_pb2.FieldDef(field_id=5, field_name='TL',
492 field_type=tracker_pb2.FieldTypes.USER_TYPE),
493 tracker_pb2.FieldDef(field_id=9, field_name='UX',
494 field_type=tracker_pb2.FieldTypes.USER_TYPE),
495 tracker_pb2.FieldDef(field_id=6, field_name='TE')
496 ])
497
498 # test no USER_TYPE te field exists in project
499 self.assertRaisesRegexp(
500 AssertionError, 'project has no FieldDef %s' % fltconversion.TE_FIELD,
501 self.task.FetchAndAssertProjectInfo, mr)
502
503 self.config.field_defs[-1].field_type = tracker_pb2.FieldTypes.USER_TYPE
504 self.config.field_defs.extend([
505 tracker_pb2.FieldDef(
506 field_id=7, field_name='M-Target', is_phase_field=True),
507 tracker_pb2.FieldDef(
508 field_id=8, field_name='M-Approved', is_multivalued=True,
509 field_type=tracker_pb2.FieldTypes.INT_TYPE)
510 ])
511
512 # test no M-Target INT_TYPE multivalued Phase FieldDefs
513 self.assertRaisesRegexp(
514 AssertionError,
515 'project has no FieldDef %s' % fltconversion.MTARGET_FIELD,
516 self.task.FetchAndAssertProjectInfo, mr)
517
518 self.config.field_defs[-2].field_type = tracker_pb2.FieldTypes.INT_TYPE
519 self.config.field_defs[-2].is_multivalued = True
520
521 # test no M-Approved INT_TYPE multivalued Phase FieldDefs
522 self.assertRaisesRegexp(
523 AssertionError,
524 'project has no FieldDef %s' % fltconversion.MAPPROVED_FIELD,
525 self.task.FetchAndAssertProjectInfo, mr)
526
527 self.config.field_defs[-1].is_phase_field = True
528
529 self.assertEqual(
530 self.task.FetchAndAssertProjectInfo(mr),
531 fltconversion.ProjectInfo(
532 self.config, fltconversion.QUERY_MAP['default'],
533 template.approval_values, template.phases, 4, 5, 6, 9, 7, 8,
534 fltconversion.BROWSER_PHASE_MAP,
535 fltconversion.BROWSER_APPROVALS_TO_LABELS,
536 fltconversion.BROWSER_M_LABELS_RE))
537
538 # FINCH special case
539 # test approvals for Finch not required
540 mr = testing_helpers.MakeMonorailRequest(path='url/url?launch=finch')
541 self.assertRaisesRegexp(
542 AssertionError, 'finch template not set up correctly',
543 self.task.FetchAndAssertProjectInfo, mr)
544
545 for av in template.approval_values:
546 av.status = tracker_pb2.ApprovalStatus.NEEDS_REVIEW
547
548 self.assertEqual(
549 self.task.FetchAndAssertProjectInfo(mr),
550 fltconversion.ProjectInfo(
551 self.config, fltconversion.QUERY_MAP['finch'],
552 template.approval_values, template.phases, 4, 5, 6, 9, 7, 8,
553 fltconversion.BROWSER_PHASE_MAP,
554 fltconversion.BROWSER_APPROVALS_TO_LABELS,
555 fltconversion.BROWSER_M_LABELS_RE))
556
557 def testFetchAndAssertProjectInfo_OS(self):
558 self.task.services.project.GetProjectByName = mock.Mock()
559 self.task.services.config.GetProjectConfig = mock.Mock(
560 return_value=self.config)
561
562 mr = testing_helpers.MakeMonorailRequest(path='url/url?launch=os')
563 template = tracker_bizobj.MakeIssueTemplate(
564 'template', 'sum', 'New', 111, 'content', [], [], [], [])
565 self.task.services.template.GetTemplateByName = mock.Mock(
566 return_value=template)
567
568 # test phases not recognized
569 template.phases = [tracker_pb2.Phase(name='Chrome-Test')]
570 template.approval_values = [tracker_pb2.ApprovalValue()]
571 self.assertRaisesRegexp(
572 AssertionError, 'one or more phases not recognized',
573 self.task.FetchAndAssertProjectInfo, mr)
574
575 template.phases = [tracker_pb2.Phase(name='feature freeze'),
576 tracker_pb2.Phase(name='branch')]
577
578 # test template not set up correctly
579 template.approval_values = [
580 tracker_pb2.ApprovalValue(approval_id=1),
581 tracker_pb2.ApprovalValue(approval_id=2),
582 tracker_pb2.ApprovalValue(approval_id=3)]
583 self.assertRaisesRegexp(
584 AssertionError, 'os template not set up correctly',
585 self.task.FetchAndAssertProjectInfo, mr)
586
587 for av in template.approval_values:
588 av.status = tracker_pb2.ApprovalStatus.NEEDS_REVIEW
589
590 # test approvals not recognized
591 self.assertRaisesRegexp(
592 AssertionError, 'one or more approvals not recognized',
593 self.task.FetchAndAssertProjectInfo, mr)
594
595 self.config.field_defs = [
596 tracker_pb2.FieldDef(field_id=1, field_name='ChromeOS-Enterprise',
597 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE),
598 tracker_pb2.FieldDef(field_id=2, field_name='ChromeOS-UX',
599 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE),
600 tracker_pb2.FieldDef(field_id=3, field_name='ChromeOS-Privacy',
601 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE)
602 ]
603
604 # Skip remaining checks. No different from Browser process.
605 self.config.approval_defs = [
606 tracker_pb2.ApprovalDef(approval_id=1),
607 tracker_pb2.ApprovalDef(approval_id=2),
608 tracker_pb2.ApprovalDef(approval_id=3)]
609
610 self.config.field_defs.extend([
611 tracker_pb2.FieldDef(field_id=4, field_name='PM',
612 field_type=tracker_pb2.FieldTypes.USER_TYPE),
613 tracker_pb2.FieldDef(field_id=5, field_name='TL',
614 field_type=tracker_pb2.FieldTypes.USER_TYPE),
615 tracker_pb2.FieldDef(field_id=6, field_name='TE',
616 field_type=tracker_pb2.FieldTypes.USER_TYPE),
617 tracker_pb2.FieldDef(field_id=9, field_name='UX',
618 field_type=tracker_pb2.FieldTypes.USER_TYPE)
619 ])
620 self.config.field_defs.extend([
621 tracker_pb2.FieldDef(
622 field_id=7, field_name='M-Target', is_phase_field=True,
623 is_multivalued=True, field_type=tracker_pb2.FieldTypes.INT_TYPE),
624 tracker_pb2.FieldDef(
625 field_id=8, field_name='M-Approved', is_phase_field=True,
626 is_multivalued=True, field_type=tracker_pb2.FieldTypes.INT_TYPE)
627 ])
628
629 self.assertEqual(
630 self.task.FetchAndAssertProjectInfo(mr),
631 fltconversion.ProjectInfo(
632 self.config, fltconversion.QUERY_MAP['os'],
633 template.approval_values, template.phases, 4, 5, 6, 9, 7, 8,
634 fltconversion.OS_PHASE_MAP, fltconversion.OS_APPROVALS_TO_LABELS,
635 fltconversion.OS_M_LABELS_RE))
636
637 @mock.patch('time.time')
638 def testExecuteIssueChanges(self, mockTime):
639 mockTime.return_value = 123
640 self.task.services.issue._UpdateIssuesApprovals = mock.Mock()
641 self.task.services.issue.DeltaUpdateIssue = mock.Mock(
642 return_value=([], None))
643 self.task.services.issue.InsertComment = mock.Mock()
644 self.config.approval_defs = [
645 tracker_pb2.ApprovalDef(
646 # test empty survey
647 approval_id=1, survey='', approver_ids=[111, 222]),
648 tracker_pb2.ApprovalDef(approval_id=2), # test missing survey
649 tracker_pb2.ApprovalDef(survey='Missing approval_id should not error.'),
650 tracker_pb2.ApprovalDef(approval_id=3, survey='Q1\nQ2\n\nQ3'),
651 tracker_pb2.ApprovalDef(approval_id=4, survey='Q1\nQ2\n\nQ3 two'),
652 tracker_pb2.ApprovalDef()]
653
654 new_avs = [tracker_pb2.ApprovalValue(
655 approval_id=1, status=tracker_pb2.ApprovalStatus.APPROVED),
656 tracker_pb2.ApprovalValue(approval_id=4),
657 tracker_pb2.ApprovalValue(approval_id=2),
658 tracker_pb2.ApprovalValue(approval_id=3)]
659
660 phases = [tracker_pb2.Phase(phase_id=1, name='Phase1', rank=1)]
661 new_fvs = [tracker_bizobj.MakeFieldValue(
662 11, 70, None, None, None, None, False, phase_id=1),
663 tracker_bizobj.MakeFieldValue(
664 12, None, 'strfield', None, None, None, False)]
665 _amendments = self.task.ExecuteIssueChanges(
666 self.config, self.issue, new_avs, phases, new_fvs)
667
668 # approver_ids set in ExecuteIssueChanges()
669 new_avs[0].approver_ids = [111, 222]
670 self.issue.approval_values = new_avs
671 self.issue.phases = phases
672 delta = tracker_pb2.IssueDelta(
673 labels_add=['Type-FLT-Launch', 'FLT-Conversion'],
674 labels_remove=['Type-Launch'], field_vals_add=new_fvs)
675 cmt_1 = tracker_pb2.IssueComment(
676 issue_id=78901, project_id=789, user_id=self.mr.auth.user_id,
677 content='', is_description=True, approval_id=1, timestamp=123)
678 cmt_2 = tracker_pb2.IssueComment(
679 issue_id=78901, project_id=789, user_id=self.mr.auth.user_id,
680 content='', is_description=True, approval_id=2, timestamp=123)
681 cmt_3 = tracker_pb2.IssueComment(
682 issue_id=78901, project_id=789, user_id=self.mr.auth.user_id,
683 content='<b>Q1</b>\n<b>Q2</b>\n<b></b>\n<b>Q3</b>',
684 is_description=True, approval_id=3, timestamp=123)
685 cmt_4 = tracker_pb2.IssueComment(
686 issue_id=78901, project_id=789, user_id=self.mr.auth.user_id,
687 content='<b>Q1</b>\n<b>Q2</b>\n<b></b>\n<b>Q3 two</b>',
688 is_description=True, approval_id=4, timestamp=123)
689
690
691 comment_calls = [mock.call(self.mr.cnxn, cmt_1),
692 mock.call(self.mr.cnxn, cmt_4),
693 mock.call(self.mr.cnxn, cmt_2),
694 mock.call(self.mr.cnxn, cmt_3)]
695 self.task.services.issue.InsertComment.assert_has_calls(comment_calls)
696
697 self.task.services.issue._UpdateIssuesApprovals.assert_called_once_with(
698 self.mr.cnxn, self.issue)
699 self.task.services.issue.DeltaUpdateIssue.assert_called_once_with(
700 self.mr.cnxn, self.task.services, self.mr.auth.user_id, 789,
701 self.config, self.issue, delta,
702 comment=fltconversion.CONVERSION_COMMENT)
703
704 def testConvertPeopleLabels(self):
705 self.task.services.user.LookupUserID = mock.Mock(
706 side_effect=[1, 2, 3, 4, 5, 6])
707 labels = [
708 'pm-u1', 'pm-u2', 'tl-u2', 'test-3', 'test-4', 'ux-u5', 'ux-6']
709 fvs = self.task.ConvertPeopleLabels(self.mr, labels, 11, 12, 13, 14)
710 expected = [
711 tracker_bizobj.MakeFieldValue(11, None, None, 1, None, None, False),
712 tracker_bizobj.MakeFieldValue(12, None, None, 2, None, None, False),
713 tracker_bizobj.MakeFieldValue(13, None, None, 3, None, None, False),
714 tracker_bizobj.MakeFieldValue(13, None, None, 4, None, None, False),
715 tracker_bizobj.MakeFieldValue(14, None, None, 5, None, None, False),
716 tracker_bizobj.MakeFieldValue(14, None, None, 6, None, None, False),
717 ]
718 self.assertEqual(fvs, expected)
719
720 def testConvertPeopleLabels_NoUsers(self):
721 def side_effect(_cnxn, _email):
722 raise exceptions.NoSuchUserException()
723 labels = []
724 self.task.services.user.LookupUserID = mock.Mock(side_effect=side_effect)
725 self.assertFalse(
726 len(self.task.ConvertPeopleLabels(self.mr, labels, 11, 12, 13, 14)))
727
728 def testCreateUserFieldValue_Chromium(self):
729 self.task.services.user.LookupUserID = mock.Mock(return_value=1)
730 actual = self.task.CreateUserFieldValue(self.mr, 'ldap', 11)
731 expected = tracker_bizobj.MakeFieldValue(
732 11, None, None, 1, None, None, False)
733 self.assertEqual(actual, expected)
734 self.task.services.user.LookupUserID.assert_called_once_with(
735 self.mr.cnxn, 'ldap@chromium.org')
736
737 def testCreateUserFieldValue_Goog(self):
738 def side_effect(_cnxn, email):
739 if email.endswith('chromium.org'):
740 raise exceptions.NoSuchUserException()
741 else:
742 return 2
743 self.task.services.user.LookupUserID = mock.Mock(side_effect=side_effect)
744 actual = self.task.CreateUserFieldValue(self.mr, 'ldap', 11)
745 expected = tracker_bizobj.MakeFieldValue(
746 11, None, None, 2, None, None, False)
747 self.assertEqual(actual, expected)
748 self.task.services.user.LookupUserID.assert_any_call(
749 self.mr.cnxn, 'ldap@chromium.org')
750 self.task.services.user.LookupUserID.assert_any_call(
751 self.mr.cnxn, 'ldap@google.com')
752
753 def testCreateUserFieldValue_NoUserFound(self):
754 def side_effect(_cnxn, _email):
755 raise exceptions.NoSuchUserException()
756 self.task.services.user.LookupUserID = mock.Mock(side_effect=side_effect)
757 self.assertIsNone(self.task.CreateUserFieldValue(self.mr, 'ldap', 11))
758
759
760class ConvertMLabels(unittest.TestCase):
761
762 def setUp(self):
763 self.target_id = 24
764 self.approved_id = 27
765 self.beta_phase = tracker_pb2.Phase(phase_id=1, name='bEtA')
766 self.stable_phase = tracker_pb2.Phase(phase_id=2, name='StAbLe')
767 self.stable_full_phase = tracker_pb2.Phase(phase_id=3, name='stable-FULL')
768 self.stable_exp_phase = tracker_pb2.Phase(phase_id=4, name='STABLE-exp')
769 self.feature_freeze_phase = tracker_pb2.Phase(
770 phase_id=5, name='FEATURE Freeze')
771 self.branch_phase = tracker_pb2.Phase(phase_id=6, name='bRANCH')
772
773 def testConvertMLabels_NormalFinch(self):
774
775 phases = [self.stable_exp_phase, self.beta_phase, self.stable_full_phase]
776 labels = [
777 'launch-m-approved-81-beta', # beta:M-Approved=81
778 'launch-m-target-80-stable-car', # ignore
779 'a-Launch-M-Target-80-Stable-car', # ignore
780 'launch-m-target-70-Stable', # stable-full:M-Target=70
781 'LAUNCH-M-TARGET-71-STABLE', # stable-full:M-Target=71
782 'launch-m-target-70-stable-exp', # stable-exp:M-Target=70
783 'launch-m-target-69-stable-exp', # stable-exp:M-Target=69
784 'launch-M-APPROVED-70-Stable-Exp', # stable-exp:M-Approved-70
785 'launch-m-approved-73-stable', # stable-full:M-Approved-73
786 'launch-m-error-73-stable', # ignore
787 'launch-m-approved-8-stable', #ignore
788 'irrelevant label-weird', # ignore
789 ]
790 actual_fvs = fltconversion.ConvertMLabels(
791 labels, phases, self.target_id, self.approved_id,
792 fltconversion.BROWSER_M_LABELS_RE, fltconversion.BROWSER_PHASE_MAP)
793
794 expected_fvs = [
795 tracker_pb2.FieldValue(
796 field_id=self.approved_id, int_value=81,
797 phase_id=self.beta_phase.phase_id, derived=False,),
798 tracker_pb2.FieldValue(
799 field_id=self.target_id, int_value=70,
800 phase_id=self.stable_full_phase.phase_id, derived=False),
801 tracker_pb2.FieldValue(
802 field_id=self.target_id, int_value=71,
803 phase_id=self.stable_full_phase.phase_id, derived=False),
804 tracker_pb2.FieldValue(
805 field_id=self.target_id, int_value=70,
806 phase_id=self.stable_exp_phase.phase_id, derived=False),
807 tracker_pb2.FieldValue(
808 field_id=self.target_id, int_value=69,
809 phase_id=self.stable_exp_phase.phase_id, derived=False),
810 tracker_pb2.FieldValue(
811 field_id=self.approved_id, int_value=70,
812 phase_id=self.stable_exp_phase.phase_id, derived=False),
813 tracker_pb2.FieldValue(
814 field_id=self.approved_id, int_value=73,
815 phase_id=self.stable_full_phase.phase_id, derived=False)
816 ]
817
818 self.assertEqual(actual_fvs, expected_fvs)
819
820 def testConvertMLabels_OS(self):
821 phases = [self.feature_freeze_phase, self.branch_phase, self.stable_phase]
822 labels = [
823 'launch-m-approved-81-beta', # ignore
824 'launch-m-target-80-stable-car', # ignore
825 'a-Launch-M-Target-80-Stable-car', # ignore
826 'launch-m-target-70-Stable', # stable:M-Target=70
827 'LAUNCH-M-TARGET-71-STABLE', # stable:M-Target=71
828 'launch-m-target-70-stable-exp', # ignore
829 'launch-M-APPROVED-70-Stable-Exp', # ignore
830 'launch-m-approved-73-stable', # stable:M-Approved-73
831 'launch-m-error-73-stable', # ignore
832 'launch-m-approved-8-stable', #ignore
833 'irrelevant label-weird', # ignore
834 ]
835 actual_fvs = fltconversion.ConvertMLabels(
836 labels, phases, self.target_id, self.approved_id,
837 fltconversion.OS_M_LABELS_RE, fltconversion.OS_PHASE_MAP)
838
839 expected_fvs = [
840 tracker_pb2.FieldValue(
841 field_id=self.target_id, int_value=70,
842 phase_id=self.stable_phase.phase_id, derived=False,),
843 tracker_pb2.FieldValue(
844 field_id=self.target_id, int_value=71,
845 phase_id=self.stable_phase.phase_id, derived=False),
846 tracker_pb2.FieldValue(
847 field_id=self.approved_id, int_value=73,
848 phase_id=self.stable_phase.phase_id, derived=False)
849 ]
850
851 self.assertEqual(actual_fvs, expected_fvs)
852
853
854class ConvertLaunchLabels(unittest.TestCase):
855
856 def setUp(self):
857 self.project_fds = [
858 tracker_pb2.FieldDef(
859 field_id=1, project_id=789, field_name='String',
860 field_type=tracker_pb2.FieldTypes.STR_TYPE),
861 tracker_pb2.FieldDef(
862 field_id=2, project_id=789, field_name='Chrome-UX',
863 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE),
864 tracker_pb2.FieldDef(
865 field_id=3, project_id=789, field_name='Chrome-Privacy',
866 field_type=tracker_pb2.FieldTypes.APPROVAL_TYPE)
867 ]
868 approvalUX = tracker_pb2.ApprovalValue(
869 approval_id=2, status=tracker_pb2.ApprovalStatus.NEEDS_REVIEW)
870 approvalPrivacy = tracker_pb2.ApprovalValue(approval_id=3)
871 self.approvals = [approvalUX, approvalPrivacy]
872
873 def testConvertLaunchLabels_Normal(self):
874 labels = [
875 'Launch-UX-NotReviewed', 'Launch-Privacy-Yes', 'Launch-NotRelevant']
876 actual = fltconversion.ConvertLaunchLabels(
877 labels, self.approvals, self.project_fds,
878 fltconversion.BROWSER_APPROVALS_TO_LABELS)
879 expected = [
880 tracker_pb2.ApprovalValue(
881 approval_id=2, status=tracker_pb2.ApprovalStatus.NEEDS_REVIEW),
882 tracker_pb2.ApprovalValue(
883 approval_id=3, status=tracker_pb2.ApprovalStatus.APPROVED)
884 ]
885 self.assertEqual(actual, expected)
886
887 def testConvertLaunchLabels_ExtraAndMissingLabels(self):
888 labels = [
889 'Blah-Launch-Privacy-Yes', # Missing, this is not a valid Label
890 'Launch-Security-Yes', # Extra, no matching approval in given approvals
891 'Launch-UI-Yes'] # Missing Launch-Privacy
892 actual = fltconversion.ConvertLaunchLabels(
893 labels, self.approvals, self.project_fds,
894 fltconversion.BROWSER_APPROVALS_TO_LABELS)
895 expected = [
896 tracker_pb2.ApprovalValue(
897 approval_id=2, status=tracker_pb2.ApprovalStatus.APPROVED),
898 tracker_pb2.ApprovalValue(
899 approval_id=3, status=tracker_pb2.ApprovalStatus.NOT_SET)
900 ]
901 self.assertEqual(actual, expected)
902
903class ExtractLabelLDAPs(unittest.TestCase):
904
905 def testExtractLabelLDAPs_Normal(self):
906 labels = [
907 'tl-USER1',
908 'pm-',
909 'tL-User2',
910 'test-user4',
911 'PM-USER3',
912 'pm',
913 'test-user5',
914 'test-',
915 'ux-user9']
916 (actual_pm, actual_tl, actual_tests,
917 actual_ux) = fltconversion.ExtractLabelLDAPs(labels)
918 self.assertEqual(actual_pm, 'user3')
919 self.assertEqual(actual_tl, 'user2')
920 self.assertEqual(actual_tests, ['user4', 'user5'])
921 self.assertEqual(actual_ux, ['user9'])
922
923 def testExtractLabelLDAPs_NoLabels(self):
924 (actual_pm, actual_tl, actual_tests,
925 actual_ux) = fltconversion.ExtractLabelLDAPs([])
926 self.assertIsNone(actual_pm)
927 self.assertIsNone(actual_tl)
928 self.assertFalse(len(actual_tests))
929 self.assertFalse(len(actual_ux))