diff options
Diffstat (limited to 'tests/modules')
-rw-r--r-- | tests/modules/Makefile | 4 | ||||
-rw-r--r-- | tests/modules/aclcheck.c | 20 | ||||
-rw-r--r-- | tests/modules/auth.c | 14 | ||||
-rw-r--r-- | tests/modules/basics.c | 31 | ||||
-rw-r--r-- | tests/modules/blockedclient.c | 2 | ||||
-rw-r--r-- | tests/modules/blockonkeys.c | 13 | ||||
-rw-r--r-- | tests/modules/eventloop.c | 1 | ||||
-rw-r--r-- | tests/modules/hooks.c | 15 | ||||
-rw-r--r-- | tests/modules/moduleconfigs.c | 142 | ||||
-rw-r--r-- | tests/modules/moduleconfigstwo.c | 39 |
10 files changed, 275 insertions, 6 deletions
diff --git a/tests/modules/Makefile b/tests/modules/Makefile index ce842f3af..1b7159c89 100644 --- a/tests/modules/Makefile +++ b/tests/modules/Makefile @@ -54,7 +54,9 @@ TEST_MODULES = \ subcommands.so \ reply.so \ cmdintrospection.so \ - eventloop.so + eventloop.so \ + moduleconfigs.so \ + moduleconfigstwo.so .PHONY: all diff --git a/tests/modules/aclcheck.c b/tests/modules/aclcheck.c index 0e9c9af29..8a9d468a6 100644 --- a/tests/modules/aclcheck.c +++ b/tests/modules/aclcheck.c @@ -136,6 +136,22 @@ int rm_call_aclcheck_cmd_module_user(RedisModuleCtx *ctx, RedisModuleString **ar return res; } +int rm_call_aclcheck_with_errors(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ + REDISMODULE_NOT_USED(argv); + REDISMODULE_NOT_USED(argc); + + if(argc < 2){ + return RedisModule_WrongArity(ctx); + } + + const char* cmd = RedisModule_StringPtrLen(argv[1], NULL); + + RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "vEC", argv + 2, argc - 2); + RedisModule_ReplyWithCallReply(ctx, rep); + RedisModule_FreeCallReply(rep); + return REDISMODULE_OK; +} + /* A wrap for RM_Call that pass the 'C' flag to do ACL check on the command. */ int rm_call_aclcheck(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ REDISMODULE_NOT_USED(argv); @@ -190,5 +206,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) "write",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx,"aclcheck.rm_call_with_errors", rm_call_aclcheck_with_errors, + "write",0,0,0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + return REDISMODULE_OK; } diff --git a/tests/modules/auth.c b/tests/modules/auth.c index 040a447ec..612320dbc 100644 --- a/tests/modules/auth.c +++ b/tests/modules/auth.c @@ -54,6 +54,16 @@ int Auth_AuthRealUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { return RedisModule_ReplyWithLongLong(ctx, (uint64_t) client_id); } +/* This command redacts every other arguments and returns OK */ +int Auth_RedactedAPI(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + REDISMODULE_NOT_USED(argv); + for(int i = argc - 1; i > 0; i -= 2) { + int result = RedisModule_RedactClientCommandArgument(ctx, i); + RedisModule_Assert(result == REDISMODULE_OK); + } + return RedisModule_ReplyWithSimpleString(ctx, "OK"); +} + int Auth_ChangeCount(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argc); @@ -87,6 +97,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) Auth_ChangeCount,"",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx,"auth.redact", + Auth_RedactedAPI,"",0,0,0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + return REDISMODULE_OK; } diff --git a/tests/modules/basics.c b/tests/modules/basics.c index 4d639d682..ecd1b8852 100644 --- a/tests/modules/basics.c +++ b/tests/modules/basics.c @@ -718,6 +718,25 @@ end: /* Return 1 if the reply matches the specified string, otherwise log errors * in the server log and return 0. */ +int TestAssertErrorReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, char *str, size_t len) { + RedisModuleString *mystr, *expected; + if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ERROR) { + return 0; + } + + mystr = RedisModule_CreateStringFromCallReply(reply); + expected = RedisModule_CreateString(ctx,str,len); + if (RedisModule_StringCompare(mystr,expected) != 0) { + const char *mystr_ptr = RedisModule_StringPtrLen(mystr,NULL); + const char *expected_ptr = RedisModule_StringPtrLen(expected,NULL); + RedisModule_Log(ctx,"warning", + "Unexpected Error reply reply '%s' (instead of '%s')", + mystr_ptr, expected_ptr); + return 0; + } + return 1; +} + int TestAssertStringReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, char *str, size_t len) { RedisModuleString *mystr, *expected; @@ -846,6 +865,18 @@ int TestBasics(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (!TestAssertStringReply(ctx,RedisModule_CallReplyArrayElement(reply, 0),"test",4)) goto fail; if (!TestAssertStringReply(ctx,RedisModule_CallReplyArrayElement(reply, 1),"1234",4)) goto fail; + T("foo", "E"); + if (!TestAssertErrorReply(ctx,reply,"ERR Unknown Redis command 'foo'.",32)) goto fail; + + T("set", "Ec", "x"); + if (!TestAssertErrorReply(ctx,reply,"ERR Wrong number of args calling Redis command 'set'.",53)) goto fail; + + T("shutdown", "SE"); + if (!TestAssertErrorReply(ctx,reply,"ERR command 'shutdown' is not allowed on script mode",52)) goto fail; + + T("set", "WEcc", "x", "1"); + if (!TestAssertErrorReply(ctx,reply,"ERR Write command 'set' was called while write is not allowed.",62)) goto fail; + RedisModule_ReplyWithSimpleString(ctx,"ALL TESTS PASSED"); return REDISMODULE_OK; diff --git a/tests/modules/blockedclient.c b/tests/modules/blockedclient.c index a2d7c6d00..9c2598a34 100644 --- a/tests/modules/blockedclient.c +++ b/tests/modules/blockedclient.c @@ -195,7 +195,7 @@ int do_rm_call(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ const char* cmd = RedisModule_StringPtrLen(argv[1], NULL); - RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "v", argv + 2, argc - 2); + RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "Ev", argv + 2, argc - 2); if(!rep){ RedisModule_ReplyWithError(ctx, "NULL reply returned"); }else{ diff --git a/tests/modules/blockonkeys.c b/tests/modules/blockonkeys.c index 4568b9fa8..1aa576489 100644 --- a/tests/modules/blockonkeys.c +++ b/tests/modules/blockonkeys.c @@ -135,22 +135,29 @@ int bpop_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int arg return RedisModule_ReplyWithSimpleString(ctx, "Request timedout"); } -/* FSL.BPOP <key> <timeout> - Block clients until list has two or more elements. +/* FSL.BPOP <key> <timeout> [NO_TO_CB]- Block clients until list has two or more elements. * When that happens, unblock client and pop the last two elements (from the right). */ int fsl_bpop(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) + if (argc < 3) return RedisModule_WrongArity(ctx); long long timeout; if (RedisModule_StringToLongLong(argv[2],&timeout) != REDISMODULE_OK || timeout < 0) return RedisModule_ReplyWithError(ctx,"ERR invalid timeout"); + int to_cb = 1; + if (argc == 4) { + if (strcasecmp("NO_TO_CB", RedisModule_StringPtrLen(argv[3], NULL))) + return RedisModule_ReplyWithError(ctx,"ERR invalid argument"); + to_cb = 0; + } + fsl_t *fsl; if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1)) return REDISMODULE_OK; if (!fsl) { - RedisModule_BlockClientOnKeys(ctx, bpop_reply_callback, bpop_timeout_callback, + RedisModule_BlockClientOnKeys(ctx, bpop_reply_callback, to_cb ? bpop_timeout_callback : NULL, NULL, timeout, &argv[1], 1, NULL); } else { RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]); diff --git a/tests/modules/eventloop.c b/tests/modules/eventloop.c index 50d3bc052..c0cfdf04f 100644 --- a/tests/modules/eventloop.c +++ b/tests/modules/eventloop.c @@ -11,7 +11,6 @@ * 4- test.oneshot : Test for oneshot API */ -#define REDISMODULE_EXPERIMENTAL_API #include "redismodule.h" #include <stdlib.h> #include <unistd.h> diff --git a/tests/modules/hooks.c b/tests/modules/hooks.c index af4681cf9..94d902d22 100644 --- a/tests/modules/hooks.c +++ b/tests/modules/hooks.c @@ -267,6 +267,18 @@ void swapDbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void LogNumericEvent(ctx, "swapdb-second", ei->dbnum_second); } +void configChangeCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void *data) +{ + REDISMODULE_NOT_USED(e); + if (sub != REDISMODULE_SUBEVENT_CONFIG_CHANGE) { + return; + } + + RedisModuleConfigChangeV1 *ei = data; + LogNumericEvent(ctx, "config-change-count", ei->num_changes); + LogStringEvent(ctx, "config-change-first", ei->config_names[0]); +} + /* This function must be present on each Redis module. It is used in order to * register the commands into the Redis server. */ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { @@ -317,6 +329,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_SwapDB, swapDbCallback); + RedisModule_SubscribeToServerEvent(ctx, + RedisModuleEvent_Config, configChangeCallback); + event_log = RedisModule_CreateDict(ctx); if (RedisModule_CreateCommand(ctx,"hooks.event_count", cmdEventCount,"",0,0,0) == REDISMODULE_ERR) diff --git a/tests/modules/moduleconfigs.c b/tests/modules/moduleconfigs.c new file mode 100644 index 000000000..a9e434a7b --- /dev/null +++ b/tests/modules/moduleconfigs.c @@ -0,0 +1,142 @@ +#include "redismodule.h" +#include <strings.h> +int mutable_bool_val; +int immutable_bool_val; +long long longval; +long long memval; +RedisModuleString *strval = NULL; +int enumval; + +/* 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 + * use names if they wanted, and store anything in privdata. */ +int getBoolConfigCommand(const char *name, void *privdata) { + REDISMODULE_NOT_USED(name); + return (*(int *)privdata); +} + +int setBoolConfigCommand(const char *name, int new, void *privdata, RedisModuleString **err) { + REDISMODULE_NOT_USED(name); + REDISMODULE_NOT_USED(err); + *(int *)privdata = new; + return REDISMODULE_OK; +} + +long long getNumericConfigCommand(const char *name, void *privdata) { + REDISMODULE_NOT_USED(name); + return (*(long long *) privdata); +} + +int setNumericConfigCommand(const char *name, long long new, void *privdata, RedisModuleString **err) { + REDISMODULE_NOT_USED(name); + REDISMODULE_NOT_USED(err); + *(long long *)privdata = new; + return REDISMODULE_OK; +} + +RedisModuleString *getStringConfigCommand(const char *name, void *privdata) { + REDISMODULE_NOT_USED(name); + REDISMODULE_NOT_USED(privdata); + return strval; +} +int setStringConfigCommand(const char *name, RedisModuleString *new, void *privdata, RedisModuleString **err) { + REDISMODULE_NOT_USED(name); + REDISMODULE_NOT_USED(err); + REDISMODULE_NOT_USED(privdata); + size_t len; + if (!strcasecmp(RedisModule_StringPtrLen(new, &len), "rejectisfreed")) { + *err = RedisModule_CreateString(NULL, "Cannot set string to 'rejectisfreed'", 36); + return REDISMODULE_ERR; + } + if (strval) RedisModule_FreeString(NULL, strval); + RedisModule_RetainString(NULL, new); + strval = new; + return REDISMODULE_OK; +} + +int getEnumConfigCommand(const char *name, void *privdata) { + REDISMODULE_NOT_USED(name); + REDISMODULE_NOT_USED(privdata); + return enumval; +} + +int setEnumConfigCommand(const char *name, int val, void *privdata, RedisModuleString **err) { + REDISMODULE_NOT_USED(name); + REDISMODULE_NOT_USED(err); + REDISMODULE_NOT_USED(privdata); + enumval = val; + return REDISMODULE_OK; +} + +int boolApplyFunc(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err) { + REDISMODULE_NOT_USED(ctx); + REDISMODULE_NOT_USED(privdata); + if (mutable_bool_val && immutable_bool_val) { + *err = RedisModule_CreateString(NULL, "Bool configs cannot both be yes.", 32); + return REDISMODULE_ERR; + } + return REDISMODULE_OK; +} + +int longlongApplyFunc(RedisModuleCtx *ctx, void *privdata, RedisModuleString **err) { + REDISMODULE_NOT_USED(ctx); + REDISMODULE_NOT_USED(privdata); + if (longval == memval) { + *err = RedisModule_CreateString(NULL, "These configs cannot equal each other.", 38); + return REDISMODULE_ERR; + } + return REDISMODULE_OK; +} + +int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + REDISMODULE_NOT_USED(argv); + REDISMODULE_NOT_USED(argc); + + if (RedisModule_Init(ctx, "moduleconfigs", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; + + if (RedisModule_RegisterBoolConfig(ctx, "mutable_bool", 1, REDISMODULE_CONFIG_DEFAULT, getBoolConfigCommand, setBoolConfigCommand, boolApplyFunc, &mutable_bool_val) == REDISMODULE_ERR) { + return REDISMODULE_ERR; + } + /* Immutable config here. */ + if (RedisModule_RegisterBoolConfig(ctx, "immutable_bool", 0, REDISMODULE_CONFIG_IMMUTABLE, getBoolConfigCommand, setBoolConfigCommand, boolApplyFunc, &immutable_bool_val) == REDISMODULE_ERR) { + return REDISMODULE_ERR; + } + if (RedisModule_RegisterStringConfig(ctx, "string", "secret password", REDISMODULE_CONFIG_DEFAULT, getStringConfigCommand, setStringConfigCommand, NULL, NULL) == REDISMODULE_ERR) { + return REDISMODULE_ERR; + } + + /* On the stack to make sure we're copying them. */ + const char *enum_vals[3] = {"one", "two", "three"}; + const int int_vals[3] = {0, 2, 4}; + + if (RedisModule_RegisterEnumConfig(ctx, "enum", 0, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 3, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL) == REDISMODULE_ERR) { + return REDISMODULE_ERR; + } + /* Memory config here. */ + if (RedisModule_RegisterNumericConfig(ctx, "memory_numeric", 1024, REDISMODULE_CONFIG_DEFAULT | REDISMODULE_CONFIG_MEMORY, 0, 3000000, getNumericConfigCommand, setNumericConfigCommand, longlongApplyFunc, &memval) == REDISMODULE_ERR) { + return REDISMODULE_ERR; + } + if (RedisModule_RegisterNumericConfig(ctx, "numeric", -1, REDISMODULE_CONFIG_DEFAULT, -5, 2000, getNumericConfigCommand, setNumericConfigCommand, longlongApplyFunc, &longval) == REDISMODULE_ERR) { + return REDISMODULE_ERR; + } + size_t len; + if (argc && !strcasecmp(RedisModule_StringPtrLen(argv[0], &len), "noload")) { + return REDISMODULE_OK; + } else if (RedisModule_LoadConfigs(ctx) == REDISMODULE_ERR) { + if (strval) { + RedisModule_FreeString(ctx, strval); + strval = NULL; + } + return REDISMODULE_ERR; + } + return REDISMODULE_OK; +} + +int RedisModule_OnUnload(RedisModuleCtx *ctx) { + REDISMODULE_NOT_USED(ctx); + if (strval) { + RedisModule_FreeString(ctx, strval); + strval = NULL; + } + return REDISMODULE_OK; +}
\ No newline at end of file diff --git a/tests/modules/moduleconfigstwo.c b/tests/modules/moduleconfigstwo.c new file mode 100644 index 000000000..c0e8f9136 --- /dev/null +++ b/tests/modules/moduleconfigstwo.c @@ -0,0 +1,39 @@ +#include "redismodule.h" +#include <strings.h> + +/* Second module configs module, for testing. + * Need to make sure that multiple modules with configs don't interfere with each other */ +int bool_config; + +int getBoolConfigCommand(const char *name, void *privdata) { + REDISMODULE_NOT_USED(privdata); + if (!strcasecmp(name, "test")) { + return bool_config; + } + return 0; +} + +int setBoolConfigCommand(const char *name, int new, void *privdata, RedisModuleString **err) { + REDISMODULE_NOT_USED(privdata); + REDISMODULE_NOT_USED(err); + if (!strcasecmp(name, "test")) { + bool_config = new; + return REDISMODULE_OK; + } + return REDISMODULE_ERR; +} + +/* No arguments are expected */ +int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + REDISMODULE_NOT_USED(argv); + REDISMODULE_NOT_USED(argc); + if (RedisModule_Init(ctx, "configs", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; + + if (RedisModule_RegisterBoolConfig(ctx, "test", 1, REDISMODULE_CONFIG_DEFAULT, getBoolConfigCommand, setBoolConfigCommand, NULL, &argc) == REDISMODULE_ERR) { + return REDISMODULE_ERR; + } + if (RedisModule_LoadConfigs(ctx) == REDISMODULE_ERR) { + return REDISMODULE_ERR; + } + return REDISMODULE_OK; +}
\ No newline at end of file |