summaryrefslogtreecommitdiff
path: root/tests/modules
diff options
context:
space:
mode:
Diffstat (limited to 'tests/modules')
-rw-r--r--tests/modules/Makefile4
-rw-r--r--tests/modules/aclcheck.c20
-rw-r--r--tests/modules/auth.c14
-rw-r--r--tests/modules/basics.c31
-rw-r--r--tests/modules/blockedclient.c2
-rw-r--r--tests/modules/blockonkeys.c13
-rw-r--r--tests/modules/eventloop.c1
-rw-r--r--tests/modules/hooks.c15
-rw-r--r--tests/modules/moduleconfigs.c142
-rw-r--r--tests/modules/moduleconfigstwo.c39
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