blob: 1ab572ffccf735bc49dc893e428ddcdb54d84a91 [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
Alex Klein2008aee2019-08-20 16:25:27 -060061class IsInTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -060062 """Tests for the is_in validator."""
Alex Klein231d2da2019-07-22 16:44:45 -060063
Alex Klein1699fab2022-09-08 08:46:06 -060064 def test_in(self):
65 """Test a valid value."""
Alex Klein231d2da2019-07-22 16:44:45 -060066
Alex Klein1699fab2022-09-08 08:46:06 -060067 @validate.is_in("path", ["/chroot/path", "/other/chroot/path"])
68 def impl(_input_proto, _output_proto, _config):
69 pass
Alex Klein231d2da2019-07-22 16:44:45 -060070
Alex Klein1699fab2022-09-08 08:46:06 -060071 # Make sure all of the values work.
72 impl(common_pb2.Chroot(path="/chroot/path"), None, self.api_config)
73 impl(
74 common_pb2.Chroot(path="/other/chroot/path"), None, self.api_config
75 )
Alex Klein231d2da2019-07-22 16:44:45 -060076
Alex Klein1699fab2022-09-08 08:46:06 -060077 def test_not_in(self):
78 """Test an invalid value."""
Alex Klein231d2da2019-07-22 16:44:45 -060079
Alex Klein1699fab2022-09-08 08:46:06 -060080 @validate.is_in("path", ["/chroot/path", "/other/chroot/path"])
81 def impl(_input_proto, _output_proto, _config):
82 pass
Alex Klein231d2da2019-07-22 16:44:45 -060083
Alex Klein1699fab2022-09-08 08:46:06 -060084 # Should be failing on the invalid value.
85 with self.assertRaises(cros_build_lib.DieSystemExit):
86 impl(common_pb2.Chroot(path="/bad/value"), None, self.api_config)
Alex Klein2008aee2019-08-20 16:25:27 -060087
Alex Klein1699fab2022-09-08 08:46:06 -060088 def test_not_set(self):
89 """Test an unset value."""
Alex Klein2008aee2019-08-20 16:25:27 -060090
Alex Klein1699fab2022-09-08 08:46:06 -060091 @validate.is_in("path", ["/chroot/path", "/other/chroot/path"])
92 def impl(_input_proto, _output_proto, _config):
93 pass
94
95 # Should be failing without a value set.
96 with self.assertRaises(cros_build_lib.DieSystemExit):
97 impl(common_pb2.Chroot(), None, self.api_config)
98
99 def test_skip_validation(self):
100 """Test skipping validation case."""
101
102 @validate.is_in("path", ["/chroot/path", "/other/chroot/path"])
103 def impl(_input_proto, _output_proto, _config):
104 pass
105
106 # This would otherwise raise an error for an invalid path.
107 impl(common_pb2.Chroot(), None, self.no_validate_config)
Alex Klein231d2da2019-07-22 16:44:45 -0600108
109
Alex Kleinbdace302020-12-03 14:40:23 -0700110class EachInTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600111 """Tests for the each_in validator."""
Alex Kleinbdace302020-12-03 14:40:23 -0700112
Alex Klein1699fab2022-09-08 08:46:06 -0600113 # Easier access to the enum values.
114 ENUM_FOO = build_api_test_pb2.TEST_ENUM_FOO
115 ENUM_BAR = build_api_test_pb2.TEST_ENUM_BAR
116 ENUM_BAZ = build_api_test_pb2.TEST_ENUM_BAZ
Alex Kleinbdace302020-12-03 14:40:23 -0700117
Alex Klein1699fab2022-09-08 08:46:06 -0600118 # pylint: disable=docstring-misnamed-args
119 def _message_request(self, *messages):
120 """Build a request instance, filling out the messages field.
Alex Kleinbdace302020-12-03 14:40:23 -0700121
Alex Klein1699fab2022-09-08 08:46:06 -0600122 Args:
123 messages: Each messages data (id, name, flag, enum) as lists. Only
124 requires as many as are set. e.g. _request([1], [2]) will create two
125 messages with only ids set. _request([1, 'name']) will create one with
126 id and name set, but not flag or enum.
127 """
128 request = build_api_test_pb2.TestRequestMessage()
129 for message in messages or []:
130 msg = request.messages.add()
131 try:
132 msg.id = message[0]
133 msg.name = message[1]
134 msg.flag = message[2]
135 except IndexError:
136 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700137
Alex Klein1699fab2022-09-08 08:46:06 -0600138 return request
Alex Kleinbdace302020-12-03 14:40:23 -0700139
Alex Klein1699fab2022-09-08 08:46:06 -0600140 def _enums_request(self, *enum_values):
141 """Build a request instance, setting the test_enums field."""
142 request = build_api_test_pb2.TestRequestMessage()
143 for value in enum_values:
144 request.test_enums.append(value)
Alex Kleinbdace302020-12-03 14:40:23 -0700145
Alex Klein1699fab2022-09-08 08:46:06 -0600146 return request
Alex Kleinbdace302020-12-03 14:40:23 -0700147
Alex Klein1699fab2022-09-08 08:46:06 -0600148 def _numbers_request(self, *numbers):
149 """Build a request instance, setting the numbers field."""
150 request = build_api_test_pb2.TestRequestMessage()
151 request.numbers.extend(numbers)
Alex Kleinbdace302020-12-03 14:40:23 -0700152
Alex Klein1699fab2022-09-08 08:46:06 -0600153 return request
Alex Kleinbdace302020-12-03 14:40:23 -0700154
Alex Klein1699fab2022-09-08 08:46:06 -0600155 def test_message_in(self):
156 """Test valid values."""
Alex Kleinbdace302020-12-03 14:40:23 -0700157
Alex Klein1699fab2022-09-08 08:46:06 -0600158 @validate.each_in("messages", "name", ["foo", "bar"])
159 def impl(_input_proto, _output_proto, _config):
160 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700161
Alex Klein1699fab2022-09-08 08:46:06 -0600162 impl(self._message_request([1, "foo"]), None, self.api_config)
163 impl(
164 self._message_request([1, "foo"], [2, "bar"]), None, self.api_config
165 )
Alex Kleinbdace302020-12-03 14:40:23 -0700166
Alex Klein1699fab2022-09-08 08:46:06 -0600167 def test_enum_in(self):
168 """Test valid enum values."""
Alex Kleinbdace302020-12-03 14:40:23 -0700169
Alex Klein1699fab2022-09-08 08:46:06 -0600170 @validate.each_in("test_enums", None, [self.ENUM_FOO, self.ENUM_BAR])
171 def impl(_input_proto, _output_proto, _config):
172 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700173
Alex Klein1699fab2022-09-08 08:46:06 -0600174 impl(self._enums_request(self.ENUM_FOO), None, self.api_config)
175 impl(
176 self._enums_request(self.ENUM_FOO, self.ENUM_BAR),
177 None,
178 self.api_config,
179 )
Alex Kleinbdace302020-12-03 14:40:23 -0700180
Alex Klein1699fab2022-09-08 08:46:06 -0600181 def test_scalar_in(self):
182 """Test valid scalar values."""
Alex Kleinbdace302020-12-03 14:40:23 -0700183
Alex Klein1699fab2022-09-08 08:46:06 -0600184 @validate.each_in("numbers", None, [1, 2])
185 def impl(_input_proto, _output_proto, _config):
186 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 impl(self._numbers_request(1), None, self.api_config)
189 impl(self._numbers_request(1, 2), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700190
Alex Klein1699fab2022-09-08 08:46:06 -0600191 def test_message_not_in(self):
192 """Test an invalid value."""
Alex Kleinbdace302020-12-03 14:40:23 -0700193
Alex Klein1699fab2022-09-08 08:46:06 -0600194 @validate.each_in("messages", "name", ["foo", "bar"])
195 def impl(_input_proto, _output_proto, _config):
196 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700197
Alex Klein1699fab2022-09-08 08:46:06 -0600198 # Should be failing on the invalid value.
199 with self.assertRaises(cros_build_lib.DieSystemExit):
200 impl(self._message_request([1, "invalid"]), None, self.api_config)
201 with self.assertRaises(cros_build_lib.DieSystemExit):
202 impl(
203 self._message_request([1, "invalid"], [2, "invalid"]),
204 None,
205 self.api_config,
206 )
207 with self.assertRaises(cros_build_lib.DieSystemExit):
208 impl(
209 self._message_request([1, "foo"], [2, "invalid"]),
210 None,
211 self.api_config,
212 )
Alex Kleinbdace302020-12-03 14:40:23 -0700213
Alex Klein1699fab2022-09-08 08:46:06 -0600214 def test_enum_not_in(self):
215 """Test an invalid enum value."""
Alex Kleinbdace302020-12-03 14:40:23 -0700216
Alex Klein1699fab2022-09-08 08:46:06 -0600217 @validate.each_in("test_enums", None, [self.ENUM_FOO, self.ENUM_BAR])
218 def impl(_input_proto, _output_proto, _config):
219 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700220
Alex Klein1699fab2022-09-08 08:46:06 -0600221 # Only invalid values.
222 with self.assertRaises(cros_build_lib.DieSystemExit):
223 impl(self._enums_request(self.ENUM_BAZ), None, self.api_config)
224 # Mixed valid/invalid values.
225 with self.assertRaises(cros_build_lib.DieSystemExit):
226 impl(
227 self._enums_request(self.ENUM_FOO, self.ENUM_BAZ),
228 None,
229 self.api_config,
230 )
Alex Kleinbdace302020-12-03 14:40:23 -0700231
Alex Klein1699fab2022-09-08 08:46:06 -0600232 def test_scalar_not_in(self):
233 """Test invalid scalar value."""
Alex Kleinbdace302020-12-03 14:40:23 -0700234
Alex Klein1699fab2022-09-08 08:46:06 -0600235 @validate.each_in("numbers", None, [1, 2])
236 def impl(_input_proto, _output_proto, _config):
237 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700238
Alex Klein1699fab2022-09-08 08:46:06 -0600239 # Only invalid values.
240 with self.assertRaises(cros_build_lib.DieSystemExit):
241 impl(self._numbers_request(3), None, self.api_config)
242 # Mixed valid/invalid values.
243 with self.assertRaises(cros_build_lib.DieSystemExit):
244 impl(self._numbers_request(1, 2, 3), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700245
Alex Klein1699fab2022-09-08 08:46:06 -0600246 def test_not_set(self):
247 """Test an unset value."""
Alex Kleinbdace302020-12-03 14:40:23 -0700248
Alex Klein1699fab2022-09-08 08:46:06 -0600249 @validate.each_in("messages", "name", ["foo", "bar"])
250 def impl(_input_proto, _output_proto, _config):
251 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700252
Alex Klein1699fab2022-09-08 08:46:06 -0600253 # Should be failing without a value set.
254 # No entries in the field.
255 with self.assertRaises(cros_build_lib.DieSystemExit):
256 impl(self._message_request(), None, self.api_config)
257 # No value set on lone entry.
258 with self.assertRaises(cros_build_lib.DieSystemExit):
259 impl(self._message_request([1]), None, self.api_config)
260 # No value set on multiple entries.
261 with self.assertRaises(cros_build_lib.DieSystemExit):
262 impl(self._message_request([1], [2]), None, self.api_config)
263 # Some valid and some invalid entries.
264 with self.assertRaises(cros_build_lib.DieSystemExit):
265 impl(self._message_request([1, "foo"], [2]), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700266
Alex Klein1699fab2022-09-08 08:46:06 -0600267 def test_optional(self):
268 """Test optional argument."""
Alex Kleinbdace302020-12-03 14:40:23 -0700269
Alex Klein1699fab2022-09-08 08:46:06 -0600270 @validate.each_in("messages", "name", ["foo", "bar"], optional=True)
271 @validate.each_in(
272 "test_enums", None, [self.ENUM_FOO, self.ENUM_BAR], optional=True
273 )
274 @validate.each_in("numbers", None, [1, 2], optional=True)
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 # No entries in the field succeeds.
279 impl(self._message_request(), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700280
Alex Klein1699fab2022-09-08 08:46:06 -0600281 # Still fails when entries exist but value unset cases.
282 # No value set on lone entry.
283 with self.assertRaises(cros_build_lib.DieSystemExit):
284 impl(self._message_request([1]), None, self.api_config)
285 # No value set on multiple entries.
286 with self.assertRaises(cros_build_lib.DieSystemExit):
287 impl(self._message_request([1], [2]), None, self.api_config)
288 # Some valid and some invalid entries.
289 with self.assertRaises(cros_build_lib.DieSystemExit):
290 impl(self._message_request([1, "foo"], [2]), None, self.api_config)
Alex Kleinbdace302020-12-03 14:40:23 -0700291
Alex Klein1699fab2022-09-08 08:46:06 -0600292 def test_skip_validation(self):
293 """Test skipping validation case."""
Alex Kleinbdace302020-12-03 14:40:23 -0700294
Alex Klein1699fab2022-09-08 08:46:06 -0600295 @validate.each_in("messages", "name", ["foo", "bar"])
296 @validate.each_in("test_enums", None, [self.ENUM_FOO, self.ENUM_BAR])
297 @validate.each_in("numbers", None, [1, 2])
298 def impl(_input_proto, _output_proto, _config):
299 pass
Alex Kleinbdace302020-12-03 14:40:23 -0700300
Alex Klein1699fab2022-09-08 08:46:06 -0600301 # This would otherwise raise an error for multiple invalid fields.
302 impl(
303 self._message_request([1, "invalid"]), None, self.no_validate_config
304 )
Alex Kleinbdace302020-12-03 14:40:23 -0700305
306
Alex Klein60c80522020-10-13 18:05:38 -0600307class RequireTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600308 """Tests for the require validator."""
Alex Klein2b236722019-06-19 15:44:26 -0600309
Alex Klein1699fab2022-09-08 08:46:06 -0600310 def test_invalid_field(self):
311 """Test validator fails when given an unset value."""
Alex Klein2b236722019-06-19 15:44:26 -0600312
Alex Klein1699fab2022-09-08 08:46:06 -0600313 @validate.require("does.not.exist")
314 def impl(_input_proto, _output_proto, _config):
315 self.fail("Incorrectly allowed method to execute.")
Alex Klein2b236722019-06-19 15:44:26 -0600316
Alex Klein1699fab2022-09-08 08:46:06 -0600317 with self.assertRaises(cros_build_lib.DieSystemExit):
318 impl(common_pb2.Chroot(), None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -0600319
Alex Klein1699fab2022-09-08 08:46:06 -0600320 def test_not_set(self):
321 """Test validator fails when given an unset value."""
Alex Klein2b236722019-06-19 15:44:26 -0600322
Alex Klein1699fab2022-09-08 08:46:06 -0600323 @validate.require("env.use_flags")
324 def impl(_input_proto, _output_proto, _config):
325 self.fail("Incorrectly allowed method to execute.")
Alex Klein2b236722019-06-19 15:44:26 -0600326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 with self.assertRaises(cros_build_lib.DieSystemExit):
328 impl(common_pb2.Chroot(), None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -0600329
Alex Klein1699fab2022-09-08 08:46:06 -0600330 def test_set(self):
331 """Test validator passes when given set values."""
Alex Klein2b236722019-06-19 15:44:26 -0600332
Alex Klein1699fab2022-09-08 08:46:06 -0600333 @validate.require("path", "env.use_flags")
334 def impl(_input_proto, _output_proto, _config):
335 pass
Alex Klein2b236722019-06-19 15:44:26 -0600336
Alex Klein1699fab2022-09-08 08:46:06 -0600337 in_proto = common_pb2.Chroot(
338 path="/chroot/path", env={"use_flags": [{"flag": "test"}]}
339 )
340 impl(in_proto, None, self.api_config)
Alex Klein2b236722019-06-19 15:44:26 -0600341
Alex Klein1699fab2022-09-08 08:46:06 -0600342 def test_mixed(self):
343 """Test validator fails when given a set value and an unset value."""
Alex Klein2b236722019-06-19 15:44:26 -0600344
Alex Klein1699fab2022-09-08 08:46:06 -0600345 @validate.require("path", "env.use_flags")
346 def impl(_input_proto, _output_proto, _config):
347 pass
Alex Klein2b236722019-06-19 15:44:26 -0600348
Alex Klein1699fab2022-09-08 08:46:06 -0600349 with self.assertRaises(cros_build_lib.DieSystemExit):
350 impl(common_pb2.Chroot(path="/chroot/path"), None, self.api_config)
Alex Klein2008aee2019-08-20 16:25:27 -0600351
Alex Klein1699fab2022-09-08 08:46:06 -0600352 def test_skip_validation(self):
353 """Test skipping validation case."""
Alex Klein2008aee2019-08-20 16:25:27 -0600354
Alex Klein1699fab2022-09-08 08:46:06 -0600355 @validate.require("path", "env.use_flags")
356 def impl(_input_proto, _output_proto, _config):
357 pass
358
359 # This would otherwise raise an error for an invalid path.
360 impl(common_pb2.Chroot(), None, self.no_validate_config)
Alex Klein69339cc2019-07-22 14:08:35 -0600361
362
Alex Klein60c80522020-10-13 18:05:38 -0600363class RequireAnyTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600364 """Tests for the require_any validator."""
Alex Klein60c80522020-10-13 18:05:38 -0600365
Alex Klein1699fab2022-09-08 08:46:06 -0600366 def _get_request(
367 self, mid: int = None, name: str = None, flag: bool = None
368 ):
369 """Build a request instance from the given data."""
370 request = build_api_test_pb2.MultiFieldMessage()
Alex Klein60c80522020-10-13 18:05:38 -0600371
Alex Klein1699fab2022-09-08 08:46:06 -0600372 if mid:
373 request.id = mid
374 if name:
375 request.name = name
376 if flag:
377 request.flag = flag
Alex Klein60c80522020-10-13 18:05:38 -0600378
Alex Klein1699fab2022-09-08 08:46:06 -0600379 return request
Alex Klein60c80522020-10-13 18:05:38 -0600380
Alex Klein1699fab2022-09-08 08:46:06 -0600381 def test_invalid_field(self):
382 """Test validator fails when given an invalid field."""
Alex Klein60c80522020-10-13 18:05:38 -0600383
Alex Klein1699fab2022-09-08 08:46:06 -0600384 @validate.require_any("does.not.exist", "also.invalid")
385 def impl(_input_proto, _output_proto, _config):
386 self.fail("Incorrectly allowed method to execute.")
Alex Klein60c80522020-10-13 18:05:38 -0600387
Alex Klein1699fab2022-09-08 08:46:06 -0600388 with self.assertRaises(cros_build_lib.DieSystemExit):
389 impl(self._get_request(), None, self.api_config)
Alex Klein60c80522020-10-13 18:05:38 -0600390
Alex Klein1699fab2022-09-08 08:46:06 -0600391 def test_not_set(self):
392 """Test validator fails when given unset values."""
Alex Klein60c80522020-10-13 18:05:38 -0600393
Alex Klein1699fab2022-09-08 08:46:06 -0600394 @validate.require_any("id", "name")
395 def impl(_input_proto, _output_proto, _config):
396 self.fail("Incorrectly allowed method to execute.")
Alex Klein60c80522020-10-13 18:05:38 -0600397
Alex Klein1699fab2022-09-08 08:46:06 -0600398 with self.assertRaises(cros_build_lib.DieSystemExit):
399 impl(self._get_request(flag=True), None, self.api_config)
Alex Klein60c80522020-10-13 18:05:38 -0600400
Alex Klein1699fab2022-09-08 08:46:06 -0600401 def test_set(self):
402 """Test validator passes when given set values."""
Alex Klein60c80522020-10-13 18:05:38 -0600403
Alex Klein1699fab2022-09-08 08:46:06 -0600404 @validate.require_any("id", "name")
405 def impl(_input_proto, _output_proto, _config):
406 pass
Alex Klein60c80522020-10-13 18:05:38 -0600407
Alex Klein1699fab2022-09-08 08:46:06 -0600408 impl(self._get_request(1), None, self.api_config)
409 impl(self._get_request(name="foo"), None, self.api_config)
410 impl(self._get_request(1, name="foo"), None, self.api_config)
Alex Klein60c80522020-10-13 18:05:38 -0600411
412
Alex Klein86242bf2020-09-22 15:23:23 -0600413class RequireEachTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600414 """Tests for the require_each validator."""
Alex Klein86242bf2020-09-22 15:23:23 -0600415
Alex Klein1699fab2022-09-08 08:46:06 -0600416 def _multi_field_message(self, msg_id=None, name=None, flag=None):
417 msg = build_api_test_pb2.MultiFieldMessage()
418 if msg_id is not None:
419 msg.id = int(msg_id)
420 if name is not None:
421 msg.name = str(name)
422 if flag is not None:
423 msg.flag = bool(flag)
424 return msg
Alex Klein86242bf2020-09-22 15:23:23 -0600425
Alex Klein1699fab2022-09-08 08:46:06 -0600426 def _request(self, messages=None, count=0):
427 """Build the request."""
428 if messages is None:
429 messages = [self._multi_field_message() for _ in range(count)]
Alex Klein86242bf2020-09-22 15:23:23 -0600430
Alex Klein1699fab2022-09-08 08:46:06 -0600431 request = build_api_test_pb2.TestRequestMessage()
432 for message in messages:
433 msg = request.messages.add()
434 msg.CopyFrom(message)
Alex Klein86242bf2020-09-22 15:23:23 -0600435
Alex Klein1699fab2022-09-08 08:46:06 -0600436 return request
Alex Klein86242bf2020-09-22 15:23:23 -0600437
Alex Klein1699fab2022-09-08 08:46:06 -0600438 def test_invalid_field(self):
439 """Test validator fails when given an invalid field."""
Alex Klein86242bf2020-09-22 15:23:23 -0600440
Alex Klein1699fab2022-09-08 08:46:06 -0600441 @validate.require_each("does.not", ["exist"])
442 def impl(_input_proto, _output_proto, _config):
443 self.fail("Incorrectly allowed method to execute.")
Alex Klein86242bf2020-09-22 15:23:23 -0600444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 with self.assertRaises(cros_build_lib.DieSystemExit):
446 impl(self._request(), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600447
Alex Klein1699fab2022-09-08 08:46:06 -0600448 def test_invalid_call_no_subfields(self):
449 """Test validator fails when given no subfields."""
Alex Klein86242bf2020-09-22 15:23:23 -0600450
Alex Klein1699fab2022-09-08 08:46:06 -0600451 with self.assertRaises(AssertionError):
Alex Klein86242bf2020-09-22 15:23:23 -0600452
Alex Klein1699fab2022-09-08 08:46:06 -0600453 @validate.require_each("does.not", [])
454 def _(_input_proto, _output_proto, _config):
455 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600456
Alex Klein1699fab2022-09-08 08:46:06 -0600457 def test_invalid_call_invalid_subfields(self):
458 """Test validator fails when given subfields incorrectly."""
Alex Klein86242bf2020-09-22 15:23:23 -0600459
Alex Klein1699fab2022-09-08 08:46:06 -0600460 with self.assertRaises(AssertionError):
Alex Klein86242bf2020-09-22 15:23:23 -0600461
Alex Klein1699fab2022-09-08 08:46:06 -0600462 @validate.require_each("does.not", "exist")
463 def _(_input_proto, _output_proto, _config):
464 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600465
Alex Klein1699fab2022-09-08 08:46:06 -0600466 def test_not_set(self):
467 """Test validator fails when given an unset value."""
Alex Klein86242bf2020-09-22 15:23:23 -0600468
Alex Klein1699fab2022-09-08 08:46:06 -0600469 @validate.require_each("messages", ["id"])
470 def impl(_input_proto, _output_proto, _config):
471 self.fail("Incorrectly allowed method to execute.")
Alex Klein86242bf2020-09-22 15:23:23 -0600472
Alex Klein1699fab2022-09-08 08:46:06 -0600473 with self.assertRaises(cros_build_lib.DieSystemExit):
474 impl(self._request(count=2), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600475
Alex Klein1699fab2022-09-08 08:46:06 -0600476 def test_no_elements_success(self):
477 """Test validator fails when given no messages in the repeated field."""
Alex Klein86242bf2020-09-22 15:23:23 -0600478
Alex Klein1699fab2022-09-08 08:46:06 -0600479 @validate.require_each("messages", ["id"])
480 def impl(_input_proto, _output_proto, _config):
481 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600482
Alex Klein1699fab2022-09-08 08:46:06 -0600483 impl(self._request(), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600484
Alex Klein1699fab2022-09-08 08:46:06 -0600485 def test_no_elements_failure(self):
486 """Test validator fails when given no messages in the repeated field."""
Alex Klein86242bf2020-09-22 15:23:23 -0600487
Alex Klein1699fab2022-09-08 08:46:06 -0600488 @validate.require_each("messages", ["id"], allow_empty=False)
489 def impl(_input_proto, _output_proto, _config):
490 self.fail("Incorrectly allowed method to execute.")
Alex Klein86242bf2020-09-22 15:23:23 -0600491
Alex Klein1699fab2022-09-08 08:46:06 -0600492 with self.assertRaises(cros_build_lib.DieSystemExit):
493 impl(self._request(), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600494
Alex Klein1699fab2022-09-08 08:46:06 -0600495 def test_set(self):
496 """Test validator passes when given set values."""
Alex Klein86242bf2020-09-22 15:23:23 -0600497
Alex Klein1699fab2022-09-08 08:46:06 -0600498 @validate.require_each("messages", ["id"])
499 def impl(_input_proto, _output_proto, _config):
500 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600501
Alex Klein1699fab2022-09-08 08:46:06 -0600502 messages = [self._multi_field_message(msg_id=i) for i in range(1, 5)]
503 impl(self._request(messages=messages), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600504
Alex Klein1699fab2022-09-08 08:46:06 -0600505 def test_one_set_fails(self):
506 """Test validator passes when given set values."""
Alex Klein86242bf2020-09-22 15:23:23 -0600507
Alex Klein1699fab2022-09-08 08:46:06 -0600508 @validate.require_each("messages", ["id", "name"])
509 def impl(_input_proto, _output_proto, _config):
510 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600511
Alex Klein1699fab2022-09-08 08:46:06 -0600512 messages = [self._multi_field_message(msg_id=i) for i in range(1, 5)]
513 with self.assertRaises(cros_build_lib.DieSystemExit):
514 impl(self._request(messages=messages), None, self.api_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600515
Alex Klein1699fab2022-09-08 08:46:06 -0600516 def test_multi_set(self):
517 """Test validator passes when all values set."""
Alex Klein86242bf2020-09-22 15:23:23 -0600518
Alex Klein1699fab2022-09-08 08:46:06 -0600519 @validate.require_each("messages", ["id", "name"])
520 def impl(_input_proto, _output_proto, _config):
521 pass
Alex Klein86242bf2020-09-22 15:23:23 -0600522
Alex Klein1699fab2022-09-08 08:46:06 -0600523 messages = [
524 self._multi_field_message(msg_id=i, name=i) for i in range(1, 5)
525 ]
526 impl(self._request(messages=messages), None, self.api_config)
527
528 def test_skip_validation(self):
529 """Test skipping validation case."""
530
531 @validate.require_each("messages", ["id"], allow_empty=False)
532 def impl(_input_proto, _output_proto, _config):
533 pass
534
535 impl(self._request(), None, self.no_validate_config)
Alex Klein86242bf2020-09-22 15:23:23 -0600536
537
Alex Klein69339cc2019-07-22 14:08:35 -0600538class ValidateOnlyTest(cros_test_lib.TestCase, api_config.ApiConfigMixin):
Alex Klein1699fab2022-09-08 08:46:06 -0600539 """validate_only decorator tests."""
Alex Klein69339cc2019-07-22 14:08:35 -0600540
Alex Klein1699fab2022-09-08 08:46:06 -0600541 def test_validate_only(self):
542 """Test validate only."""
Alex Klein69339cc2019-07-22 14:08:35 -0600543
Alex Klein1699fab2022-09-08 08:46:06 -0600544 @validate.require("path")
545 @validate.validation_complete
546 def impl(_input_proto, _output_proto, _config):
547 self.fail("Implementation was called.")
548 return 1
Alex Klein69339cc2019-07-22 14:08:35 -0600549
Alex Klein1699fab2022-09-08 08:46:06 -0600550 # Just using arbitrary messages, we just need the
551 # (request, response, config) arguments so it can check the config.
552 rc = impl(
553 common_pb2.Chroot(path="/chroot/path"),
554 common_pb2.Chroot(),
555 self.validate_only_config,
556 )
Alex Klein69339cc2019-07-22 14:08:35 -0600557
Alex Klein1699fab2022-09-08 08:46:06 -0600558 self.assertEqual(0, rc)
Alex Klein69339cc2019-07-22 14:08:35 -0600559
Alex Klein1699fab2022-09-08 08:46:06 -0600560 def test_no_validate_only(self):
561 """Test no use of validate only."""
562
563 @validate.validation_complete
564 def impl(_input_proto, _output_proto, _config):
565 self.fail("Incorrectly allowed method to execute.")
566
567 # We will get an assertion error unless validate_only prevents the function
568 # from being called.
569 with self.assertRaises(AssertionError):
570 impl(common_pb2.Chroot(), common_pb2.Chroot(), self.api_config)