blob: 09a61a1e54dc33ce83751a87ebdb1f0256b67699 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2019 The ChromiumOS Authors
Alex Klein2b236722019-06-19 15:44:26 -06002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Tests for the validate module."""
6
Alex Klein2b236722019-06-19 15:44:26 -06007import os
8
Alex Klein69339cc2019-07-22 14:08:35 -06009from chromite.api import api_config
Alex Klein2b236722019-06-19 15:44:26 -060010from chromite.api import validate
Alex Klein86242bf2020-09-22 15:23:23 -060011from chromite.api.gen.chromite.api import build_api_test_pb2
Alex Klein2b236722019-06-19 15:44:26 -060012from chromite.api.gen.chromiumos import common_pb2
13from chromite.lib import cros_build_lib
14from chromite.lib import cros_test_lib
15from chromite.lib import osutils
16
Mike Frysingeref94e4c2020-02-10 23:59:54 -050017
Alex Kleinbdace302020-12-03 14:40:23 -070018# These tests test the validators by defining a local `impl` function that
19# has the same parameters as a controller function and the validator being
20# tested. The validators don't care that they aren't actually controller
21# functions, they just need the function to look like one, so it works
22# to pass an arbitrary message; i.e. passing one of the Request messages
23# we'd usually expect in a controller is not required. The validator
24# just needs to be checking one of the fields on the message being used.
Alex Klein2008aee2019-08-20 16:25:27 -060025class ExistsTest(cros_test_lib.TempDirTestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -060026 """Tests for the exists validator."""
Alex Klein2b236722019-06-19 15:44:26 -060027
Alex Klein1699fab2022-09-08 08:46:06 -060028 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")
Alex Klein2b236722019-06-19 15:44:26 -060031
Alex Klein1699fab2022-09-08 08:46:06 -060032 @validate.exists("path")
33 def impl(_input_proto, _output_proto, _config):
34 self.fail("Incorrectly allowed method to execute.")
Alex Klein2b236722019-06-19 15:44:26 -060035
Alex Klein1699fab2022-09-08 08:46:06 -060036 with self.assertRaises(cros_build_lib.DieSystemExit):
37 impl(common_pb2.Chroot(path=path), None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -060038
Alex Klein1699fab2022-09-08 08:46:06 -060039 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)
Alex Klein2b236722019-06-19 15:44:26 -060043
Alex Klein1699fab2022-09-08 08:46:06 -060044 @validate.exists("path")
45 def impl(_input_proto, _output_proto, _config):
46 pass
Alex Klein2b236722019-06-19 15:44:26 -060047
Alex Klein1699fab2022-09-08 08:46:06 -060048 impl(common_pb2.Chroot(path=path), None, self.api_config)
Alex Klein2008aee2019-08-20 16:25:27 -060049
Alex Klein1699fab2022-09-08 08:46:06 -060050 def test_skip_validation(self):
51 """Test skipping validation case."""
Alex Klein2008aee2019-08-20 16:25:27 -060052
Alex Klein1699fab2022-09-08 08:46:06 -060053 @validate.exists("path")
54 def impl(_input_proto, _output_proto, _config):
55 pass
56
57 # This would otherwise raise an error for an invalid path.
58 impl(common_pb2.Chroot(), None, self.no_validate_config)
Alex Klein2b236722019-06-19 15:44:26 -060059
60
Greg Edelston53d34a12023-03-27 15:43:53 -060061class EqTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
62 """Tests for the eq validator."""
63
64 def test_eq(self):
65 """Test a valid value."""
66
67 @validate.eq("location", common_pb2.Path.Location.OUTSIDE)
68 def impl(_input_proto, _output_proto, _config):
69 pass
70
71 impl(
72 common_pb2.Path(
73 path="/", location=common_pb2.Path.Location.OUTSIDE
74 ),
75 None,
76 self.api_config,
77 )
78
79 def test_not_eq(self):
80 """Test an invalid value."""
81
82 @validate.eq("location", common_pb2.Path.Location.OUTSIDE)
83 def impl(_input_proto, _output_proto, _config):
84 pass
85
86 # Should be failing on the invalid value.
87 with self.assertRaises(cros_build_lib.DieSystemExit):
88 impl(
89 common_pb2.Path(
90 path="/", location=common_pb2.Path.Location.INSIDE
91 ),
92 None,
93 self.api_config,
94 )
95
96 def test_not_set(self):
97 """Test an unset value."""
98
99 @validate.eq("location", common_pb2.Path.Location.OUTSIDE)
100 def impl(_input_proto, _output_proto, _config):
101 pass
102
103 # Should be failing without a value set.
104 with self.assertRaises(cros_build_lib.DieSystemExit):
105 impl(common_pb2.Path(path="/"), None, self.api_config)
106
107 def test_skip_validation(self):
108 """Test skipping validation case."""
109
110 @validate.eq("location", common_pb2.Path.Location.OUTSIDE)
111 def impl(_input_proto, _output_proto, _config):
112 pass
113
114 # This would otherwise raise an error for an invalid path.
115 impl(common_pb2.Path(path="/"), None, self.no_validate_config)
116
117
Alex Klein2008aee2019-08-20 16:25:27 -0600118class IsInTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600119 """Tests for the is_in validator."""
Alex Klein231d2da2019-07-22 16:44:45 -0600120
Alex Klein1699fab2022-09-08 08:46:06 -0600121 def test_in(self):
122 """Test a valid value."""
Alex Klein231d2da2019-07-22 16:44:45 -0600123
Alex Klein1699fab2022-09-08 08:46:06 -0600124 @validate.is_in("path", ["/chroot/path", "/other/chroot/path"])
125 def impl(_input_proto, _output_proto, _config):
126 pass
Alex Klein231d2da2019-07-22 16:44:45 -0600127
Alex Klein1699fab2022-09-08 08:46:06 -0600128 # Make sure all of the values work.
129 impl(common_pb2.Chroot(path="/chroot/path"), None, self.api_config)
130 impl(
131 common_pb2.Chroot(path="/other/chroot/path"), None, self.api_config
132 )
Alex Klein231d2da2019-07-22 16:44:45 -0600133
Alex Klein1699fab2022-09-08 08:46:06 -0600134 def test_not_in(self):
135 """Test an invalid value."""
Alex Klein231d2da2019-07-22 16:44:45 -0600136
Alex Klein1699fab2022-09-08 08:46:06 -0600137 @validate.is_in("path", ["/chroot/path", "/other/chroot/path"])
138 def impl(_input_proto, _output_proto, _config):
139 pass
Alex Klein231d2da2019-07-22 16:44:45 -0600140
Alex Klein1699fab2022-09-08 08:46:06 -0600141 # Should be failing on the invalid value.
142 with self.assertRaises(cros_build_lib.DieSystemExit):
143 impl(common_pb2.Chroot(path="/bad/value"), None, self.api_config)
Alex Klein2008aee2019-08-20 16:25:27 -0600144
Alex Klein1699fab2022-09-08 08:46:06 -0600145 def test_not_set(self):
146 """Test an unset value."""
Alex Klein2008aee2019-08-20 16:25:27 -0600147
Alex Klein1699fab2022-09-08 08:46:06 -0600148 @validate.is_in("path", ["/chroot/path", "/other/chroot/path"])
149 def impl(_input_proto, _output_proto, _config):
150 pass
151
152 # Should be failing without a value set.
153 with self.assertRaises(cros_build_lib.DieSystemExit):
154 impl(common_pb2.Chroot(), None, self.api_config)
155
156 def test_skip_validation(self):
157 """Test skipping validation case."""
158
159 @validate.is_in("path", ["/chroot/path", "/other/chroot/path"])
160 def impl(_input_proto, _output_proto, _config):
161 pass
162
163 # This would otherwise raise an error for an invalid path.
164 impl(common_pb2.Chroot(), None, self.no_validate_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600165
166
Alex Kleinbdace302020-12-03 14:40:23 -0700167class EachInTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600168 """Tests for the each_in validator."""
Alex Kleinbdace302020-12-03 14:40:23 -0700169
Alex Klein1699fab2022-09-08 08:46:06 -0600170 # Easier access to the enum values.
171 ENUM_FOO = build_api_test_pb2.TEST_ENUM_FOO
172 ENUM_BAR = build_api_test_pb2.TEST_ENUM_BAR
173 ENUM_BAZ = build_api_test_pb2.TEST_ENUM_BAZ
Alex Kleinbdace302020-12-03 14:40:23 -0700174
Alex Klein1699fab2022-09-08 08:46:06 -0600175 # pylint: disable=docstring-misnamed-args
176 def _message_request(self, *messages):
177 """Build a request instance, filling out the messages field.
Alex Kleinbdace302020-12-03 14:40:23 -0700178
Alex Klein1699fab2022-09-08 08:46:06 -0600179 Args:
Alex Kleina0442682022-10-10 13:47:38 -0600180 messages: Each messages data (id, name, flag, enum) as lists. Only
181 requires as many as are set. e.g. _request([1], [2]) will create
182 two messages with only ids set. _request([1, 'name']) will
183 create one with id and name set, but not flag or enum.
Alex Klein1699fab2022-09-08 08:46:06 -0600184 """
185 request = build_api_test_pb2.TestRequestMessage()
186 for message in messages or []:
187 msg = request.messages.add()
188 try:
189 msg.id = message[0]
190 msg.name = message[1]
191 msg.flag = message[2]
192 except IndexError:
193 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700194
Alex Klein1699fab2022-09-08 08:46:06 -0600195 return request
Alex Kleinbdace302020-12-03 14:40:23 -0700196
Alex Klein1699fab2022-09-08 08:46:06 -0600197 def _enums_request(self, *enum_values):
198 """Build a request instance, setting the test_enums field."""
199 request = build_api_test_pb2.TestRequestMessage()
200 for value in enum_values:
201 request.test_enums.append(value)
Alex Kleinbdace302020-12-03 14:40:23 -0700202
Alex Klein1699fab2022-09-08 08:46:06 -0600203 return request
Alex Kleinbdace302020-12-03 14:40:23 -0700204
Alex Klein1699fab2022-09-08 08:46:06 -0600205 def _numbers_request(self, *numbers):
206 """Build a request instance, setting the numbers field."""
207 request = build_api_test_pb2.TestRequestMessage()
208 request.numbers.extend(numbers)
Alex Kleinbdace302020-12-03 14:40:23 -0700209
Alex Klein1699fab2022-09-08 08:46:06 -0600210 return request
Alex Kleinbdace302020-12-03 14:40:23 -0700211
Alex Klein1699fab2022-09-08 08:46:06 -0600212 def test_message_in(self):
213 """Test valid values."""
Alex Kleinbdace302020-12-03 14:40:23 -0700214
Alex Klein1699fab2022-09-08 08:46:06 -0600215 @validate.each_in("messages", "name", ["foo", "bar"])
216 def impl(_input_proto, _output_proto, _config):
217 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700218
Alex Klein1699fab2022-09-08 08:46:06 -0600219 impl(self._message_request([1, "foo"]), None, self.api_config)
220 impl(
221 self._message_request([1, "foo"], [2, "bar"]), None, self.api_config
222 )
Alex Kleinbdace302020-12-03 14:40:23 -0700223
Alex Klein1699fab2022-09-08 08:46:06 -0600224 def test_enum_in(self):
225 """Test valid enum values."""
Alex Kleinbdace302020-12-03 14:40:23 -0700226
Alex Klein1699fab2022-09-08 08:46:06 -0600227 @validate.each_in("test_enums", None, [self.ENUM_FOO, self.ENUM_BAR])
228 def impl(_input_proto, _output_proto, _config):
229 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700230
Alex Klein1699fab2022-09-08 08:46:06 -0600231 impl(self._enums_request(self.ENUM_FOO), None, self.api_config)
232 impl(
233 self._enums_request(self.ENUM_FOO, self.ENUM_BAR),
234 None,
235 self.api_config,
236 )
Alex Kleinbdace302020-12-03 14:40:23 -0700237
Alex Klein1699fab2022-09-08 08:46:06 -0600238 def test_scalar_in(self):
239 """Test valid scalar values."""
Alex Kleinbdace302020-12-03 14:40:23 -0700240
Alex Klein1699fab2022-09-08 08:46:06 -0600241 @validate.each_in("numbers", None, [1, 2])
242 def impl(_input_proto, _output_proto, _config):
243 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700244
Alex Klein1699fab2022-09-08 08:46:06 -0600245 impl(self._numbers_request(1), None, self.api_config)
246 impl(self._numbers_request(1, 2), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700247
Alex Klein1699fab2022-09-08 08:46:06 -0600248 def test_message_not_in(self):
249 """Test an invalid value."""
Alex Kleinbdace302020-12-03 14:40:23 -0700250
Alex Klein1699fab2022-09-08 08:46:06 -0600251 @validate.each_in("messages", "name", ["foo", "bar"])
252 def impl(_input_proto, _output_proto, _config):
253 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700254
Alex Klein1699fab2022-09-08 08:46:06 -0600255 # Should be failing on the invalid value.
256 with self.assertRaises(cros_build_lib.DieSystemExit):
257 impl(self._message_request([1, "invalid"]), None, self.api_config)
258 with self.assertRaises(cros_build_lib.DieSystemExit):
259 impl(
260 self._message_request([1, "invalid"], [2, "invalid"]),
261 None,
262 self.api_config,
263 )
264 with self.assertRaises(cros_build_lib.DieSystemExit):
265 impl(
266 self._message_request([1, "foo"], [2, "invalid"]),
267 None,
268 self.api_config,
269 )
Alex Kleinbdace302020-12-03 14:40:23 -0700270
Alex Klein1699fab2022-09-08 08:46:06 -0600271 def test_enum_not_in(self):
272 """Test an invalid enum value."""
Alex Kleinbdace302020-12-03 14:40:23 -0700273
Alex Klein1699fab2022-09-08 08:46:06 -0600274 @validate.each_in("test_enums", None, [self.ENUM_FOO, self.ENUM_BAR])
275 def impl(_input_proto, _output_proto, _config):
276 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700277
Alex Klein1699fab2022-09-08 08:46:06 -0600278 # Only invalid values.
279 with self.assertRaises(cros_build_lib.DieSystemExit):
280 impl(self._enums_request(self.ENUM_BAZ), None, self.api_config)
281 # Mixed valid/invalid values.
282 with self.assertRaises(cros_build_lib.DieSystemExit):
283 impl(
284 self._enums_request(self.ENUM_FOO, self.ENUM_BAZ),
285 None,
286 self.api_config,
287 )
Alex Kleinbdace302020-12-03 14:40:23 -0700288
Alex Klein1699fab2022-09-08 08:46:06 -0600289 def test_scalar_not_in(self):
290 """Test invalid scalar value."""
Alex Kleinbdace302020-12-03 14:40:23 -0700291
Alex Klein1699fab2022-09-08 08:46:06 -0600292 @validate.each_in("numbers", None, [1, 2])
293 def impl(_input_proto, _output_proto, _config):
294 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700295
Alex Klein1699fab2022-09-08 08:46:06 -0600296 # Only invalid values.
297 with self.assertRaises(cros_build_lib.DieSystemExit):
298 impl(self._numbers_request(3), None, self.api_config)
299 # Mixed valid/invalid values.
300 with self.assertRaises(cros_build_lib.DieSystemExit):
301 impl(self._numbers_request(1, 2, 3), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700302
Alex Klein1699fab2022-09-08 08:46:06 -0600303 def test_not_set(self):
304 """Test an unset value."""
Alex Kleinbdace302020-12-03 14:40:23 -0700305
Alex Klein1699fab2022-09-08 08:46:06 -0600306 @validate.each_in("messages", "name", ["foo", "bar"])
307 def impl(_input_proto, _output_proto, _config):
308 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700309
Alex Klein1699fab2022-09-08 08:46:06 -0600310 # Should be failing without a value set.
311 # No entries in the field.
312 with self.assertRaises(cros_build_lib.DieSystemExit):
313 impl(self._message_request(), None, self.api_config)
314 # No value set on lone entry.
315 with self.assertRaises(cros_build_lib.DieSystemExit):
316 impl(self._message_request([1]), None, self.api_config)
317 # No value set on multiple entries.
318 with self.assertRaises(cros_build_lib.DieSystemExit):
319 impl(self._message_request([1], [2]), None, self.api_config)
320 # Some valid and some invalid entries.
321 with self.assertRaises(cros_build_lib.DieSystemExit):
322 impl(self._message_request([1, "foo"], [2]), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700323
Alex Klein1699fab2022-09-08 08:46:06 -0600324 def test_optional(self):
325 """Test optional argument."""
Alex Kleinbdace302020-12-03 14:40:23 -0700326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 @validate.each_in("messages", "name", ["foo", "bar"], optional=True)
328 @validate.each_in(
329 "test_enums", None, [self.ENUM_FOO, self.ENUM_BAR], optional=True
330 )
331 @validate.each_in("numbers", None, [1, 2], optional=True)
332 def impl(_input_proto, _output_proto, _config):
333 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700334
Alex Klein1699fab2022-09-08 08:46:06 -0600335 # No entries in the field succeeds.
336 impl(self._message_request(), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700337
Alex Klein1699fab2022-09-08 08:46:06 -0600338 # Still fails when entries exist but value unset cases.
339 # No value set on lone entry.
340 with self.assertRaises(cros_build_lib.DieSystemExit):
341 impl(self._message_request([1]), None, self.api_config)
342 # No value set on multiple entries.
343 with self.assertRaises(cros_build_lib.DieSystemExit):
344 impl(self._message_request([1], [2]), None, self.api_config)
345 # Some valid and some invalid entries.
346 with self.assertRaises(cros_build_lib.DieSystemExit):
347 impl(self._message_request([1, "foo"], [2]), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700348
Alex Klein1699fab2022-09-08 08:46:06 -0600349 def test_skip_validation(self):
350 """Test skipping validation case."""
Alex Kleinbdace302020-12-03 14:40:23 -0700351
Alex Klein1699fab2022-09-08 08:46:06 -0600352 @validate.each_in("messages", "name", ["foo", "bar"])
353 @validate.each_in("test_enums", None, [self.ENUM_FOO, self.ENUM_BAR])
354 @validate.each_in("numbers", None, [1, 2])
355 def impl(_input_proto, _output_proto, _config):
356 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700357
Alex Klein1699fab2022-09-08 08:46:06 -0600358 # This would otherwise raise an error for multiple invalid fields.
359 impl(
360 self._message_request([1, "invalid"]), None, self.no_validate_config
361 )
Alex Kleinbdace302020-12-03 14:40:23 -0700362
363
Alex Klein60c80522020-10-13 18:05:38 -0600364class RequireTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600365 """Tests for the require validator."""
Alex Klein2b236722019-06-19 15:44:26 -0600366
Alex Klein1699fab2022-09-08 08:46:06 -0600367 def test_invalid_field(self):
368 """Test validator fails when given an unset value."""
Alex Klein2b236722019-06-19 15:44:26 -0600369
Alex Klein1699fab2022-09-08 08:46:06 -0600370 @validate.require("does.not.exist")
371 def impl(_input_proto, _output_proto, _config):
372 self.fail("Incorrectly allowed method to execute.")
Alex Klein2b236722019-06-19 15:44:26 -0600373
Alex Klein1699fab2022-09-08 08:46:06 -0600374 with self.assertRaises(cros_build_lib.DieSystemExit):
375 impl(common_pb2.Chroot(), None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -0600376
Alex Klein1699fab2022-09-08 08:46:06 -0600377 def test_not_set(self):
378 """Test validator fails when given an unset value."""
Alex Klein2b236722019-06-19 15:44:26 -0600379
Alex Klein1699fab2022-09-08 08:46:06 -0600380 @validate.require("env.use_flags")
381 def impl(_input_proto, _output_proto, _config):
382 self.fail("Incorrectly allowed method to execute.")
Alex Klein2b236722019-06-19 15:44:26 -0600383
Alex Klein1699fab2022-09-08 08:46:06 -0600384 with self.assertRaises(cros_build_lib.DieSystemExit):
385 impl(common_pb2.Chroot(), None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -0600386
Alex Klein1699fab2022-09-08 08:46:06 -0600387 def test_set(self):
388 """Test validator passes when given set values."""
Alex Klein2b236722019-06-19 15:44:26 -0600389
Alex Klein1699fab2022-09-08 08:46:06 -0600390 @validate.require("path", "env.use_flags")
391 def impl(_input_proto, _output_proto, _config):
392 pass
Alex Klein2b236722019-06-19 15:44:26 -0600393
Alex Klein1699fab2022-09-08 08:46:06 -0600394 in_proto = common_pb2.Chroot(
395 path="/chroot/path", env={"use_flags": [{"flag": "test"}]}
396 )
397 impl(in_proto, None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -0600398
Alex Klein1699fab2022-09-08 08:46:06 -0600399 def test_mixed(self):
400 """Test validator fails when given a set value and an unset value."""
Alex Klein2b236722019-06-19 15:44:26 -0600401
Alex Klein1699fab2022-09-08 08:46:06 -0600402 @validate.require("path", "env.use_flags")
403 def impl(_input_proto, _output_proto, _config):
404 pass
Alex Klein2b236722019-06-19 15:44:26 -0600405
Alex Klein1699fab2022-09-08 08:46:06 -0600406 with self.assertRaises(cros_build_lib.DieSystemExit):
407 impl(common_pb2.Chroot(path="/chroot/path"), None, self.api_config)
Alex Klein2008aee2019-08-20 16:25:27 -0600408
Alex Klein1699fab2022-09-08 08:46:06 -0600409 def test_skip_validation(self):
410 """Test skipping validation case."""
Alex Klein2008aee2019-08-20 16:25:27 -0600411
Alex Klein1699fab2022-09-08 08:46:06 -0600412 @validate.require("path", "env.use_flags")
413 def impl(_input_proto, _output_proto, _config):
414 pass
415
416 # This would otherwise raise an error for an invalid path.
417 impl(common_pb2.Chroot(), None, self.no_validate_config)
Alex Klein69339cc2019-07-22 14:08:35 -0600418
419
Alex Klein60c80522020-10-13 18:05:38 -0600420class RequireAnyTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600421 """Tests for the require_any validator."""
Alex Klein60c80522020-10-13 18:05:38 -0600422
Alex Klein1699fab2022-09-08 08:46:06 -0600423 def _get_request(
424 self, mid: int = None, name: str = None, flag: bool = None
425 ):
426 """Build a request instance from the given data."""
427 request = build_api_test_pb2.MultiFieldMessage()
Alex Klein60c80522020-10-13 18:05:38 -0600428
Alex Klein1699fab2022-09-08 08:46:06 -0600429 if mid:
430 request.id = mid
431 if name:
432 request.name = name
433 if flag:
434 request.flag = flag
Alex Klein60c80522020-10-13 18:05:38 -0600435
Alex Klein1699fab2022-09-08 08:46:06 -0600436 return request
Alex Klein60c80522020-10-13 18:05:38 -0600437
Alex Klein1699fab2022-09-08 08:46:06 -0600438 def test_invalid_field(self):
439 """Test validator fails when given an invalid field."""
Alex Klein60c80522020-10-13 18:05:38 -0600440
Alex Klein1699fab2022-09-08 08:46:06 -0600441 @validate.require_any("does.not.exist", "also.invalid")
442 def impl(_input_proto, _output_proto, _config):
443 self.fail("Incorrectly allowed method to execute.")
Alex Klein60c80522020-10-13 18:05:38 -0600444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 with self.assertRaises(cros_build_lib.DieSystemExit):
446 impl(self._get_request(), None, self.api_config)
Alex Klein60c80522020-10-13 18:05:38 -0600447
Alex Klein1699fab2022-09-08 08:46:06 -0600448 def test_not_set(self):
449 """Test validator fails when given unset values."""
Alex Klein60c80522020-10-13 18:05:38 -0600450
Alex Klein1699fab2022-09-08 08:46:06 -0600451 @validate.require_any("id", "name")
452 def impl(_input_proto, _output_proto, _config):
453 self.fail("Incorrectly allowed method to execute.")
Alex Klein60c80522020-10-13 18:05:38 -0600454
Alex Klein1699fab2022-09-08 08:46:06 -0600455 with self.assertRaises(cros_build_lib.DieSystemExit):
456 impl(self._get_request(flag=True), None, self.api_config)
Alex Klein60c80522020-10-13 18:05:38 -0600457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 def test_set(self):
459 """Test validator passes when given set values."""
Alex Klein60c80522020-10-13 18:05:38 -0600460
Alex Klein1699fab2022-09-08 08:46:06 -0600461 @validate.require_any("id", "name")
462 def impl(_input_proto, _output_proto, _config):
463 pass
Alex Klein60c80522020-10-13 18:05:38 -0600464
Alex Klein1699fab2022-09-08 08:46:06 -0600465 impl(self._get_request(1), None, self.api_config)
466 impl(self._get_request(name="foo"), None, self.api_config)
467 impl(self._get_request(1, name="foo"), None, self.api_config)
Alex Klein60c80522020-10-13 18:05:38 -0600468
469
Alex Klein86242bf2020-09-22 15:23:23 -0600470class RequireEachTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600471 """Tests for the require_each validator."""
Alex Klein86242bf2020-09-22 15:23:23 -0600472
Alex Klein1699fab2022-09-08 08:46:06 -0600473 def _multi_field_message(self, msg_id=None, name=None, flag=None):
474 msg = build_api_test_pb2.MultiFieldMessage()
475 if msg_id is not None:
476 msg.id = int(msg_id)
477 if name is not None:
478 msg.name = str(name)
479 if flag is not None:
480 msg.flag = bool(flag)
481 return msg
Alex Klein86242bf2020-09-22 15:23:23 -0600482
Alex Klein1699fab2022-09-08 08:46:06 -0600483 def _request(self, messages=None, count=0):
484 """Build the request."""
485 if messages is None:
486 messages = [self._multi_field_message() for _ in range(count)]
Alex Klein86242bf2020-09-22 15:23:23 -0600487
Alex Klein1699fab2022-09-08 08:46:06 -0600488 request = build_api_test_pb2.TestRequestMessage()
489 for message in messages:
490 msg = request.messages.add()
491 msg.CopyFrom(message)
Alex Klein86242bf2020-09-22 15:23:23 -0600492
Alex Klein1699fab2022-09-08 08:46:06 -0600493 return request
Alex Klein86242bf2020-09-22 15:23:23 -0600494
Alex Klein1699fab2022-09-08 08:46:06 -0600495 def test_invalid_field(self):
496 """Test validator fails when given an invalid field."""
Alex Klein86242bf2020-09-22 15:23:23 -0600497
Alex Klein1699fab2022-09-08 08:46:06 -0600498 @validate.require_each("does.not", ["exist"])
499 def impl(_input_proto, _output_proto, _config):
500 self.fail("Incorrectly allowed method to execute.")
Alex Klein86242bf2020-09-22 15:23:23 -0600501
Alex Klein1699fab2022-09-08 08:46:06 -0600502 with self.assertRaises(cros_build_lib.DieSystemExit):
503 impl(self._request(), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600504
Alex Klein1699fab2022-09-08 08:46:06 -0600505 def test_invalid_call_no_subfields(self):
506 """Test validator fails when given no subfields."""
Alex Klein86242bf2020-09-22 15:23:23 -0600507
Alex Klein1699fab2022-09-08 08:46:06 -0600508 with self.assertRaises(AssertionError):
Alex Klein86242bf2020-09-22 15:23:23 -0600509
Alex Klein1699fab2022-09-08 08:46:06 -0600510 @validate.require_each("does.not", [])
511 def _(_input_proto, _output_proto, _config):
512 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600513
Alex Klein1699fab2022-09-08 08:46:06 -0600514 def test_invalid_call_invalid_subfields(self):
515 """Test validator fails when given subfields incorrectly."""
Alex Klein86242bf2020-09-22 15:23:23 -0600516
Alex Klein1699fab2022-09-08 08:46:06 -0600517 with self.assertRaises(AssertionError):
Alex Klein86242bf2020-09-22 15:23:23 -0600518
Alex Klein1699fab2022-09-08 08:46:06 -0600519 @validate.require_each("does.not", "exist")
520 def _(_input_proto, _output_proto, _config):
521 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600522
Alex Klein1699fab2022-09-08 08:46:06 -0600523 def test_not_set(self):
524 """Test validator fails when given an unset value."""
Alex Klein86242bf2020-09-22 15:23:23 -0600525
Alex Klein1699fab2022-09-08 08:46:06 -0600526 @validate.require_each("messages", ["id"])
527 def impl(_input_proto, _output_proto, _config):
528 self.fail("Incorrectly allowed method to execute.")
Alex Klein86242bf2020-09-22 15:23:23 -0600529
Alex Klein1699fab2022-09-08 08:46:06 -0600530 with self.assertRaises(cros_build_lib.DieSystemExit):
531 impl(self._request(count=2), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600532
Alex Klein1699fab2022-09-08 08:46:06 -0600533 def test_no_elements_success(self):
534 """Test validator fails when given no messages in the repeated field."""
Alex Klein86242bf2020-09-22 15:23:23 -0600535
Alex Klein1699fab2022-09-08 08:46:06 -0600536 @validate.require_each("messages", ["id"])
537 def impl(_input_proto, _output_proto, _config):
538 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600539
Alex Klein1699fab2022-09-08 08:46:06 -0600540 impl(self._request(), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600541
Alex Klein1699fab2022-09-08 08:46:06 -0600542 def test_no_elements_failure(self):
543 """Test validator fails when given no messages in the repeated field."""
Alex Klein86242bf2020-09-22 15:23:23 -0600544
Alex Klein1699fab2022-09-08 08:46:06 -0600545 @validate.require_each("messages", ["id"], allow_empty=False)
546 def impl(_input_proto, _output_proto, _config):
547 self.fail("Incorrectly allowed method to execute.")
Alex Klein86242bf2020-09-22 15:23:23 -0600548
Alex Klein1699fab2022-09-08 08:46:06 -0600549 with self.assertRaises(cros_build_lib.DieSystemExit):
550 impl(self._request(), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600551
Alex Klein1699fab2022-09-08 08:46:06 -0600552 def test_set(self):
553 """Test validator passes when given set values."""
Alex Klein86242bf2020-09-22 15:23:23 -0600554
Alex Klein1699fab2022-09-08 08:46:06 -0600555 @validate.require_each("messages", ["id"])
556 def impl(_input_proto, _output_proto, _config):
557 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600558
Alex Klein1699fab2022-09-08 08:46:06 -0600559 messages = [self._multi_field_message(msg_id=i) for i in range(1, 5)]
560 impl(self._request(messages=messages), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600561
Alex Klein1699fab2022-09-08 08:46:06 -0600562 def test_one_set_fails(self):
563 """Test validator passes when given set values."""
Alex Klein86242bf2020-09-22 15:23:23 -0600564
Alex Klein1699fab2022-09-08 08:46:06 -0600565 @validate.require_each("messages", ["id", "name"])
566 def impl(_input_proto, _output_proto, _config):
567 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600568
Alex Klein1699fab2022-09-08 08:46:06 -0600569 messages = [self._multi_field_message(msg_id=i) for i in range(1, 5)]
570 with self.assertRaises(cros_build_lib.DieSystemExit):
571 impl(self._request(messages=messages), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600572
Alex Klein1699fab2022-09-08 08:46:06 -0600573 def test_multi_set(self):
574 """Test validator passes when all values set."""
Alex Klein86242bf2020-09-22 15:23:23 -0600575
Alex Klein1699fab2022-09-08 08:46:06 -0600576 @validate.require_each("messages", ["id", "name"])
577 def impl(_input_proto, _output_proto, _config):
578 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600579
Alex Klein1699fab2022-09-08 08:46:06 -0600580 messages = [
581 self._multi_field_message(msg_id=i, name=i) for i in range(1, 5)
582 ]
583 impl(self._request(messages=messages), None, self.api_config)
584
585 def test_skip_validation(self):
586 """Test skipping validation case."""
587
588 @validate.require_each("messages", ["id"], allow_empty=False)
589 def impl(_input_proto, _output_proto, _config):
590 pass
591
592 impl(self._request(), None, self.no_validate_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600593
594
Alex Klein69339cc2019-07-22 14:08:35 -0600595class ValidateOnlyTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600596 """validate_only decorator tests."""
Alex Klein69339cc2019-07-22 14:08:35 -0600597
Alex Klein1699fab2022-09-08 08:46:06 -0600598 def test_validate_only(self):
599 """Test validate only."""
Alex Klein69339cc2019-07-22 14:08:35 -0600600
Alex Klein1699fab2022-09-08 08:46:06 -0600601 @validate.require("path")
602 @validate.validation_complete
603 def impl(_input_proto, _output_proto, _config):
604 self.fail("Implementation was called.")
605 return 1
Alex Klein69339cc2019-07-22 14:08:35 -0600606
Alex Klein1699fab2022-09-08 08:46:06 -0600607 # Just using arbitrary messages, we just need the
608 # (request, response, config) arguments so it can check the config.
609 rc = impl(
610 common_pb2.Chroot(path="/chroot/path"),
611 common_pb2.Chroot(),
612 self.validate_only_config,
613 )
Alex Klein69339cc2019-07-22 14:08:35 -0600614
Alex Klein1699fab2022-09-08 08:46:06 -0600615 self.assertEqual(0, rc)
Alex Klein69339cc2019-07-22 14:08:35 -0600616
Alex Klein1699fab2022-09-08 08:46:06 -0600617 def test_no_validate_only(self):
618 """Test no use of validate only."""
619
620 @validate.validation_complete
621 def impl(_input_proto, _output_proto, _config):
622 self.fail("Incorrectly allowed method to execute.")
623
Alex Klein54c891a2023-01-24 10:45:41 -0700624 # We will get an assertion error unless validate_only prevents the
625 # function from being called.
Alex Klein1699fab2022-09-08 08:46:06 -0600626 with self.assertRaises(AssertionError):
627 impl(common_pb2.Chroot(), common_pb2.Chroot(), self.api_config)