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