1 # Copyright (c) 2019 AT&T Intellectual Property.
2 # Copyright (c) 2018-2019 Nokia.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 # This source code is part of the near-RT RIC (RAN Intelligent Controller)
18 # platform project (RICP).
22 from unittest.mock import patch, Mock
24 from ricsdl.syncstorage import SyncStorage
25 from ricsdl.syncstorage import SyncLock
26 from ricsdl.syncstorage import func_arg_checker
27 from ricsdl.exceptions import (SdlTypeError, NotConnected)
31 def sync_storage_fixture(request):
32 request.cls.ns = 'some-ns'
34 request.cls.keys = {'a', 'b'}
35 request.cls.dm = {'b': b'2', 'a': b'1'}
36 request.cls.old_data = b'1'
37 request.cls.new_data = b'3'
38 request.cls.keyprefix = 'x'
39 request.cls.matchedkeys = ['x1', 'x2', 'x3', 'x4', 'x5']
40 request.cls.group = 'some-group'
41 request.cls.groupmembers = set([b'm1', b'm2'])
42 request.cls.groupmember = b'm1'
43 request.cls.lock_name = 'some-lock-name'
44 request.cls.lock_int_expiration = 10
45 request.cls.lock_float_expiration = 1.1
47 with patch('ricsdl.backend.get_backend_instance') as mock_db_backend:
48 storage = SyncStorage()
49 request.cls.mock_db_backend = mock_db_backend.return_value
50 request.cls.storage = storage
54 @pytest.mark.usefixtures('sync_storage_fixture')
55 class TestSyncStorage:
56 def test_is_active_function_success(self):
57 self.mock_db_backend.is_connected.return_value = True
58 ret = self.storage.is_active()
59 self.mock_db_backend.is_connected.assert_called_once()
62 def test_is_active_function_can_catch_backend_exception_and_return_false(self):
63 self.mock_db_backend.is_connected.side_effect = NotConnected
64 ret = self.storage.is_active()
65 self.mock_db_backend.is_connected.assert_called_once()
68 def test_set_function_success(self):
69 self.storage.set(self.ns, self.dm)
70 self.mock_db_backend.set.assert_called_once_with(self.ns, self.dm)
72 def test_set_function_can_raise_exception_for_wrong_argument(self):
73 with pytest.raises(SdlTypeError):
74 self.storage.set(123, {'a': b'v1'})
75 with pytest.raises(SdlTypeError):
76 self.storage.set('ns', [1, 2])
77 with pytest.raises(SdlTypeError):
78 self.storage.set('ns', {0xbad: b'v1'})
79 with pytest.raises(SdlTypeError):
80 self.storage.set('ns', {'a': 0xbad})
82 def test_set_if_function_success(self):
83 self.mock_db_backend.set_if.return_value = True
84 ret = self.storage.set_if(self.ns, self.key, self.old_data, self.new_data)
85 self.mock_db_backend.set_if.assert_called_once_with(self.ns, self.key, self.old_data,
89 def test_set_if_function_can_return_false_if_same_data_already_exists(self):
90 self.mock_db_backend.set_if.return_value = False
91 ret = self.storage.set_if(self.ns, self.key, self.old_data, self.new_data)
92 self.mock_db_backend.set_if.assert_called_once_with(self.ns, self.key, self.old_data,
96 def test_set_if_function_can_raise_exception_for_wrong_argument(self):
97 with pytest.raises(SdlTypeError):
98 self.storage.set_if(0xbad, 'key', b'v1', b'v2')
99 with pytest.raises(SdlTypeError):
100 self.storage.set_if('ns', 0xbad, b'v1', b'v2')
101 with pytest.raises(SdlTypeError):
102 self.storage.set_if('ns', 'key', 0xbad, b'v2')
103 with pytest.raises(SdlTypeError):
104 self.storage.set_if('ns', 'key', b'v1', 0xbad)
106 def test_set_if_not_exists_function_success(self):
107 self.mock_db_backend.set_if_not_exists.return_value = True
108 ret = self.storage.set_if_not_exists(self.ns, self.key, self.new_data)
109 self.mock_db_backend.set_if_not_exists.assert_called_once_with(self.ns, self.key,
113 def test_set_if_not_exists_function_can_return_false_if_key_already_exists(self):
114 self.mock_db_backend.set_if_not_exists.return_value = False
115 ret = self.storage.set_if_not_exists(self.ns, self.key, self.new_data)
116 self.mock_db_backend.set_if_not_exists.assert_called_once_with(self.ns, self.key,
120 def test_set_if_not_exists_function_can_raise_exception_for_wrong_argument(self):
121 with pytest.raises(SdlTypeError):
122 self.storage.set_if_not_exists(0xbad, 'key', b'v1')
123 with pytest.raises(SdlTypeError):
124 self.storage.set_if_not_exists('ns', 0xbad, b'v1')
125 with pytest.raises(SdlTypeError):
126 self.storage.set_if_not_exists('ns', 'key', 0xbad)
128 def test_get_function_success(self):
129 self.mock_db_backend.get.return_value = self.dm
130 ret = self.storage.get(self.ns, self.keys)
131 self.mock_db_backend.get.assert_called_once()
132 call_args, _ = self.mock_db_backend.get.call_args
133 assert call_args[0] == self.ns
134 assert len(call_args[1]) == len(self.keys)
135 assert all(k in call_args[1] for k in self.keys)
136 assert ret == self.dm
137 # Validate that SDL returns a dictionary with keys in alphabetical order
138 assert sorted(self.dm)[0] == list(ret.keys())[0]
140 def test_get_function_can_return_empty_dict_when_no_key_values_exist(self):
141 self.mock_db_backend.get.return_value = dict()
142 ret = self.storage.get(self.ns, self.keys)
143 self.mock_db_backend.get.assert_called_once()
144 call_args, _ = self.mock_db_backend.get.call_args
145 assert call_args[0] == self.ns
146 assert len(call_args[1]) == len(self.keys)
147 assert all(k in call_args[1] for k in self.keys)
150 def test_get_function_can_raise_exception_for_wrong_argument(self):
151 with pytest.raises(SdlTypeError):
152 self.storage.get(0xbad, self.key)
153 with pytest.raises(SdlTypeError):
154 self.storage.get(self.ns, 0xbad)
156 def test_find_keys_function_success(self):
157 self.mock_db_backend.find_keys.return_value = self.matchedkeys
158 ret = self.storage.find_keys(self.ns, self.keyprefix)
159 self.mock_db_backend.find_keys.assert_called_once_with(self.ns, self.keyprefix)
160 assert ret == self.matchedkeys
162 def test_find_keys_function_can_return_empty_list_when_no_keys_exist(self):
163 self.mock_db_backend.find_keys.return_value = list()
164 ret = self.storage.find_keys(self.ns, self.keyprefix)
165 self.mock_db_backend.find_keys.assert_called_once_with(self.ns, self.keyprefix)
168 def test_find_keys_function_can_raise_exception_for_wrong_argument(self):
169 with pytest.raises(SdlTypeError):
170 self.storage.find_keys(0xbad, self.keyprefix)
171 with pytest.raises(SdlTypeError):
172 self.storage.find_keys(self.ns, 0xbad)
174 def test_find_and_get_function_success(self):
175 self.mock_db_backend.find_and_get.return_value = self.dm
176 ret = self.storage.find_and_get(self.ns, self.keyprefix)
177 self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix)
178 assert ret == self.dm
179 # Validate that SDL returns a dictionary with keys in alphabetical order
180 assert sorted(self.dm)[0] == list(ret.keys())[0]
182 def test_find_and_get_function_can_return_empty_dict_when_no_keys_exist(self):
183 self.mock_db_backend.find_and_get.return_value = dict()
184 ret = self.storage.find_and_get(self.ns, self.keyprefix)
185 self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix)
188 def test_find_and_get_function_can_raise_exception_for_wrong_argument(self):
189 with pytest.raises(SdlTypeError):
190 self.storage.find_and_get(0xbad, self.keyprefix)
191 with pytest.raises(SdlTypeError):
192 self.storage.find_and_get(self.ns, 0xbad)
194 def test_remove_function_success(self):
195 self.storage.remove(self.ns, self.keys)
196 self.mock_db_backend.remove.assert_called_once()
197 call_args, _ = self.mock_db_backend.remove.call_args
198 assert call_args[0] == self.ns
199 assert isinstance(call_args[1], list)
200 assert len(call_args[1]) == len(self.keys)
201 assert all(k in call_args[1] for k in self.keys)
203 def test_remove_function_can_raise_exception_for_wrong_argument(self):
204 with pytest.raises(SdlTypeError):
205 self.storage.remove(0xbad, self.keys)
206 with pytest.raises(SdlTypeError):
207 self.storage.remove(self.ns, 0xbad)
209 def test_remove_if_function_success(self):
210 self.mock_db_backend.remove_if.return_value = True
211 ret = self.storage.remove_if(self.ns, self.key, self.new_data)
212 self.mock_db_backend.remove_if.assert_called_once_with(self.ns, self.key, self.new_data)
215 def test_remove_if_function_can_return_false_if_data_does_not_match(self):
216 self.mock_db_backend.remove_if.return_value = False
217 ret = self.storage.remove_if(self.ns, self.key, self.old_data)
218 self.mock_db_backend.remove_if.assert_called_once_with(self.ns, self.key, self.old_data)
221 def test_remove_if_function_can_raise_exception_for_wrong_argument(self):
222 with pytest.raises(SdlTypeError):
223 self.storage.remove_if(0xbad, self.keys, self.old_data)
224 with pytest.raises(SdlTypeError):
225 self.storage.remove_if(self.ns, 0xbad, self.old_data)
226 with pytest.raises(SdlTypeError):
227 self.storage.remove_if(self.ns, self.keys, 0xbad)
229 def test_remove_all_function_success(self):
230 self.mock_db_backend.find_keys.return_value = ['a1']
231 self.storage.remove_all(self.ns)
232 self.mock_db_backend.find_keys.assert_called_once_with(self.ns, '*')
233 self.mock_db_backend.remove.assert_called_once_with(self.ns,
234 self.mock_db_backend.find_keys.return_value)
236 def test_remove_all_function_can_raise_exception_for_wrong_argument(self):
237 with pytest.raises(SdlTypeError):
238 self.storage.remove_all(0xbad)
240 def test_add_member_function_success(self):
241 self.storage.add_member(self.ns, self.group, self.groupmembers)
242 self.mock_db_backend.add_member.assert_called_once_with(self.ns,
243 self.group, self.groupmembers)
245 def test_add_member_function_can_raise_exception_for_wrong_argument(self):
246 with pytest.raises(SdlTypeError):
247 self.storage.add_member(0xbad, self.group, self.groupmembers)
248 with pytest.raises(SdlTypeError):
249 self.storage.add_member(self.ns, 0xbad, self.groupmembers)
250 with pytest.raises(SdlTypeError):
251 self.storage.add_member(self.ns, self.group, 0xbad)
253 def test_remove_member_function_success(self):
254 self.storage.remove_member(self.ns, self.group, self.groupmembers)
255 self.mock_db_backend.remove_member.assert_called_once_with(self.ns, self.group,
258 def test_remove_member_function_can_raise_exception_for_wrong_argument(self):
259 with pytest.raises(SdlTypeError):
260 self.storage.remove_member(0xbad, self.group, self.groupmembers)
261 with pytest.raises(SdlTypeError):
262 self.storage.remove_member(self.ns, 0xbad, self.groupmembers)
263 with pytest.raises(SdlTypeError):
264 self.storage.remove_member(self.ns, self.group, 0xbad)
266 def test_remove_group_function_success(self):
267 self.storage.remove_group(self.ns, self.group)
268 self.mock_db_backend.remove_group.assert_called_once_with(self.ns, self.group)
270 def test_remove_group_function_can_raise_exception_for_wrong_argument(self):
271 with pytest.raises(SdlTypeError):
272 self.storage.remove_group(0xbad, self.group)
273 with pytest.raises(SdlTypeError):
274 self.storage.remove_group(self.ns, 0xbad)
276 def test_get_members_function_success(self):
277 self.mock_db_backend.get_members.return_value = self.groupmembers
278 ret = self.storage.get_members(self.ns, self.group)
279 self.mock_db_backend.get_members.assert_called_once_with(self.ns, self.group)
280 assert ret == self.groupmembers
282 def test_get_members_function_can_raise_exception_for_wrong_argument(self):
283 with pytest.raises(SdlTypeError):
284 self.storage.get_members(0xbad, self.group)
285 with pytest.raises(SdlTypeError):
286 self.storage.get_members(self.ns, 0xbad)
288 def test_is_member_function_success(self):
289 self.mock_db_backend.is_member.return_value = True
290 ret = self.storage.is_member(self.ns, self.group, self.groupmember)
291 self.mock_db_backend.is_member.assert_called_once_with(self.ns, self.group,
295 def test_is_member_function_can_raise_exception_for_wrong_argument(self):
296 with pytest.raises(SdlTypeError):
297 self.storage.is_member(0xbad, self.group, self.groupmember)
298 with pytest.raises(SdlTypeError):
299 self.storage.is_member(self.ns, 0xbad, self.groupmember)
300 with pytest.raises(SdlTypeError):
301 self.storage.is_member(self.ns, self.group, 0xbad)
303 def test_group_size_function_success(self):
304 self.mock_db_backend.group_size.return_value = 100
305 ret = self.storage.group_size(self.ns, self.group)
306 self.mock_db_backend.group_size.assert_called_once_with(self.ns, self.group)
309 def test_group_size_function_can_raise_exception_for_wrong_argument(self):
310 with pytest.raises(SdlTypeError):
311 self.storage.group_size(0xbad, self.group)
312 with pytest.raises(SdlTypeError):
313 self.storage.group_size(self.ns, 0xbad)
315 @patch('ricsdl.syncstorage.SyncLock')
316 def test_get_lock_resource_function_success_when_expiration_time_is_integer(self, mock_db_lock):
317 ret = self.storage.get_lock_resource(self.ns, self.lock_name, self.lock_int_expiration)
318 mock_db_lock.assert_called_once_with(self.ns, self.lock_name, self.lock_int_expiration,
320 assert ret == mock_db_lock.return_value
322 @patch('ricsdl.syncstorage.SyncLock')
323 def test_get_lock_resource_function_success_when_expiration_time_is_float_number(self,
325 ret = self.storage.get_lock_resource(self.ns, self.lock_name, self.lock_float_expiration)
326 mock_db_lock.assert_called_once_with(self.ns, self.lock_name, self.lock_float_expiration,
328 assert ret == mock_db_lock.return_value
330 def test_get_lock_resource_function_can_raise_exception_for_wrong_argument(self):
331 with pytest.raises(SdlTypeError):
332 self.storage.get_lock_resource(0xbad, self.lock_name, self.lock_int_expiration)
333 with pytest.raises(SdlTypeError):
334 self.storage.get_lock_resource(self.ns, 0xbad, self.lock_int_expiration)
335 with pytest.raises(SdlTypeError):
336 self.storage.get_lock_resource(self.ns, self.lock_name, 'bad')
338 def test_get_backend_function_success(self):
339 ret = self.storage.get_backend()
340 assert ret == self.mock_db_backend
342 def test_storage_object_string_representation(self):
343 str_out = str(self.storage)
344 assert str_out is not None
348 def lock_fixture(request):
349 request.cls.ns = 'some-ns'
350 request.cls.lockname = 'some-lock-name'
351 request.cls.expiration = 10
352 request.cls.retry_interval = 0.1
353 request.cls.retry_timeout = 1
355 with patch('ricsdl.backend.get_backend_lock_instance') as mock_db_backend_lock:
356 lock = SyncLock('test-ns', 'test-lock-name', request.cls.expiration, Mock())
357 request.cls.mock_db_backend_lock = mock_db_backend_lock.return_value
358 request.cls.lock = lock
362 @pytest.mark.usefixtures('lock_fixture')
364 def test_acquire_function_success_when_timeout_and_interval_are_integers(self):
365 self.lock.acquire(self.retry_interval, self.retry_timeout)
366 self.mock_db_backend_lock.acquire.assert_called_once_with(self.retry_interval,
369 def test_acquire_function_success_when_timeout_and_interval_are_float_numbers(self):
370 self.lock.acquire(float(self.retry_interval), float(self.retry_timeout))
371 self.mock_db_backend_lock.acquire.assert_called_once_with(float(self.retry_interval),
372 float(self.retry_timeout))
374 def test_acquire_function_can_raise_exception_for_wrong_argument(self):
375 with pytest.raises(SdlTypeError):
376 self.lock.acquire('bad', self.retry_timeout)
377 with pytest.raises(SdlTypeError):
378 self.lock.acquire(self.retry_interval, 'bad')
380 def test_release_function_success(self):
382 self.mock_db_backend_lock.release.assert_called_once()
384 def test_refresh_function_success(self):
386 self.mock_db_backend_lock.refresh.assert_called_once()
388 def test_get_validity_time_function_success(self):
389 self.mock_db_backend_lock.get_validity_time.return_value = self.expiration
390 ret = self.lock.get_validity_time()
391 self.mock_db_backend_lock.get_validity_time.assert_called_once()
392 assert ret == self.expiration
394 def test_get_validity_time_function_success_when_returned_time_is_float(self):
395 self.mock_db_backend_lock.get_validity_time.return_value = float(self.expiration)
396 ret = self.lock.get_validity_time()
397 self.mock_db_backend_lock.get_validity_time.assert_called_once()
398 assert ret == float(self.expiration)
400 def test_lock_object_string_representation(self):
401 str_out = str(self.lock)
402 assert str_out is not None
405 def test_function_arg_validator():
406 @func_arg_checker(SdlTypeError, 0, a=str, b=(int, float), c=set, d=(dict, type(None)))
407 def _my_func(a='abc', b=1, c={'x', 'y'}, d={'x': b'1'}):
409 with pytest.raises(SdlTypeError, match=r"Wrong argument type: 'a'=<class 'NoneType'>. "
410 r"Must be: <class 'str'>"):
413 with pytest.raises(SdlTypeError, match=r"Wrong argument type: 'b'=<class 'str'>. "):
414 _my_func('abc', 'wrong type')
416 with pytest.raises(SdlTypeError, match=r"Wrong argument type: 'c'=<class 'str'>. "
417 r"Must be: <class 'set'>"):
418 _my_func('abc', 1.0, 'wrong type')
420 with pytest.raises(SdlTypeError, match=r"Wrong argument type: 'd'=<class 'str'>. "):
421 _my_func('abc', 1.0, {'x', 'y'}, 'wrong type')
424 def test_function_kwarg_validator():
425 @func_arg_checker(SdlTypeError, 0, a=str, b=(int, float), c=set, d=(dict, type(None)))
426 def _my_func(a='abc', b=1, c={'x', 'y'}, d={'x': b'1'}):
428 with pytest.raises(SdlTypeError, match=r"Wrong argument type: 'a'=<class 'NoneType'>. "
429 r"Must be: <class 'str'>"):
432 with pytest.raises(SdlTypeError, match=r"Wrong argument type: 'b'=<class 'str'>. "):
433 _my_func(b='wrong type')
435 with pytest.raises(SdlTypeError, match=r"Wrong argument type: 'c'=<class 'str'>. "
436 r"Must be: <class 'set'>"):
437 _my_func(c='wrong type')
439 with pytest.raises(SdlTypeError, match=r"Wrong argument type: 'd'=<class 'str'>. "):
440 _my_func(d='wrong type')