Build API: Implement validate_only calls.

Add validate-only support to all existing endpoints and
tests to enforce the setting is respected.
Add is_in validator to help transition some endpoints
to decorator-only validation.
Some cleanup and standardization in the controller tests.

BUG=chromium:987263
TEST=run_tests

Cq-Depend: chromium:1726252
Change-Id: I108dfc1a221847eae47a18f2f60e12d2575c9ea8
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1726253
Reviewed-by: David Burger <dburger@chromium.org>
Commit-Queue: Alex Klein <saklein@chromium.org>
Tested-by: Alex Klein <saklein@chromium.org>
diff --git a/api/validate_unittest.py b/api/validate_unittest.py
index d5c49b5..b38488a 100644
--- a/api/validate_unittest.py
+++ b/api/validate_unittest.py
@@ -43,6 +43,40 @@
     impl(common_pb2.Chroot(path=path))
 
 
+class IsInTest(cros_test_lib.TestCase):
+  """Tests for the is_in validator."""
+
+  def test_in(self):
+    """Test a valid value."""
+    @validate.is_in('path', ['/chroot/path', '/other/chroot/path'])
+    def impl(*_args):
+      pass
+
+    # Make sure all of the values work.
+    impl(common_pb2.Chroot(path='/chroot/path'))
+    impl(common_pb2.Chroot(path='/other/chroot/path'))
+
+  def test_not_in(self):
+    """Test an invalid value."""
+    @validate.is_in('path', ['/chroot/path', '/other/chroot/path'])
+    def impl(*_args):
+      pass
+
+    # Should be failing on the invalid value.
+    with self.assertRaises(cros_build_lib.DieSystemExit):
+      impl(common_pb2.Chroot(path='/bad/value'))
+
+  def test_not_set(self):
+    """Test an unset value."""
+    @validate.is_in('path', ['/chroot/path', '/other/chroot/path'])
+    def impl(*_args):
+      pass
+
+    # Should be failing without a value set.
+    with self.assertRaises(cros_build_lib.DieSystemExit):
+      impl(common_pb2.Chroot())
+
+
 class RequiredTest(cros_test_lib.TestCase):
   """Tests for the required validator."""