blob: 00e57ab1dfb6643a3bf694b4cc047c5377ede4e8 [file] [log] [blame]
Alex Klein2b236722019-06-19 15:44:26 -06001# -*- coding: utf-8 -*-
2# Copyright 2019 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Tests for the validate module."""
7
8from __future__ import print_function
9
10import os
Mike Frysingeref94e4c2020-02-10 23:59:54 -050011import sys
Alex Klein2b236722019-06-19 15:44:26 -060012
Alex Klein69339cc2019-07-22 14:08:35 -060013from chromite.api import api_config
Alex Klein2b236722019-06-19 15:44:26 -060014from chromite.api import validate
Alex Klein86242bf2020-09-22 15:23:23 -060015from chromite.api.gen.chromite.api import build_api_test_pb2
Alex Klein2b236722019-06-19 15:44:26 -060016from chromite.api.gen.chromiumos import common_pb2
17from chromite.lib import cros_build_lib
18from chromite.lib import cros_test_lib
19from chromite.lib import osutils
20
21
Mike Frysingeref94e4c2020-02-10 23:59:54 -050022assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
23
24
Alex Klein2008aee2019-08-20 16:25:27 -060025class ExistsTest(cros_test_lib.TempDirTestCase, api_config.ApiConfigMixin):
Alex Klein2b236722019-06-19 15:44:26 -060026 """Tests for the exists validator."""
27
28 def test_not_exists(self):
29 """Test the validator fails when given a path that doesn't exist."""
30 path = os.path.join(self.tempdir, 'DOES_NOT_EXIST')
31
32 @validate.exists('path')
Alex Klein2008aee2019-08-20 16:25:27 -060033 def impl(_input_proto, _output_proto, _config):
Alex Klein2b236722019-06-19 15:44:26 -060034 self.fail('Incorrectly allowed method to execute.')
35
36 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein2008aee2019-08-20 16:25:27 -060037 impl(common_pb2.Chroot(path=path), None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -060038
39 def test_exists(self):
40 """Test the validator fails when given a path that doesn't exist."""
41 path = os.path.join(self.tempdir, 'chroot')
42 osutils.SafeMakedirs(path)
43
44 @validate.exists('path')
Alex Klein2008aee2019-08-20 16:25:27 -060045 def impl(_input_proto, _output_proto, _config):
Alex Klein2b236722019-06-19 15:44:26 -060046 pass
47
Alex Klein2008aee2019-08-20 16:25:27 -060048 impl(common_pb2.Chroot(path=path), None, self.api_config)
49
50 def test_skip_validation(self):
51 """Test skipping validation case."""
52 @validate.exists('path')
53 def impl(_input_proto, _output_proto, _config):
54 pass
55
56 # This would otherwise raise an error for an invalid path.
57 impl(common_pb2.Chroot(), None, self.no_validate_config)
Alex Klein2b236722019-06-19 15:44:26 -060058
59
Alex Klein2008aee2019-08-20 16:25:27 -060060class IsInTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein231d2da2019-07-22 16:44:45 -060061 """Tests for the is_in validator."""
62
63 def test_in(self):
64 """Test a valid value."""
65 @validate.is_in('path', ['/chroot/path', '/other/chroot/path'])
Alex Klein2008aee2019-08-20 16:25:27 -060066 def impl(_input_proto, _output_proto, _config):
Alex Klein231d2da2019-07-22 16:44:45 -060067 pass
68
69 # Make sure all of the values work.
Alex Klein2008aee2019-08-20 16:25:27 -060070 impl(common_pb2.Chroot(path='/chroot/path'), None, self.api_config)
71 impl(common_pb2.Chroot(path='/other/chroot/path'), None, self.api_config)
Alex Klein231d2da2019-07-22 16:44:45 -060072
73 def test_not_in(self):
74 """Test an invalid value."""
75 @validate.is_in('path', ['/chroot/path', '/other/chroot/path'])
Alex Klein2008aee2019-08-20 16:25:27 -060076 def impl(_input_proto, _output_proto, _config):
Alex Klein231d2da2019-07-22 16:44:45 -060077 pass
78
79 # Should be failing on the invalid value.
80 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein2008aee2019-08-20 16:25:27 -060081 impl(common_pb2.Chroot(path='/bad/value'), None, self.api_config)
Alex Klein231d2da2019-07-22 16:44:45 -060082
83 def test_not_set(self):
84 """Test an unset value."""
85 @validate.is_in('path', ['/chroot/path', '/other/chroot/path'])
Alex Klein2008aee2019-08-20 16:25:27 -060086 def impl(_input_proto, _output_proto, _config):
Alex Klein231d2da2019-07-22 16:44:45 -060087 pass
88
89 # Should be failing without a value set.
90 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein2008aee2019-08-20 16:25:27 -060091 impl(common_pb2.Chroot(), None, self.api_config)
92
93 def test_skip_validation(self):
94 """Test skipping validation case."""
95 @validate.is_in('path', ['/chroot/path', '/other/chroot/path'])
96 def impl(_input_proto, _output_proto, _config):
97 pass
98
99 # This would otherwise raise an error for an invalid path.
100 impl(common_pb2.Chroot(), None, self.no_validate_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600101
102
Alex Klein60c80522020-10-13 18:05:38 -0600103class RequireTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
104 """Tests for the require validator."""
Alex Klein2b236722019-06-19 15:44:26 -0600105
106 def test_invalid_field(self):
107 """Test validator fails when given an unset value."""
108
109 @validate.require('does.not.exist')
Alex Klein2008aee2019-08-20 16:25:27 -0600110 def impl(_input_proto, _output_proto, _config):
Alex Klein2b236722019-06-19 15:44:26 -0600111 self.fail('Incorrectly allowed method to execute.')
112
113 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein2008aee2019-08-20 16:25:27 -0600114 impl(common_pb2.Chroot(), None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -0600115
116 def test_not_set(self):
117 """Test validator fails when given an unset value."""
118
119 @validate.require('env.use_flags')
Alex Klein2008aee2019-08-20 16:25:27 -0600120 def impl(_input_proto, _output_proto, _config):
Alex Klein2b236722019-06-19 15:44:26 -0600121 self.fail('Incorrectly allowed method to execute.')
122
123 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein2008aee2019-08-20 16:25:27 -0600124 impl(common_pb2.Chroot(), None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -0600125
126 def test_set(self):
127 """Test validator passes when given set values."""
128
129 @validate.require('path', 'env.use_flags')
Alex Klein2008aee2019-08-20 16:25:27 -0600130 def impl(_input_proto, _output_proto, _config):
Alex Klein2b236722019-06-19 15:44:26 -0600131 pass
132
Alex Klein2008aee2019-08-20 16:25:27 -0600133 in_proto = common_pb2.Chroot(path='/chroot/path',
134 env={'use_flags': [{'flag': 'test'}]})
135 impl(in_proto, None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -0600136
137 def test_mixed(self):
Alex Klein60c80522020-10-13 18:05:38 -0600138 """Test validator fails when given a set value and an unset value."""
Alex Klein2b236722019-06-19 15:44:26 -0600139
140 @validate.require('path', 'env.use_flags')
Alex Klein2008aee2019-08-20 16:25:27 -0600141 def impl(_input_proto, _output_proto, _config):
Alex Klein2b236722019-06-19 15:44:26 -0600142 pass
143
144 with self.assertRaises(cros_build_lib.DieSystemExit):
Alex Klein2008aee2019-08-20 16:25:27 -0600145 impl(common_pb2.Chroot(path='/chroot/path'), None, self.api_config)
146
147 def test_skip_validation(self):
148 """Test skipping validation case."""
149 @validate.require('path', 'env.use_flags')
150 def impl(_input_proto, _output_proto, _config):
151 pass
152
153 # This would otherwise raise an error for an invalid path.
154 impl(common_pb2.Chroot(), None, self.no_validate_config)
Alex Klein69339cc2019-07-22 14:08:35 -0600155
156
Alex Klein60c80522020-10-13 18:05:38 -0600157class RequireAnyTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
158 """Tests for the require_any validator."""
159
160 def _get_request(self, mid: int = None, name: str = None, flag: bool = None):
161 """Build a request instance from the given data."""
162 request = build_api_test_pb2.MultiFieldMessage()
163
164 if mid:
165 request.id = mid
166 if name:
167 request.name = name
168 if flag:
169 request.flag = flag
170
171 return request
172
173 def test_invalid_field(self):
174 """Test validator fails when given an invalid field."""
175
176 @validate.require_any('does.not.exist', 'also.invalid')
177 def impl(_input_proto, _output_proto, _config):
178 self.fail('Incorrectly allowed method to execute.')
179
180 with self.assertRaises(cros_build_lib.DieSystemExit):
181 impl(self._get_request(), None, self.api_config)
182
183 def test_not_set(self):
184 """Test validator fails when given unset values."""
185
186 @validate.require_any('id', 'name')
187 def impl(_input_proto, _output_proto, _config):
188 self.fail('Incorrectly allowed method to execute.')
189
190 with self.assertRaises(cros_build_lib.DieSystemExit):
191 impl(self._get_request(flag=True), None, self.api_config)
192
193 def test_set(self):
194 """Test validator passes when given set values."""
195
196 @validate.require_any('id', 'name')
197 def impl(_input_proto, _output_proto, _config):
198 pass
199
200 impl(self._get_request(1), None, self.api_config)
201 impl(self._get_request(name='foo'), None, self.api_config)
202 impl(self._get_request(1, name='foo'), None, self.api_config)
203
204
Alex Klein86242bf2020-09-22 15:23:23 -0600205class RequireEachTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
206 """Tests for the require_each validator."""
207
208 def _multi_field_message(self, msg_id=None, name=None, flag=None):
209 msg = build_api_test_pb2.MultiFieldMessage()
210 if msg_id is not None:
211 msg.id = int(msg_id)
212 if name is not None:
213 msg.name = str(name)
214 if flag is not None:
215 msg.flag = bool(flag)
216 return msg
217
218 def _request(self, messages=None, count=0):
219 """Build the request."""
220 if messages is None:
221 messages = [self._multi_field_message() for _ in range(count)]
222
223 request = build_api_test_pb2.TestRequestMessage()
224 for message in messages:
225 msg = request.messages.add()
226 msg.CopyFrom(message)
227
228 return request
229
230 def test_invalid_field(self):
231 """Test validator fails when given an invalid field."""
232
233 @validate.require_each('does.not', ['exist'])
234 def impl(_input_proto, _output_proto, _config):
235 self.fail('Incorrectly allowed method to execute.')
236
237 with self.assertRaises(cros_build_lib.DieSystemExit):
238 impl(self._request(), None, self.api_config)
239
240 def test_invalid_call_no_subfields(self):
241 """Test validator fails when given no subfields."""
242
243 with self.assertRaises(AssertionError):
244 @validate.require_each('does.not', [])
245 def _(_input_proto, _output_proto, _config):
246 pass
247
248 def test_invalid_call_invalid_subfields(self):
249 """Test validator fails when given subfields incorrectly."""
250
251 with self.assertRaises(AssertionError):
252 @validate.require_each('does.not', 'exist')
253 def _(_input_proto, _output_proto, _config):
254 pass
255
256 def test_not_set(self):
257 """Test validator fails when given an unset value."""
258
259 @validate.require_each('messages', ['id'])
260 def impl(_input_proto, _output_proto, _config):
261 self.fail('Incorrectly allowed method to execute.')
262
263 with self.assertRaises(cros_build_lib.DieSystemExit):
264 impl(self._request(count=2), None, self.api_config)
265
266 def test_no_elements_success(self):
267 """Test validator fails when given no messages in the repeated field."""
268
269 @validate.require_each('messages', ['id'])
270 def impl(_input_proto, _output_proto, _config):
271 pass
272
273 impl(self._request(), None, self.api_config)
274
275 def test_no_elements_failure(self):
276 """Test validator fails when given no messages in the repeated field."""
277
278 @validate.require_each('messages', ['id'], allow_empty=False)
279 def impl(_input_proto, _output_proto, _config):
280 self.fail('Incorrectly allowed method to execute.')
281
282 with self.assertRaises(cros_build_lib.DieSystemExit):
283 impl(self._request(), None, self.api_config)
284
285 def test_set(self):
286 """Test validator passes when given set values."""
287
288 @validate.require_each('messages', ['id'])
289 def impl(_input_proto, _output_proto, _config):
290 pass
291
292 messages = [self._multi_field_message(msg_id=i) for i in range(1, 5)]
293 impl(self._request(messages=messages), None, self.api_config)
294
295 def test_one_set_fails(self):
296 """Test validator passes when given set values."""
297
298 @validate.require_each('messages', ['id', 'name'])
299 def impl(_input_proto, _output_proto, _config):
300 pass
301
302 messages = [self._multi_field_message(msg_id=i) for i in range(1, 5)]
303 with self.assertRaises(cros_build_lib.DieSystemExit):
304 impl(self._request(messages=messages), None, self.api_config)
305
306 def test_multi_set(self):
307 """Test validator passes when all values set."""
308
309 @validate.require_each('messages', ['id', 'name'])
310 def impl(_input_proto, _output_proto, _config):
311 pass
312
313 messages = [self._multi_field_message(msg_id=i, name=i)
314 for i in range(1, 5)]
315 impl(self._request(messages=messages), None, self.api_config)
316
317 def test_skip_validation(self):
318 """Test skipping validation case."""
319 @validate.require_each('messages', ['id'], allow_empty=False)
320 def impl(_input_proto, _output_proto, _config):
321 pass
322
323 impl(self._request(), None, self.no_validate_config)
324
325
Alex Klein69339cc2019-07-22 14:08:35 -0600326class ValidateOnlyTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
327 """validate_only decorator tests."""
328
329 def test_validate_only(self):
330 """Test validate only."""
331 @validate.require('path')
332 @validate.validation_complete
333 def impl(_input_proto, _output_proto, _config):
334 self.fail('Implementation was called.')
335 return 1
336
337 # Just using arbitrary messages, we just need the
338 # (request, response, config) arguments so it can check the config.
339 rc = impl(common_pb2.Chroot(path='/chroot/path'), common_pb2.Chroot(),
340 self.validate_only_config)
341
342 self.assertEqual(0, rc)
343
344 def test_no_validate_only(self):
345 """Test no use of validate only."""
346 @validate.validation_complete
347 def impl(_input_proto, _output_proto, _config):
Alex Klein2008aee2019-08-20 16:25:27 -0600348 self.fail('Incorrectly allowed method to execute.')
Alex Klein69339cc2019-07-22 14:08:35 -0600349
350 # We will get an assertion error unless validate_only prevents the function
351 # from being called.
352 with self.assertRaises(AssertionError):
353 impl(common_pb2.Chroot(), common_pb2.Chroot(), self.api_config)