From 276ed3c833c81a4142d7c4607474ee95a7f01355 Mon Sep 17 00:00:00 2001 From: Timo Tietavainen Date: Sun, 15 Dec 2019 20:16:23 +0200 Subject: [PATCH] Implement regular expression support for key searching There is a use case to use only some part of the full key as a searching criterion, as an example of such a criterion could be gNBID, arp or qci. That's why change find_keys() and find_and_get() API functions to take in a key search pattern instead of a key prefix. A pattern can contain glob-style regular expression to match a key. Removed also 'atomic' parameter of find_and_get(), because only reasonable selection would be to use non-atomic option, which does not block the whole Redis for a long time as atomic option could do. Returned dictionary of matched key values are now sorted by key values in alphabetical order. Signed-off-by: Timo Tietavainen Change-Id: I870089f51b1ce4d72c7984220af0f770b562563f --- docs/release-notes.rst | 7 ++ ricsdl-package/examples/sync.py | 7 +- ricsdl-package/ricsdl/__init__.py | 2 +- ricsdl-package/ricsdl/backend/dbbackend_abc.py | 4 +- ricsdl-package/ricsdl/backend/redis.py | 31 +++----- ricsdl-package/ricsdl/syncstorage.py | 18 +++-- ricsdl-package/ricsdl/syncstorage_abc.py | 55 +++++++++---- ricsdl-package/tests/backend/test_redis.py | 103 +++++++++++++++++++------ ricsdl-package/tests/test_syncstorage.py | 25 +++--- 9 files changed, 169 insertions(+), 83 deletions(-) diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 92a49a6..e91338d 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -33,6 +33,13 @@ This document provides the release notes of the ricsdl library. Version history --------------- +[2.0.0] - 2020-01-03 + +* Change find_keys() and find_and_get() API functions to support glob-style + regular expression in a key search pattern. API backward incompatible change. +* Remove 'atomic' parameter of find_and_get() API function. API backward + incompatible change. + [1.0.2] - 2019-12-18 * Take Hiredis package into use in Redis database backend. diff --git a/ricsdl-package/examples/sync.py b/ricsdl-package/examples/sync.py index c75dfb6..24c8fe8 100644 --- a/ricsdl-package/examples/sync.py +++ b/ricsdl-package/examples/sync.py @@ -132,13 +132,14 @@ assert my_ret_dict == {} # Finds keys under given namespace that are matching to given key prefix 'my_k'. _try_func_return(lambda: mysdl.set(MY_NS, {'my_key': b'my_value'})) -ret_keys = _try_func_return(lambda: mysdl.find_keys(MY_NS, '')) +ret_keys = _try_func_return(lambda: mysdl.find_keys(MY_NS, 'my_k*')) assert ret_keys == ['my_key'] -# Finds keys and their values under given namespace that are matching to given key prefix 'my_k'. +# Finds keys and their values under given namespace that are matching to given key search +# pattern 'my_k*'. # Note that the type of returned value is bytes. -ret_key_values = _try_func_return(lambda: mysdl.find_and_get(MY_NS, '', atomic=True)) +ret_key_values = _try_func_return(lambda: mysdl.find_and_get(MY_NS, 'my_k*')) assert ret_key_values == {'my_key': b'my_value'} _try_func_return(lambda: mysdl.remove_all(MY_NS)) diff --git a/ricsdl-package/ricsdl/__init__.py b/ricsdl-package/ricsdl/__init__.py index 91eff80..7fd7dd6 100644 --- a/ricsdl-package/ricsdl/__init__.py +++ b/ricsdl-package/ricsdl/__init__.py @@ -31,7 +31,7 @@ from .exceptions import ( ) -__version__ = '1.0.2' +__version__ = '2.0.0' __all__ = [ diff --git a/ricsdl-package/ricsdl/backend/dbbackend_abc.py b/ricsdl-package/ricsdl/backend/dbbackend_abc.py index 4be5737..08f4600 100644 --- a/ricsdl-package/ricsdl/backend/dbbackend_abc.py +++ b/ricsdl-package/ricsdl/backend/dbbackend_abc.py @@ -54,12 +54,12 @@ class DbBackendAbc(ABC): pass @abstractmethod - def find_keys(self, ns: str, key_prefix: str) -> List[str]: + def find_keys(self, ns: str, key_pattern: str) -> List[str]: """"Return all the keys matching search pattern under a namespace in database.""" pass @abstractmethod - def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]: + def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]: """ Return all the keys with their values matching search pattern under a namespace in database. diff --git a/ricsdl-package/ricsdl/backend/redis.py b/ricsdl-package/ricsdl/backend/redis.py index afa7450..319469e 100644 --- a/ricsdl-package/ricsdl/backend/redis.py +++ b/ricsdl-package/ricsdl/backend/redis.py @@ -118,18 +118,17 @@ class RedisBackend(DbBackendAbc): ret[keys[idx]] = val return ret - def find_keys(self, ns: str, key_prefix: str) -> List[str]: - escaped_key_prefix = self._escape_characters(key_prefix) - db_escaped_key_prefix = self._add_key_ns_prefix(ns, escaped_key_prefix + '*') + def find_keys(self, ns: str, key_pattern: str) -> List[str]: + db_key_pattern = self._add_key_ns_prefix(ns, key_pattern) with _map_to_sdl_exception(): - ret = self.__redis.keys(db_escaped_key_prefix) + ret = self.__redis.keys(db_key_pattern) return self._strip_ns_from_bin_keys(ns, ret) - def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]: + def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]: # todo: replace below implementation with redis 'NGET' module ret = dict() # type: Dict[str, bytes] with _map_to_sdl_exception(): - matched_keys = self.find_keys(ns, key_prefix) + matched_keys = self.find_keys(ns, key_pattern) if matched_keys: ret = self.get(ns, matched_keys) return ret @@ -196,24 +195,18 @@ class RedisBackend(DbBackendAbc): def _strip_ns_from_bin_keys(cls, ns: str, nskeylist: List[bytes]) -> List[str]: ret_keys = [] for k in nskeylist: - nskey = k.decode("utf-8").split(',', 1) + try: + redis_key = k.decode("utf-8") + except UnicodeDecodeError as exc: + msg = u'Namespace %s key conversion to string failed: %s' % (ns, str(exc)) + raise RejectedByBackend(msg) + nskey = redis_key.split(',', 1) if len(nskey) != 2: - msg = u'Illegal namespace %s key:%s' % (ns, nskey) + msg = u'Namespace %s key:%s has no namespace prefix' % (ns, redis_key) raise RejectedByBackend(msg) ret_keys.append(nskey[1]) return ret_keys - @classmethod - def _escape_characters(cls, pattern: str) -> str: - return pattern.translate(str.maketrans( - {"(": r"\(", - ")": r"\)", - "[": r"\[", - "]": r"\]", - "*": r"\*", - "?": r"\?", - "\\": r"\\"})) - def get_redis_connection(self): """Return existing Redis database connection.""" return self.__redis diff --git a/ricsdl-package/ricsdl/syncstorage.py b/ricsdl-package/ricsdl/syncstorage.py index 92bb88a..cec5761 100644 --- a/ricsdl-package/ricsdl/syncstorage.py +++ b/ricsdl-package/ricsdl/syncstorage.py @@ -142,15 +142,17 @@ class SyncStorage(SyncStorageAbc): @func_arg_checker(SdlTypeError, 1, ns=str, keys=(str, builtins.set)) def get(self, ns: str, keys: Union[str, Set[str]]) -> Dict[str, bytes]: - return self.__dbbackend.get(ns, list(keys)) + disordered = self.__dbbackend.get(ns, list(keys)) + return {k: disordered[k] for k in sorted(disordered)} - @func_arg_checker(SdlTypeError, 1, ns=str, key_prefix=str) - def find_keys(self, ns: str, key_prefix: str) -> List[str]: - return self.__dbbackend.find_keys(ns, key_prefix) + @func_arg_checker(SdlTypeError, 1, ns=str, key_pattern=str) + def find_keys(self, ns: str, key_pattern: str) -> List[str]: + return self.__dbbackend.find_keys(ns, key_pattern) - @func_arg_checker(SdlTypeError, 1, ns=str, key_prefix=str, atomic=bool) - def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]: - return self.__dbbackend.find_and_get(ns, key_prefix, atomic) + @func_arg_checker(SdlTypeError, 1, ns=str, key_pattern=str) + def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]: + disordered = self.__dbbackend.find_and_get(ns, key_pattern) + return {k: disordered[k] for k in sorted(disordered)} @func_arg_checker(SdlTypeError, 1, ns=str, keys=(str, builtins.set)) def remove(self, ns: str, keys: Union[str, Set[str]]) -> None: @@ -162,7 +164,7 @@ class SyncStorage(SyncStorageAbc): @func_arg_checker(SdlTypeError, 1, ns=str) def remove_all(self, ns: str) -> None: - keys = self.__dbbackend.find_keys(ns, '') + keys = self.__dbbackend.find_keys(ns, '*') if keys: self.__dbbackend.remove(ns, keys) diff --git a/ricsdl-package/ricsdl/syncstorage_abc.py b/ricsdl-package/ricsdl/syncstorage_abc.py index ff5eea9..9ccd99d 100644 --- a/ricsdl-package/ricsdl/syncstorage_abc.py +++ b/ricsdl-package/ricsdl/syncstorage_abc.py @@ -290,6 +290,7 @@ class SyncStorageAbc(ABC): Returns: (dict of str: bytes): A dictionary mapping of a key to the read data from the storage. + Dictionary is sorted by key values in alphabetical order. Raises: SdlTypeError: If function's argument is of an inappropriate type. @@ -300,12 +301,26 @@ class SyncStorageAbc(ABC): pass @abstractmethod - def find_keys(self, ns: str, key_prefix: str) -> List[str]: - """ + def find_keys(self, ns: str, key_pattern: str) -> List[str]: + r""" Find all keys matching search pattern under the namespace. - No prior knowledge about the keys in the given namespace exists, thus operation is not - guaranteed to be atomic or isolated. + Supported glob-style patterns: + `?` matches any single character. For example `?at` matches Cat, cat, Bat or bat. + `*` matches any number of any characters including none. For example `*Law*` matches + Law, GrokLaw, or Lawyer. + `[abc]` matches one character given in the bracket. For example `[CB]at` matches Cat or + Bat. + `[a-z]` matches one character from the range given in the bracket. For example + `Letter[0-9]` matches Letter0 up to Letter9. + `[^abc]` matches any single character what is not given in the bracket. For example + `h[^e]llo` matches hallo, hillo but not hello. + + If searched key itself contains a special character, use a backslash (\) character to + escape the special character to match it verbatim. + + NOTE: `find_keys` function is not guaranteed to be atomic or isolated. + All the exceptions except SdlTypeError are derived from SdlException base class. Client can catch only that exception if separate handling for different SDL error situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates @@ -313,8 +328,7 @@ class SyncStorageAbc(ABC): Args: ns (str): Namespace under which this operation is targeted. - key_prefix (str): Only keys starting with given keyPrefix are returned. Passing empty - string as keyPrefix will return all the keys. + key_pattern (str): Key search pattern. Returns: (list of str): A list of found keys. @@ -328,13 +342,26 @@ class SyncStorageAbc(ABC): pass @abstractmethod - def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]: - """ + def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]: + r""" Find keys and get their respective data from SDL storage. - Only those entries that are matching prefix will be returned. - NOTE: In atomic action, if the prefix produces huge number of matches, that can have - a severe impact on system performance, due to DB is blocked for long time. + Supported glob-style patterns: + `?` matches any single character. For example `?at` matches Cat, cat, Bat or bat. + `*` matches any number of any characters including none. For example `*Law*` matches + Law, GrokLaw, or Lawyer. + `[abc]` matches one character given in the bracket. For example `[CB]at` matches Cat or + Bat. + `[a-z]` matches one character from the range given in the bracket. For example + `Letter[0-9]` matches Letter0 up to Letter9. + `[^abc]` matches any single character what is not given in the bracket. For example + `h[^e]llo` matches hallo, hillo but not hello. + + If searched key itself contains a special character, use a backslash (\) character to + escape the special character to match it verbatim. + + NOTE: `find_and_get` function is not guaranteed to be atomic or isolated. + All the exceptions except SdlTypeError are derived from SdlException base class. Client can catch only that exception if separate handling for different SDL error situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates @@ -342,13 +369,11 @@ class SyncStorageAbc(ABC): Args: ns (str): Namespace under which this operation is targeted. - key_prefix (str): Only keys starting with given keyPrefix are returned. Passing empty - string as keyPrefix will return all the keys. - atomic (bool): True to find keys and get their respective data in one atomic operation, - false to find keys and get their respective data non-atomically. + key_pattern (str): Key search pattern. Returns: (dict of str: bytes): A dictionary mapping of a key to the read data from the storage. + Dictionary is sorted by key values in alphabetical order. Raises: SdlTypeError: If function's argument is of an inappropriate type. diff --git a/ricsdl-package/tests/backend/test_redis.py b/ricsdl-package/tests/backend/test_redis.py index d1b8223..72d7c02 100644 --- a/ricsdl-package/tests/backend/test_redis.py +++ b/ricsdl-package/tests/backend/test_redis.py @@ -41,19 +41,18 @@ def redis_backend_fixture(request): request.cls.data = b'123' request.cls.old_data = b'1' request.cls.new_data = b'3' - request.cls.keyprefix = 'x?' - request.cls.keyprefix_redis = r'{some-ns},x\?*' - request.cls.matchedkeys = ['x1', 'x2', 'x3', 'x4', 'x5'] - request.cls.matchedkeys_redis = [b'{some-ns},x1', b'{some-ns},x2', b'{some-ns},x3', - b'{some-ns},x4', b'{some-ns},x5'] - request.cls.matcheddata_dl_redis = [b'10', b'11', b'12', b'13', b'14'] - request.cls.matcheddata_dm = {'x1': b'10', 'x2': b'11', 'x3': b'12', - 'x4': b'13', 'x5': b'14'} + request.cls.keypattern = r'[Aa]bc-\[1\].?-*' + request.cls.keypattern_redis = r'{some-ns},[Aa]bc-\[1\].?-*' + request.cls.matchedkeys = ['Abc-[1].0-def', 'abc-[1].1-ghi'] + request.cls.matchedkeys_redis = [b'{some-ns},Abc-[1].0-def', + b'{some-ns},abc-[1].1-ghi'] + request.cls.matcheddata_redis = [b'10', b'11'] + request.cls.matchedkeydata = {'Abc-[1].0-def': b'10', + 'abc-[1].1-ghi': b'11'} request.cls.group = 'some-group' request.cls.group_redis = '{some-ns},some-group' request.cls.groupmembers = set([b'm1', b'm2']) request.cls.groupmember = b'm1' - request.cls.is_atomic = True request.cls.configuration = Mock() mock_conf_params = _Configuration.Params(db_host=None, @@ -141,36 +140,77 @@ class TestRedisBackend: def test_find_keys_function_success(self): self.mock_redis.keys.return_value = self.matchedkeys_redis - ret = self.db.find_keys(self.ns, self.keyprefix) - self.mock_redis.keys.assert_called_once_with(self.keyprefix_redis) + ret = self.db.find_keys(self.ns, self.keypattern) + self.mock_redis.keys.assert_called_once_with(self.keypattern_redis) assert ret == self.matchedkeys def test_find_keys_function_returns_empty_list_when_no_matching_keys_found(self): self.mock_redis.keys.return_value = [] - ret = self.db.find_keys(self.ns, self.keyprefix) - self.mock_redis.keys.assert_called_once_with(self.keyprefix_redis) + ret = self.db.find_keys(self.ns, self.keypattern) + self.mock_redis.keys.assert_called_once_with(self.keypattern_redis) assert ret == [] def test_find_keys_function_can_map_redis_exception_to_sdl_exception(self): self.mock_redis.keys.side_effect = redis_exceptions.ResponseError('redis error!') with pytest.raises(ricsdl.exceptions.RejectedByBackend): - self.db.find_keys(self.ns, self.keyprefix) + self.db.find_keys(self.ns, self.keypattern) + + def test_find_keys_function_can_raise_exception_when_redis_key_convert_to_string_fails(self): + # Redis returns an illegal key, which conversion to string fails + corrupt_redis_key = b'\x81' + self.mock_redis.keys.return_value = [corrupt_redis_key] + with pytest.raises(ricsdl.exceptions.RejectedByBackend) as excinfo: + self.db.find_keys(self.ns, self.keypattern) + assert f"Namespace {self.ns} key:{corrupt_redis_key} " + "has no namespace prefix" in str(excinfo.value) + + def test_find_keys_function_can_raise_exception_when_redis_key_is_without_prefix(self): + # Redis returns an illegal key, which doesn't have comma separated namespace prefix + corrupt_redis_key = 'some-corrupt-key' + self.mock_redis.keys.return_value = [f'{corrupt_redis_key}'.encode()] + with pytest.raises(ricsdl.exceptions.RejectedByBackend) as excinfo: + self.db.find_keys(self.ns, self.keypattern) + assert f"Namespace {self.ns} key:{corrupt_redis_key} " + "has no namespace prefix" in str(excinfo.value) def test_find_and_get_function_success(self): self.mock_redis.keys.return_value = self.matchedkeys_redis - self.mock_redis.mget.return_value = self.matcheddata_dl_redis - ret = self.db.find_and_get(self.ns, self.keyprefix, self.is_atomic) - self.mock_redis.keys.assert_called_once_with(self.keyprefix_redis) + self.mock_redis.mget.return_value = self.matcheddata_redis + ret = self.db.find_and_get(self.ns, self.keypattern) + self.mock_redis.keys.assert_called_once_with(self.keypattern_redis) self.mock_redis.mget.assert_called_once_with([i.decode() for i in self.matchedkeys_redis]) - assert ret == self.matcheddata_dm + assert ret == self.matchedkeydata def test_find_and_get_function_returns_empty_dict_when_no_matching_keys_exist(self): self.mock_redis.keys.return_value = list() - ret = self.db.find_and_get(self.ns, self.keyprefix, self.is_atomic) - self.mock_redis.keys.assert_called_once_with(self.keyprefix_redis) + ret = self.db.find_and_get(self.ns, self.keypattern) + self.mock_redis.keys.assert_called_once_with(self.keypattern_redis) assert not self.mock_redis.mget.called assert ret == dict() + def test_find_and_get_function_can_map_redis_exception_to_sdl_exception(self): + self.mock_redis.keys.side_effect = redis_exceptions.ResponseError('redis error!') + with pytest.raises(ricsdl.exceptions.RejectedByBackend): + self.db.find_and_get(self.ns, self.keypattern) + + def test_find_and_get_function_can_raise_exception_when_redis_key_convert_to_string_fails(self): + # Redis returns an illegal key, which conversion to string fails + corrupt_redis_key = b'\x81' + self.mock_redis.keys.return_value = [corrupt_redis_key] + with pytest.raises(ricsdl.exceptions.RejectedByBackend) as excinfo: + self.db.find_and_get(self.ns, self.keypattern) + assert f"Namespace {self.ns} key:{corrupt_redis_key} " + "has no namespace prefix" in str(excinfo.value) + + def test_find_and_get_function_can_raise_exception_when_redis_key_is_without_prefix(self): + # Redis returns an illegal key, which doesn't have comma separated namespace prefix + corrupt_redis_key = 'some-corrupt-key' + self.mock_redis.keys.return_value = [f'{corrupt_redis_key}'.encode()] + with pytest.raises(ricsdl.exceptions.RejectedByBackend) as excinfo: + self.db.find_and_get(self.ns, self.keypattern) + assert f"Namespace {self.ns} key:{corrupt_redis_key} " + "has no namespace prefix" in str(excinfo.value) + def test_remove_function_success(self): self.db.remove(self.ns, self.keys) self.mock_redis.delete.assert_called_once_with(*self.keys_redis) @@ -353,6 +393,16 @@ class TestRedisBackendLock: keys=[self.lockname_redis], args=[123], client=self.mock_redis) assert ret == 2 + def test_get_validity_time_function_second_fraction_success(self): + self.mock_redis_lock.name = self.lockname_redis + self.mock_redis_lock.local.token = 123 + self.mock_lua_get_validity_time.return_value = 234 + + ret = self.lock.get_validity_time() + self.mock_lua_get_validity_time.assert_called_once_with( + keys=[self.lockname_redis], args=[123], client=self.mock_redis) + assert ret == 0.234 + def test_get_validity_time_function_can_raise_exception_if_lock_is_unlocked(self): self.mock_redis_lock.name = self.lockname_redis self.mock_redis_lock.local.token = None @@ -371,8 +421,17 @@ class TestRedisBackendLock: assert f"Getting validity time of a lock {self.lockname} failed with error code: -10" in str(excinfo.value) def test_redis_backend_lock_object_string_representation(self): - str_out = str(self.lock) - assert str_out is not None + expected_lock_info = {'lock namespace': 'some-ns', + 'lock name': 'some-lock-name', + 'lock status': 'locked'} + assert str(self.lock) == str(expected_lock_info) + + def test_redis_backend_lock_object_string_representation_can_catch_redis_exception(self): + self.mock_redis_lock.owned.side_effect = redis_exceptions.LockError('redis lock error!') + expected_lock_info = {'lock namespace': 'some-ns', + 'lock name': 'some-lock-name', + 'lock status': 'Error: redis lock error!'} + assert str(self.lock) == str(expected_lock_info) def test_redis_response_error_exception_is_mapped_to_rejected_by_backend_sdl_exception(): diff --git a/ricsdl-package/tests/test_syncstorage.py b/ricsdl-package/tests/test_syncstorage.py index 5f0cba4..00d3ed8 100644 --- a/ricsdl-package/tests/test_syncstorage.py +++ b/ricsdl-package/tests/test_syncstorage.py @@ -32,12 +32,11 @@ def sync_storage_fixture(request): request.cls.ns = 'some-ns' request.cls.key = 'a' request.cls.keys = {'a', 'b'} - request.cls.dm = {'a': b'1', 'b': b'2'} + request.cls.dm = {'b': b'2', 'a': b'1'} request.cls.old_data = b'1' request.cls.new_data = b'3' request.cls.keyprefix = 'x' request.cls.matchedkeys = ['x1', 'x2', 'x3', 'x4', 'x5'] - request.cls.is_atomic = True request.cls.group = 'some-group' request.cls.groupmembers = set([b'm1', b'm2']) request.cls.groupmember = b'm1' @@ -119,6 +118,8 @@ class TestSyncStorage: assert len(call_args[1]) == len(self.keys) assert all(k in call_args[1] for k in self.keys) assert ret == self.dm + # Validate that SDL returns a dictionary with keys in alphabetical order + assert sorted(self.dm)[0] == list(ret.keys())[0] def test_get_function_can_return_empty_dict_when_no_key_values_exist(self): self.mock_db_backend.get.return_value = dict() @@ -156,25 +157,23 @@ class TestSyncStorage: def test_find_and_get_function_success(self): self.mock_db_backend.find_and_get.return_value = self.dm - ret = self.storage.find_and_get(self.ns, self.keyprefix, self.is_atomic) - self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix, - self.is_atomic) + ret = self.storage.find_and_get(self.ns, self.keyprefix) + self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix) assert ret == self.dm + # Validate that SDL returns a dictionary with keys in alphabetical order + assert sorted(self.dm)[0] == list(ret.keys())[0] def test_find_and_get_function_can_return_empty_dict_when_no_keys_exist(self): self.mock_db_backend.find_and_get.return_value = dict() - ret = self.storage.find_and_get(self.ns, self.keyprefix, self.is_atomic) - self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix, - self.is_atomic) + ret = self.storage.find_and_get(self.ns, self.keyprefix) + self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix) assert ret == dict() def test_find_and_get_function_can_raise_exception_for_wrong_argument(self): with pytest.raises(SdlTypeError): - self.storage.find_and_get(0xbad, self.keyprefix, self.is_atomic) + self.storage.find_and_get(0xbad, self.keyprefix) with pytest.raises(SdlTypeError): - self.storage.find_and_get(self.ns, 0xbad, self.is_atomic) - with pytest.raises(SdlTypeError): - self.storage.find_and_get(self.ns, self.keyprefix, 0xbad) + self.storage.find_and_get(self.ns, 0xbad) def test_remove_function_success(self): self.storage.remove(self.ns, self.keys) @@ -214,7 +213,7 @@ class TestSyncStorage: def test_remove_all_function_success(self): self.mock_db_backend.find_keys.return_value = ['a1'] self.storage.remove_all(self.ns) - self.mock_db_backend.find_keys.assert_called_once_with(self.ns, '') + self.mock_db_backend.find_keys.assert_called_once_with(self.ns, '*') self.mock_db_backend.remove.assert_called_once_with(self.ns, self.mock_db_backend.find_keys.return_value) -- 2.16.6