diff options
author | Chayim <chayim@users.noreply.github.com> | 2021-10-25 17:06:04 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-25 17:06:04 +0300 |
commit | 3946da29d7e451a20289fb6e282516fa24e402af (patch) | |
tree | 25cf4b73b4e00d66c75288790616ea882823e2b7 /tests | |
parent | 0ef4c0711693b4b313ce97261214bd151d8261d5 (diff) | |
download | redis-py-3946da29d7e451a20289fb6e282516fa24e402af.tar.gz |
redisjson support (#1636)
Diffstat (limited to 'tests')
-rw-r--r-- | tests/conftest.py | 33 | ||||
-rw-r--r-- | tests/test_commands.py | 9 | ||||
-rw-r--r-- | tests/test_connection.py | 33 | ||||
-rw-r--r-- | tests/test_json.py | 235 |
4 files changed, 304 insertions, 6 deletions
diff --git a/tests/conftest.py b/tests/conftest.py index c099463..9ca429d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,6 +11,7 @@ from urllib.parse import urlparse REDIS_INFO = {} default_redis_url = "redis://localhost:6379/9" +default_redismod_url = "redis://localhost:36379/9" def pytest_addoption(parser): @@ -19,6 +20,12 @@ def pytest_addoption(parser): help="Redis connection string," " defaults to `%(default)s`") + parser.addoption('--redismod-url', default=default_redismod_url, + action="store", + help="Connection string to redis server" + " with loaded modules," + " defaults to `%(default)s`") + def _get_info(redis_url): client = redis.Redis.from_url(redis_url) @@ -35,6 +42,11 @@ def pytest_sessionstart(session): REDIS_INFO["version"] = version REDIS_INFO["arch_bits"] = arch_bits + # module info + redismod_url = session.config.getoption("--redismod-url") + info = _get_info(redismod_url) + REDIS_INFO["modules"] = info["modules"] + def skip_if_server_version_lt(min_version): redis_version = REDIS_INFO["version"] @@ -57,6 +69,21 @@ def skip_unless_arch_bits(arch_bits): reason="server is not {}-bit".format(arch_bits)) +def skip_ifmodversion_lt(min_version: str, module_name: str): + modules = REDIS_INFO["modules"] + if modules == []: + return pytest.mark.skipif(True, reason="No redis modules found") + + for j in modules: + if module_name == j.get('name'): + version = j.get('ver') + mv = int(min_version.replace(".", "")) + check = version < mv + return pytest.mark.skipif(check, reason="Redis module version") + + raise AttributeError("No redis module named {}".format(module_name)) + + def _get_client(cls, request, single_connection_client=True, flushdb=True, **kwargs): """ @@ -89,6 +116,12 @@ def _get_client(cls, request, single_connection_client=True, flushdb=True, @pytest.fixture() +def modclient(request, port=36379, **kwargs): + with _get_client(redis.Redis, request, port=port, **kwargs) as client: + yield client + + +@pytest.fixture() def r(request): with _get_client(redis.Redis, request) as client: yield client diff --git a/tests/test_commands.py b/tests/test_commands.py index 694090e..6d65931 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -2552,7 +2552,7 @@ class TestRedisCommands: assert r.geosearch('barcelona', member='place3', radius=100, unit='km', count=2) == [b'place3', b'\x80place2'] assert r.geosearch('barcelona', member='place3', radius=100, - unit='km', count=1, any=True)[0] \ + unit='km', count=1, any=1)[0] \ in [b'place1', b'place3', b'\x80place2'] @skip_unless_arch_bits(64) @@ -2657,8 +2657,7 @@ class TestRedisCommands: # use any without count with pytest.raises(exceptions.DataError): - assert r.geosearch('barcelona', member='place3', - radius=100, any=True) + assert r.geosearch('barcelona', member='place3', radius=100, any=1) @skip_if_server_version_lt('6.2.0') def test_geosearchstore(self, r): @@ -3239,7 +3238,6 @@ class TestRedisCommands: response = r.xpending_range(stream, group, min='-', max='+', count=5, consumername=consumer1) - assert len(response) == 1 assert response[0]['message_id'] == m1 assert response[0]['consumer'] == consumer1.encode() @@ -3604,7 +3602,8 @@ class TestRedisCommands: @skip_if_server_version_lt('4.0.0') def test_module_list(self, r): assert isinstance(r.module_list(), list) - assert not r.module_list() + for x in r.module_list(): + assert isinstance(x, dict) @skip_if_server_version_lt('2.8.13') def test_command_count(self, r): diff --git a/tests/test_connection.py b/tests/test_connection.py index 128bac7..6728e0a 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1,8 +1,10 @@ from unittest import mock +import types import pytest -from redis.exceptions import InvalidResponse +from redis.exceptions import InvalidResponse, ModuleError from redis.utils import HIREDIS_AVAILABLE +from .conftest import skip_if_server_version_lt @pytest.mark.skipif(HIREDIS_AVAILABLE, reason='PythonParser only') @@ -13,3 +15,32 @@ def test_invalid_response(r): with pytest.raises(InvalidResponse) as cm: parser.read_response() assert str(cm.value) == 'Protocol Error: %r' % raw + + +@skip_if_server_version_lt('4.0.0') +def test_loaded_modules(r, modclient): + assert r.loaded_modules == [] + assert 'rejson' in modclient.loaded_modules + + +@skip_if_server_version_lt('4.0.0') +def test_loading_external_modules(r, modclient): + def inner(): + pass + + with pytest.raises(ModuleError): + r.load_external_module('rejson', 'myfuncname', inner) + + modclient.load_external_module('rejson', 'myfuncname', inner) + assert getattr(modclient, 'myfuncname') == inner + assert isinstance(getattr(modclient, 'myfuncname'), types.FunctionType) + + # and call it + from redis.commands import RedisModuleCommands + j = RedisModuleCommands.json + modclient.load_external_module('rejson', 'sometestfuncname', j) + + d = {'hello': 'world!'} + mod = j(modclient) + mod.set("fookey", ".", d) + assert mod.get('fookey') == d diff --git a/tests/test_json.py b/tests/test_json.py new file mode 100644 index 0000000..96675f1 --- /dev/null +++ b/tests/test_json.py @@ -0,0 +1,235 @@ +import pytest +import redis +from redis.commands.json.path import Path +from .conftest import skip_ifmodversion_lt + + +@pytest.fixture +def client(modclient): + modclient.flushdb() + return modclient + + +@pytest.mark.json +def test_json_setbinarykey(client): + d = {"hello": "world", b"some": "value"} + with pytest.raises(TypeError): + client.json().set("somekey", Path.rootPath(), d) + assert client.json().set("somekey", Path.rootPath(), d, decode_keys=True) + + +@pytest.mark.json +def test_json_setgetdeleteforget(client): + assert client.json().set("foo", Path.rootPath(), "bar") + assert client.json().get("foo") == "bar" + assert client.json().get("baz") is None + assert client.json().delete("foo") == 1 + assert client.json().forget("foo") == 0 # second delete + assert client.exists("foo") == 0 + + +@pytest.mark.json +def test_justaget(client): + client.json().set("foo", Path.rootPath(), "bar") + assert client.json().get("foo") == "bar" + + +@pytest.mark.json +def test_json_get_jset(client): + assert client.json().set("foo", Path.rootPath(), "bar") + assert "bar" == client.json().get("foo") + assert client.json().get("baz") is None + assert 1 == client.json().delete("foo") + assert client.exists("foo") == 0 + + +@pytest.mark.json +def test_nonascii_setgetdelete(client): + assert client.json().set("notascii", Path.rootPath(), + "hyvää-élève") is True + assert "hyvää-élève" == client.json().get("notascii", no_escape=True) + assert 1 == client.json().delete("notascii") + assert client.exists("notascii") == 0 + + +@pytest.mark.json +def test_jsonsetexistentialmodifiersshouldsucceed(client): + obj = {"foo": "bar"} + assert client.json().set("obj", Path.rootPath(), obj) + + # Test that flags prevent updates when conditions are unmet + assert client.json().set("obj", Path("foo"), "baz", nx=True) is None + assert client.json().set("obj", Path("qaz"), "baz", xx=True) is None + + # Test that flags allow updates when conditions are met + assert client.json().set("obj", Path("foo"), "baz", xx=True) + assert client.json().set("obj", Path("qaz"), "baz", nx=True) + + # Test that flags are mutually exlusive + with pytest.raises(Exception): + client.json().set("obj", Path("foo"), "baz", nx=True, xx=True) + + +@pytest.mark.json +def test_mgetshouldsucceed(client): + client.json().set("1", Path.rootPath(), 1) + client.json().set("2", Path.rootPath(), 2) + r = client.json().mget(Path.rootPath(), "1", "2") + e = [1, 2] + assert e == r + + +@pytest.mark.json +@skip_ifmodversion_lt("99.99.99", "ReJSON") # todo: update after the release +def test_clearShouldSucceed(client): + client.json().set("arr", Path.rootPath(), [0, 1, 2, 3, 4]) + assert 1 == client.json().clear("arr", Path.rootPath()) + assert [] == client.json().get("arr") + + +@pytest.mark.json +def test_typeshouldsucceed(client): + client.json().set("1", Path.rootPath(), 1) + assert b"integer" == client.json().type("1") + + +@pytest.mark.json +def test_numincrbyshouldsucceed(client): + client.json().set("num", Path.rootPath(), 1) + assert 2 == client.json().numincrby("num", Path.rootPath(), 1) + assert 2.5 == client.json().numincrby("num", Path.rootPath(), 0.5) + assert 1.25 == client.json().numincrby("num", Path.rootPath(), -1.25) + + +@pytest.mark.json +def test_nummultbyshouldsucceed(client): + client.json().set("num", Path.rootPath(), 1) + assert 2 == client.json().nummultby("num", Path.rootPath(), 2) + assert 5 == client.json().nummultby("num", Path.rootPath(), 2.5) + assert 2.5 == client.json().nummultby("num", Path.rootPath(), 0.5) + + +@pytest.mark.json +@skip_ifmodversion_lt("99.99.99", "ReJSON") # todo: update after the release +def test_toggleShouldSucceed(client): + client.json().set("bool", Path.rootPath(), False) + assert client.json().toggle("bool", Path.rootPath()) + assert not client.json().toggle("bool", Path.rootPath()) + # check non-boolean value + client.json().set("num", Path.rootPath(), 1) + with pytest.raises(redis.exceptions.ResponseError): + client.json().toggle("num", Path.rootPath()) + + +@pytest.mark.json +def test_strappendshouldsucceed(client): + client.json().set("str", Path.rootPath(), "foo") + assert 6 == client.json().strappend("str", "bar", Path.rootPath()) + assert "foobar" == client.json().get("str", Path.rootPath()) + + +@pytest.mark.json +def test_debug(client): + client.json().set("str", Path.rootPath(), "foo") + assert 24 == client.json().debug("str", Path.rootPath()) + + +@pytest.mark.json +def test_strlenshouldsucceed(client): + client.json().set("str", Path.rootPath(), "foo") + assert 3 == client.json().strlen("str", Path.rootPath()) + client.json().strappend("str", "bar", Path.rootPath()) + assert 6 == client.json().strlen("str", Path.rootPath()) + + +@pytest.mark.json +def test_arrappendshouldsucceed(client): + client.json().set("arr", Path.rootPath(), [1]) + assert 2 == client.json().arrappend("arr", Path.rootPath(), 2) + assert 4 == client.json().arrappend("arr", Path.rootPath(), 3, 4) + assert 7 == client.json().arrappend("arr", Path.rootPath(), *[5, 6, 7]) + + +@pytest.mark.json +def testArrIndexShouldSucceed(client): + client.json().set("arr", Path.rootPath(), [0, 1, 2, 3, 4]) + assert 1 == client.json().arrindex("arr", Path.rootPath(), 1) + assert -1 == client.json().arrindex("arr", Path.rootPath(), 1, 2) + + +@pytest.mark.json +def test_arrinsertshouldsucceed(client): + client.json().set("arr", Path.rootPath(), [0, 4]) + assert 5 - -client.json().arrinsert( + "arr", + Path.rootPath(), + 1, + *[ + 1, + 2, + 3, + ] + ) + assert [0, 1, 2, 3, 4] == client.json().get("arr") + + +@pytest.mark.json +def test_arrlenshouldsucceed(client): + client.json().set("arr", Path.rootPath(), [0, 1, 2, 3, 4]) + assert 5 == client.json().arrlen("arr", Path.rootPath()) + + +@pytest.mark.json +def test_arrpopshouldsucceed(client): + client.json().set("arr", Path.rootPath(), [0, 1, 2, 3, 4]) + assert 4 == client.json().arrpop("arr", Path.rootPath(), 4) + assert 3 == client.json().arrpop("arr", Path.rootPath(), -1) + assert 2 == client.json().arrpop("arr", Path.rootPath()) + assert 0 == client.json().arrpop("arr", Path.rootPath(), 0) + assert [1] == client.json().get("arr") + + +@pytest.mark.json +def test_arrtrimshouldsucceed(client): + client.json().set("arr", Path.rootPath(), [0, 1, 2, 3, 4]) + assert 3 == client.json().arrtrim("arr", Path.rootPath(), 1, 3) + assert [1, 2, 3] == client.json().get("arr") + + +@pytest.mark.json +def test_respshouldsucceed(client): + obj = {"foo": "bar", "baz": 1, "qaz": True} + client.json().set("obj", Path.rootPath(), obj) + assert b"bar" == client.json().resp("obj", Path("foo")) + assert 1 == client.json().resp("obj", Path("baz")) + assert client.json().resp("obj", Path("qaz")) + + +@pytest.mark.json +def test_objkeysshouldsucceed(client): + obj = {"foo": "bar", "baz": "qaz"} + client.json().set("obj", Path.rootPath(), obj) + keys = client.json().objkeys("obj", Path.rootPath()) + keys.sort() + exp = list(obj.keys()) + exp.sort() + assert exp == keys + + +@pytest.mark.json +def test_objlenshouldsucceed(client): + obj = {"foo": "bar", "baz": "qaz"} + client.json().set("obj", Path.rootPath(), obj) + assert len(obj) == client.json().objlen("obj", Path.rootPath()) + + +# @pytest.mark.pipeline +# @pytest.mark.json +# def test_pipelineshouldsucceed(client): +# p = client.json().pipeline() +# p.set("foo", Path.rootPath(), "bar") +# p.get("foo") +# p.delete("foo") +# assert [True, "bar", 1] == p.execute() +# assert client.keys() == [] +# assert client.get("foo") is None |