blob: 60db78b64204293da315df951d7d8af320c056bd [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 test for Template creation servlet."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import mox
12import unittest
13import settings
14
15from mock import Mock
16
17import ezt
18
19from framework import permissions
20from services import service_manager
21from services import template_svc
22from testing import fake
23from testing import testing_helpers
24from tracker import templatecreate
25from tracker import tracker_bizobj
26from tracker import tracker_views
27from proto import tracker_pb2
28
29
30class TemplateCreateTest(unittest.TestCase):
31 """Tests for the TemplateCreate servlet."""
32
33 def setUp(self):
34 self.cnxn = 'fake cnxn'
35 self.services = service_manager.Services(
36 project=fake.ProjectService(),
37 config=fake.ConfigService(),
38 template=Mock(spec=template_svc.TemplateService),
39 user=fake.UserService())
40 self.servlet = templatecreate.TemplateCreate('req', 'res',
41 services=self.services)
42 self.project = self.services.project.TestAddProject('proj')
43
44 self.fd_1 = tracker_bizobj.MakeFieldDef(
45 1, self.project.project_id, 'StringFieldName',
46 tracker_pb2.FieldTypes.STR_TYPE, None, '', False,
47 False, False, None, None, '', False, '', '',
48 tracker_pb2.NotifyTriggers.NEVER, 'no_action',
49 'some approval thing', False, approval_id=2)
50
51 self.fd_2 = tracker_bizobj.MakeFieldDef(
52 2, self.project.project_id, 'UXApproval',
53 tracker_pb2.FieldTypes.APPROVAL_TYPE, None, '', False, False, False,
54 None, None, '', False, '', '', tracker_pb2.NotifyTriggers.NEVER,
55 'no_action', 'Approval for UX review', False)
56 self.fd_3 = tracker_bizobj.MakeFieldDef(
57 3, self.project.project_id, 'TestApproval',
58 tracker_pb2.FieldTypes.APPROVAL_TYPE, None, '', False, False, False,
59 None, None, '', False, '', '', tracker_pb2.NotifyTriggers.NEVER,
60 'no_action', 'Approval for Test review', False)
61 self.fd_4 = tracker_bizobj.MakeFieldDef(
62 4, self.project.project_id, 'Target',
63 tracker_pb2.FieldTypes.INT_TYPE, None, '', False, False, False, None,
64 None, '', False, '', '', tracker_pb2.NotifyTriggers.NEVER, 'no_action',
65 'milestone target', False, is_phase_field=True)
66 self.fd_5 = tracker_bizobj.MakeFieldDef(
67 5,
68 self.project.project_id,
69 'RestrictedField',
70 tracker_pb2.FieldTypes.INT_TYPE,
71 None,
72 '',
73 False,
74 False,
75 False,
76 None,
77 None,
78 '',
79 False,
80 '',
81 '',
82 tracker_pb2.NotifyTriggers.NEVER,
83 'no_action',
84 'RestrictedField',
85 False,
86 is_restricted_field=True)
87 self.fd_6 = tracker_bizobj.MakeFieldDef(
88 6,
89 self.project.project_id,
90 'RestrictedEnumField',
91 tracker_pb2.FieldTypes.ENUM_TYPE,
92 None,
93 '',
94 False,
95 False,
96 False,
97 None,
98 None,
99 '',
100 False,
101 '',
102 '',
103 tracker_pb2.NotifyTriggers.NEVER,
104 'no_action',
105 'RestrictedEnumField',
106 False,
107 is_restricted_field=True)
108 ad_2 = tracker_pb2.ApprovalDef(approval_id=2)
109 ad_3 = tracker_pb2.ApprovalDef(approval_id=3)
110
111 self.config = self.services.config.GetProjectConfig(
112 'fake cnxn', self.project.project_id)
113 self.config.approval_defs.extend([ad_2, ad_3])
114 self.config.field_defs.extend(
115 [self.fd_1, self.fd_2, self.fd_3, self.fd_4, self.fd_5, self.fd_6])
116
117 first_tmpl = tracker_bizobj.MakeIssueTemplate(
118 'sometemplate', 'summary', None, None, 'content', [], [], [],
119 [])
120 self.services.config.StoreConfig(None, self.config)
121
122 templates = testing_helpers.DefaultTemplates()
123 templates.append(first_tmpl)
124 self.services.template.GetProjectTemplates = Mock(
125 return_value=templates)
126
127 self.mr = testing_helpers.MakeMonorailRequest(
128 project=self.project, perms=permissions.OWNER_ACTIVE_PERMISSIONSET)
129 self.mox = mox.Mox()
130
131 def tearDown(self):
132 self.mox.UnsetStubs()
133 self.mox.ResetAll()
134
135 def testAssertBasePermission(self):
136 # Anon users can never do it
137 self.mr.perms = permissions.READ_ONLY_PERMISSIONSET
138 self.assertRaises(
139 permissions.PermissionException,
140 self.servlet.AssertBasePermission, self.mr)
141
142 # Project owner can do it.
143 self.mr.perms = permissions.OWNER_ACTIVE_PERMISSIONSET
144 self.servlet.AssertBasePermission(self.mr)
145
146 # Project member cannot do it
147 self.mr.perms = permissions.COMMITTER_ACTIVE_PERMISSIONSET
148 self.assertRaises(
149 permissions.PermissionException,
150 self.servlet.AssertBasePermission, self.mr)
151 self.mr.perms = permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET
152 self.assertRaises(
153 permissions.PermissionException,
154 self.servlet.AssertBasePermission, self.mr)
155
156 def testGatherPageData(self):
157 precomp_view_info = tracker_views._PrecomputeInfoForValueViews(
158 [], [], [], self.config, [])
159 fv = tracker_views._MakeFieldValueView(
160 self.fd_1, self.config, precomp_view_info, {})
161 page_data = self.servlet.GatherPageData(self.mr)
162 self.assertEqual(self.servlet.PROCESS_TAB_TEMPLATES,
163 page_data['admin_tab_mode'])
164 self.assertTrue(page_data['allow_edit'])
165 self.assertEqual(page_data['uneditable_fields'], ezt.boolean(False))
166 self.assertTrue(page_data['new_template_form'])
167 self.assertFalse(page_data['initial_members_only'])
168 self.assertEqual(page_data['template_name'], '')
169 self.assertEqual(page_data['initial_summary'], '')
170 self.assertFalse(page_data['initial_must_edit_summary'])
171 self.assertEqual(page_data['initial_content'], '')
172 self.assertEqual(page_data['initial_status'], '')
173 self.assertEqual(page_data['initial_owner'], '')
174 self.assertFalse(page_data['initial_owner_defaults_to_member'])
175 self.assertEqual(page_data['initial_components'], '')
176 self.assertFalse(page_data['initial_component_required'])
177 self.assertEqual(page_data['fields'][2].field_name, fv.field_name)
178 self.assertEqual(page_data['initial_admins'], '')
179 self.assertEqual(page_data['approval_subfields_present'], ezt.boolean(True))
180 self.assertEqual(page_data['phase_fields_present'], ezt.boolean(False))
181
182 def testProcessFormData_Reject(self):
183 self.services.user.TestAddUser('user@example.com', 222)
184 self.mr.auth.effective_ids = {222}
185 post_data = fake.PostData(
186 name=['sometemplate'],
187 members_only=['on'],
188 summary=['TLDR'],
189 summary_must_be_edited=['on'],
190 content=['HEY WHY'],
191 status=['Accepted'],
192 owner=['someone@world.com'],
193 label=['label-One', 'label-Two'],
194 custom_1=['NO'],
195 custom_2=['MOOD'],
196 components=['hey, hey2,he3'],
197 component_required=['on'],
198 owner_defaults_to_member=['no'],
199 add_approvals = ['on'],
200 phase_0=['Canary'],
201 phase_1=['Stable-Exp'],
202 phase_2=['Stable'],
203 phase_3=[''],
204 phase_4=[''],
205 phase_5=[''],
206 approval_2=['phase_1'],
207 approval_3=['phase_2']
208 )
209
210 self.mox.StubOutWithMock(self.servlet, 'PleaseCorrect')
211 self.servlet.PleaseCorrect(
212 self.mr,
213 initial_members_only=ezt.boolean(True),
214 template_name='sometemplate',
215 initial_content='TLDR',
216 initial_must_edit_summary=ezt.boolean(True),
217 initial_description='HEY WHY',
218 initial_status='Accepted',
219 initial_owner='someone@world.com',
220 initial_owner_defaults_to_member=ezt.boolean(False),
221 initial_components='hey, hey2, he3',
222 initial_component_required=ezt.boolean(True),
223 initial_admins='',
224 labels=['label-One', 'label-Two'],
225 fields=mox.IgnoreArg(),
226 initial_add_approvals=ezt.boolean(True),
227 initial_phases=[tracker_pb2.Phase(name=name) for
228 name in ['Canary', 'Stable-Exp', 'Stable', '', '', '']],
229 approvals=mox.IgnoreArg(),
230 prechecked_approvals=['2_phase_1', '3_phase_2'],
231 required_approval_ids=[]
232 )
233 self.mox.ReplayAll()
234 url = self.servlet.ProcessFormData(self.mr, post_data)
235 self.mox.VerifyAll()
236 self.assertEqual('Owner not found.', self.mr.errors.owner)
237 self.assertEqual('Unknown component he3', self.mr.errors.components)
238 self.assertEqual(
239 'Template with name sometemplate already exists', self.mr.errors.name)
240 self.assertEqual('Defined gates must have assigned approvals.',
241 self.mr.errors.phase_approvals)
242 self.assertIsNone(url)
243
244 def testProcessFormData_RejectRestrictedFields(self):
245 self.services.template.GetTemplateByName = Mock(return_value=None)
246 self.mr.perms = permissions.PermissionSet([])
247 post_data_add_fv = fake.PostData(
248 name=['secondtemplate'],
249 members_only=['on'],
250 summary=['TLDR'],
251 summary_must_be_edited=['on'],
252 content=['HEY WHY'],
253 status=['Accepted'],
254 label=['label-One', 'label-Two'],
255 custom_1=['Hey'],
256 custom_5=['7'],
257 component_required=['on'],
258 owner_defaults_to_member=['no'],
259 add_approvals=['no'],
260 phase_0=[''],
261 phase_1=[''],
262 phase_2=[''],
263 phase_3=[''],
264 phase_4=[''],
265 phase_5=['OOPs'],
266 approval_2=['phase_0'],
267 approval_3=['phase_2'])
268 post_data_label_edits_enum = fake.PostData(
269 name=['secondtemplate'],
270 members_only=['on'],
271 summary=['TLDR'],
272 summary_must_be_edited=['on'],
273 content=['HEY WHY'],
274 status=['Accepted'],
275 label=['label-One', 'label-Two', 'RestrictedEnumField-7'],
276 component_required=['on'],
277 owner_defaults_to_member=['no'],
278 add_approvals=['no'],
279 phase_0=[''],
280 phase_1=[''],
281 phase_2=[''],
282 phase_3=[''],
283 phase_4=[''],
284 phase_5=['OOPs'],
285 approval_2=['phase_0'],
286 approval_3=['phase_2'])
287
288 self.assertRaises(
289 AssertionError, self.servlet.ProcessFormData, self.mr, post_data_add_fv)
290 self.assertRaises(
291 AssertionError, self.servlet.ProcessFormData, self.mr,
292 post_data_label_edits_enum)
293
294 def testProcessFormData_Accept(self):
295 self.services.user.TestAddUser('user@example.com', 222)
296 self.mr.auth.effective_ids = {222}
297 self.services.template.GetTemplateByName = Mock(return_value=None)
298 post_data = fake.PostData(
299 name=['secondtemplate'],
300 members_only=['on'],
301 summary=['TLDR'],
302 summary_must_be_edited=['on'],
303 content=['HEY WHY'],
304 status=['Accepted'],
305 label=['label-One', 'label-Two', 'RestrictedEnumField-7'],
306 custom_1=['NO'],
307 custom_5=['37'],
308 component_required=['on'],
309 owner_defaults_to_member=['no'],
310 add_approvals=['no'],
311 phase_0=[''],
312 phase_1=[''],
313 phase_2=[''],
314 phase_3=[''],
315 phase_4=[''],
316 phase_5=['OOPs'],
317 approval_2=['phase_0'],
318 approval_3=['phase_2'])
319
320 url = self.servlet.ProcessFormData(self.mr, post_data)
321
322 self.assertTrue('/adminTemplates?saved=1&ts' in url)
323
324 self.assertEqual(0,
325 self.services.template.UpdateIssueTemplateDef.call_count)
326
327 # errors in phases should not matter if add_approvals is not 'on'
328 self.assertIsNone(self.mr.errors.phase_approvals)
329
330 def testProcessFormData_AcceptPhases(self):
331 self.services.user.TestAddUser('user@example.com', 222)
332 self.mr.auth.effective_ids = {222}
333 self.services.template.GetTemplateByName = Mock(return_value=None)
334 post_data = fake.PostData(
335 name=['secondtemplate'],
336 members_only=['on'],
337 summary=['TLDR'],
338 summary_must_be_edited=['on'],
339 content=['HEY WHY'],
340 status=['Accepted'],
341 label=['label-One', 'label-Two'],
342 custom_1=['NO'],
343 component_required=['on'],
344 owner_defaults_to_member=['no'],
345 add_approvals = ['on'],
346 phase_0=['Canary'],
347 phase_1=['Stable'],
348 phase_2=[''],
349 phase_3=[''],
350 phase_4=[''],
351 phase_5=[''],
352 approval_2=['phase_0'],
353 approval_3=['phase_1'],
354 approval_3_required=['on']
355 )
356
357 url = self.servlet.ProcessFormData(self.mr, post_data)
358 self.assertTrue('/adminTemplates?saved=1&ts' in url)
359
360 fv = tracker_pb2.FieldValue(field_id=1, str_value='NO', derived=False)
361 phases = [
362 tracker_pb2.Phase(name='Canary', rank=0, phase_id=0),
363 tracker_pb2.Phase(name='Stable', rank=1, phase_id=1)
364 ]
365 approval_values = [
366 tracker_pb2.ApprovalValue(approval_id=2, phase_id=0),
367 tracker_pb2.ApprovalValue(
368 approval_id=3, status=tracker_pb2.ApprovalStatus(
369 tracker_pb2.ApprovalStatus.NEEDS_REVIEW), phase_id=1)
370 ]
371 self.services.template.CreateIssueTemplateDef.assert_called_once_with(
372 self.mr.cnxn, 47925, 'secondtemplate', 'HEY WHY', 'TLDR', True,
373 'Accepted', True, False, True, 0, ['label-One', 'label-Two'], [], [],
374 [fv], phases=phases, approval_values=approval_values)