Commit 20618c71 authored by Oran Agra's avatar Oran Agra
Browse files

Merge remote-tracking branch 'origin/unstable' into 7.0

parents fb4e0d40 89772ed8
#include "redismodule.h"
#include <string.h>
#include <assert.h>
#include <unistd.h>
#define UNUSED(V) ((void) V)
/* Registered type */
RedisModuleType *mallocsize_type = NULL;
typedef enum {
UDT_RAW,
UDT_STRING,
UDT_DICT
} udt_type_t;
typedef struct {
void *ptr;
size_t len;
} raw_t;
typedef struct {
udt_type_t type;
union {
raw_t raw;
RedisModuleString *str;
RedisModuleDict *dict;
} data;
} udt_t;
void udt_free(void *value) {
udt_t *udt = value;
switch (udt->type) {
case (UDT_RAW): {
RedisModule_Free(udt->data.raw.ptr);
break;
}
case (UDT_STRING): {
RedisModule_FreeString(NULL, udt->data.str);
break;
}
case (UDT_DICT): {
RedisModuleString *dk, *dv;
RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(udt->data.dict, "^", NULL, 0);
while((dk = RedisModule_DictNext(NULL, iter, (void **)&dv)) != NULL) {
RedisModule_FreeString(NULL, dk);
RedisModule_FreeString(NULL, dv);
}
RedisModule_DictIteratorStop(iter);
RedisModule_FreeDict(NULL, udt->data.dict);
break;
}
}
RedisModule_Free(udt);
}
void udt_rdb_save(RedisModuleIO *rdb, void *value) {
udt_t *udt = value;
RedisModule_SaveUnsigned(rdb, udt->type);
switch (udt->type) {
case (UDT_RAW): {
RedisModule_SaveStringBuffer(rdb, udt->data.raw.ptr, udt->data.raw.len);
break;
}
case (UDT_STRING): {
RedisModule_SaveString(rdb, udt->data.str);
break;
}
case (UDT_DICT): {
RedisModule_SaveUnsigned(rdb, RedisModule_DictSize(udt->data.dict));
RedisModuleString *dk, *dv;
RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(udt->data.dict, "^", NULL, 0);
while((dk = RedisModule_DictNext(NULL, iter, (void **)&dv)) != NULL) {
RedisModule_SaveString(rdb, dk);
RedisModule_SaveString(rdb, dv);
RedisModule_FreeString(NULL, dk); /* Allocated by RedisModule_DictNext */
}
RedisModule_DictIteratorStop(iter);
break;
}
}
}
void *udt_rdb_load(RedisModuleIO *rdb, int encver) {
if (encver != 0)
return NULL;
udt_t *udt = RedisModule_Alloc(sizeof(*udt));
udt->type = RedisModule_LoadUnsigned(rdb);
switch (udt->type) {
case (UDT_RAW): {
udt->data.raw.ptr = RedisModule_LoadStringBuffer(rdb, &udt->data.raw.len);
break;
}
case (UDT_STRING): {
udt->data.str = RedisModule_LoadString(rdb);
break;
}
case (UDT_DICT): {
long long dict_len = RedisModule_LoadUnsigned(rdb);
udt->data.dict = RedisModule_CreateDict(NULL);
for (int i = 0; i < dict_len; i += 2) {
RedisModuleString *key = RedisModule_LoadString(rdb);
RedisModuleString *val = RedisModule_LoadString(rdb);
RedisModule_DictSet(udt->data.dict, key, val);
}
break;
}
}
return udt;
}
size_t udt_mem_usage(RedisModuleKeyOptCtx *ctx, const void *value, size_t sample_size) {
UNUSED(ctx);
UNUSED(sample_size);
const udt_t *udt = value;
size_t size = sizeof(*udt);
switch (udt->type) {
case (UDT_RAW): {
size += RedisModule_MallocSize(udt->data.raw.ptr);
break;
}
case (UDT_STRING): {
size += RedisModule_MallocSizeString(udt->data.str);
break;
}
case (UDT_DICT): {
void *dk;
size_t keylen;
RedisModuleString *dv;
RedisModuleDictIter *iter = RedisModule_DictIteratorStartC(udt->data.dict, "^", NULL, 0);
while((dk = RedisModule_DictNextC(iter, &keylen, (void **)&dv)) != NULL) {
size += keylen;
size += RedisModule_MallocSizeString(dv);
}
RedisModule_DictIteratorStop(iter);
break;
}
}
return size;
}
/* MALLOCSIZE.SETRAW key len */
int cmd_setraw(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 3)
return RedisModule_WrongArity(ctx);
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);
udt_t *udt = RedisModule_Alloc(sizeof(*udt));
udt->type = UDT_RAW;
long long raw_len;
RedisModule_StringToLongLong(argv[2], &raw_len);
udt->data.raw.ptr = RedisModule_Alloc(raw_len);
udt->data.raw.len = raw_len;
RedisModule_ModuleTypeSetValue(key, mallocsize_type, udt);
RedisModule_CloseKey(key);
return RedisModule_ReplyWithSimpleString(ctx, "OK");
}
/* MALLOCSIZE.SETSTR key string */
int cmd_setstr(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 3)
return RedisModule_WrongArity(ctx);
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);
udt_t *udt = RedisModule_Alloc(sizeof(*udt));
udt->type = UDT_STRING;
udt->data.str = argv[2];
RedisModule_RetainString(ctx, argv[2]);
RedisModule_ModuleTypeSetValue(key, mallocsize_type, udt);
RedisModule_CloseKey(key);
return RedisModule_ReplyWithSimpleString(ctx, "OK");
}
/* MALLOCSIZE.SETDICT key field value [field value ...] */
int cmd_setdict(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc < 4 || argc % 2)
return RedisModule_WrongArity(ctx);
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE);
udt_t *udt = RedisModule_Alloc(sizeof(*udt));
udt->type = UDT_DICT;
udt->data.dict = RedisModule_CreateDict(ctx);
for (int i = 2; i < argc; i += 2) {
RedisModule_DictSet(udt->data.dict, argv[i], argv[i+1]);
/* No need to retain argv[i], it is copied as the rax key */
RedisModule_RetainString(ctx, argv[i+1]);
}
RedisModule_ModuleTypeSetValue(key, mallocsize_type, udt);
RedisModule_CloseKey(key);
return RedisModule_ReplyWithSimpleString(ctx, "OK");
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
if (RedisModule_Init(ctx,"mallocsize",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)
return REDISMODULE_ERR;
RedisModuleTypeMethods tm = {
.version = REDISMODULE_TYPE_METHOD_VERSION,
.rdb_load = udt_rdb_load,
.rdb_save = udt_rdb_save,
.free = udt_free,
.mem_usage2 = udt_mem_usage,
};
mallocsize_type = RedisModule_CreateDataType(ctx, "allocsize", 0, &tm);
if (mallocsize_type == NULL)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "mallocsize.setraw", cmd_setraw, "", 1, 1, 1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "mallocsize.setstr", cmd_setstr, "", 1, 1, 1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "mallocsize.setdict", cmd_setdict, "", 1, 1, 1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
...@@ -6,6 +6,7 @@ long long longval; ...@@ -6,6 +6,7 @@ long long longval;
long long memval; long long memval;
RedisModuleString *strval = NULL; RedisModuleString *strval = NULL;
int enumval; int enumval;
int flagsval;
/* Series of get and set callbacks for each type of config, these rely on the privdata ptr /* Series of get and set callbacks for each type of config, these rely on the privdata ptr
* to point to the config, and they register the configs as such. Note that one could also just * to point to the config, and they register the configs as such. Note that one could also just
...@@ -68,6 +69,20 @@ int setEnumConfigCommand(const char *name, int val, void *privdata, RedisModuleS ...@@ -68,6 +69,20 @@ int setEnumConfigCommand(const char *name, int val, void *privdata, RedisModuleS
return REDISMODULE_OK; return REDISMODULE_OK;
} }
int getFlagsConfigCommand(const char *name, void *privdata) {
REDISMODULE_NOT_USED(name);
REDISMODULE_NOT_USED(privdata);
return flagsval;
}
int setFlagsConfigCommand(const char *name, int val, void *privdata, RedisModuleString **err) {
REDISMODULE_NOT_USED(name);
REDISMODULE_NOT_USED(err);
REDISMODULE_NOT_USED(privdata);
flagsval = val;
return REDISMODULE_OK;
}
int boolApplyFunc(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err) { int boolApplyFunc(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err) {
REDISMODULE_NOT_USED(ctx); REDISMODULE_NOT_USED(ctx);
REDISMODULE_NOT_USED(privdata); REDISMODULE_NOT_USED(privdata);
...@@ -106,10 +121,13 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -106,10 +121,13 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
} }
/* On the stack to make sure we're copying them. */ /* On the stack to make sure we're copying them. */
const char *enum_vals[3] = {"one", "two", "three"}; const char *enum_vals[] = {"none", "one", "two", "three"};
const int int_vals[3] = {0, 2, 4}; const int int_vals[] = {0, 1, 2, 4};
if (RedisModule_RegisterEnumConfig(ctx, "enum", 0, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 3, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL) == REDISMODULE_ERR) { if (RedisModule_RegisterEnumConfig(ctx, "enum", 1, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 4, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
if (RedisModule_RegisterEnumConfig(ctx, "flags", 3, REDISMODULE_CONFIG_DEFAULT | REDISMODULE_CONFIG_BITFLAGS, enum_vals, int_vals, 4, getFlagsConfigCommand, setFlagsConfigCommand, NULL, NULL) == REDISMODULE_ERR) {
return REDISMODULE_ERR; return REDISMODULE_ERR;
} }
/* Memory config here. */ /* Memory config here. */
...@@ -139,4 +157,4 @@ int RedisModule_OnUnload(RedisModuleCtx *ctx) { ...@@ -139,4 +157,4 @@ int RedisModule_OnUnload(RedisModuleCtx *ctx) {
strval = NULL; strval = NULL;
} }
return REDISMODULE_OK; return REDISMODULE_OK;
} }
\ No newline at end of file
#include "redismodule.h"
#include <string.h>
#include <assert.h>
#include <unistd.h>
#define UNUSED(V) ((void) V)
int cmd_publish_classic(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
if (argc != 3)
return RedisModule_WrongArity(ctx);
int receivers = RedisModule_PublishMessage(ctx, argv[1], argv[2]);
RedisModule_ReplyWithLongLong(ctx, receivers);
return REDISMODULE_OK;
}
int cmd_publish_shard(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
if (argc != 3)
return RedisModule_WrongArity(ctx);
int receivers = RedisModule_PublishMessageShard(ctx, argv[1], argv[2]);
RedisModule_ReplyWithLongLong(ctx, receivers);
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
if (RedisModule_Init(ctx,"publish",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"publish.classic",cmd_publish_classic,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"publish.shard",cmd_publish_shard,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
...@@ -11,7 +11,10 @@ int cmd_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { ...@@ -11,7 +11,10 @@ int cmd_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
int cmd_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { int cmd_get(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv); UNUSED(argv);
UNUSED(argc);
if (argc > 4) /* For testing */
return RedisModule_WrongArity(ctx);
RedisModule_ReplyWithSimpleString(ctx, "OK"); RedisModule_ReplyWithSimpleString(ctx, "OK");
return REDISMODULE_OK; return REDISMODULE_OK;
} }
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
source "../tests/includes/init-tests.tcl" source "../tests/includes/init-tests.tcl"
foreach_sentinel_id id { foreach_sentinel_id id {
S $id sentinel debug info-period 1000 S $id sentinel debug info-period 2000
S $id sentinel debug default-down-after 3000 S $id sentinel debug default-down-after 6000
S $id sentinel debug publish-period 500 S $id sentinel debug publish-period 1000
} }
test "Manual failover works" { test "Manual failover works" {
......
...@@ -28,7 +28,8 @@ test "(init) Sentinels can start monitoring a master" { ...@@ -28,7 +28,8 @@ test "(init) Sentinels can start monitoring a master" {
foreach_sentinel_id id { foreach_sentinel_id id {
assert {[S $id sentinel master mymaster] ne {}} assert {[S $id sentinel master mymaster] ne {}}
S $id SENTINEL SET mymaster down-after-milliseconds 2000 S $id SENTINEL SET mymaster down-after-milliseconds 2000
S $id SENTINEL SET mymaster failover-timeout 20000 S $id SENTINEL SET mymaster failover-timeout 10000
S $id SENTINEL debug tilt-period 5000
S $id SENTINEL SET mymaster parallel-syncs 10 S $id SENTINEL SET mymaster parallel-syncs 10
if {$::leaked_fds_file != "" && [exec uname] == "Linux"} { if {$::leaked_fds_file != "" && [exec uname] == "Linux"} {
S $id SENTINEL SET mymaster notification-script ../../tests/helpers/check_leaked_fds.tcl S $id SENTINEL SET mymaster notification-script ../../tests/helpers/check_leaked_fds.tcl
......
...@@ -77,6 +77,12 @@ proc getInfoProperty {infostr property} { ...@@ -77,6 +77,12 @@ proc getInfoProperty {infostr property} {
} }
} }
proc cluster_info {r field} {
if {[regexp "^$field:(.*?)\r\n" [$r cluster info] _ value]} {
set _ $value
}
}
# Return value for INFO property # Return value for INFO property
proc status {r property} { proc status {r property} {
set _ [getInfoProperty [{*}$r info] $property] set _ [getInfoProperty [{*}$r info] $property]
...@@ -823,11 +829,21 @@ proc subscribe {client channels} { ...@@ -823,11 +829,21 @@ proc subscribe {client channels} {
consume_subscribe_messages $client subscribe $channels consume_subscribe_messages $client subscribe $channels
} }
proc ssubscribe {client channels} {
$client ssubscribe {*}$channels
consume_subscribe_messages $client ssubscribe $channels
}
proc unsubscribe {client {channels {}}} { proc unsubscribe {client {channels {}}} {
$client unsubscribe {*}$channels $client unsubscribe {*}$channels
consume_subscribe_messages $client unsubscribe $channels consume_subscribe_messages $client unsubscribe $channels
} }
proc sunsubscribe {client {channels {}}} {
$client sunsubscribe {*}$channels
consume_subscribe_messages $client sunsubscribe $channels
}
proc psubscribe {client channels} { proc psubscribe {client channels} {
$client psubscribe {*}$channels $client psubscribe {*}$channels
consume_subscribe_messages $client psubscribe $channels consume_subscribe_messages $client psubscribe $channels
......
...@@ -94,6 +94,7 @@ set ::all_tests { ...@@ -94,6 +94,7 @@ set ::all_tests {
unit/client-eviction unit/client-eviction
unit/violations unit/violations
unit/replybufsize unit/replybufsize
unit/cluster-scripting
} }
# Index to the next test to run in the ::all_tests list. # Index to the next test to run in the ::all_tests list.
set ::next_test 0 set ::next_test 0
...@@ -274,6 +275,16 @@ proc s {args} { ...@@ -274,6 +275,16 @@ proc s {args} {
status [srv $level "client"] [lindex $args 0] status [srv $level "client"] [lindex $args 0]
} }
# Provide easy access to CLUSTER INFO properties. Same semantic as "proc s".
proc csi {args} {
set level 0
if {[string is integer [lindex $args 0]]} {
set level [lindex $args 0]
set args [lrange $args 1 end]
}
cluster_info [srv $level "client"] [lindex $args 0]
}
# Test wrapped into run_solo are sent back from the client to the # Test wrapped into run_solo are sent back from the client to the
# test server, so that the test server will send them again to # test server, so that the test server will send them again to
# clients once the clients are idle. # clients once the clients are idle.
......
...@@ -173,7 +173,7 @@ start_server {tags {"acl external:skip"}} { ...@@ -173,7 +173,7 @@ start_server {tags {"acl external:skip"}} {
assert_equal PONG [$r2 PING] assert_equal PONG [$r2 PING]
assert_equal {} [$r2 get readwrite_str] assert_equal {} [$r2 get readwrite_str]
assert_error {ERR* not an integer *} {$r2 set readwrite_str bar ex get} assert_error {ERR * not an integer *} {$r2 set readwrite_str bar ex get}
assert_equal {OK} [$r2 set readwrite_str bar] assert_equal {OK} [$r2 set readwrite_str bar]
assert_equal {bar} [$r2 get readwrite_str] assert_equal {bar} [$r2 get readwrite_str]
......
...@@ -511,7 +511,7 @@ start_server {tags {"acl external:skip"}} { ...@@ -511,7 +511,7 @@ start_server {tags {"acl external:skip"}} {
test "ACL CAT with illegal arguments" { test "ACL CAT with illegal arguments" {
assert_error {*Unknown category 'NON_EXISTS'} {r ACL CAT NON_EXISTS} assert_error {*Unknown category 'NON_EXISTS'} {r ACL CAT NON_EXISTS}
assert_error {*Unknown subcommand or wrong number of arguments for 'CAT'*} {r ACL CAT NON_EXISTS NON_EXISTS2} assert_error {*unknown subcommand or wrong number of arguments for 'CAT'*} {r ACL CAT NON_EXISTS NON_EXISTS2}
} }
test "ACL CAT without category - list all categories" { test "ACL CAT without category - list all categories" {
......
...@@ -2,7 +2,7 @@ start_server {tags {"auth external:skip"}} { ...@@ -2,7 +2,7 @@ start_server {tags {"auth external:skip"}} {
test {AUTH fails if there is no password configured server side} { test {AUTH fails if there is no password configured server side} {
catch {r auth foo} err catch {r auth foo} err
set _ $err set _ $err
} {ERR*any password*} } {ERR *any password*}
test {Arity check for auth command} { test {Arity check for auth command} {
catch {r auth a b c} err catch {r auth a b c} err
......
...@@ -133,12 +133,12 @@ start_server {tags {"bitops"}} { ...@@ -133,12 +133,12 @@ start_server {tags {"bitops"}} {
test {BITCOUNT syntax error #1} { test {BITCOUNT syntax error #1} {
catch {r bitcount s 0} e catch {r bitcount s 0} e
set e set e
} {ERR*syntax*} } {ERR *syntax*}
test {BITCOUNT syntax error #2} { test {BITCOUNT syntax error #2} {
catch {r bitcount s 0 1 hello} e catch {r bitcount s 0 1 hello} e
set e set e
} {ERR*syntax*} } {ERR *syntax*}
test {BITCOUNT regression test for github issue #582} { test {BITCOUNT regression test for github issue #582} {
r del foo r del foo
...@@ -546,7 +546,10 @@ start_server {tags {"bitops"}} { ...@@ -546,7 +546,10 @@ start_server {tags {"bitops"}} {
} }
} }
} }
}
run_solo {bitops-large-memory} {
start_server {tags {"bitops"}} {
test "BIT pos larger than UINT_MAX" { test "BIT pos larger than UINT_MAX" {
set bytes [expr (1 << 29) + 1] set bytes [expr (1 << 29) + 1]
set bitpos [expr (1 << 32)] set bitpos [expr (1 << 32)]
...@@ -587,3 +590,4 @@ start_server {tags {"bitops"}} { ...@@ -587,3 +590,4 @@ start_server {tags {"bitops"}} {
r del mykey r del mykey
} {1} {large-memory needs:debug} } {1} {large-memory needs:debug}
} }
} ;#run_solo
# make sure the test infra won't use SELECT
set old_singledb $::singledb
set ::singledb 1
start_server {overrides {cluster-enabled yes} tags {external:skip cluster}} {
r 0 cluster addslotsrange 0 16383
wait_for_condition 50 100 {
[csi 0 cluster_state] eq "ok"
} else {
fail "Cluster never became 'ok'"
}
test {Eval scripts with shebangs and functions default to no cross slots} {
# Test that scripts with shebang block cross slot operations
assert_error "ERR Script attempted to access keys that do not hash to the same slot*" {
r 0 eval {#!lua
redis.call('set', 'foo', 'bar')
redis.call('set', 'bar', 'foo')
return 'OK'
} 0}
# Test the functions by default block cross slot operations
r 0 function load REPLACE {#!lua name=crossslot
local function test_cross_slot(keys, args)
redis.call('set', 'foo', 'bar')
redis.call('set', 'bar', 'foo')
return 'OK'
end
redis.register_function('test_cross_slot', test_cross_slot)}
assert_error "ERR Script attempted to access keys that do not hash to the same slot*" {r FCALL test_cross_slot 0}
}
test {Cross slot commands are allowed by default for eval scripts and with allow-cross-slot-keys flag} {
# Old style lua scripts are allowed to access cross slot operations
r 0 eval "redis.call('set', 'foo', 'bar'); redis.call('set', 'bar', 'foo')" 0
# scripts with allow-cross-slot-keys flag are allowed
r 0 eval {#!lua flags=allow-cross-slot-keys
redis.call('set', 'foo', 'bar'); redis.call('set', 'bar', 'foo')
} 0
# Functions with allow-cross-slot-keys flag are allowed
r 0 function load REPLACE {#!lua name=crossslot
local function test_cross_slot(keys, args)
redis.call('set', 'foo', 'bar')
redis.call('set', 'bar', 'foo')
return 'OK'
end
redis.register_function{function_name='test_cross_slot', callback=test_cross_slot, flags={ 'allow-cross-slot-keys' }}}
r FCALL test_cross_slot 0
}
test {Cross slot commands are also blocked if they disagree with pre-declared keys} {
assert_error "ERR Script attempted to access keys that do not hash to the same slot*" {
r 0 eval {#!lua
redis.call('set', 'foo', 'bar')
return 'OK'
} 1 bar}
}
}
set ::singledb $old_singledb
...@@ -117,7 +117,7 @@ start_server {tags {"scripting"}} { ...@@ -117,7 +117,7 @@ start_server {tags {"scripting"}} {
r function bad_subcommand r function bad_subcommand
} e } e
set _ $e set _ $e
} {*Unknown subcommand*} } {*unknown subcommand*}
test {FUNCTION - test loading from rdb} { test {FUNCTION - test loading from rdb} {
r debug reload r debug reload
...@@ -205,7 +205,7 @@ start_server {tags {"scripting"}} { ...@@ -205,7 +205,7 @@ start_server {tags {"scripting"}} {
test {FUNCTION - test function restore with wrong number of arguments} { test {FUNCTION - test function restore with wrong number of arguments} {
catch {r function restore arg1 args2 arg3} e catch {r function restore arg1 args2 arg3} e
set _ $e set _ $e
} {*Unknown subcommand or wrong number of arguments for 'restore'. Try FUNCTION HELP.} } {*unknown subcommand or wrong number of arguments for 'restore'. Try FUNCTION HELP.}
test {FUNCTION - test fcall_ro with write command} { test {FUNCTION - test fcall_ro with write command} {
r function load REPLACE [get_no_writes_function_code lua test test {return redis.call('set', 'x', '1')}] r function load REPLACE [get_no_writes_function_code lua test test {return redis.call('set', 'x', '1')}]
...@@ -298,7 +298,7 @@ start_server {tags {"scripting"}} { ...@@ -298,7 +298,7 @@ start_server {tags {"scripting"}} {
assert_match {*only supports SYNC|ASYNC*} $e assert_match {*only supports SYNC|ASYNC*} $e
catch {r function flush sync extra_arg} e catch {r function flush sync extra_arg} e
assert_match {*Unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.} $e assert_match {*unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.} $e
} }
} }
...@@ -624,16 +624,16 @@ start_server {tags {"scripting"}} { ...@@ -624,16 +624,16 @@ start_server {tags {"scripting"}} {
} }
} e } e
set _ $e set _ $e
} {*attempt to call field 'call' (a nil value)*} } {*attempted to access nonexistent global variable 'call'*}
test {LIBRARIES - redis.call from function load} { test {LIBRARIES - redis.setresp from function load} {
catch { catch {
r function load replace {#!lua name=lib2 r function load replace {#!lua name=lib2
return redis.setresp(3) return redis.setresp(3)
} }
} e } e
set _ $e set _ $e
} {*attempt to call field 'setresp' (a nil value)*} } {*attempted to access nonexistent global variable 'setresp'*}
test {LIBRARIES - redis.set_repl from function load} { test {LIBRARIES - redis.set_repl from function load} {
catch { catch {
...@@ -642,7 +642,7 @@ start_server {tags {"scripting"}} { ...@@ -642,7 +642,7 @@ start_server {tags {"scripting"}} {
} }
} e } e
set _ $e set _ $e
} {*attempt to call field 'set_repl' (a nil value)*} } {*attempted to access nonexistent global variable 'set_repl'*}
test {LIBRARIES - malicious access test} { test {LIBRARIES - malicious access test} {
# the 'library' API is not exposed inside a # the 'library' API is not exposed inside a
...@@ -669,37 +669,18 @@ start_server {tags {"scripting"}} { ...@@ -669,37 +669,18 @@ start_server {tags {"scripting"}} {
end) end)
end) end)
} }
assert_equal {OK} [r fcall f1 0] catch {[r fcall f1 0]} e
assert_match {*Attempt to modify a readonly table*} $e
catch {[r function load {#!lua name=lib2 catch {[r function load {#!lua name=lib2
redis.math.random() redis.math.random()
}]} e }]} e
assert_match {*can only be called inside a script invocation*} $e assert_match {*Script attempted to access nonexistent global variable 'math'*} $e
catch {[r function load {#!lua name=lib2
redis.math.randomseed()
}]} e
assert_match {*can only be called inside a script invocation*} $e
catch {[r function load {#!lua name=lib2 catch {[r function load {#!lua name=lib2
redis.redis.call('ping') redis.redis.call('ping')
}]} e }]} e
assert_match {*can only be called inside a script invocation*} $e assert_match {*Script attempted to access nonexistent global variable 'redis'*} $e
catch {[r function load {#!lua name=lib2
redis.redis.pcall('ping')
}]} e
assert_match {*can only be called inside a script invocation*} $e
catch {[r function load {#!lua name=lib2
redis.redis.setresp(3)
}]} e
assert_match {*can only be called inside a script invocation*} $e
catch {[r function load {#!lua name=lib2
redis.redis.set_repl(redis.redis.REPL_NONE)
}]} e
assert_match {*can only be called inside a script invocation*} $e
catch {[r fcall f2 0]} e catch {[r fcall f2 0]} e
assert_match {*can only be called on FUNCTION LOAD command*} $e assert_match {*can only be called on FUNCTION LOAD command*} $e
...@@ -756,7 +737,7 @@ start_server {tags {"scripting"}} { ...@@ -756,7 +737,7 @@ start_server {tags {"scripting"}} {
} }
} e } e
set _ $e set _ $e
} {*attempted to create global variable 'a'*} } {*Attempt to modify a readonly table*}
test {LIBRARIES - named arguments} { test {LIBRARIES - named arguments} {
r function load {#!lua name=lib r function load {#!lua name=lib
...@@ -986,7 +967,7 @@ start_server {tags {"scripting"}} { ...@@ -986,7 +967,7 @@ start_server {tags {"scripting"}} {
assert_match {*command not allowed when used memory*} $e assert_match {*command not allowed when used memory*} $e
r config set maxmemory 0 r config set maxmemory 0
} } {OK} {needs:config-maxmemory}
test {FUNCTION - verify allow-omm allows running any command} { test {FUNCTION - verify allow-omm allows running any command} {
r FUNCTION load replace {#!lua name=f1 r FUNCTION load replace {#!lua name=f1
...@@ -999,11 +980,11 @@ start_server {tags {"scripting"}} { ...@@ -999,11 +980,11 @@ start_server {tags {"scripting"}} {
r config set maxmemory 1 r config set maxmemory 1
assert_match {OK} [r fcall f1 1 k] assert_match {OK} [r fcall f1 1 x]
assert_match {1} [r get x] assert_match {1} [r get x]
r config set maxmemory 0 r config set maxmemory 0
} } {OK} {needs:config-maxmemory}
} }
start_server {tags {"scripting"}} { start_server {tags {"scripting"}} {
...@@ -1074,7 +1055,7 @@ start_server {tags {"scripting"}} { ...@@ -1074,7 +1055,7 @@ start_server {tags {"scripting"}} {
assert_match {*can not run it when used memory > 'maxmemory'*} $e assert_match {*can not run it when used memory > 'maxmemory'*} $e
r config set maxmemory 0 r config set maxmemory 0
} } {OK} {needs:config-maxmemory}
test {FUNCTION - deny oom on no-writes function} { test {FUNCTION - deny oom on no-writes function} {
r FUNCTION load replace {#!lua name=test r FUNCTION load replace {#!lua name=test
...@@ -1090,7 +1071,7 @@ start_server {tags {"scripting"}} { ...@@ -1090,7 +1071,7 @@ start_server {tags {"scripting"}} {
assert_match {*can not run it when used memory > 'maxmemory'*} $e assert_match {*can not run it when used memory > 'maxmemory'*} $e
r config set maxmemory 0 r config set maxmemory 0
} } {OK} {needs:config-maxmemory}
test {FUNCTION - allow stale} { test {FUNCTION - allow stale} {
r FUNCTION load replace {#!lua name=test r FUNCTION load replace {#!lua name=test
...@@ -1198,4 +1179,32 @@ start_server {tags {"scripting"}} { ...@@ -1198,4 +1179,32 @@ start_server {tags {"scripting"}} {
redis.register_function('foo', function() return 1 end) redis.register_function('foo', function() return 1 end)
} }
} {foo} } {foo}
test {FUNCTION - trick global protection 1} {
r FUNCTION FLUSH
r FUNCTION load {#!lua name=test1
redis.register_function('f1', function()
mt = getmetatable(_G)
original_globals = mt.__index
original_globals['redis'] = function() return 1 end
end)
}
catch {[r fcall f1 0]} e
set _ $e
} {*Attempt to modify a readonly table*}
test {FUNCTION - test getmetatable on script load} {
r FUNCTION FLUSH
catch {
r FUNCTION load {#!lua name=test1
mt = getmetatable(_G)
}
} e
set _ $e
} {*Script attempted to access nonexistent global variable 'getmetatable'*}
} }
...@@ -193,14 +193,14 @@ start_server {tags {"geo"}} { ...@@ -193,14 +193,14 @@ start_server {tags {"geo"}} {
r geoadd nyc xx nx -73.9454966 40.747533 "lic market" r geoadd nyc xx nx -73.9454966 40.747533 "lic market"
} err } err
set err set err
} {ERR*syntax*} } {ERR *syntax*}
test {GEOADD update with invalid option} { test {GEOADD update with invalid option} {
catch { catch {
r geoadd nyc ch xx foo -73.9454966 40.747533 "lic market" r geoadd nyc ch xx foo -73.9454966 40.747533 "lic market"
} err } err
set err set err
} {ERR*syntax*} } {ERR *syntax*}
test {GEOADD invalid coordinates} { test {GEOADD invalid coordinates} {
catch { catch {
...@@ -229,27 +229,27 @@ start_server {tags {"geo"}} { ...@@ -229,27 +229,27 @@ start_server {tags {"geo"}} {
test {GEOSEARCH FROMLONLAT and FROMMEMBER cannot exist at the same time} { test {GEOSEARCH FROMLONLAT and FROMMEMBER cannot exist at the same time} {
catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 frommember xxx bybox 6 6 km asc} e catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 frommember xxx bybox 6 6 km asc} e
set e set e
} {ERR*syntax*} } {ERR *syntax*}
test {GEOSEARCH FROMLONLAT and FROMMEMBER one must exist} { test {GEOSEARCH FROMLONLAT and FROMMEMBER one must exist} {
catch {r geosearch nyc bybox 3 3 km asc desc withhash withdist withcoord} e catch {r geosearch nyc bybox 3 3 km asc desc withhash withdist withcoord} e
set e set e
} {ERR*exactly one of FROMMEMBER or FROMLONLAT*} } {ERR *exactly one of FROMMEMBER or FROMLONLAT*}
test {GEOSEARCH BYRADIUS and BYBOX cannot exist at the same time} { test {GEOSEARCH BYRADIUS and BYBOX cannot exist at the same time} {
catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 byradius 3 km bybox 3 3 km asc} e catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 byradius 3 km bybox 3 3 km asc} e
set e set e
} {ERR*syntax*} } {ERR *syntax*}
test {GEOSEARCH BYRADIUS and BYBOX one must exist} { test {GEOSEARCH BYRADIUS and BYBOX one must exist} {
catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 asc desc withhash withdist withcoord} e catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 asc desc withhash withdist withcoord} e
set e set e
} {ERR*exactly one of BYRADIUS and BYBOX*} } {ERR *exactly one of BYRADIUS and BYBOX*}
test {GEOSEARCH with STOREDIST option} { test {GEOSEARCH with STOREDIST option} {
catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 bybox 6 6 km asc storedist} e catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 bybox 6 6 km asc storedist} e
set e set e
} {ERR*syntax*} } {ERR *syntax*}
test {GEORADIUS withdist (sorted)} { test {GEORADIUS withdist (sorted)} {
r georadius nyc -73.9798091 40.7598464 3 km withdist asc r georadius nyc -73.9798091 40.7598464 3 km withdist asc
...@@ -274,12 +274,12 @@ start_server {tags {"geo"}} { ...@@ -274,12 +274,12 @@ start_server {tags {"geo"}} {
test {GEORADIUS with ANY but no COUNT} { test {GEORADIUS with ANY but no COUNT} {
catch {r georadius nyc -73.9798091 40.7598464 10 km ANY ASC} e catch {r georadius nyc -73.9798091 40.7598464 10 km ANY ASC} e
set e set e
} {ERR*ANY*requires*COUNT*} } {ERR *ANY*requires*COUNT*}
test {GEORADIUS with COUNT but missing integer argument} { test {GEORADIUS with COUNT but missing integer argument} {
catch {r georadius nyc -73.9798091 40.7598464 10 km COUNT} e catch {r georadius nyc -73.9798091 40.7598464 10 km COUNT} e
set e set e
} {ERR*syntax*} } {ERR *syntax*}
test {GEORADIUS with COUNT DESC} { test {GEORADIUS with COUNT DESC} {
r georadius nyc -73.9798091 40.7598464 10 km COUNT 2 DESC r georadius nyc -73.9798091 40.7598464 10 km COUNT 2 DESC
......
...@@ -23,9 +23,9 @@ start_server {tags {"introspection"}} { ...@@ -23,9 +23,9 @@ start_server {tags {"introspection"}} {
assert_error "ERR wrong number of arguments for 'client|kill' command" {r client kill} assert_error "ERR wrong number of arguments for 'client|kill' command" {r client kill}
assert_error "ERR syntax error*" {r client kill id 10 wrong_arg} assert_error "ERR syntax error*" {r client kill id 10 wrong_arg}
assert_error "ERR*greater than 0*" {r client kill id str} assert_error "ERR *greater than 0*" {r client kill id str}
assert_error "ERR*greater than 0*" {r client kill id -1} assert_error "ERR *greater than 0*" {r client kill id -1}
assert_error "ERR*greater than 0*" {r client kill id 0} assert_error "ERR *greater than 0*" {r client kill id 0}
assert_error "ERR Unknown client type*" {r client kill type wrong_type} assert_error "ERR Unknown client type*" {r client kill type wrong_type}
...@@ -215,6 +215,7 @@ start_server {tags {"introspection"}} { ...@@ -215,6 +215,7 @@ start_server {tags {"introspection"}} {
dbfilename dbfilename
logfile logfile
dir dir
socket-mark-id
} }
if {!$::tls} { if {!$::tls} {
...@@ -285,16 +286,22 @@ start_server {tags {"introspection"}} { ...@@ -285,16 +286,22 @@ start_server {tags {"introspection"}} {
} }
} {} {external:skip} } {} {external:skip}
test {CONFIG REWRITE handles save properly} { test {CONFIG REWRITE handles save and shutdown properly} {
r config set save "3600 1 300 100 60 10000" r config set save "3600 1 300 100 60 10000"
r config set shutdown-on-sigterm "nosave now"
r config set shutdown-on-sigint "save"
r config rewrite r config rewrite
restart_server 0 true false restart_server 0 true false
assert_equal [r config get save] {save {3600 1 300 100 60 10000}} assert_equal [r config get save] {save {3600 1 300 100 60 10000}}
assert_equal [r config get shutdown-on-sigterm] {shutdown-on-sigterm {nosave now}}
assert_equal [r config get shutdown-on-sigint] {shutdown-on-sigint save}
r config set save "" r config set save ""
r config set shutdown-on-sigterm "default"
r config rewrite r config rewrite
restart_server 0 true false restart_server 0 true false
assert_equal [r config get save] {save {}} assert_equal [r config get save] {save {}}
assert_equal [r config get shutdown-on-sigterm] {shutdown-on-sigterm default}
start_server {config "minimal.conf"} { start_server {config "minimal.conf"} {
assert_equal [r config get save] {save {3600 1 300 100 60 10000}} assert_equal [r config get save] {save {3600 1 300 100 60 10000}}
...@@ -409,11 +416,11 @@ start_server {tags {"introspection"}} { ...@@ -409,11 +416,11 @@ start_server {tags {"introspection"}} {
} }
test {CONFIG SET duplicate configs} { test {CONFIG SET duplicate configs} {
assert_error "ERR*duplicate*" {r config set maxmemory 10000001 maxmemory 10000002} assert_error "ERR *duplicate*" {r config set maxmemory 10000001 maxmemory 10000002}
} }
test {CONFIG SET set immutable} { test {CONFIG SET set immutable} {
assert_error "ERR*immutable*" {r config set daemonize yes} assert_error "ERR *immutable*" {r config set daemonize yes}
} }
test {CONFIG GET hidden configs} { test {CONFIG GET hidden configs} {
...@@ -448,8 +455,8 @@ start_server {tags {"introspection"}} { ...@@ -448,8 +455,8 @@ start_server {tags {"introspection"}} {
start_server {tags {"introspection external:skip"} overrides {enable-protected-configs {no} enable-debug-command {no}}} { start_server {tags {"introspection external:skip"} overrides {enable-protected-configs {no} enable-debug-command {no}}} {
test {cannot modify protected configuration - no} { test {cannot modify protected configuration - no} {
assert_error "ERR*protected*" {r config set dir somedir} assert_error "ERR *protected*" {r config set dir somedir}
assert_error "ERR*DEBUG command not allowed*" {r DEBUG HELP} assert_error "ERR *DEBUG command not allowed*" {r DEBUG HELP}
} {} {needs:debug} } {} {needs:debug}
} }
...@@ -464,8 +471,8 @@ start_server {config "minimal.conf" tags {"introspection external:skip"} overrid ...@@ -464,8 +471,8 @@ start_server {config "minimal.conf" tags {"introspection external:skip"} overrid
if {$myaddr != "" && ![string match {127.*} $myaddr]} { if {$myaddr != "" && ![string match {127.*} $myaddr]} {
# Non-loopback client should fail # Non-loopback client should fail
set r2 [get_nonloopback_client] set r2 [get_nonloopback_client]
assert_error "ERR*protected*" {$r2 config set dir somedir} assert_error "ERR *protected*" {$r2 config set dir somedir}
assert_error "ERR*DEBUG command not allowed*" {$r2 DEBUG HELP} assert_error "ERR *DEBUG command not allowed*" {$r2 DEBUG HELP}
} }
} {} {needs:debug} } {} {needs:debug}
} }
......
...@@ -16,6 +16,7 @@ start_server {tags {"modules acl"}} { ...@@ -16,6 +16,7 @@ start_server {tags {"modules acl"}} {
assert {[dict get $entry username] eq {default}} assert {[dict get $entry username] eq {default}}
assert {[dict get $entry context] eq {module}} assert {[dict get $entry context] eq {module}}
assert {[dict get $entry object] eq {set}} assert {[dict get $entry object] eq {set}}
assert {[dict get $entry reason] eq {command}}
} }
test {test module check acl for key perm} { test {test module check acl for key perm} {
...@@ -75,6 +76,7 @@ start_server {tags {"modules acl"}} { ...@@ -75,6 +76,7 @@ start_server {tags {"modules acl"}} {
assert {[dict get $entry username] eq {default}} assert {[dict get $entry username] eq {default}}
assert {[dict get $entry context] eq {module}} assert {[dict get $entry context] eq {module}}
assert {[dict get $entry object] eq {z}} assert {[dict get $entry object] eq {z}}
assert {[dict get $entry reason] eq {key}}
# rm call check for command permission # rm call check for command permission
r acl setuser default -set r acl setuser default -set
...@@ -88,6 +90,7 @@ start_server {tags {"modules acl"}} { ...@@ -88,6 +90,7 @@ start_server {tags {"modules acl"}} {
assert {[dict get $entry username] eq {default}} assert {[dict get $entry username] eq {default}}
assert {[dict get $entry context] eq {module}} assert {[dict get $entry context] eq {module}}
assert {[dict get $entry object] eq {set}} assert {[dict get $entry object] eq {set}}
assert {[dict get $entry reason] eq {command}}
} }
test "Unload the module - aclcheck" { test "Unload the module - aclcheck" {
......
...@@ -36,6 +36,6 @@ start_server {tags {"modules"}} { ...@@ -36,6 +36,6 @@ start_server {tags {"modules"}} {
start_server {tags {"modules external:skip"} overrides {enable-module-command no}} { start_server {tags {"modules external:skip"} overrides {enable-module-command no}} {
test {module command disabled} { test {module command disabled} {
assert_error "ERR*MODULE command not allowed*" {r module load $testmodule} assert_error "ERR *MODULE command not allowed*" {r module load $testmodule}
} }
} }
\ No newline at end of file
...@@ -90,7 +90,8 @@ start_server {tags {"modules"}} { ...@@ -90,7 +90,8 @@ start_server {tags {"modules"}} {
} }
} }
test {Busy module command} { foreach call_type {nested normal} {
test "Busy module command - $call_type" {
set busy_time_limit 50 set busy_time_limit 50
set old_time_limit [lindex [r config get busy-reply-threshold] 1] set old_time_limit [lindex [r config get busy-reply-threshold] 1]
r config set busy-reply-threshold $busy_time_limit r config set busy-reply-threshold $busy_time_limit
...@@ -98,7 +99,15 @@ start_server {tags {"modules"}} { ...@@ -98,7 +99,15 @@ start_server {tags {"modules"}} {
# run command that blocks until released # run command that blocks until released
set start [clock clicks -milliseconds] set start [clock clicks -milliseconds]
$rd slow_fg_command 0 if {$call_type == "nested"} {
$rd do_rm_call slow_fg_command 0
} else {
$rd slow_fg_command 0
}
$rd flush
# send another command after the blocked one, to make sure we don't attempt to process it
$rd ping
$rd flush $rd flush
# make sure we get BUSY error, and that we didn't get it too early # make sure we get BUSY error, and that we didn't get it too early
...@@ -112,11 +121,16 @@ start_server {tags {"modules"}} { ...@@ -112,11 +121,16 @@ start_server {tags {"modules"}} {
} else { } else {
fail "Failed waiting for busy command to end" fail "Failed waiting for busy command to end"
} }
$rd read assert_equal [$rd read] "1"
assert_equal [$rd read] "PONG"
#run command that blocks for 200ms # run command that blocks for 200ms
set start [clock clicks -milliseconds] set start [clock clicks -milliseconds]
$rd slow_fg_command 200000 if {$call_type == "nested"} {
$rd do_rm_call slow_fg_command 200000
} else {
$rd slow_fg_command 200000
}
$rd flush $rd flush
after 10 ;# try to make sure redis started running the command before we proceed after 10 ;# try to make sure redis started running the command before we proceed
...@@ -128,6 +142,7 @@ start_server {tags {"modules"}} { ...@@ -128,6 +142,7 @@ start_server {tags {"modules"}} {
$rd close $rd close
r config set busy-reply-threshold $old_time_limit r config set busy-reply-threshold $old_time_limit
} }
}
test {RM_Call from blocked client} { test {RM_Call from blocked client} {
set busy_time_limit 50 set busy_time_limit 50
...@@ -141,6 +156,10 @@ start_server {tags {"modules"}} { ...@@ -141,6 +156,10 @@ start_server {tags {"modules"}} {
set start [clock clicks -milliseconds] set start [clock clicks -milliseconds]
$rd do_bg_rm_call hgetall hash $rd do_bg_rm_call hgetall hash
# send another command after the blocked one, to make sure we don't attempt to process it
$rd ping
$rd flush
# wait till we know we're blocked inside the module # wait till we know we're blocked inside the module
wait_for_condition 50 100 { wait_for_condition 50 100 {
[r is_in_slow_bg_operation] eq 1 [r is_in_slow_bg_operation] eq 1
...@@ -162,10 +181,10 @@ start_server {tags {"modules"}} { ...@@ -162,10 +181,10 @@ start_server {tags {"modules"}} {
assert_equal [r ping] {PONG} assert_equal [r ping] {PONG}
r config set busy-reply-threshold $old_time_limit r config set busy-reply-threshold $old_time_limit
set res [$rd read] assert_equal [$rd read] {foo bar}
assert_equal [$rd read] {PONG}
$rd close $rd close
set _ $res }
} {foo bar}
test {blocked client reaches client output buffer limit} { test {blocked client reaches client output buffer limit} {
r hset hash big [string repeat x 50000] r hset hash big [string repeat x 50000]
...@@ -184,9 +203,13 @@ start_server {tags {"modules"}} { ...@@ -184,9 +203,13 @@ start_server {tags {"modules"}} {
r config resetstat r config resetstat
# simple module command that replies with string error # simple module command that replies with string error
assert_error "ERR Unknown Redis command 'hgetalllll'." {r do_rm_call hgetalllll} assert_error "ERR unknown command 'hgetalllll', with args beginning with:" {r do_rm_call hgetalllll}
assert_equal [errorrstat ERR r] {count=1} assert_equal [errorrstat ERR r] {count=1}
# simple module command that replies with string error
assert_error "ERR unknown subcommand 'bla'. Try CONFIG HELP." {r do_rm_call config bla}
assert_equal [errorrstat ERR r] {count=2}
# module command that replies with string error from bg thread # module command that replies with string error from bg thread
assert_error "NULL reply returned" {r do_bg_rm_call hgetalllll} assert_error "NULL reply returned" {r do_bg_rm_call hgetalllll}
assert_equal [errorrstat NULL r] {count=1} assert_equal [errorrstat NULL r] {count=1}
...@@ -194,7 +217,7 @@ start_server {tags {"modules"}} { ...@@ -194,7 +217,7 @@ start_server {tags {"modules"}} {
# module command that returns an arity error # module command that returns an arity error
r do_rm_call set x x r do_rm_call set x x
assert_error "ERR wrong number of arguments for 'do_rm_call' command" {r do_rm_call} assert_error "ERR wrong number of arguments for 'do_rm_call' command" {r do_rm_call}
assert_equal [errorrstat ERR r] {count=2} assert_equal [errorrstat ERR r] {count=3}
# RM_Call that propagates an error # RM_Call that propagates an error
assert_error "WRONGTYPE*" {r do_rm_call hgetall x} assert_error "WRONGTYPE*" {r do_rm_call hgetall x}
...@@ -206,8 +229,8 @@ start_server {tags {"modules"}} { ...@@ -206,8 +229,8 @@ start_server {tags {"modules"}} {
assert_equal [errorrstat WRONGTYPE r] {count=2} assert_equal [errorrstat WRONGTYPE r] {count=2}
assert_match {*calls=2,*,rejected_calls=0,failed_calls=2} [cmdrstat hgetall r] assert_match {*calls=2,*,rejected_calls=0,failed_calls=2} [cmdrstat hgetall r]
assert_equal [s total_error_replies] 5 assert_equal [s total_error_replies] 6
assert_match {*calls=4,*,rejected_calls=0,failed_calls=3} [cmdrstat do_rm_call r] assert_match {*calls=5,*,rejected_calls=0,failed_calls=4} [cmdrstat do_rm_call r]
assert_match {*calls=2,*,rejected_calls=0,failed_calls=2} [cmdrstat do_bg_rm_call r] assert_match {*calls=2,*,rejected_calls=0,failed_calls=2} [cmdrstat do_bg_rm_call r]
} }
......
...@@ -2,22 +2,6 @@ ...@@ -2,22 +2,6 @@
source tests/support/cli.tcl source tests/support/cli.tcl
proc cluster_info {r field} {
if {[regexp "^$field:(.*?)\r\n" [$r cluster info] _ value]} {
set _ $value
}
}
# Provide easy access to CLUSTER INFO properties. Same semantic as "proc s".
proc csi {args} {
set level 0
if {[string is integer [lindex $args 0]]} {
set level [lindex $args 0]
set args [lrange $args 1 end]
}
cluster_info [srv $level "client"] [lindex $args 0]
}
set testmodule [file normalize tests/modules/blockonkeys.so] set testmodule [file normalize tests/modules/blockonkeys.so]
set testmodule_nokey [file normalize tests/modules/blockonbackground.so] set testmodule_nokey [file normalize tests/modules/blockonbackground.so]
set testmodule_blockedclient [file normalize tests/modules/blockedclient.so] set testmodule_blockedclient [file normalize tests/modules/blockedclient.so]
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment