Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding tests for modules ACL and modules config changes in 8.0 #3489

Merged
merged 3 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions redis/commands/search/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,10 @@ def dict_dump(self, name: str):
cmd = [DICT_DUMP_CMD, name]
return self.execute_command(*cmd)

@deprecated_function(
version="8.0.0",
reason="deprecated since Redis 8.0, call config_set from core module instead",
)
def config_set(self, option: str, value: str) -> bool:
"""Set runtime configuration option.

Expand All @@ -706,6 +710,10 @@ def config_set(self, option: str, value: str) -> bool:
raw = self.execute_command(*cmd)
return raw == "OK"

@deprecated_function(
version="8.0.0",
reason="deprecated since Redis 8.0, call config_get from core module instead",
)
def config_get(self, option: str) -> str:
"""Get runtime configuration option value.

Expand Down
210 changes: 210 additions & 0 deletions tests/test_asyncio/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
parse_info,
)
from redis.client import EMPTY_RESPONSE, NEVER_DECODE
from redis.commands.json.path import Path
from redis.commands.search.field import TextField
from redis.commands.search.query import Query
from tests.conftest import (
assert_resp_response,
assert_resp_response_in,
Expand Down Expand Up @@ -49,6 +52,12 @@ def factory(username):
return r

yield factory
try:
client_info = await r.client_info()
except exceptions.NoPermissionError:
client_info = {}
if "default" != client_info.get("user", ""):
await r.auth("", "default")
for username in usernames:
await r.acl_deluser(username)

Expand Down Expand Up @@ -115,12 +124,65 @@ async def test_acl_cat_no_category(self, r: redis.Redis):
assert isinstance(categories, list)
assert "read" in categories or b"read" in categories

@pytest.mark.redismod
@skip_if_server_version_lt("7.9.0")
async def test_acl_cat_contain_modules_no_category(self, r: redis.Redis):
modules_list = [
"search",
"bloom",
"json",
"cuckoo",
"timeseries",
"cms",
"topk",
"tdigest",
]
categories = await r.acl_cat()
assert isinstance(categories, list)
for module_cat in modules_list:
assert module_cat in categories or module_cat.encode() in categories

@skip_if_server_version_lt(REDIS_6_VERSION)
async def test_acl_cat_with_category(self, r: redis.Redis):
commands = await r.acl_cat("read")
assert isinstance(commands, list)
assert "get" in commands or b"get" in commands

@pytest.mark.redismod
@skip_if_server_version_lt("7.9.0")
async def test_acl_modules_cat_with_category(self, r: redis.Redis):
search_commands = await r.acl_cat("search")
assert isinstance(search_commands, list)
assert "FT.SEARCH" in search_commands or b"FT.SEARCH" in search_commands

bloom_commands = await r.acl_cat("bloom")
assert isinstance(bloom_commands, list)
assert "bf.add" in bloom_commands or b"bf.add" in bloom_commands

json_commands = await r.acl_cat("json")
assert isinstance(json_commands, list)
assert "json.get" in json_commands or b"json.get" in json_commands

cuckoo_commands = await r.acl_cat("cuckoo")
assert isinstance(cuckoo_commands, list)
assert "cf.insert" in cuckoo_commands or b"cf.insert" in cuckoo_commands

cms_commands = await r.acl_cat("cms")
assert isinstance(cms_commands, list)
assert "cms.query" in cms_commands or b"cms.query" in cms_commands

topk_commands = await r.acl_cat("topk")
assert isinstance(topk_commands, list)
assert "topk.list" in topk_commands or b"topk.list" in topk_commands

tdigest_commands = await r.acl_cat("tdigest")
assert isinstance(tdigest_commands, list)
assert "tdigest.rank" in tdigest_commands or b"tdigest.rank" in tdigest_commands

timeseries_commands = await r.acl_cat("timeseries")
assert isinstance(timeseries_commands, list)
assert "ts.range" in timeseries_commands or b"ts.range" in timeseries_commands

@skip_if_server_version_lt(REDIS_6_VERSION)
async def test_acl_deluser(self, r_teardown):
username = "redis-py-user"
Expand Down Expand Up @@ -316,6 +378,116 @@ async def test_acl_whoami(self, r: redis.Redis):
username = await r.acl_whoami()
assert isinstance(username, (str, bytes))

@pytest.mark.redismod
@skip_if_server_version_lt("7.9.0")
async def test_acl_modules_commands(self, r_teardown):
username = "redis-py-user"
password = "pass-for-test-user"

r = r_teardown(username)
await r.flushdb()

await r.ft().create_index((TextField("txt"),))
await r.hset("doc1", mapping={"txt": "foo baz"})
await r.hset("doc2", mapping={"txt": "foo bar"})

await r.acl_setuser(
username,
enabled=True,
reset=True,
passwords=[f"+{password}"],
categories=["-all"],
commands=[
"+FT.SEARCH",
"-FT.DROPINDEX",
"+json.set",
"+json.get",
"-json.clear",
"+bf.reserve",
"-bf.info",
"+cf.reserve",
"+cms.initbydim",
"+topk.reserve",
"+tdigest.create",
"+ts.create",
"-ts.info",
],
keys=["*"],
)

await r.auth(password, username)

assert await r.ft().search(Query("foo ~bar"))
with pytest.raises(exceptions.NoPermissionError):
await r.ft().dropindex()

await r.json().set("foo", Path.root_path(), "bar")
assert await r.json().get("foo") == "bar"
with pytest.raises(exceptions.NoPermissionError):
await r.json().clear("foo")

assert await r.bf().create("bloom", 0.01, 1000)
assert await r.cf().create("cuckoo", 1000)
assert await r.cms().initbydim("cmsDim", 100, 5)
assert await r.topk().reserve("topk", 5, 100, 5, 0.9)
assert await r.tdigest().create("to-tDigest", 10)
with pytest.raises(exceptions.NoPermissionError):
await r.bf().info("bloom")

assert await r.ts().create(1, labels={"Redis": "Labs"})
with pytest.raises(exceptions.NoPermissionError):
await r.ts().info(1)

@pytest.mark.redismod
@skip_if_server_version_lt("7.9.0")
async def test_acl_modules_category_commands(self, r_teardown):
username = "redis-py-user"
password = "pass-for-test-user"

r = r_teardown(username)
await r.flushdb()

# validate modules categories acl config
await r.acl_setuser(
username,
enabled=True,
reset=True,
passwords=[f"+{password}"],
categories=[
"-all",
"+@search",
"+@json",
"+@bloom",
"+@cuckoo",
"+@topk",
"+@cms",
"+@timeseries",
"+@tdigest",
],
keys=["*"],
)
await r.ft().create_index((TextField("txt"),))
await r.hset("doc1", mapping={"txt": "foo baz"})
await r.hset("doc2", mapping={"txt": "foo bar"})

await r.auth(password, username)

assert await r.ft().search(Query("foo ~bar"))
assert await r.ft().dropindex()

assert await r.json().set("foo", Path.root_path(), "bar")
assert await r.json().get("foo") == "bar"

assert await r.bf().create("bloom", 0.01, 1000)
assert await r.bf().info("bloom")
assert await r.cf().create("cuckoo", 1000)
assert await r.cms().initbydim("cmsDim", 100, 5)
assert await r.topk().reserve("topk", 5, 100, 5, 0.9)
assert await r.tdigest().create("to-tDigest", 10)

assert await r.ts().create(1, labels={"Redis": "Labs"})
assert await r.ts().info(1)

@pytest.mark.onlynoncluster
async def test_client_list(self, r: redis.Redis):
clients = await r.client_list()
Expand Down Expand Up @@ -512,6 +684,44 @@ async def test_config_set(self, r: redis.Redis):
assert await r.config_set("timeout", 0)
assert (await r.config_get())["timeout"] == "0"

@pytest.mark.redismod
@skip_if_server_version_lt("7.9.0")
async def test_config_get_for_modules(self, r: redis.Redis):
search_module_configs = await r.config_get("search-*")
assert "search-timeout" in search_module_configs

ts_module_configs = await r.config_get("ts-*")
assert "ts-retention-policy" in ts_module_configs

bf_module_configs = await r.config_get("bf-*")
assert "bf-error-rate" in bf_module_configs

cf_module_configs = await r.config_get("cf-*")
assert "cf-initial-size" in cf_module_configs

@pytest.mark.redismod
@skip_if_server_version_lt("7.9.0")
async def test_config_set_for_search_module(self, r: redis.Redis):
petyaslavova marked this conversation as resolved.
Show resolved Hide resolved
config = await r.config_get("*")
initial_default_search_dialect = config["search-default-dialect"]
try:
default_dialect_new = "3"
assert await r.config_set("search-default-dialect", default_dialect_new)
assert (await r.config_get("*"))[
"search-default-dialect"
] == default_dialect_new
assert (
(await r.ft().config_get("*"))[b"DEFAULT_DIALECT"]
).decode() == default_dialect_new
except AssertionError as ex:
raise ex
finally:
assert await r.config_set(
"search-default-dialect", initial_default_search_dialect
)
with pytest.raises(exceptions.ResponseError):
await r.config_set("search-max-doctablesize", 2000000)

@pytest.mark.onlynoncluster
async def test_dbsize(self, r: redis.Redis):
await r.set("a", "foo")
Expand Down
Loading
Loading