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