From 091f15df0a26c5612997477fdbf996ced50a017c Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff Date: Sun, 4 Oct 2020 09:14:12 +0200 Subject: [PATCH 1/2] Add support for SCRIPT EXISTS subcommand I've added support for the SCRIPT EXISTS subcommand in the SCRIPTS command handler. The subcommand takes in a sequence of sha1 hash values of Lua scripts and checks if scripts with those sha1 hash values are currently registered with Redis. The command will return a redis List with the integers 1 and 0 to indicate which sha1 hash is known to Redis. In accordance with Redis, this subcommand returns an empty List if no hash values were passed as arguments to the subcommand. --- README.rst | 1 - fakeredis/_server.py | 4 +++- test/test_fakeredis.py | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 59074777..cc4920c3 100644 --- a/README.rst +++ b/README.rst @@ -310,7 +310,6 @@ scripting --------- * script debug - * script exists * script flush * script kill diff --git a/fakeredis/_server.py b/fakeredis/_server.py index a9af12e4..12541a28 100644 --- a/fakeredis/_server.py +++ b/fakeredis/_server.py @@ -2316,7 +2316,7 @@ def time(self): return [str(now_s).encode(), str(now_us).encode()] # Script commands - # TODO: script exists, script flush + # TODO: script flush # (script debug and script kill will probably not be supported) def _convert_redis_arg(self, lua_runtime, value): @@ -2475,6 +2475,8 @@ def script(self, subcmd, *args): sha1 = hashlib.sha1(script).hexdigest().encode() self._server.script_cache[sha1] = script return sha1 + elif casematch(subcmd, b'exists'): + return [int(sha1 in self._server.script_cache) for sha1 in args] else: raise SimpleError(BAD_SUBCOMMAND_MSG.format('SCRIPT')) diff --git a/test/test_fakeredis.py b/test/test_fakeredis.py index 2bb41648..2b150f24 100644 --- a/test/test_fakeredis.py +++ b/test/test_fakeredis.py @@ -4486,6 +4486,26 @@ def test_script(r): assert result == b'42' +def test_script_exists(r): + # test response for no arguments by bypassing the py-redis command + # as it requires at least one argument + assert raw_command(r, "SCRIPT EXISTS") == [] + + # use single character characters for non-existing scripts, as those + # will never be equal to an actual sha1 hash digest + assert r.script_exists("a") == [0] + assert r.script_exists("a", "b", "c", "d", "e", "f") == [0, 0, 0, 0, 0, 0] + + sha1_one = r.script_load("return 'a'") + assert r.script_exists(sha1_one) == [1] + assert r.script_exists(sha1_one, "a") == [1, 0] + assert r.script_exists("a", "b", "c", sha1_one, "e") == [0, 0, 0, 1, 0] + + sha1_two = r.script_load("return 'b'") + assert r.script_exists(sha1_one, sha1_two) == [1, 1] + assert r.script_exists("a", sha1_one, "c", sha1_two, "e", "f") == [0, 1, 0, 1, 0, 0] + + @fake_only def test_lua_log(r, caplog): logger = fakeredis._server.LOGGER From 6ca031c3f82038a89e3fba2ccd34080aad063002 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff Date: Sun, 4 Oct 2020 14:55:19 +0200 Subject: [PATCH 2/2] Add support for SCRIPT FLUSH subcommand This commit adds support for the SCRIPT FLUSH subcommand which flushes all the loaded scripts from Redis. The subcommand takes no arguments and returns OK after completing its work. --- README.rst | 1 - fakeredis/_server.py | 8 ++++++-- test/test_fakeredis.py | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index cc4920c3..374f2223 100644 --- a/README.rst +++ b/README.rst @@ -310,7 +310,6 @@ scripting --------- * script debug - * script flush * script kill diff --git a/fakeredis/_server.py b/fakeredis/_server.py index 12541a28..d114a1a4 100644 --- a/fakeredis/_server.py +++ b/fakeredis/_server.py @@ -2316,8 +2316,7 @@ def time(self): return [str(now_s).encode(), str(now_us).encode()] # Script commands - # TODO: script flush - # (script debug and script kill will probably not be supported) + # script debug and script kill will probably not be supported def _convert_redis_arg(self, lua_runtime, value): # Type checks are exact to avoid issues like bool being a subclass of int. @@ -2477,6 +2476,11 @@ def script(self, subcmd, *args): return sha1 elif casematch(subcmd, b'exists'): return [int(sha1 in self._server.script_cache) for sha1 in args] + elif casematch(subcmd, b'flush'): + if len(args) != 0: + raise SimpleError(BAD_SUBCOMMAND_MSG.format('SCRIPT')) + self._server.script_cache = {} + return OK else: raise SimpleError(BAD_SUBCOMMAND_MSG.format('SCRIPT')) diff --git a/test/test_fakeredis.py b/test/test_fakeredis.py index 2b150f24..1541cff2 100644 --- a/test/test_fakeredis.py +++ b/test/test_fakeredis.py @@ -4506,6 +4506,26 @@ def test_script_exists(r): assert r.script_exists("a", sha1_one, "c", sha1_two, "e", "f") == [0, 1, 0, 1, 0, 0] +@pytest.mark.parametrize("args", [("a",), tuple("abcdefghijklmn")]) +def test_script_flush_errors_with_args(r, args): + with pytest.raises(redis.ResponseError): + raw_command(r, "SCRIPT FLUSH %s" % " ".join(args)) + + +def test_script_flush(r): + # generate/load six unique scripts and store their sha1 hash values + sha1_values = [r.script_load("return '%s'" % char) for char in "abcdef"] + + # assert the scripts all exist prior to flushing + assert r.script_exists(*sha1_values) == [1] * len(sha1_values) + + # flush and assert OK response + assert r.script_flush() is True + + # assert none of the scripts exists after flushing + assert r.script_exists(*sha1_values) == [0] * len(sha1_values) + + @fake_only def test_lua_log(r, caplog): logger = fakeredis._server.LOGGER