21126: Merge branch 'main' into 21126-trash-when-ro
[arvados.git] / sdk / python / tests / test_vocabulary.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: Apache-2.0
4
5 import arvados
6 import unittest
7 import mock
8
9 from arvados import api, vocabulary
10
11 class VocabularyTest(unittest.TestCase):
12     EXAMPLE_VOC = {
13         'tags': {
14             'IDTAGANIMALS': {
15                 'strict': False,
16                 'labels': [
17                     {'label': 'Animal'},
18                     {'label': 'Creature'},
19                 ],
20                 'values': {
21                     'IDVALANIMAL1': {
22                         'labels': [
23                             {'label': 'Human'},
24                             {'label': 'Homo sapiens'},
25                         ],
26                     },
27                     'IDVALANIMAL2': {
28                         'labels': [
29                             {'label': 'Elephant'},
30                             {'label': 'Loxodonta'},
31                         ],
32                     },
33                 },
34             },
35             'IDTAGIMPORTANCES': {
36                 'strict': True,
37                 'labels': [
38                     {'label': 'Importance'},
39                     {'label': 'Priority'},
40                 ],
41                 'values': {
42                     'IDVALIMPORTANCE1': {
43                         'labels': [
44                             {'label': 'High'},
45                             {'label': 'High priority'},
46                         ],
47                     },
48                     'IDVALIMPORTANCE2': {
49                         'labels': [
50                             {'label': 'Medium'},
51                             {'label': 'Medium priority'},
52                         ],
53                     },
54                     'IDVALIMPORTANCE3': {
55                         'labels': [
56                             {'label': 'Low'},
57                             {'label': 'Low priority'},
58                         ],
59                     },
60                 },
61             },
62             'IDTAGCOMMENTS': {
63                 'strict': False,
64                 'labels': [
65                     {'label': 'Comment'},
66                     {'label': 'Notes'},
67                 ],
68                 'values': None,
69             },
70         },
71     }
72
73     def setUp(self):
74         self.api = arvados.api('v1')
75         self.voc = vocabulary.Vocabulary(self.EXAMPLE_VOC)
76         self.api.vocabulary = mock.MagicMock(return_value=self.EXAMPLE_VOC)
77
78     def test_vocabulary_keys(self):
79         self.assertEqual(self.voc.strict_keys, False)
80         self.assertEqual(
81             self.voc.key_aliases.keys(),
82             set(['idtaganimals', 'creature', 'animal',
83                 'idtagimportances', 'importance', 'priority',
84                 'idtagcomments', 'comment', 'notes'])
85         )
86
87         vk = self.voc.key_aliases['creature']
88         self.assertEqual(vk.strict, False)
89         self.assertEqual(vk.identifier, 'IDTAGANIMALS')
90         self.assertEqual(vk.aliases, ['Animal', 'Creature'])
91         self.assertEqual(vk.preferred_label, 'Animal')
92         self.assertEqual(
93             vk.value_aliases.keys(),
94             set(['idvalanimal1', 'human', 'homo sapiens',
95                 'idvalanimal2', 'elephant', 'loxodonta'])
96         )
97
98     def test_vocabulary_values(self):
99         vk = self.voc.key_aliases['creature']
100         vv = vk.value_aliases['human']
101         self.assertEqual(vv.identifier, 'IDVALANIMAL1')
102         self.assertEqual(vv.aliases, ['Human', 'Homo sapiens'])
103         self.assertEqual(vv.preferred_label, 'Human')
104
105     def test_vocabulary_indexing(self):
106         self.assertEqual(self.voc['creature']['human'].identifier, 'IDVALANIMAL1')
107         self.assertEqual(self.voc['Creature']['Human'].identifier, 'IDVALANIMAL1')
108         self.assertEqual(self.voc['CREATURE']['HUMAN'].identifier, 'IDVALANIMAL1')
109         with self.assertRaises(KeyError):
110             inexistant = self.voc['foo']
111
112     def test_empty_vocabulary(self):
113         voc = vocabulary.Vocabulary({})
114         self.assertEqual(voc.strict_keys, False)
115         self.assertEqual(voc.key_aliases, {})
116
117     def test_load_vocabulary_with_api(self):
118         voc = vocabulary.load_vocabulary(self.api)
119         self.assertEqual(voc['creature']['human'].identifier, 'IDVALANIMAL1')
120         self.assertEqual(voc['Creature']['Human'].identifier, 'IDVALANIMAL1')
121         self.assertEqual(voc['CREATURE']['HUMAN'].identifier, 'IDVALANIMAL1')
122
123     def test_convert_to_identifiers(self):
124         cases = [
125             {'IDTAGIMPORTANCES': 'IDVALIMPORTANCE1'},
126             {'IDTAGIMPORTANCES': 'High'},
127             {'importance': 'IDVALIMPORTANCE1'},
128             {'priority': 'high priority'},
129         ]
130         for case in cases:
131             self.assertEqual(
132                 self.voc.convert_to_identifiers(case),
133                 {'IDTAGIMPORTANCES': 'IDVALIMPORTANCE1'},
134                 "failing test case: {}".format(case)
135             )
136
137     def test_convert_to_identifiers_multiple_pairs(self):
138         cases = [
139             {'IDTAGIMPORTANCES': 'IDVALIMPORTANCE1', 'IDTAGANIMALS': 'IDVALANIMAL1', 'IDTAGCOMMENTS': 'Very important person'},
140             {'IDTAGIMPORTANCES': 'High', 'IDTAGANIMALS': 'IDVALANIMAL1', 'comment': 'Very important person'},
141             {'importance': 'IDVALIMPORTANCE1', 'animal': 'IDVALANIMAL1', 'notes': 'Very important person'},
142             {'priority': 'high priority', 'animal': 'IDVALANIMAL1', 'NOTES': 'Very important person'},
143         ]
144         for case in cases:
145             self.assertEqual(
146                 self.voc.convert_to_identifiers(case),
147                 {'IDTAGIMPORTANCES': 'IDVALIMPORTANCE1', 'IDTAGANIMALS': 'IDVALANIMAL1', 'IDTAGCOMMENTS': 'Very important person'},
148                 "failing test case: {}".format(case)
149             )
150
151     def test_convert_to_identifiers_value_lists(self):
152         cases = [
153             {'IDTAGIMPORTANCES': ['IDVALIMPORTANCE1', 'IDVALIMPORTANCE2']},
154             {'IDTAGIMPORTANCES': ['High', 'Medium']},
155             {'importance': ['IDVALIMPORTANCE1', 'IDVALIMPORTANCE2']},
156             {'priority': ['high', 'medium']},
157         ]
158         for case in cases:
159             self.assertEqual(
160                 self.voc.convert_to_identifiers(case),
161                 {'IDTAGIMPORTANCES': ['IDVALIMPORTANCE1', 'IDVALIMPORTANCE2']},
162                 "failing test case: {}".format(case)
163             )
164
165     def test_convert_to_identifiers_unknown_key(self):
166         # Non-strict vocabulary
167         self.assertEqual(self.voc.strict_keys, False)
168         self.assertEqual(self.voc.convert_to_identifiers({'foo': 'bar'}), {'foo': 'bar'})
169         # Strict vocabulary
170         strict_voc = arvados.vocabulary.Vocabulary(self.EXAMPLE_VOC)
171         strict_voc.strict_keys = True
172         with self.assertRaises(vocabulary.VocabularyKeyError):
173             strict_voc.convert_to_identifiers({'foo': 'bar'})
174
175     def test_convert_to_identifiers_invalid_key(self):
176         with self.assertRaises(vocabulary.VocabularyKeyError):
177             self.voc.convert_to_identifiers({42: 'bar'})
178         with self.assertRaises(vocabulary.VocabularyKeyError):
179             self.voc.convert_to_identifiers({None: 'bar'})
180         with self.assertRaises(vocabulary.VocabularyKeyError):
181             self.voc.convert_to_identifiers({('f', 'o', 'o'): 'bar'})
182
183     def test_convert_to_identifiers_unknown_value(self):
184         # Non-strict key
185         self.assertEqual(self.voc['animal'].strict, False)
186         self.assertEqual(self.voc.convert_to_identifiers({'Animal': 'foo'}), {'IDTAGANIMALS': 'foo'})
187         # Strict key
188         self.assertEqual(self.voc['priority'].strict, True)
189         with self.assertRaises(vocabulary.VocabularyValueError):
190             self.voc.convert_to_identifiers({'Priority': 'foo'})
191
192     def test_convert_to_identifiers_invalid_value(self):
193         with self.assertRaises(vocabulary.VocabularyValueError):
194             self.voc.convert_to_identifiers({'Animal': 42})
195         with self.assertRaises(vocabulary.VocabularyValueError):
196             self.voc.convert_to_identifiers({'Animal': None})
197         with self.assertRaises(vocabulary.VocabularyValueError):
198             self.voc.convert_to_identifiers({'Animal': {'hello': 'world'}})
199         with self.assertRaises(vocabulary.VocabularyValueError):
200             self.voc.convert_to_identifiers({'Animal': [42]})
201         with self.assertRaises(vocabulary.VocabularyValueError):
202             self.voc.convert_to_identifiers({'Animal': [None]})
203         with self.assertRaises(vocabulary.VocabularyValueError):
204             self.voc.convert_to_identifiers({'Animal': [{'hello': 'world'}]})
205
206     def test_convert_to_identifiers_unknown_value_list(self):
207         # Non-strict key
208         self.assertEqual(self.voc['animal'].strict, False)
209         self.assertEqual(
210             self.voc.convert_to_identifiers({'Animal': ['foo', 'loxodonta']}),
211             {'IDTAGANIMALS': ['foo', 'IDVALANIMAL2']}
212         )
213         # Strict key
214         self.assertEqual(self.voc['priority'].strict, True)
215         with self.assertRaises(vocabulary.VocabularyValueError):
216             self.voc.convert_to_identifiers({'Priority': ['foo', 'bar']})
217
218     def test_convert_to_labels(self):
219         cases = [
220             {'IDTAGIMPORTANCES': 'IDVALIMPORTANCE1'},
221             {'IDTAGIMPORTANCES': 'High'},
222             {'importance': 'IDVALIMPORTANCE1'},
223             {'priority': 'high priority'},
224         ]
225         for case in cases:
226             self.assertEqual(
227                 self.voc.convert_to_labels(case),
228                 {'Importance': 'High'},
229                 "failing test case: {}".format(case)
230             )
231
232     def test_convert_to_labels_multiple_pairs(self):
233         cases = [
234             {'IDTAGIMPORTANCES': 'IDVALIMPORTANCE1', 'IDTAGANIMALS': 'IDVALANIMAL1', 'IDTAGCOMMENTS': 'Very important person'},
235             {'IDTAGIMPORTANCES': 'High', 'IDTAGANIMALS': 'IDVALANIMAL1', 'comment': 'Very important person'},
236             {'importance': 'IDVALIMPORTANCE1', 'animal': 'IDVALANIMAL1', 'notes': 'Very important person'},
237             {'priority': 'high priority', 'animal': 'IDVALANIMAL1', 'NOTES': 'Very important person'},
238         ]
239         for case in cases:
240             self.assertEqual(
241                 self.voc.convert_to_labels(case),
242                 {'Importance': 'High', 'Animal': 'Human', 'Comment': 'Very important person'},
243                 "failing test case: {}".format(case)
244             )
245
246     def test_convert_to_labels_value_lists(self):
247         cases = [
248             {'IDTAGIMPORTANCES': ['IDVALIMPORTANCE1', 'IDVALIMPORTANCE2']},
249             {'IDTAGIMPORTANCES': ['High', 'Medium']},
250             {'importance': ['IDVALIMPORTANCE1', 'IDVALIMPORTANCE2']},
251             {'priority': ['high', 'medium']},
252         ]
253         for case in cases:
254             self.assertEqual(
255                 self.voc.convert_to_labels(case),
256                 {'Importance': ['High', 'Medium']},
257                 "failing test case: {}".format(case)
258             )
259
260     def test_convert_to_labels_unknown_key(self):
261         # Non-strict vocabulary
262         self.assertEqual(self.voc.strict_keys, False)
263         self.assertEqual(self.voc.convert_to_labels({'foo': 'bar'}), {'foo': 'bar'})
264         # Strict vocabulary
265         strict_voc = arvados.vocabulary.Vocabulary(self.EXAMPLE_VOC)
266         strict_voc.strict_keys = True
267         with self.assertRaises(vocabulary.VocabularyKeyError):
268             strict_voc.convert_to_labels({'foo': 'bar'})
269
270     def test_convert_to_labels_invalid_key(self):
271         with self.assertRaises(vocabulary.VocabularyKeyError):
272             self.voc.convert_to_labels({42: 'bar'})
273         with self.assertRaises(vocabulary.VocabularyKeyError):
274             self.voc.convert_to_labels({None: 'bar'})
275         with self.assertRaises(vocabulary.VocabularyKeyError):
276             self.voc.convert_to_labels({('f', 'o', 'o'): 'bar'})
277
278     def test_convert_to_labels_unknown_value(self):
279         # Non-strict key
280         self.assertEqual(self.voc['animal'].strict, False)
281         self.assertEqual(self.voc.convert_to_labels({'IDTAGANIMALS': 'foo'}), {'Animal': 'foo'})
282         # Strict key
283         self.assertEqual(self.voc['priority'].strict, True)
284         with self.assertRaises(vocabulary.VocabularyValueError):
285             self.voc.convert_to_labels({'IDTAGIMPORTANCES': 'foo'})
286
287     def test_convert_to_labels_invalid_value(self):
288         with self.assertRaises(vocabulary.VocabularyValueError):
289             self.voc.convert_to_labels({'IDTAGIMPORTANCES': {'high': True}})
290         with self.assertRaises(vocabulary.VocabularyValueError):
291             self.voc.convert_to_labels({'IDTAGIMPORTANCES': None})
292         with self.assertRaises(vocabulary.VocabularyValueError):
293             self.voc.convert_to_labels({'IDTAGIMPORTANCES': 42})
294         with self.assertRaises(vocabulary.VocabularyValueError):
295             self.voc.convert_to_labels({'IDTAGIMPORTANCES': False})
296         with self.assertRaises(vocabulary.VocabularyValueError):
297             self.voc.convert_to_labels({'IDTAGIMPORTANCES': [42]})
298         with self.assertRaises(vocabulary.VocabularyValueError):
299             self.voc.convert_to_labels({'IDTAGIMPORTANCES': [None]})
300         with self.assertRaises(vocabulary.VocabularyValueError):
301             self.voc.convert_to_labels({'IDTAGIMPORTANCES': [{'high': True}]})
302
303     def test_convert_to_labels_unknown_value_list(self):
304         # Non-strict key
305         self.assertEqual(self.voc['animal'].strict, False)
306         self.assertEqual(
307             self.voc.convert_to_labels({'IDTAGANIMALS': ['foo', 'IDVALANIMAL1']}),
308             {'Animal': ['foo', 'Human']}
309         )
310         # Strict key
311         self.assertEqual(self.voc['priority'].strict, True)
312         with self.assertRaises(vocabulary.VocabularyValueError):
313             self.voc.convert_to_labels({'IDTAGIMPORTANCES': ['foo', 'bar']})
314
315     def test_convert_roundtrip(self):
316         initial = {'IDTAGIMPORTANCES': 'IDVALIMPORTANCE1', 'IDTAGANIMALS': 'IDVALANIMAL1', 'IDTAGCOMMENTS': 'Very important person'}
317         converted = self.voc.convert_to_labels(initial)
318         self.assertNotEqual(converted, initial)
319         self.assertEqual(self.voc.convert_to_identifiers(converted), initial)