blob: 42fc46b9e7bab9c84cba809c87283d839a62c518 [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"""Unit tests for Template editing/viewing servlet."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
Adrià Vilanova Martínez9f9ade52022-10-10 23:20:11 +020011try:
12 from mox3 import mox
13except ImportError:
14 import mox
Copybara854996b2021-09-07 19:36:02 +000015import logging
16import unittest
17import settings
18
19from mock import Mock
20
21import ezt
22
23from framework import permissions
24from services import service_manager
25from services import template_svc
26from testing import fake
27from testing import testing_helpers
28from tracker import templatedetail
29from tracker import tracker_bizobj
30from proto import tracker_pb2
31
32
33class TemplateDetailTest(unittest.TestCase):
34 """Tests for the TemplateDetail servlet."""
35
36 def setUp(self):
37 self.cnxn = 'fake cnxn'
38 mock_template_service = Mock(spec=template_svc.TemplateService)
39 self.services = service_manager.Services(project=fake.ProjectService(),
40 config=fake.ConfigService(),
41 template=mock_template_service,
42 usergroup=fake.UserGroupService(),
43 user=fake.UserService())
44 self.servlet = templatedetail.TemplateDetail('req', 'res',
45 services=self.services)
46
47 self.services.user.TestAddUser('gatsby@example.com', 111)
48 self.services.user.TestAddUser('sport@example.com', 222)
49 self.services.user.TestAddUser('gatsby@example.com', 111)
50 self.services.user.TestAddUser('daisy@example.com', 333)
51
52 self.project = self.services.project.TestAddProject('proj')
53 self.services.project.TestAddProjectMembers(
54 [333], self.project, 'CONTRIBUTOR_ROLE')
55
56 self.template = self.test_template = tracker_bizobj.MakeIssueTemplate(
57 'TestTemplate', 'sum', 'New', 111, 'content', ['label1', 'label2'],
58 [], [222], [], summary_must_be_edited=True,
59 owner_defaults_to_member=True, component_required=False,
60 members_only=False)
61 self.template.template_id = 12345
62 self.services.template.GetTemplateByName = Mock(
63 return_value=self.template)
64
65 self.mr = testing_helpers.MakeMonorailRequest(project=self.project)
66 self.mr.template_name = 'TestTemplate'
67
68 self.mox = mox.Mox()
69
70 self.fd_1 = tracker_bizobj.MakeFieldDef(
71 1, 789, 'UXReview', tracker_pb2.FieldTypes.STR_TYPE, None,
72 '', False, False, False, None, None, '', False, '', '',
73 tracker_pb2.NotifyTriggers.NEVER, 'no_action',
74 'Approval for UX review', False, approval_id=2)
75 self.fd_2 = tracker_bizobj.MakeFieldDef(
76 2, 789, 'UXReview', tracker_pb2.FieldTypes.STR_TYPE, None,
77 '', False, False, False, None, None, '', False, '', '',
78 tracker_pb2.NotifyTriggers.NEVER, 'no_action',
79 'Approval for UX review', False)
80 self.fd_3 = tracker_bizobj.MakeFieldDef(
81 3, 789, 'TestApproval', tracker_pb2.FieldTypes.APPROVAL_TYPE, None,
82 '', False, False, False, None, None, '', False, '', '',
83 tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'Approval for Test',
84 False)
85 self.fd_4 = tracker_bizobj.MakeFieldDef(
86 4, 789, 'SecurityApproval', tracker_pb2.FieldTypes.APPROVAL_TYPE, None,
87 '', False, False, False, None, None, '', False, '', '',
88 tracker_pb2.NotifyTriggers.NEVER, 'no_action', 'Approval for Security',
89 False)
90 self.fd_5 = tracker_bizobj.MakeFieldDef(
91 5, 789, 'GateTarget', tracker_pb2.FieldTypes.INT_TYPE, None,
92 '', False, False, False, None, None, '', False, '', '',
93 tracker_pb2.NotifyTriggers.NEVER, 'no_action',
94 'milestone target', False, is_phase_field=True)
95 self.fd_6 = tracker_bizobj.MakeFieldDef(
96 6, 789, 'Choices', tracker_pb2.FieldTypes.ENUM_TYPE, None,
97 '', False, False, False, None, None, '', False, '', '',
98 tracker_pb2.NotifyTriggers.NEVER, 'no_action',
99 'milestone target', False, is_phase_field=True)
100 self.fd_7 = tracker_bizobj.MakeFieldDef(
101 7,
102 789,
103 'RestrictedField',
104 tracker_pb2.FieldTypes.INT_TYPE,
105 None,
106 '',
107 False,
108 False,
109 False,
110 None,
111 None,
112 '',
113 False,
114 '',
115 '',
116 tracker_pb2.NotifyTriggers.NEVER,
117 'no_action',
118 'RestrictedField',
119 False,
120 is_restricted_field=True)
121 self.fd_8 = tracker_bizobj.MakeFieldDef(
122 8,
123 789,
124 'RestrictedEnumField',
125 tracker_pb2.FieldTypes.ENUM_TYPE,
126 None,
127 '',
128 False,
129 False,
130 False,
131 None,
132 None,
133 '',
134 False,
135 '',
136 '',
137 tracker_pb2.NotifyTriggers.NEVER,
138 'no_action',
139 'RestrictedEnumField',
140 False,
141 is_restricted_field=True)
142 self.fd_9 = tracker_bizobj.MakeFieldDef(
143 9,
144 789,
145 'RestrictedField_2',
146 tracker_pb2.FieldTypes.INT_TYPE,
147 None,
148 '',
149 False,
150 False,
151 False,
152 None,
153 None,
154 '',
155 False,
156 '',
157 '',
158 tracker_pb2.NotifyTriggers.NEVER,
159 'no_action',
160 'RestrictedField_2',
161 False,
162 is_restricted_field=True)
163 self.fd_10 = tracker_bizobj.MakeFieldDef(
164 10,
165 789,
166 'RestrictedEnumField_2',
167 tracker_pb2.FieldTypes.ENUM_TYPE,
168 None,
169 '',
170 False,
171 False,
172 False,
173 None,
174 None,
175 '',
176 False,
177 '',
178 '',
179 tracker_pb2.NotifyTriggers.NEVER,
180 'no_action',
181 'RestrictedEnumField_2',
182 False,
183 is_restricted_field=True)
184
185 self.ad_3 = tracker_pb2.ApprovalDef(approval_id=3)
186 self.ad_4 = tracker_pb2.ApprovalDef(approval_id=4)
187
188 self.cd_1 = tracker_bizobj.MakeComponentDef(
189 1, 789, 'BackEnd', 'doc', False, [111], [], 100000, 222)
190 self.template.component_ids.append(1)
191
192 self.canary_phase = tracker_pb2.Phase(
193 name='Canary', phase_id=1, rank=1)
194 self.av_3 = tracker_pb2.ApprovalValue(approval_id=3, phase_id=1)
195 self.stable_phase = tracker_pb2.Phase(
196 name='Stable', phase_id=2, rank=3)
197 self.av_4 = tracker_pb2.ApprovalValue(approval_id=4, phase_id=2)
198 self.template.phases.extend([self.stable_phase, self.canary_phase])
199 self.template.approval_values.extend([self.av_3, self.av_4])
200
201 self.config = self.services.config.GetProjectConfig(
202 'fake cnxn', self.project.project_id)
203 self.templates = testing_helpers.DefaultTemplates()
204 self.template.labels.extend(
205 ['GateTarget-Should-Not', 'GateTarget-Be-Masked',
206 'Choices-Wrapped', 'Choices-Burritod'])
207 self.templates.append(self.template)
208 self.services.template.GetProjectTemplates = Mock(
209 return_value=self.templates)
210 self.services.template.FindTemplateByName = Mock(return_value=self.template)
211 self.config.component_defs.append(self.cd_1)
212 self.config.field_defs.extend(
213 [
214 self.fd_1, self.fd_2, self.fd_3, self.fd_4, self.fd_5, self.fd_6,
215 self.fd_7, self.fd_8, self.fd_9, self.fd_10
216 ])
217 self.config.approval_defs.extend([self.ad_3, self.ad_4])
218 self.services.config.StoreConfig(None, self.config)
219
220 def tearDown(self):
221 self.mox.UnsetStubs()
222 self.mox.ResetAll()
223
224 def testAssertBasePermission_Anyone(self):
225 self.mr.auth.effective_ids = {222}
226 self.servlet.AssertBasePermission(self.mr)
227
228 self.mr.auth.effective_ids = {333}
229 self.servlet.AssertBasePermission(self.mr)
230
231 self.mr.perms = permissions.OWNER_ACTIVE_PERMISSIONSET
232 self.servlet.AssertBasePermission(self.mr)
233
234 self.mr.perms = permissions.READ_ONLY_PERMISSIONSET
235 self.servlet.AssertBasePermission(self.mr)
236
237 def testAssertBasePermision_MembersOnly(self):
238 self.template.members_only = True
239 self.mr.auth.effective_ids = {222}
240 self.servlet.AssertBasePermission(self.mr)
241
242 self.mr.auth.effective_ids = {333}
243 self.servlet.AssertBasePermission(self.mr)
244
245 self.mr.auth.effective_ids = {444}
246 self.assertRaises(
247 permissions.PermissionException,
248 self.servlet.AssertBasePermission, self.mr)
249
250 self.mr.perms = permissions.READ_ONLY_PERMISSIONSET
251 self.assertRaises(
252 permissions.PermissionException,
253 self.servlet.AssertBasePermission, self.mr)
254
255 def testGatherPageData(self):
256 self.mr.perms = permissions.PermissionSet([])
257 self.mr.auth.effective_ids = {222} # template admin
258 page_data = self.servlet.GatherPageData(self.mr)
259 self.assertEqual(self.servlet.PROCESS_TAB_TEMPLATES,
260 page_data['admin_tab_mode'])
261 self.assertTrue(page_data['allow_edit'])
262 self.assertEqual(page_data['uneditable_fields'], ezt.boolean(True))
263 self.assertFalse(page_data['new_template_form'])
264 self.assertFalse(page_data['initial_members_only'])
265 self.assertEqual(page_data['template_name'], 'TestTemplate')
266 self.assertEqual(page_data['initial_summary'], 'sum')
267 self.assertTrue(page_data['initial_must_edit_summary'])
268 self.assertEqual(page_data['initial_content'], 'content')
269 self.assertEqual(page_data['initial_status'], 'New')
270 self.assertEqual(page_data['initial_owner'], 'gatsby@example.com')
271 self.assertTrue(page_data['initial_owner_defaults_to_member'])
272 self.assertEqual(page_data['initial_components'], 'BackEnd')
273 self.assertFalse(page_data['initial_component_required'])
274 self.assertItemsEqual(
275 page_data['labels'],
276 ['label1', 'label2', 'GateTarget-Should-Not', 'GateTarget-Be-Masked'])
277 self.assertEqual(page_data['initial_admins'], 'sport@example.com')
278 self.assertTrue(page_data['initial_add_approvals'])
279 self.assertEqual(len(page_data['initial_phases']), 6)
280 phases = [phase for phase in page_data['initial_phases'] if phase.name]
281 self.assertEqual(len(phases), 2)
282 self.assertEqual(len(page_data['approvals']), 2)
283 self.assertItemsEqual(page_data['prechecked_approvals'],
284 ['3_phase_0', '4_phase_1'])
285 self.assertTrue(page_data['fields'][3].is_editable) #nonRestrictedField
286 self.assertIsNone(page_data['fields'][4].is_editable) #restrictedField
287
288 def testProcessFormData_Reject(self):
289 self.mr.auth.effective_ids = {222}
290 post_data = fake.PostData(
291 name=['TestTemplate'],
292 members_only=['on'],
293 summary=['TLDR'],
294 summary_must_be_edited=['on'],
295 content=['HEY WHY'],
296 status=['Accepted'],
297 owner=['someone@world.com'],
298 label=['label-One', 'label-Two'],
299 custom_1=['NO'],
300 custom_2=['MOOD'],
301 components=['hey, hey2,he3'],
302 component_required=['on'],
303 owner_defaults_to_member=['no'],
304 add_approvals = ['on'],
305 phase_0=['Canary'],
306 phase_1=['Stable-Exp'],
307 phase_2=['Stable'],
308 phase_3=[''],
309 phase_4=[''],
310 phase_5=[''],
311 approval_3=['phase_0'],
312 approval_4=['phase_2']
313 )
314
315 self.mox.StubOutWithMock(self.servlet, 'PleaseCorrect')
316 self.servlet.PleaseCorrect(
317 self.mr,
318 initial_members_only=ezt.boolean(True),
319 template_name='TestTemplate',
320 initial_summary='TLDR',
321 initial_must_edit_summary=ezt.boolean(True),
322 initial_content='HEY WHY',
323 initial_status='Accepted',
324 initial_owner='someone@world.com',
325 initial_owner_defaults_to_member=ezt.boolean(False),
326 initial_components='hey, hey2, he3',
327 initial_component_required=ezt.boolean(True),
328 initial_admins='',
329 labels=['label-One', 'label-Two'],
330 fields=mox.IgnoreArg(),
331 initial_add_approvals=ezt.boolean(True),
332 initial_phases=[tracker_pb2.Phase(name=name) for
333 name in ['Canary', 'Stable-Exp', 'Stable', '', '', '']],
334 approvals=mox.IgnoreArg(),
335 prechecked_approvals=['3_phase_0', '4_phase_2'],
336 required_approval_ids=[]
337 )
338 self.mox.ReplayAll()
339 url = self.servlet.ProcessFormData(self.mr, post_data)
340 self.mox.VerifyAll()
341 self.assertEqual('Owner not found.', self.mr.errors.owner)
342 self.assertEqual('Unknown component he3', self.mr.errors.components)
343 self.assertIsNone(url)
344 self.assertEqual('Defined gates must have assigned approvals.',
345 self.mr.errors.phase_approvals)
346
347 def testProcessFormData_RejectRestrictedFields(self):
348 """Template admins cannot set restricted fields by default."""
349 self.mr.perms = permissions.PermissionSet([])
350 self.mr.auth.effective_ids = {222} # template admin
351 post_data_add_fv = fake.PostData(
352 name=['TestTemplate'],
353 members_only=['on'],
354 summary=['TLDR'],
355 summary_must_be_edited=[''],
356 content=['HEY WHY'],
357 status=['Accepted'],
358 owner=['daisy@example.com'],
359 label=['label-One', 'label-Two'],
360 custom_1=['NO'],
361 custom_2=['MOOD'],
362 custom_7=['37'],
363 components=['BackEnd'],
364 component_required=['on'],
365 owner_defaults_to_member=['on'],
366 add_approvals=['no'],
367 phase_0=[''],
368 phase_1=[''],
369 phase_2=[''],
370 phase_3=[''],
371 phase_4=[''],
372 phase_5=['OOPs'],
373 approval_3=['phase_0'],
374 approval_4=['phase_2'])
375 post_data_label_edits_enum = fake.PostData(
376 name=['TestTemplate'],
377 members_only=['on'],
378 summary=['TLDR'],
379 summary_must_be_edited=[''],
380 content=['HEY WHY'],
381 status=['Accepted'],
382 owner=['daisy@example.com'],
383 label=['label-One', 'label-Two', 'RestrictedEnumField-7'],
384 components=['BackEnd'],
385 component_required=['on'],
386 owner_defaults_to_member=['on'],
387 add_approvals=['no'],
388 phase_0=[''],
389 phase_1=[''],
390 phase_2=[''],
391 phase_3=[''],
392 phase_4=[''],
393 phase_5=['OOPs'],
394 approval_3=['phase_0'],
395 approval_4=['phase_2'])
396
397 self.assertRaises(
398 AssertionError, self.servlet.ProcessFormData, self.mr, post_data_add_fv)
399 self.assertRaises(
400 AssertionError, self.servlet.ProcessFormData, self.mr,
401 post_data_label_edits_enum)
402
403 def testProcessFormData_Accept(self):
404 self.fd_7.editor_ids = [222]
405 self.fd_8.editor_ids = [222]
406 self.mr.perms = permissions.PermissionSet([])
407 self.mr.auth.effective_ids = {222} # template admin
408 temp_restricted_fv = tracker_bizobj.MakeFieldValue(
409 9, 3737, None, None, None, None, False)
410 self.template.field_values.append(temp_restricted_fv)
411 self.template.labels.append('RestrictedEnumField_2-b')
412 post_data = fake.PostData(
413 name=['TestTemplate'],
414 members_only=['on'],
415 summary=['TLDR'],
416 summary_must_be_edited=[''],
417 content=['HEY WHY'],
418 status=['Accepted'],
419 owner=['daisy@example.com'],
420 label=['label-One', 'label-Two', 'RestrictedEnumField-7'],
421 custom_1=['NO'],
422 custom_2=['MOOD'],
423 custom_7=['37'],
424 components=['BackEnd'],
425 component_required=['on'],
426 owner_defaults_to_member=['on'],
427 add_approvals=['no'],
428 phase_0=[''],
429 phase_1=[''],
430 phase_2=[''],
431 phase_3=[''],
432 phase_4=[''],
433 phase_5=['OOPs'],
434 approval_3=['phase_0'],
435 approval_4=['phase_2'])
436 url = self.servlet.ProcessFormData(self.mr, post_data)
437
438 self.assertTrue('/templates/detail?saved=1&template=TestTemplate&' in url)
439
440 self.services.template.UpdateIssueTemplateDef.assert_called_once_with(
441 self.mr.cnxn,
442 47925,
443 12345,
444 status='Accepted',
445 component_required=True,
446 phases=[],
447 approval_values=[],
448 name='TestTemplate',
449 field_values=[
450 tracker_pb2.FieldValue(field_id=1, str_value='NO', derived=False),
451 tracker_pb2.FieldValue(field_id=2, str_value='MOOD', derived=False),
452 tracker_pb2.FieldValue(field_id=7, int_value=37, derived=False),
453 tracker_pb2.FieldValue(field_id=9, int_value=3737, derived=False)
454 ],
455 labels=[
456 'label-One', 'label-Two', 'RestrictedEnumField-7',
457 'RestrictedEnumField_2-b'
458 ],
459 owner_defaults_to_member=True,
460 admin_ids=[],
461 content='HEY WHY',
462 component_ids=[1],
463 summary_must_be_edited=False,
464 summary='TLDR',
465 members_only=True,
466 owner_id=333)
467
468 def testProcessFormData_AcceptPhases(self):
469 self.mr.auth.effective_ids = {222}
470 post_data = fake.PostData(
471 name=['TestTemplate'],
472 members_only=['on'],
473 summary=['TLDR'],
474 summary_must_be_edited=[''],
475 content=['HEY WHY'],
476 status=['Accepted'],
477 owner=['daisy@example.com'],
478 label=['label-One', 'label-Two'],
479 custom_1=['NO'],
480 custom_2=['MOOD'],
481 components=['BackEnd'],
482 component_required=['on'],
483 owner_defaults_to_member=['on'],
484 add_approvals = ['on'],
485 phase_0=['Canary'],
486 phase_1=['Stable'],
487 phase_2=[''],
488 phase_3=[''],
489 phase_4=[''],
490 phase_5=[''],
491 approval_3=['phase_0'],
492 approval_4=['phase_1']
493 )
494 url = self.servlet.ProcessFormData(self.mr, post_data)
495
496 self.assertTrue('/templates/detail?saved=1&template=TestTemplate&' in url)
497
498 self.services.template.UpdateIssueTemplateDef.assert_called_once_with(
499 self.mr.cnxn, 47925, 12345, status='Accepted', component_required=True,
500 phases=[
501 tracker_pb2.Phase(name='Canary', rank=0, phase_id=0),
502 tracker_pb2.Phase(name='Stable', rank=1, phase_id=1)],
503 approval_values=[tracker_pb2.ApprovalValue(approval_id=3, phase_id=0),
504 tracker_pb2.ApprovalValue(approval_id=4, phase_id=1)],
505 name='TestTemplate', field_values=[
506 tracker_pb2.FieldValue(field_id=1, str_value='NO', derived=False),
507 tracker_pb2.FieldValue(
508 field_id=2, str_value='MOOD', derived=False)],
509 labels=['label-One', 'label-Two'], owner_defaults_to_member=True,
510 admin_ids=[], content='HEY WHY', component_ids=[1],
511 summary_must_be_edited=False, summary='TLDR', members_only=True,
512 owner_id=333)
513
514 def testProcessFormData_Delete(self):
515 post_data = fake.PostData(
516 deletetemplate=['Submit'],
517 name=['TestTemplate'],
518 members_only=['on'],
519 )
520 url = self.servlet.ProcessFormData(self.mr, post_data)
521
522 self.assertTrue('/p/None/adminTemplates?deleted=1' in url)
523 self.services.template.DeleteIssueTemplateDef\
524 .assert_called_once_with(self.mr.cnxn, 47925, 12345)