blob: e42b74056e7cd8f330e4ada8d214e58e78e0e4b9 [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 Klein2008aee2019-08-20 16:25:27 -0600103class RequiredTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein2b236722019-06-19 15:44:26 -0600104 """Tests for the required validator."""
105
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):
138 """Test validator passes when given a set value."""
139
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 Klein86242bf2020-09-22 15:23:23 -0600157class RequireEachTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
158 """Tests for the require_each validator."""
159
160 def _multi_field_message(self, msg_id=None, name=None, flag=None):
161 msg = build_api_test_pb2.MultiFieldMessage()
162 if msg_id is not None:
163 msg.id = int(msg_id)
164 if name is not None:
165 msg.name = str(name)
166 if flag is not None:
167 msg.flag = bool(flag)
168 return msg
169
170 def _request(self, messages=None, count=0):
171 """Build the request."""
172 if messages is None:
173 messages = [self._multi_field_message() for _ in range(count)]
174
175 request = build_api_test_pb2.TestRequestMessage()
176 for message in messages:
177 msg = request.messages.add()
178 msg.CopyFrom(message)
179
180 return request
181
182 def test_invalid_field(self):
183 """Test validator fails when given an invalid field."""
184
185 @validate.require_each('does.not', ['exist'])
186 def impl(_input_proto, _output_proto, _config):
187 self.fail('Incorrectly allowed method to execute.')
188
189 with self.assertRaises(cros_build_lib.DieSystemExit):
190 impl(self._request(), None, self.api_config)
191
192 def test_invalid_call_no_subfields(self):
193 """Test validator fails when given no subfields."""
194
195 with self.assertRaises(AssertionError):
196 @validate.require_each('does.not', [])
197 def _(_input_proto, _output_proto, _config):
198 pass
199
200 def test_invalid_call_invalid_subfields(self):
201 """Test validator fails when given subfields incorrectly."""
202
203 with self.assertRaises(AssertionError):
204 @validate.require_each('does.not', 'exist')
205 def _(_input_proto, _output_proto, _config):
206 pass
207
208 def test_not_set(self):
209 """Test validator fails when given an unset value."""
210
211 @validate.require_each('messages', ['id'])
212 def impl(_input_proto, _output_proto, _config):
213 self.fail('Incorrectly allowed method to execute.')
214
215 with self.assertRaises(cros_build_lib.DieSystemExit):
216 impl(self._request(count=2), None, self.api_config)
217
218 def test_no_elements_success(self):
219 """Test validator fails when given no messages in the repeated field."""
220
221 @validate.require_each('messages', ['id'])
222 def impl(_input_proto, _output_proto, _config):
223 pass
224
225 impl(self._request(), None, self.api_config)
226
227 def test_no_elements_failure(self):
228 """Test validator fails when given no messages in the repeated field."""
229
230 @validate.require_each('messages', ['id'], allow_empty=False)
231 def impl(_input_proto, _output_proto, _config):
232 self.fail('Incorrectly allowed method to execute.')
233
234 with self.assertRaises(cros_build_lib.DieSystemExit):
235 impl(self._request(), None, self.api_config)
236
237 def test_set(self):
238 """Test validator passes when given set values."""
239
240 @validate.require_each('messages', ['id'])
241 def impl(_input_proto, _output_proto, _config):
242 pass
243
244 messages = [self._multi_field_message(msg_id=i) for i in range(1, 5)]
245 impl(self._request(messages=messages), None, self.api_config)
246
247 def test_one_set_fails(self):
248 """Test validator passes when given set values."""
249
250 @validate.require_each('messages', ['id', 'name'])
251 def impl(_input_proto, _output_proto, _config):
252 pass
253
254 messages = [self._multi_field_message(msg_id=i) for i in range(1, 5)]
255 with self.assertRaises(cros_build_lib.DieSystemExit):
256 impl(self._request(messages=messages), None, self.api_config)
257
258 def test_multi_set(self):
259 """Test validator passes when all values set."""
260
261 @validate.require_each('messages', ['id', 'name'])
262 def impl(_input_proto, _output_proto, _config):
263 pass
264
265 messages = [self._multi_field_message(msg_id=i, name=i)
266 for i in range(1, 5)]
267 impl(self._request(messages=messages), None, self.api_config)
268
269 def test_skip_validation(self):
270 """Test skipping validation case."""
271 @validate.require_each('messages', ['id'], allow_empty=False)
272 def impl(_input_proto, _output_proto, _config):
273 pass
274
275 impl(self._request(), None, self.no_validate_config)
276
277
Alex Klein69339cc2019-07-22 14:08:35 -0600278class ValidateOnlyTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
279 """validate_only decorator tests."""
280
281 def test_validate_only(self):
282 """Test validate only."""
283 @validate.require('path')
284 @validate.validation_complete
285 def impl(_input_proto, _output_proto, _config):
286 self.fail('Implementation was called.')
287 return 1
288
289 # Just using arbitrary messages, we just need the
290 # (request, response, config) arguments so it can check the config.
291 rc = impl(common_pb2.Chroot(path='/chroot/path'), common_pb2.Chroot(),
292 self.validate_only_config)
293
294 self.assertEqual(0, rc)
295
296 def test_no_validate_only(self):
297 """Test no use of validate only."""
298 @validate.validation_complete
299 def impl(_input_proto, _output_proto, _config):
Alex Klein2008aee2019-08-20 16:25:27 -0600300 self.fail('Incorrectly allowed method to execute.')
Alex Klein69339cc2019-07-22 14:08:35 -0600301
302 # We will get an assertion error unless validate_only prevents the function
303 # from being called.
304 with self.assertRaises(AssertionError):
305 impl(common_pb2.Chroot(), common_pb2.Chroot(), self.api_config)