1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: Apache-2.0
13 from pathlib import Path
15 from parameterized import parameterized
17 import arvados.commands._util as cmd_util
19 FILE_PATH = Path(__file__)
21 class ValidateFiltersTestCase(unittest.TestCase):
25 ('name', '=', 'tuple'),
26 {'filters': ['name', '=', 'object']},
28 NON_FILTER_TYPES = NON_FIELD_TYPES + ['string']
30 ['owner_uuid', '=', 'zzzzz-tpzed-12345abcde67890'],
31 ['name', 'in', ['foo', 'bar']],
32 '(replication_desired > replication_cofirmed)',
33 '(replication_confirmed>=replication_desired)',
36 @parameterized.expand(itertools.combinations(VALID_FILTERS, 2))
37 def test_valid_filters(self, f1, f2):
39 actual = cmd_util.validate_filters(copy.deepcopy(expected))
40 self.assertEqual(actual, expected)
42 @parameterized.expand([(t,) for t in NON_FILTER_TYPES])
43 def test_filters_wrong_type(self, value):
44 with self.assertRaisesRegex(ValueError, r'^filters are not a list\b'):
45 cmd_util.validate_filters(value)
47 @parameterized.expand([(t,) for t in NON_FIELD_TYPES])
48 def test_single_filter_wrong_type(self, value):
49 with self.assertRaisesRegex(ValueError, r'^filter at index 0 is not a string or list\b'):
50 cmd_util.validate_filters([value])
52 @parameterized.expand([
55 (['owner_uuid', 'zzzzz-tpzed-12345abcde67890'],),
56 (['name', 'not in', 'foo', 'bar'],),
57 (['name', 'in', 'foo', 'bar', 'baz'],),
59 def test_filters_wrong_arity(self, value):
60 with self.assertRaisesRegex(ValueError, r'^filter at index 0 does not have three items\b'):
61 cmd_util.validate_filters([value])
63 @parameterized.expand(itertools.product(
67 def test_filter_definition_wrong_type(self, index, bad_value):
68 value = ['owner_uuid', '=', 'zzzzz-tpzed-12345abcde67890']
69 value[index] = bad_value
70 name = ('field name', 'operator')[index]
71 with self.assertRaisesRegex(ValueError, rf'^filter at index 0 {name} is not a string\b'):
72 cmd_util.validate_filters([value])
74 @parameterized.expand([
75 # Not enclosed in parentheses
79 # Not exactly one operator
82 '(file_count version)',
83 # Invalid field identifiers
86 '(replication.desired <= replication.confirmed)',
88 '(file_count\t=\tversion)',
89 '(file_count >= version\n)',
91 def test_invalid_string_filter(self, value):
92 with self.assertRaisesRegex(ValueError, r'^filter at index 0 has invalid syntax\b'):
93 cmd_util.validate_filters([value])
96 class JSONArgumentTestCase(unittest.TestCase):
103 {'object': True, 'yaml': False},
108 cls.json_file = tempfile.NamedTemporaryFile(
114 cls.parser = cmd_util.JSONArgument()
117 def tearDownClass(cls):
118 cls.json_file.close()
121 self.json_file.seek(0)
122 self.json_file.truncate()
124 @parameterized.expand((obj,) for obj in JSON_OBJECTS)
125 def test_valid_argument_string(self, obj):
126 actual = self.parser(json.dumps(obj))
127 self.assertEqual(actual, obj)
129 @parameterized.expand((obj,) for obj in JSON_OBJECTS)
130 def test_valid_argument_path(self, obj):
131 json.dump(obj, self.json_file)
132 self.json_file.flush()
133 actual = self.parser(self.json_file.name)
134 self.assertEqual(actual, obj)
136 @parameterized.expand([
141 def test_argument_not_json_or_path(self, value):
143 with tempfile.NamedTemporaryFile() as gone_file:
144 value = gone_file.name
145 with self.assertRaisesRegex(ValueError, r'\bnot a valid JSON string or file path\b'):
148 @parameterized.expand([
150 FILE_PATH / 'nonexistent.json',
153 def test_argument_path_unreadable(self, path):
155 bad_file = tempfile.NamedTemporaryFile()
156 os.chmod(bad_file.fileno(), 0o000)
158 @contextlib.contextmanager
163 os.chmod(bad_file.fileno(), 0o600)
165 ctx = contextlib.nullcontext
166 with self.assertRaisesRegex(ValueError, rf'^error reading JSON file path {str(path)!r}: '), ctx():
167 self.parser(str(path))
169 @parameterized.expand([
173 def test_argument_path_not_json(self, path):
175 path = self.json_file.name
176 with self.assertRaisesRegex(ValueError, rf'^error decoding JSON from file {str(path)!r}'):
177 self.parser(str(path))
180 class JSONArgumentValidationTestCase(unittest.TestCase):
181 @parameterized.expand((obj,) for obj in JSONArgumentTestCase.JSON_OBJECTS)
182 def test_object_returned_from_validator(self, value):
183 parser = cmd_util.JSONArgument(lambda _: copy.deepcopy(value))
184 self.assertEqual(parser('{}'), value)
186 @parameterized.expand((obj,) for obj in JSONArgumentTestCase.JSON_OBJECTS)
187 def test_exception_raised_from_validator(self, value):
188 json_value = json.dumps(value)
190 raise ValueError(json_value)
191 parser = cmd_util.JSONArgument(raise_func)
192 with self.assertRaises(ValueError) as exc_check:
194 self.assertEqual(exc_check.exception.args, (json_value,))