summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorChayim <chayim@users.noreply.github.com>2021-10-25 17:06:04 +0300
committerGitHub <noreply@github.com>2021-10-25 17:06:04 +0300
commit3946da29d7e451a20289fb6e282516fa24e402af (patch)
tree25cf4b73b4e00d66c75288790616ea882823e2b7 /tests
parent0ef4c0711693b4b313ce97261214bd151d8261d5 (diff)
downloadredis-py-3946da29d7e451a20289fb6e282516fa24e402af.tar.gz
redisjson support (#1636)
Diffstat (limited to 'tests')
-rw-r--r--tests/conftest.py33
-rw-r--r--tests/test_commands.py9
-rw-r--r--tests/test_connection.py33
-rw-r--r--tests/test_json.py235
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