refactor: refactor fakeredis tests (#3852)

* refactor:fakeredis tests
This commit is contained in:
Daniel M 2024-10-03 05:41:05 -04:00 committed by GitHub
parent 44fbe09108
commit 1958e09a9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 311 additions and 1203 deletions

View file

@ -75,8 +75,6 @@ jobs:
run: |
poetry run pytest test/ \
--ignore test/test_hypothesis.py \
--ignore test/test_geo_commands.py \
--ignore test/test_bitmap_commands.py \
--ignore test/test_json/ \
--ignore test/test_mixins/test_bitmap_commands.py \
--junit-xml=results-tests.xml --html=report-tests.html -v

View file

@ -144,22 +144,22 @@ test = ["pytest (>=6)"]
[[package]]
name = "fakeredis"
version = "2.23.5"
version = "2.25.1"
description = "Python implementation of redis API, can be used for testing purposes."
optional = false
python-versions = "<4.0,>=3.7"
files = [
{file = "fakeredis-2.23.5-py3-none-any.whl", hash = "sha256:4d85b1b6b3a80cbbb3a8967f8686f7bf6ddf5bd7cd5ac7ac90b3561d8c3a7ddb"},
{file = "fakeredis-2.23.5.tar.gz", hash = "sha256:edffc79fdce0f1d83cbb20b52694a9cba4a5fe5beb627c11722a42aa0fa44f52"},
{file = "fakeredis-2.25.1-py3-none-any.whl", hash = "sha256:d08dcbaceae0804db4644fa634106e3c42d76fe4d11aea2949eda768df0c6450"},
{file = "fakeredis-2.25.1.tar.gz", hash = "sha256:e9e73bacf412d1d942ee7f80525dc188182158e82d41be57eb9c4e71f7474ac8"},
]
[package.dependencies]
jsonpath-ng = {version = ">=1.6,<2.0", optional = true, markers = "extra == \"json\""}
lupa = {version = ">=2.1,<3.0", optional = true, markers = "extra == \"lua\""}
pyprobables = {version = ">=0.6,<0.7", optional = true, markers = "extra == \"bf\" or extra == \"cf\" or extra == \"probabilistic\""}
redis = ">=4"
redis = {version = ">=4.3", markers = "python_full_version > \"3.8.0\""}
sortedcontainers = ">=2,<3"
typing_extensions = {version = ">=4.7,<5.0", markers = "python_version < \"3.11\""}
typing-extensions = {version = ">=4.7,<5.0", markers = "python_version < \"3.11\""}
[package.extras]
bf = ["pyprobables (>=0.6,<0.7)"]
@ -170,13 +170,13 @@ probabilistic = ["pyprobables (>=0.6,<0.7)"]
[[package]]
name = "hypothesis"
version = "6.111.1"
version = "6.112.2"
description = "A library for property-based testing"
optional = false
python-versions = ">=3.8"
files = [
{file = "hypothesis-6.111.1-py3-none-any.whl", hash = "sha256:9422adbac4b2104f6cf92dc6604b5c9df975efc08ffc7145ecc39bc617243835"},
{file = "hypothesis-6.111.1.tar.gz", hash = "sha256:6ab6185a858fa692bf125c0d0a936134edc318bee01c05e407c71c9ead0b61c5"},
{file = "hypothesis-6.112.2-py3-none-any.whl", hash = "sha256:914b55f75b7c6f653cd36fef66b61a773a51c1e363939fcbc0216773ff4ee0d9"},
{file = "hypothesis-6.112.2.tar.gz", hash = "sha256:90cd62d9487eaf294bf0dceb47dbaca6432408b2e9417cfa6e3409313dbde95b"},
]
[package.dependencies]
@ -185,10 +185,10 @@ exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
sortedcontainers = ">=2.1.0,<3.0.0"
[package.extras]
all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.66)", "django (>=3.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.12)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.17.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.1)"]
all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.72)", "django (>=3.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.14)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.17.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.2)"]
cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"]
codemods = ["libcst (>=0.3.16)"]
crosshair = ["crosshair-tool (>=0.0.66)", "hypothesis-crosshair (>=0.0.12)"]
crosshair = ["crosshair-tool (>=0.0.72)", "hypothesis-crosshair (>=0.0.14)"]
dateutil = ["python-dateutil (>=1.4)"]
django = ["django (>=3.2)"]
dpcontracts = ["dpcontracts (>=0.4)"]
@ -199,7 +199,7 @@ pandas = ["pandas (>=1.1)"]
pytest = ["pytest (>=4.6)"]
pytz = ["pytz (>=2014.1)"]
redis = ["redis (>=3.0.0)"]
zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2024.1)"]
zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2024.2)"]
[[package]]
name = "iniconfig"
@ -457,13 +457,13 @@ files = [
[[package]]
name = "pytest"
version = "8.3.2"
version = "8.3.3"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"},
{file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"},
{file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"},
{file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"},
]
[package.dependencies]
@ -583,21 +583,21 @@ pytest = ">=7.0.0"
[[package]]
name = "redis"
version = "5.0.8"
version = "5.1.0"
description = "Python client for Redis database and key-value store"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"},
{file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"},
{file = "redis-5.1.0-py3-none-any.whl", hash = "sha256:fd4fccba0d7f6aa48c58a78d76ddb4afc698f5da4a2c1d03d916e4fd7ab88cdd"},
{file = "redis-5.1.0.tar.gz", hash = "sha256:b756df1e4a3858fcc0ef861f3fc53623a96c41e2b1f5304e09e0fe758d333d40"},
]
[package.dependencies]
async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""}
[package.extras]
hiredis = ["hiredis (>1.0.0)"]
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
hiredis = ["hiredis (>=3.0.0)"]
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"]
[[package]]
name = "sortedcontainers"
@ -612,13 +612,13 @@ files = [
[[package]]
name = "tomli"
version = "2.0.1"
version = "2.0.2"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
{file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"},
{file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
]
[[package]]
@ -635,4 +635,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "942f6f5d521576b80ff3df540956c1d9c16826b9d82bd4f9b15d439a1df98f9c"
content-hash = "c7fb24a461b6c934fb5ef67a10701aff7bab809ee76c3cff267959c9798a6a81"

View file

@ -19,7 +19,7 @@ maintainers = [
[tool.poetry.dependencies]
python = "^3.10"
redis = ">=5"
fakeredis = { version = "^2.23", extras = ["json", "bf", "cf", "lua"] }
fakeredis = { version = "^2.25", extras = ["json", "bf", "cf", "lua"] }
hypothesis = "^6.111"
pytest = "^8.3"
pytest-timeout = "^2.3.1"

View file

@ -1,5 +1,4 @@
import asyncio
import re
import sys
if sys.version_info >= (3, 11):
@ -11,14 +10,10 @@ import pytest_asyncio
import redis
import redis.asyncio
from fakeredis import FakeServer, aioredis, FakeAsyncRedis, FakeStrictRedis
from fakeredis import FakeServer, aioredis
from test import testtools
pytestmark = []
fake_only = pytest.mark.parametrize(
"async_redis", [pytest.param("fake", marks=pytest.mark.fake)], indirect=True
)
pytestmark.extend(
[
pytest.mark.asyncio,
@ -183,31 +178,6 @@ class TestScripts:
await async_redis.eval('return redis.call("ZCOUNT", KEYS[1])', 1, "foo")
@testtools.run_test_if_redispy_ver("gte", "5.1")
async def test_repr_redis_51(async_redis: redis.asyncio.Redis):
assert re.fullmatch(
r"<redis.asyncio.connection.ConnectionPool("
r"<fakeredis.aioredis.FakeConnection(server=<fakeredis._server.FakeServer object at .*>,db=0)>)>",
repr(async_redis.connection_pool),
)
@fake_only
@pytest.mark.disconnected
async def test_not_connected(async_redis: redis.asyncio.Redis):
with pytest.raises(redis.asyncio.ConnectionError):
await async_redis.ping()
@fake_only
async def test_disconnect_server(async_redis, fake_server):
await async_redis.ping()
fake_server.connected = False
with pytest.raises(redis.asyncio.ConnectionError):
await async_redis.ping()
fake_server.connected = True
async def test_type(async_redis: redis.asyncio.Redis):
await async_redis.set("string_key", "value")
await async_redis.lpush("list_key", "value")
@ -238,78 +208,6 @@ async def test_xdel(async_redis: redis.asyncio.Redis):
assert await async_redis.xdel(stream, m2, m3) == 2
@pytest.mark.fake
async def test_from_url():
r0 = aioredis.FakeRedis.from_url("redis://localhost?db=0")
r1 = aioredis.FakeRedis.from_url("redis://localhost?db=1")
# Check that they are indeed different databases
await r0.set("foo", "a")
await r1.set("foo", "b")
assert await r0.get("foo") == b"a"
assert await r1.get("foo") == b"b"
await r0.connection_pool.disconnect()
await r1.connection_pool.disconnect()
@pytest.mark.fake
async def test_from_url_with_version():
r0 = aioredis.FakeRedis.from_url("redis://localhost?db=0", version=(6,))
r1 = aioredis.FakeRedis.from_url("redis://localhost?db=1", version=(6,))
# Check that they are indeed different databases
await r0.set("foo", "a")
await r1.set("foo", "b")
assert await r0.get("foo") == b"a"
assert await r1.get("foo") == b"b"
await r0.connection_pool.disconnect()
await r1.connection_pool.disconnect()
@fake_only
async def test_from_url_with_server(async_redis, fake_server):
r2 = aioredis.FakeRedis.from_url("redis://localhost", server=fake_server)
await async_redis.set("foo", "bar")
assert await r2.get("foo") == b"bar"
await r2.connection_pool.disconnect()
@pytest.mark.fake
async def test_without_server():
r = aioredis.FakeRedis()
assert await r.ping()
@pytest.mark.fake
async def test_without_server_disconnected():
r = aioredis.FakeRedis(connected=False)
with pytest.raises(redis.asyncio.ConnectionError):
await r.ping()
@pytest.mark.fake
async def test_async():
# arrange
cache = aioredis.FakeRedis()
# act
await cache.set("fakeredis", "plz")
x = await cache.get("fakeredis")
# assert
assert x == b"plz"
@testtools.run_test_if_redispy_ver("gte", "4.4.0")
@pytest.mark.parametrize("nowait", [False, True])
@pytest.mark.fake
async def test_connection_disconnect(nowait):
server = FakeServer()
r = aioredis.FakeRedis(server=server)
conn = await r.connection_pool.get_connection("_")
assert conn is not None
await conn.disconnect(nowait=nowait)
assert conn._sock is None
async def test_connection_with_username_and_password():
server = FakeServer()
r = aioredis.FakeRedis(server=server, username="username", password="password")
@ -320,31 +218,6 @@ async def test_connection_with_username_and_password():
assert result.decode() == test_value
@pytest.mark.fake
async def test_init_args():
sync_r1 = FakeStrictRedis()
r1 = FakeAsyncRedis()
r5 = FakeAsyncRedis()
r2 = FakeAsyncRedis(server=FakeServer())
shared_server = FakeServer()
r3 = FakeAsyncRedis(server=shared_server)
r4 = FakeAsyncRedis(server=shared_server)
await r1.set("foo", "bar")
await r3.set("bar", "baz")
assert await r1.get("foo") == b"bar"
assert await r5.get("foo") is None
assert sync_r1.get("foo") is None
assert await r2.get("foo") is None
assert await r3.get("foo") is None
assert await r3.get("bar") == b"baz"
assert await r4.get("bar") == b"baz"
assert await r1.get("bar") is None
@pytest.mark.asyncio
async def test_cause_fakeredis_bug(async_redis):
if sys.version_info < (3, 11):

View file

@ -1,484 +0,0 @@
from test import testtools
import pytest
import redis
import redis.client
from fakeredis import _msgs as msgs
from redis.exceptions import ResponseError
from test.testtools import raw_command
def test_ping(r: redis.Redis):
assert r.ping()
assert testtools.raw_command(r, "ping", "test") == b"test"
with pytest.raises(
redis.ResponseError, match=msgs.WRONG_ARGS_MSG6.format("ping")[4:]
):
raw_command(r, "ping", "arg1", "arg2")
def test_echo(r: redis.Redis):
assert r.echo(b"hello") == b"hello"
assert r.echo("hello") == b"hello"
@testtools.fake_only
def test_time(r, mocker):
fake_time = mocker.patch("time.time")
fake_time.return_value = 1234567890.1234567
assert r.time() == (1234567890, 123457)
fake_time.return_value = 1234567890.000001
assert r.time() == (1234567890, 1)
fake_time.return_value = 1234567890.9999999
assert r.time() == (1234567891, 0)
@pytest.mark.decode_responses
class TestDecodeResponses:
def test_decode_str(self, r):
r.set("foo", "bar")
assert r.get("foo") == "bar"
def test_decode_set(self, r):
r.sadd("foo", "member1")
assert r.smembers("foo") == {"member1"}
def test_decode_list(self, r):
r.rpush("foo", "a", "b")
assert r.lrange("foo", 0, -1) == ["a", "b"]
def test_decode_dict(self, r):
r.hset("foo", "key", "value")
assert r.hgetall("foo") == {"key": "value"}
def test_decode_error(self, r):
r.set("foo", "bar")
with pytest.raises(ResponseError) as exc_info:
r.hset("foo", "bar", "baz")
assert isinstance(exc_info.value.args[0], str)
@pytest.mark.disconnected
@testtools.fake_only
class TestFakeStrictRedisConnectionErrors:
def test_flushdb(self, r):
with pytest.raises(redis.ConnectionError):
r.flushdb()
def test_flushall(self, r):
with pytest.raises(redis.ConnectionError):
r.flushall()
def test_append(self, r):
with pytest.raises(redis.ConnectionError):
r.append("key", "value")
def test_bitcount(self, r):
with pytest.raises(redis.ConnectionError):
r.bitcount("key", 0, 20)
def test_decr(self, r):
with pytest.raises(redis.ConnectionError):
r.decr("key", 2)
def test_exists(self, r):
with pytest.raises(redis.ConnectionError):
r.exists("key")
def test_expire(self, r):
with pytest.raises(redis.ConnectionError):
r.expire("key", 20)
def test_pexpire(self, r):
with pytest.raises(redis.ConnectionError):
r.pexpire("key", 20)
def test_echo(self, r):
with pytest.raises(redis.ConnectionError):
r.echo("value")
def test_get(self, r):
with pytest.raises(redis.ConnectionError):
r.get("key")
def test_getbit(self, r):
with pytest.raises(redis.ConnectionError):
r.getbit("key", 2)
def test_getset(self, r):
with pytest.raises(redis.ConnectionError):
r.getset("key", "value")
def test_incr(self, r):
with pytest.raises(redis.ConnectionError):
r.incr("key")
def test_incrby(self, r):
with pytest.raises(redis.ConnectionError):
r.incrby("key")
def test_ncrbyfloat(self, r):
with pytest.raises(redis.ConnectionError):
r.incrbyfloat("key")
def test_keys(self, r):
with pytest.raises(redis.ConnectionError):
r.keys()
def test_mget(self, r):
with pytest.raises(redis.ConnectionError):
r.mget(["key1", "key2"])
def test_mset(self, r):
with pytest.raises(redis.ConnectionError):
r.mset({"key": "value"})
def test_msetnx(self, r):
with pytest.raises(redis.ConnectionError):
r.msetnx({"key": "value"})
def test_persist(self, r):
with pytest.raises(redis.ConnectionError):
r.persist("key")
def test_rename(self, r):
server = r.connection_pool.connection_kwargs["server"]
server.connected = True
r.set("key1", "value")
server.connected = False
with pytest.raises(redis.ConnectionError):
r.rename("key1", "key2")
server.connected = True
assert r.exists("key1")
def test_eval(self, r):
with pytest.raises(redis.ConnectionError):
r.eval("", 0)
def test_lpush(self, r):
with pytest.raises(redis.ConnectionError):
r.lpush("name", 1, 2)
def test_lrange(self, r):
with pytest.raises(redis.ConnectionError):
r.lrange("name", 1, 5)
def test_llen(self, r):
with pytest.raises(redis.ConnectionError):
r.llen("name")
def test_lrem(self, r):
with pytest.raises(redis.ConnectionError):
r.lrem("name", 2, 2)
def test_rpush(self, r):
with pytest.raises(redis.ConnectionError):
r.rpush("name", 1)
def test_lpop(self, r):
with pytest.raises(redis.ConnectionError):
r.lpop("name")
def test_lset(self, r):
with pytest.raises(redis.ConnectionError):
r.lset("name", 1, 4)
def test_rpushx(self, r):
with pytest.raises(redis.ConnectionError):
r.rpushx("name", 1)
def test_ltrim(self, r):
with pytest.raises(redis.ConnectionError):
r.ltrim("name", 1, 4)
def test_lindex(self, r):
with pytest.raises(redis.ConnectionError):
r.lindex("name", 1)
def test_lpushx(self, r):
with pytest.raises(redis.ConnectionError):
r.lpushx("name", 1)
def test_rpop(self, r):
with pytest.raises(redis.ConnectionError):
r.rpop("name")
def test_linsert(self, r):
with pytest.raises(redis.ConnectionError):
r.linsert("name", "where", "refvalue", "value")
def test_rpoplpush(self, r):
with pytest.raises(redis.ConnectionError):
r.rpoplpush("src", "dst")
def test_blpop(self, r):
with pytest.raises(redis.ConnectionError):
r.blpop("keys")
def test_brpop(self, r):
with pytest.raises(redis.ConnectionError):
r.brpop("keys")
def test_brpoplpush(self, r):
with pytest.raises(redis.ConnectionError):
r.brpoplpush("src", "dst")
def test_hdel(self, r):
with pytest.raises(redis.ConnectionError):
r.hdel("name")
def test_hexists(self, r):
with pytest.raises(redis.ConnectionError):
r.hexists("name", "key")
def test_hget(self, r):
with pytest.raises(redis.ConnectionError):
r.hget("name", "key")
def test_hgetall(self, r):
with pytest.raises(redis.ConnectionError):
r.hgetall("name")
def test_hincrby(self, r):
with pytest.raises(redis.ConnectionError):
r.hincrby("name", "key")
def test_hincrbyfloat(self, r):
with pytest.raises(redis.ConnectionError):
r.hincrbyfloat("name", "key")
def test_hkeys(self, r):
with pytest.raises(redis.ConnectionError):
r.hkeys("name")
def test_hlen(self, r):
with pytest.raises(redis.ConnectionError):
r.hlen("name")
def test_hset(self, r):
with pytest.raises(redis.ConnectionError):
r.hset("name", "key", 1)
def test_hsetnx(self, r):
with pytest.raises(redis.ConnectionError):
r.hsetnx("name", "key", 2)
def test_hmset(self, r):
with pytest.raises(redis.ConnectionError):
r.hmset("name", {"key": 1})
def test_hmget(self, r):
with pytest.raises(redis.ConnectionError):
r.hmget("name", ["a", "b"])
def test_hvals(self, r):
with pytest.raises(redis.ConnectionError):
r.hvals("name")
def test_sadd(self, r):
with pytest.raises(redis.ConnectionError):
r.sadd("name", 1, 2)
def test_scard(self, r):
with pytest.raises(redis.ConnectionError):
r.scard("name")
def test_sdiff(self, r):
with pytest.raises(redis.ConnectionError):
r.sdiff(["a", "b"])
def test_sdiffstore(self, r):
with pytest.raises(redis.ConnectionError):
r.sdiffstore("dest", ["a", "b"])
def test_sinter(self, r):
with pytest.raises(redis.ConnectionError):
r.sinter(["a", "b"])
def test_sinterstore(self, r):
with pytest.raises(redis.ConnectionError):
r.sinterstore("dest", ["a", "b"])
def test_sismember(self, r):
with pytest.raises(redis.ConnectionError):
r.sismember("name", 20)
def test_smembers(self, r):
with pytest.raises(redis.ConnectionError):
r.smembers("name")
def test_smove(self, r):
with pytest.raises(redis.ConnectionError):
r.smove("src", "dest", 20)
def test_spop(self, r):
with pytest.raises(redis.ConnectionError):
r.spop("name")
def test_srandmember(self, r):
with pytest.raises(redis.ConnectionError):
r.srandmember("name")
def test_srem(self, r):
with pytest.raises(redis.ConnectionError):
r.srem("name")
def test_sunion(self, r):
with pytest.raises(redis.ConnectionError):
r.sunion(["a", "b"])
def test_sunionstore(self, r):
with pytest.raises(redis.ConnectionError):
r.sunionstore("dest", ["a", "b"])
def test_zadd(self, r):
with pytest.raises(redis.ConnectionError):
r.zadd("name", {"key": "value"})
def test_zcard(self, r):
with pytest.raises(redis.ConnectionError):
r.zcard("name")
def test_zcount(self, r):
with pytest.raises(redis.ConnectionError):
r.zcount("name", 1, 5)
def test_zincrby(self, r):
with pytest.raises(redis.ConnectionError):
r.zincrby("name", 1, 1)
def test_zinterstore(self, r):
with pytest.raises(redis.ConnectionError):
r.zinterstore("dest", ["a", "b"])
def test_zrange(self, r):
with pytest.raises(redis.ConnectionError):
r.zrange("name", 1, 5)
def test_zrangebyscore(self, r):
with pytest.raises(redis.ConnectionError):
r.zrangebyscore("name", 1, 5)
def test_rangebylex(self, r):
with pytest.raises(redis.ConnectionError):
r.zrangebylex("name", 1, 4)
def test_zrem(self, r):
with pytest.raises(redis.ConnectionError):
r.zrem("name", "value")
def test_zremrangebyrank(self, r):
with pytest.raises(redis.ConnectionError):
r.zremrangebyrank("name", 1, 5)
def test_zremrangebyscore(self, r):
with pytest.raises(redis.ConnectionError):
r.zremrangebyscore("name", 1, 5)
def test_zremrangebylex(self, r):
with pytest.raises(redis.ConnectionError):
r.zremrangebylex("name", 1, 5)
def test_zlexcount(self, r):
with pytest.raises(redis.ConnectionError):
r.zlexcount("name", 1, 5)
def test_zrevrange(self, r):
with pytest.raises(redis.ConnectionError):
r.zrevrange("name", 1, 5, 1)
def test_zrevrangebyscore(self, r):
with pytest.raises(redis.ConnectionError):
r.zrevrangebyscore("name", 5, 1)
def test_zrevrangebylex(self, r):
with pytest.raises(redis.ConnectionError):
r.zrevrangebylex("name", 5, 1)
def test_zrevran(self, r):
with pytest.raises(redis.ConnectionError):
r.zrevrank("name", 2)
def test_zscore(self, r):
with pytest.raises(redis.ConnectionError):
r.zscore("name", 2)
def test_zunionstor(self, r):
with pytest.raises(redis.ConnectionError):
r.zunionstore("dest", ["1", "2"])
def test_pipeline(self, r):
with pytest.raises(redis.ConnectionError):
r.pipeline().watch("key")
def test_transaction(self, r):
with pytest.raises(redis.ConnectionError):
def func(a):
return a * a
r.transaction(func, 3)
def test_lock(self, r):
with pytest.raises(redis.ConnectionError):
with r.lock("name"):
pass
def test_pubsub(self, r):
with pytest.raises(redis.ConnectionError):
r.pubsub().subscribe("channel")
def test_pfadd(self, r):
with pytest.raises(redis.ConnectionError):
r.pfadd("name", 1)
def test_pfmerge(self, r):
with pytest.raises(redis.ConnectionError):
r.pfmerge("dest", "a", "b")
def test_scan(self, r):
with pytest.raises(redis.ConnectionError):
list(r.scan())
def test_sscan(self, r):
with pytest.raises(redis.ConnectionError):
r.sscan("name")
def test_hscan(self, r):
with pytest.raises(redis.ConnectionError):
r.hscan("name")
def test_scan_iter(self, r):
with pytest.raises(redis.ConnectionError):
list(r.scan_iter())
def test_sscan_iter(self, r):
with pytest.raises(redis.ConnectionError):
list(r.sscan_iter("name"))
def test_hscan_iter(self, r):
with pytest.raises(redis.ConnectionError):
list(r.hscan_iter("name"))
@pytest.mark.disconnected
@testtools.fake_only
class TestPubSubConnected:
@pytest.fixture
def pubsub(self, r):
return r.pubsub()
def test_basic_subscribe(self, pubsub):
with pytest.raises(redis.ConnectionError):
pubsub.subscribe("logs")
def test_subscription_conn_lost(self, fake_server, pubsub):
fake_server.connected = True
pubsub.subscribe("logs")
fake_server.connected = False
# The initial message is already in the pipe
msg = pubsub.get_message()
check = {"type": "subscribe", "pattern": None, "channel": b"logs", "data": 1}
assert msg == check, "Message was not published to channel"
with pytest.raises(redis.ConnectionError):
pubsub.get_message()

View file

@ -2,33 +2,22 @@ import functools
import math
import operator
import sys
from typing import Tuple, Any
from typing import Any, Tuple, Union
import fakeredis
import hypothesis
import hypothesis.stateful
import hypothesis.strategies as st
import pytest
import redis
from fakeredis._server import _create_version
from hypothesis.stateful import rule, initialize, precondition
from hypothesis.strategies import SearchStrategy
import fakeredis
from fakeredis._server import _create_version
self_strategy = st.runner()
def get_redis_version() -> Tuple[int]:
try:
r = redis.StrictRedis("localhost", port=6380, db=2)
r.ping()
return _create_version(r.info()["redis_version"])
except redis.ConnectionError:
return (6,)
finally:
if hasattr(r, "close"):
r.close() # Absent in older versions of redis-py
@st.composite
def sample_attr(draw, name):
"""Strategy for sampling a specific attribute from a state machine"""
@ -38,7 +27,28 @@ def sample_attr(draw, name):
return values[position]
redis_ver = get_redis_version()
def server_info() -> Tuple[str, Union[None, Tuple[int, ...]]]:
"""Returns server's version or None if server is not running"""
client = None
try:
client = redis.Redis("localhost", port=6390, db=2)
client_info = client.info()
server_type = "dragonfly" if "dragonfly_version" in client_info else "redis"
server_version = (
client_info["redis_version"] if server_type != "dragonfly" else (7, 0)
)
server_version = _create_version(server_version) or (7,)
return server_type, server_version
except redis.ConnectionError as e:
print(e)
pytest.exit("Redis is not running")
return "redis", (6,)
finally:
if hasattr(client, "close"):
client.close() # Absent in older versions of redis-py
server_type, redis_ver = server_info()
keys = sample_attr("keys")
fields = sample_attr("fields")
@ -49,9 +59,7 @@ int_as_bytes = st.builds(lambda x: str(default_normalize(x)).encode(), st.intege
float_as_bytes = st.builds(
lambda x: repr(default_normalize(x)).encode(), st.floats(width=32)
)
counts = st.integers(min_value=-3, max_value=3) | st.integers(
min_value=-2147483648, max_value=2147483647
)
counts = st.integers(min_value=-3, max_value=3) | st.integers()
limits = st.just(()) | st.tuples(st.just("limit"), counts, counts)
# Redis has an integer overflow bug in swapdb, so we confine the numbers to
# a limited range (https://github.com/antirez/redis/issues/5737).
@ -286,7 +294,7 @@ class CommonMachine(hypothesis.stateful.RuleBasedStateMachine):
def __init__(self):
super().__init__()
try:
self.real = redis.StrictRedis("localhost", port=6380, db=2)
self.real = redis.StrictRedis("localhost", port=6390, db=2)
self.real.ping()
except redis.ConnectionError:
pytest.skip("redis is not running")
@ -294,7 +302,7 @@ class CommonMachine(hypothesis.stateful.RuleBasedStateMachine):
self.real.connection_pool.disconnect()
pytest.skip("redis server is not 64-bit")
self.fake = fakeredis.FakeStrictRedis(
server=fakeredis.FakeServer(version=redis_ver), port=6380, db=2
server=fakeredis.FakeServer(version=redis_ver), port=6390, db=2
)
# Disable the response parsing so that we can check the raw values returned
self.fake.response_callbacks.clear()
@ -403,15 +411,22 @@ class BaseTest:
command_strategy: SearchStrategy
create_command_strategy = st.nothing()
command_strategy_redis7 = st.nothing()
@pytest.mark.slow
def test(self):
class Machine(CommonMachine):
create_command_strategy = self.create_command_strategy
command_strategy = self.command_strategy
command_strategy = (
self.command_strategy | self.command_strategy_redis7
if redis_ver >= (7,)
else self.command_strategy
)
# hypothesis.settings.register_profile("debug", max_examples=10, verbosity=hypothesis.Verbosity.debug)
# hypothesis.settings.load_profile("debug")
hypothesis.settings.register_profile(
"debug", max_examples=10, verbosity=hypothesis.Verbosity.debug
)
hypothesis.settings.load_profile("debug")
hypothesis.stateful.run_state_machine_as_test(Machine)
@ -420,7 +435,7 @@ class TestConnection(BaseTest):
connection_commands = (
commands(st.just("echo"), values)
| commands(st.just("ping"), st.lists(values, max_size=2))
# | commands(st.just("swapdb"), dbnums, dbnums)
| commands(st.just("swapdb"), dbnums, dbnums)
)
command_strategy = connection_commands | common_commands
@ -478,6 +493,50 @@ class TestHash(BaseTest):
| commands(st.just("hsetnx"), keys, fields, values)
| commands(st.just("hstrlen"), keys, fields)
)
command_strategy_redis7 = (
commands(
st.just("hpersist"),
st.just("fields"),
st.just(2),
st.lists(fields, min_size=2, max_size=2),
)
| commands(
st.just("hexpiretime"),
st.just("fields"),
st.just(2),
st.lists(fields, min_size=2, max_size=2),
)
| commands(
st.just("hpexpiretime"),
st.just("fields"),
st.just(2),
st.lists(fields, min_size=2, max_size=2),
)
| commands(
st.just("hexpire"),
keys,
expires_seconds,
st.none() | st.just("nx"),
st.none() | st.just("xx"),
st.none() | st.just("gt"),
st.none() | st.just("lt"),
st.just("fields"),
st.just(2),
st.lists(fields, min_size=2, max_size=2),
)
| commands(
st.just("hpexpire"),
keys,
expires_ms,
st.none() | st.just("nx"),
st.none() | st.just("xx"),
st.none() | st.just("gt"),
st.none() | st.just("lt"),
st.just("fields"),
st.just(2),
st.lists(fields, min_size=2, max_size=2),
)
)
create_command_strategy = commands(
st.just("hset"), keys, st.lists(st.tuples(fields, values), min_size=1)
)
@ -499,7 +558,7 @@ class TestList(BaseTest):
| commands(
st.sampled_from(["lpop", "rpop"]),
keys,
st.just(None) | st.just([]) | counts,
st.just(None) | st.just([]) | st.integers(),
)
| commands(
st.sampled_from(["lpush", "lpushx", "rpush", "rpushx"]),
@ -781,8 +840,3 @@ def mutated_commands(commands):
| add_arg(x, args)
| swap_args(x),
)
class TestFuzz(BaseTest):
command_strategy = mutated_commands(TestJoint.command_strategy)
command_strategy = command_strategy.filter(lambda command: command.testable)

View file

@ -1,168 +0,0 @@
import fakeredis
import pytest
def test_multidb(create_redis):
r1 = create_redis(db=2)
r2 = create_redis(db=3)
r1["r1"] = "r1"
r2["r2"] = "r2"
assert "r2" not in r1
assert "r1" not in r2
assert r1["r1"] == b"r1"
assert r2["r2"] == b"r2"
assert r1.flushall() is True
assert "r1" not in r1
assert "r2" not in r2
@pytest.mark.fake
class TestInitArgs:
def test_singleton(self):
shared_server = fakeredis.FakeServer()
r1 = fakeredis.FakeRedis()
r2 = fakeredis.FakeRedis(server=fakeredis.FakeServer())
r3 = fakeredis.FakeRedis(server=shared_server)
r4 = fakeredis.FakeRedis(server=shared_server)
r1.set("foo", "bar")
r3.set("bar", "baz")
assert "foo" in r1
assert "foo" not in r2
assert "foo" not in r3
assert "bar" in r3
assert "bar" in r4
assert "bar" not in r1
def test_host_init_arg(self):
db = fakeredis.FakeStrictRedis(host="localhost")
db.set("foo", "bar")
assert db.get("foo") == b"bar"
def test_from_url(self):
db = fakeredis.FakeStrictRedis.from_url("redis://localhost:6380/0")
db.set("foo", "bar")
assert db.get("foo") == b"bar"
def test_from_url_user(self):
db = fakeredis.FakeStrictRedis.from_url("redis://user@localhost:6380/0")
db.set("foo", "bar")
assert db.get("foo") == b"bar"
def test_from_url_user_password(self):
db = fakeredis.FakeStrictRedis.from_url(
"redis://user:password@localhost:6380/0"
)
db.set("foo", "bar")
assert db.get("foo") == b"bar"
def test_from_url_with_db_arg(self):
db = fakeredis.FakeStrictRedis.from_url("redis://localhost:6380/0")
db1 = fakeredis.FakeStrictRedis.from_url("redis://localhost:6380/1")
db2 = fakeredis.FakeStrictRedis.from_url("redis://localhost:6380/", db=2)
db.set("foo", "foo0")
db1.set("foo", "foo1")
db2.set("foo", "foo2")
assert db.get("foo") == b"foo0"
assert db1.get("foo") == b"foo1"
assert db2.get("foo") == b"foo2"
def test_from_url_db_value_error(self):
# In the case of ValueError, should default to 0, or be absent in redis-py 4.0
db = fakeredis.FakeStrictRedis.from_url("redis://localhost:6380/a")
assert db.connection_pool.connection_kwargs.get("db", 0) == 0
def test_can_pass_through_extra_args(self):
db = fakeredis.FakeStrictRedis.from_url(
"redis://localhost:6380/0", decode_responses=True
)
db.set("foo", "bar")
assert db.get("foo") == "bar"
def test_can_allow_extra_args(self):
db = fakeredis.FakeStrictRedis.from_url(
"redis://localhost:6380/0",
socket_connect_timeout=11,
socket_timeout=12,
socket_keepalive=True,
socket_keepalive_options={60: 30},
socket_type=1,
retry_on_timeout=True,
)
fake_conn = db.connection_pool.make_connection()
assert fake_conn.socket_connect_timeout == 11
assert fake_conn.socket_timeout == 12
assert fake_conn.socket_keepalive is True
assert fake_conn.socket_keepalive_options == {60: 30}
assert fake_conn.socket_type == 1
assert fake_conn.retry_on_timeout is True
# Make fallback logic match redis-py
db = fakeredis.FakeStrictRedis.from_url(
"redis://localhost:6380/0", socket_connect_timeout=None, socket_timeout=30
)
fake_conn = db.connection_pool.make_connection()
assert fake_conn.socket_connect_timeout == fake_conn.socket_timeout
assert fake_conn.socket_keepalive_options == {}
def test_repr(self):
# repr is human-readable, so we only test that it doesn't crash,
# and that it contains the db number.
db = fakeredis.FakeStrictRedis.from_url("redis://localhost:6380/11")
rep = repr(db)
assert "db=11" in rep
def test_from_unix_socket(self):
db = fakeredis.FakeStrictRedis.from_url("unix://a/b/c")
db.set("foo", "bar")
assert db.get("foo") == b"bar"
def test_same_connection_params(self):
r1 = fakeredis.FakeStrictRedis.from_url("redis://localhost:6380/11")
r2 = fakeredis.FakeStrictRedis.from_url("redis://localhost:6380/11")
r3 = fakeredis.FakeStrictRedis(server=fakeredis.FakeServer())
r1.set("foo", "bar")
assert r2.get("foo") == b"bar"
assert not r3.exists("foo")
def test_new_server_with_positional_args(self):
from fakeredis import FakeRedis
# same host, default port and db index
fake_redis_1 = FakeRedis("localhost")
fake_redis_2 = FakeRedis("localhost")
fake_redis_1.set("foo", "bar")
assert fake_redis_2.get("foo") == b"bar"
# same host and port
fake_redis_1 = FakeRedis("localhost", 6000)
fake_redis_2 = FakeRedis("localhost", 6000)
fake_redis_1.set("foo", "bar")
assert fake_redis_2.get("foo") == b"bar"
# same connection parameters, but different db index
fake_redis_1 = FakeRedis("localhost", 6000, 0)
fake_redis_2 = FakeRedis("localhost", 6000, 1)
fake_redis_1.set("foo", "bar")
assert fake_redis_2.get("foo") is None
# mix of positional arguments and keyword args
fake_redis_1 = FakeRedis("localhost", port=6000, db=0)
fake_redis_2 = FakeRedis("localhost", port=6000, db=1)
fake_redis_1.set("foo", "bar")
assert fake_redis_2.get("foo") is None

View file

@ -1,73 +0,0 @@
import json
import pytest
import redis
pytestmark = []
pytestmark.extend(
[
pytest.mark.asyncio,
]
)
lua_modules_test = pytest.importorskip("lupa")
@pytest.mark.load_lua_modules("cjson")
async def test_async_asgi_ratelimit_script(async_redis: redis.Redis):
script = """
local ruleset = cjson.decode(ARGV[1])
-- Set limits
for i, key in pairs(KEYS) do
redis.call('SET', key, ruleset[key][1], 'EX', ruleset[key][2], 'NX')
end
-- Check limits
for i = 1, #KEYS do
local value = redis.call('GET', KEYS[i])
if value and tonumber(value) < 1 then
return ruleset[KEYS[i]][2]
end
end
-- Decrease limits
for i, key in pairs(KEYS) do
redis.call('DECR', key)
end
return 0
"""
script = async_redis.register_script(script)
ruleset = {"path:get:user:name": (1, 1)}
await script(keys=list(ruleset.keys()), args=[json.dumps(ruleset)])
@pytest.mark.load_lua_modules("cjson")
def test_asgi_ratelimit_script(r: redis.Redis):
script = """
local ruleset = cjson.decode(ARGV[1])
-- Set limits
for i, key in pairs(KEYS) do
redis.call('SET', key, ruleset[key][1], 'EX', ruleset[key][2], 'NX')
end
-- Check limits
for i = 1, #KEYS do
local value = redis.call('GET', KEYS[i])
if value and tonumber(value) < 1 then
return ruleset[KEYS[i]][2]
end
end
-- Decrease limits
for i, key in pairs(KEYS) do
redis.call('DECR', key)
end
return 0
"""
script = r.register_script(script)
ruleset = {"path:get:user:name": (1, 1)}
script(keys=list(ruleset.keys()), args=[json.dumps(ruleset)])

View file

@ -21,6 +21,19 @@ def test_getbit_wrong_type(r: redis.Redis):
r.getbit("foo", 1)
@pytest.mark.min_server("7")
def test_bitcount_error(r: redis.Redis):
with pytest.raises(redis.ResponseError) as e:
raw_command(r, b"BITCOUNT", b"", b"", b"")
assert str(e.value) == "value is not an integer or out of range"
@pytest.mark.min_server("7")
def test_bitcount_does_not_exist(r: redis.Redis):
res = raw_command(r, b"BITCOUNT", b"", 0, 0)
assert res == 0
def test_multiple_bits_set(r: redis.Redis):
r.setbit("foo", 1, 1)
r.setbit("foo", 3, 1)
@ -373,10 +386,7 @@ def test_bitfield_incr_sat(r: redis.Redis):
r.set(key, b"\xff\xf0\x00")
assert r.bitfield(key, "SAT").incrby("u8", 4, 0x123).incrby(
"u8", 8, 0x55
).execute() == [
0xFF,
0xFF,
]
).execute() == [0xFF, 0xFF]
assert r.get(key) == b"\xff\xff\x00"
assert r.bitfield(key, "SAT").incrby("u12", 0, -1).incrby("u1", 1, 2).execute() == [
0xFFE,
@ -406,17 +416,11 @@ def test_bitfield_incr_fail(r: redis.Redis):
r.set(key, b"\xff\xf0\x00")
assert r.bitfield(key, "FAIL").incrby("u8", 4, 0x123).incrby(
"u8", 8, 0x55
).execute() == [
None,
None,
]
).execute() == [None, None]
assert r.get(key) == b"\xff\xf0\x00"
assert r.bitfield(key, "FAIL").incrby("u12", 0, -1).incrby(
"u1", 1, 2
).execute() == [
0xFFE,
None,
]
).execute() == [0xFFE, None]
assert r.get(key) == b"\xff\xe0\x00"
assert r.bitfield(key, "FAIL").incrby("i4", 0, 8).incrby("i4", 4, 7).execute() == [
7,

View file

@ -0,0 +1,52 @@
import pytest
import redis
import redis.client
from fakeredis import _msgs as msgs
from redis.exceptions import ResponseError
from test import testtools
from test.testtools import raw_command
def test_ping(r: redis.Redis):
assert r.ping()
assert testtools.raw_command(r, "ping", "test") == b"test"
with pytest.raises(
redis.ResponseError, match=msgs.WRONG_ARGS_MSG6.format("ping")[4:]
):
raw_command(r, "ping", "arg1", "arg2")
def test_echo(r: redis.Redis):
assert r.echo(b"hello") == b"hello"
assert r.echo("hello") == b"hello"
def test_unknown_command(r: redis.Redis):
with pytest.raises(redis.ResponseError):
raw_command(r, "0 3 3")
@pytest.mark.decode_responses
class TestDecodeResponses:
def test_decode_str(self, r):
r.set("foo", "bar")
assert r.get("foo") == "bar"
def test_decode_set(self, r):
r.sadd("foo", "member1")
assert set(r.smembers("foo")) == {"member1"}
def test_decode_list(self, r):
r.rpush("foo", "a", "b")
assert r.lrange("foo", 0, -1) == ["a", "b"]
def test_decode_dict(self, r):
r.hset("foo", "key", "value")
assert r.hgetall("foo") == {"key": "value"}
def test_decode_error(self, r):
r.set("foo", "bar")
with pytest.raises(ResponseError) as exc_info:
r.hset("foo", "bar", "baz")
assert isinstance(exc_info.value.args[0], str)

View file

@ -5,6 +5,7 @@ import pytest
import redis
from fakeredis import _msgs as msgs
from redis.exceptions import ResponseError
from test.testtools import raw_command

View file

@ -1,9 +1,10 @@
from test import testtools
from typing import Dict, Any
import pytest
import redis
from test import testtools
def test_geoadd_ch(r: redis.Redis):
values = (2.1909389952632, 41.433791470673, "place1")

View file

@ -1,9 +1,9 @@
from test import testtools
import pytest
import redis
import redis.client
from test import testtools
def test_hstrlen_missing(r: redis.Redis):
assert r.hstrlen("foo", "doesnotexist") == 0

View file

@ -616,13 +616,6 @@ def test_blmove(r: redis.Redis):
assert r.blmove("a", "b", 1, "RIGHT", "LEFT")
@pytest.mark.disconnected
@testtools.fake_only
def test_lmove_disconnected_raises_connection_error(r: redis.Redis):
with pytest.raises(redis.ConnectionError):
r.lmove(1, 2, "LEFT", "RIGHT")
def test_lpos(r: redis.Redis):
assert r.rpush("a", "a", "b", "c", "1", "2", "3", "c", "c") == 8
assert r.lpos("a", "a") == 0

View file

@ -5,7 +5,6 @@ from queue import Queue
from time import sleep
from typing import Optional, Dict, Any
import fakeredis
import pytest
import redis
from redis.client import PubSub
@ -363,17 +362,6 @@ def test_pubsub_timeout(r, timeout_value):
assert message is None
@pytest.mark.fake
def test_socket_cleanup_pubsub(fake_server):
r1 = fakeredis.FakeStrictRedis(server=fake_server)
r2 = fakeredis.FakeStrictRedis(server=fake_server)
ps = r1.pubsub()
with ps:
ps.subscribe("test")
ps.psubscribe("test*")
r2.publish("test", "foo")
def test_pubsub_channels(r: redis.Redis):
p = r.pubsub()
p.subscribe("foo", "bar", "baz", "test")

View file

@ -2,6 +2,7 @@ from time import sleep
import pytest
import redis
from test.testtools import key_val_dict

View file

@ -1,13 +1,10 @@
from __future__ import annotations
import logging
from test import testtools
import fakeredis
import pytest
import redis
import redis.client
from redis.exceptions import ResponseError
from test.testtools import raw_command
json_tests = pytest.importorskip("lupa")
@ -552,26 +549,6 @@ def test_script(r: redis.Redis):
assert result == b"42"
@testtools.fake_only
def test_lua_log(r, caplog):
logger = fakeredis._server.LOGGER
script = """
redis.log(redis.LOG_DEBUG, "debug")
redis.log(redis.LOG_VERBOSE, "verbose")
redis.log(redis.LOG_NOTICE, "notice")
redis.log(redis.LOG_WARNING, "warning")
"""
script = r.register_script(script)
with caplog.at_level("DEBUG"):
script()
assert caplog.record_tuples == [
(logger.name, logging.DEBUG, "debug"),
(logger.name, logging.INFO, "verbose"),
(logger.name, logging.INFO, "notice"),
(logger.name, logging.WARNING, "warning"),
]
def test_lua_log_no_message(r: redis.Redis):
script = "redis.log(redis.LOG_DEBUG)"
script = r.register_script(script)
@ -579,18 +556,6 @@ def test_lua_log_no_message(r: redis.Redis):
script()
@testtools.fake_only
def test_lua_log_different_types(r, caplog):
logger = logging.getLogger("fakeredis")
script = "redis.log(redis.LOG_DEBUG, 'string', 1, true, 3.14, 'string')"
script = r.register_script(script)
with caplog.at_level("DEBUG"):
script()
assert caplog.record_tuples == [
(logger.name, logging.DEBUG, "string 1 3.14 string")
]
@pytest.mark.unsupported_server_types("dragonfly")
def test_lua_log_wrong_level(r: redis.Redis):
script = "redis.log(10, 'string')"
@ -599,19 +564,6 @@ def test_lua_log_wrong_level(r: redis.Redis):
script()
@testtools.fake_only
def test_lua_log_defined_vars(r, caplog):
logger = fakeredis._server.LOGGER
script = """
local var='string'
redis.log(redis.LOG_DEBUG, var)
"""
script = r.register_script(script)
with caplog.at_level("DEBUG"):
script()
assert caplog.record_tuples == [(logger.name, logging.DEBUG, "string")]
def test_hscan_cursors_are_bytes(r: redis.Redis):
r.hset("hkey", "foo", 1)

View file

@ -3,9 +3,7 @@ from time import sleep
import pytest
import redis
from fakeredis._commands import SUPPORTED_COMMANDS
from redis.exceptions import ResponseError
from test.testtools import fake_only
@pytest.mark.unsupported_server_types("dragonfly")
@ -46,20 +44,6 @@ def test_lastsave(r: redis.Redis):
assert isinstance(r.lastsave(), datetime)
@fake_only
def test_command(r: redis.Redis):
commands_dict = r.command()
one_word_commands = {cmd for cmd in SUPPORTED_COMMANDS if " " not in cmd}
assert one_word_commands - set(commands_dict.keys()) == set()
@fake_only
def test_command_count(r: redis.Redis):
assert r.command_count() >= len(
[cmd for cmd in SUPPORTED_COMMANDS if " " not in cmd]
)
@pytest.mark.unsupported_server_types("dragonfly")
@pytest.mark.slow
def test_bgsave_timestamp_update(r: redis.Redis):

View file

@ -2,13 +2,14 @@ from __future__ import annotations
import math
from collections import OrderedDict
from test import testtools
from typing import Tuple, List, Optional
import pytest
import redis
import redis.client
from test import testtools
def round_str(x):
assert isinstance(x, bytes)

View file

@ -1,12 +1,12 @@
import threading
import time
from test import testtools
from typing import List
import pytest
import redis
from fakeredis import _msgs as msgs
from fakeredis._stream import XStream, StreamRangeTest
from test import testtools
def get_ids(results):
@ -20,49 +20,6 @@ def add_items(r: redis.Redis, stream: str, n: int):
return id_list
@pytest.mark.fake
def test_xstream():
stream = XStream()
stream.add([0, 0, 1, 1, 2, 2, 3, 3], "0-1")
stream.add([1, 1, 2, 2, 3, 3, 4, 4], "1-2")
stream.add([2, 2, 3, 3, 4, 4], "1-3")
stream.add([3, 3, 4, 4], "2-1")
stream.add([3, 3, 4, 4], "2-2")
stream.add([3, 3, 4, 4], "3-1")
assert stream.add([3, 3, 4, 4], "4-*") == b"4-0"
assert stream.last_item_key() == b"4-0"
assert stream.add([3, 3, 4, 4], "4-*-*") is None
assert len(stream) == 7
i = iter(stream)
assert next(i) == [b"0-1", [0, 0, 1, 1, 2, 2, 3, 3]]
assert next(i) == [b"1-2", [1, 1, 2, 2, 3, 3, 4, 4]]
assert next(i) == [b"1-3", [2, 2, 3, 3, 4, 4]]
assert next(i) == [b"2-1", [3, 3, 4, 4]]
assert next(i) == [b"2-2", [3, 3, 4, 4]]
assert stream.find_index_key_as_str("1-2") == (1, True)
assert stream.find_index_key_as_str("0-1") == (0, True)
assert stream.find_index_key_as_str("2-1") == (3, True)
assert stream.find_index_key_as_str("1-4") == (3, False)
lst = stream.irange(StreamRangeTest.decode(b"0-2"), StreamRangeTest.decode(b"3-0"))
assert len(lst) == 4
stream = XStream()
assert stream.delete(["1"]) == 0
entry_key: bytes = stream.add([0, 0, 1, 1, 2, 2, 3, 3])
assert len(stream) == 1
assert (
stream.delete(
[
entry_key,
]
)
== 1
)
assert len(stream) == 0
def test_xadd_redis__green(r: redis.Redis):
stream = "stream"
before = int(1000 * time.time())

View file

@ -1,128 +0,0 @@
import pytest
import redis.commands.bf
from redis.commands.bf import BFInfo
json_tests = pytest.importorskip("probables")
def intlist(obj):
return [int(v) for v in obj]
def test_create_bf(r: redis.Redis):
"""Test CREATE/RESERVE calls"""
assert r.bf().create("bloom", 0.01, 1000)
assert r.bf().create("bloom_e", 0.01, 1000, expansion=1)
assert r.bf().create("bloom_ns", 0.01, 1000, noScale=True)
@pytest.mark.unsupported_server_types("dragonfly")
def test_create_cf(r: redis.Redis):
assert r.cf().create("cuckoo", 1000)
assert r.cf().create("cuckoo_e", 1000, expansion=1)
assert r.cf().create("cuckoo_bs", 1000, bucket_size=4)
assert r.cf().create("cuckoo_mi", 1000, max_iterations=10)
assert r.cms().initbydim("cmsDim", 100, 5)
assert r.cms().initbyprob("cmsProb", 0.01, 0.01)
assert r.topk().reserve("topk", 5, 100, 5, 0.9)
def test_bf_reserve(r: redis.Redis):
"""Testing BF.RESERVE"""
assert r.bf().reserve("bloom", 0.01, 1000)
assert r.bf().reserve("bloom_e", 0.01, 1000, expansion=1)
assert r.bf().reserve("bloom_ns", 0.01, 1000, noScale=True)
def test_bf_add(r: redis.Redis):
assert r.bf().create("bloom", 0.01, 1000)
assert 1 == r.bf().add("bloom", "foo")
assert 0 == r.bf().add("bloom", "foo")
assert [0] == intlist(r.bf().madd("bloom", "foo"))
assert [0, 1] == r.bf().madd("bloom", "foo", "bar")
assert [0, 0, 1] == r.bf().madd("bloom", "foo", "bar", "baz")
assert 1 == r.bf().exists("bloom", "foo")
assert 0 == r.bf().exists("bloom", "noexist")
assert [1, 0] == intlist(r.bf().mexists("bloom", "foo", "noexist"))
@pytest.mark.unsupported_server_types("dragonfly")
def test_bf_insert(r: redis.Redis):
assert r.bf().create("bloom", 0.01, 1000)
assert [1] == intlist(r.bf().insert("bloom", ["foo"]))
assert [0, 1] == intlist(r.bf().insert("bloom", ["foo", "bar"]))
assert [1] == intlist(r.bf().insert("captest", ["foo"], capacity=10))
assert [1] == intlist(r.bf().insert("errtest", ["foo"], error=0.01))
assert 1 == r.bf().exists("bloom", "foo")
assert 0 == r.bf().exists("bloom", "noexist")
assert [1, 0] == intlist(r.bf().mexists("bloom", "foo", "noexist"))
info = r.bf().info("bloom")
assert 2 == info.get("insertedNum")
assert 1000 == info.get("capacity")
assert 1 == info.get("filterNum")
@pytest.mark.unsupported_server_types("dragonfly")
def test_bf_scandump_and_loadchunk(r: redis.Redis):
r.bf().create("myBloom", "0.0001", "1000")
# Test is probabilistic and might fail. It is OK to change variables if
# certain to not break anything
res = 0
for x in range(1000):
r.bf().add("myBloom", x)
assert r.bf().exists("myBloom", x)
rv = r.bf().exists("myBloom", f"nonexist_{x}")
res += rv == x
assert res < 5
cmds = list()
first = 0
while first is not None:
cur = r.bf().scandump("myBloom", first)
if cur[0] == 0:
first = None
else:
first = cur[0]
cmds.append(cur)
# Remove the filter
r.bf().client.delete("myBloom")
# Now, load all the commands:
for cmd in cmds:
r.bf().loadchunk("myBloom1", *cmd)
for x in range(1000):
assert r.bf().exists("myBloom1", x), f"{x} not in filter"
@pytest.mark.unsupported_server_types("dragonfly")
def test_bf_info(r: redis.Redis):
# Store a filter
r.bf().create("nonscaling", "0.0001", "1000", noScale=True)
info: BFInfo = r.bf().info("nonscaling")
assert info.expansionRate is None
expansion = 4
r.bf().create("expanding", "0.0001", "1000", expansion=expansion)
info = r.bf().info("expanding")
assert info.expansionRate == 4
assert info.capacity == 1000
assert info.insertedNum == 0
@pytest.mark.unsupported_server_types("dragonfly")
def test_bf_card(r: redis.Redis):
# return 0 if the key does not exist
assert r.bf().card("not_exist") == 0
# Store a filter
assert r.bf().add("bf1", "item_foo") == 1
assert r.bf().card("bf1") == 1
# Error when key is of a type other than Bloom filter.
with pytest.raises(redis.ResponseError):
r.set("setKey", "value")
r.bf().card("setKey")

View file

@ -1,11 +1,44 @@
import pytest
import redis
from redis.commands.bf import BFInfo
from fakeredis import _msgs as msgs
bloom_tests = pytest.importorskip("probables")
def intlist(obj):
return [int(v) for v in obj]
def test_create_bf(r: redis.Redis):
assert r.bf().create("bloom", 0.01, 1000)
assert r.bf().create("bloom_e", 0.01, 1000, expansion=1)
assert r.bf().create("bloom_ns", 0.01, 1000, noScale=True)
@pytest.mark.unsupported_server_types("dragonfly")
def test_create_cf(r: redis.Redis):
assert r.cf().create("cuckoo", 1000)
assert r.cf().create("cuckoo_e", 1000, expansion=1)
assert r.cf().create("cuckoo_bs", 1000, bucket_size=4)
assert r.cf().create("cuckoo_mi", 1000, max_iterations=10)
assert r.cms().initbydim("cmsDim", 100, 5)
assert r.cms().initbyprob("cmsProb", 0.01, 0.01)
assert r.topk().reserve("topk", 5, 100, 5, 0.9)
def test_bf_reserve(r: redis.Redis):
assert r.bf().reserve("bloom", 0.01, 1000)
assert r.bf().reserve("bloom_ns", 0.01, 1000, noScale=True)
with pytest.raises(
redis.exceptions.ResponseError, match=msgs.NONSCALING_FILTERS_CANNOT_EXPAND_MSG
):
assert r.bf().reserve("bloom_e", 0.01, 1000, expansion=1, noScale=True)
with pytest.raises(redis.exceptions.ResponseError, match=msgs.ITEM_EXISTS_MSG):
assert r.bf().reserve("bloom", 0.01, 1000)
def test_bf_add(r: redis.Redis):
assert r.bf().add("key", "value") == 1
assert r.bf().add("key", "value") == 0
@ -13,6 +46,15 @@ def test_bf_add(r: redis.Redis):
r.set("key1", "value")
with pytest.raises(redis.exceptions.ResponseError):
r.bf().add("key1", "v")
assert r.bf().create("bloom", 0.01, 1000)
assert 1 == r.bf().add("bloom", "foo")
assert 0 == r.bf().add("bloom", "foo")
assert [0] == intlist(r.bf().madd("bloom", "foo"))
assert [0, 1] == r.bf().madd("bloom", "foo", "bar")
assert [0, 0, 1] == r.bf().madd("bloom", "foo", "bar", "baz")
assert 1 == r.bf().exists("bloom", "foo")
assert 0 == r.bf().exists("bloom", "noexist")
assert [1, 0] == intlist(r.bf().mexists("bloom", "foo", "noexist"))
def test_bf_madd(r: redis.Redis):
@ -33,6 +75,17 @@ def test_bf_card(r: redis.Redis):
r.set("key1", "value")
with pytest.raises(redis.exceptions.ResponseError):
r.bf().card("key1")
# return 0 if the key does not exist
assert r.bf().card("not_exist") == 0
# Store a filter
assert r.bf().add("bf1", "item_foo") == 1
assert r.bf().card("bf1") == 1
# Error when key is of a type other than Bloom filter.
with pytest.raises(redis.ResponseError):
r.set("setKey", "value")
r.bf().card("setKey")
def test_bf_exists(r: redis.Redis):
@ -61,29 +114,78 @@ def test_bf_mexists(r: redis.Redis):
r.bf().add("key1", "v")
@pytest.mark.unsupported_server_types("dragonfly")
def test_bf_reserve(r: redis.Redis):
assert r.bf().reserve("bloom", 0.01, 1000)
assert r.bf().reserve("bloom_ns", 0.01, 1000, noScale=True)
with pytest.raises(
redis.exceptions.ResponseError, match=msgs.NONSCALING_FILTERS_CANNOT_EXPAND_MSG
):
assert r.bf().reserve("bloom_e", 0.01, 1000, expansion=1, noScale=True)
with pytest.raises(redis.exceptions.ResponseError, match=msgs.ITEM_EXISTS_MSG):
assert r.bf().reserve("bloom", 0.01, 1000)
@pytest.mark.unsupported_server_types("dragonfly")
def test_bf_insert(r: redis.Redis):
assert r.bf().create("bloom", 0.01, 1000)
assert r.bf().insert("bloom", ["foo"]) == [1]
assert r.bf().insert("bloom", ["foo", "bar"]) == [0, 1]
assert r.bf().create("key", 0.01, 1000)
assert r.bf().insert("key", ["foo"]) == [1]
assert r.bf().insert("key", ["foo", "bar"]) == [0, 1]
assert r.bf().insert("captest", ["foo"], capacity=10) == [1]
assert r.bf().insert("errtest", ["foo"], error=0.01) == [1]
assert r.bf().exists("bloom", "foo") == 1
assert r.bf().exists("bloom", "noexist") == 0
assert r.bf().mexists("bloom", "foo", "noexist") == [1, 0]
assert r.bf().exists("key", "foo") == 1
assert r.bf().exists("key", "noexist") == 0
assert r.bf().mexists("key", "foo", "noexist") == [1, 0]
with pytest.raises(redis.exceptions.ResponseError, match=msgs.NOT_FOUND_MSG):
r.bf().insert("nocreate", [1, 2, 3], noCreate=True)
# with pytest.raises(redis.exceptions.ResponseError, match=msgs.NONSCALING_FILTERS_CANNOT_EXPAND_MSG):
# r.bf().insert("nocreate", [1, 2, 3], expansion=2, noScale=True)
assert r.bf().create("bloom", 0.01, 1000)
assert [1] == intlist(r.bf().insert("bloom", ["foo"]))
assert [0, 1] == intlist(r.bf().insert("bloom", ["foo", "bar"]))
assert 1 == r.bf().exists("bloom", "foo")
assert 0 == r.bf().exists("bloom", "noexist")
assert [1, 0] == intlist(r.bf().mexists("bloom", "foo", "noexist"))
info = r.bf().info("bloom")
assert 2 == info.get("insertedNum")
assert 1000 == info.get("capacity")
assert 1 == info.get("filterNum")
@pytest.mark.unsupported_server_types("dragonfly")
def test_bf_scandump_and_loadchunk(r: redis.Redis):
r.bf().create("myBloom", "0.0001", "1000")
# Test is probabilistic and might fail. It is OK to change variables if
# certain to not break anything
res = 0
for x in range(1000):
r.bf().add("myBloom", x)
assert r.bf().exists("myBloom", x)
rv = r.bf().exists("myBloom", f"nonexist_{x}")
res += rv == x
assert res < 5
cmds = list()
first = 0
while first is not None:
cur = r.bf().scandump("myBloom", first)
if cur[0] == 0:
first = None
else:
first = cur[0]
cmds.append(cur)
# Remove the filter
r.bf().client.delete("myBloom")
# Now, load all the commands:
for cmd in cmds:
r.bf().loadchunk("myBloom1", *cmd)
for x in range(1000):
assert r.bf().exists("myBloom1", x), f"{x} not in filter"
@pytest.mark.unsupported_server_types("dragonfly")
def test_bf_info(r: redis.Redis):
# Store a filter
r.bf().create("nonscaling", "0.0001", "1000", noScale=True)
info: BFInfo = r.bf().info("nonscaling")
assert info.expansionRate is None
expansion = 4
r.bf().create("expanding", "0.0001", "1000", expansion=expansion)
info = r.bf().info("expanding")
assert info.expansionRate == 4
assert info.capacity == 1000
assert info.insertedNum == 0