1*572c4311Sfengbojiang /* Module designed to test the Redis modules subsystem.
2*572c4311Sfengbojiang *
3*572c4311Sfengbojiang * -----------------------------------------------------------------------------
4*572c4311Sfengbojiang *
5*572c4311Sfengbojiang * Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
6*572c4311Sfengbojiang * All rights reserved.
7*572c4311Sfengbojiang *
8*572c4311Sfengbojiang * Redistribution and use in source and binary forms, with or without
9*572c4311Sfengbojiang * modification, are permitted provided that the following conditions are met:
10*572c4311Sfengbojiang *
11*572c4311Sfengbojiang * * Redistributions of source code must retain the above copyright notice,
12*572c4311Sfengbojiang * this list of conditions and the following disclaimer.
13*572c4311Sfengbojiang * * Redistributions in binary form must reproduce the above copyright
14*572c4311Sfengbojiang * notice, this list of conditions and the following disclaimer in the
15*572c4311Sfengbojiang * documentation and/or other materials provided with the distribution.
16*572c4311Sfengbojiang * * Neither the name of Redis nor the names of its contributors may be used
17*572c4311Sfengbojiang * to endorse or promote products derived from this software without
18*572c4311Sfengbojiang * specific prior written permission.
19*572c4311Sfengbojiang *
20*572c4311Sfengbojiang * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21*572c4311Sfengbojiang * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22*572c4311Sfengbojiang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23*572c4311Sfengbojiang * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24*572c4311Sfengbojiang * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25*572c4311Sfengbojiang * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26*572c4311Sfengbojiang * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27*572c4311Sfengbojiang * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28*572c4311Sfengbojiang * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*572c4311Sfengbojiang * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30*572c4311Sfengbojiang * POSSIBILITY OF SUCH DAMAGE.
31*572c4311Sfengbojiang */
32*572c4311Sfengbojiang
33*572c4311Sfengbojiang #define REDISMODULE_EXPERIMENTAL_API
34*572c4311Sfengbojiang #include "../redismodule.h"
35*572c4311Sfengbojiang #include <string.h>
36*572c4311Sfengbojiang
37*572c4311Sfengbojiang /* --------------------------------- Helpers -------------------------------- */
38*572c4311Sfengbojiang
39*572c4311Sfengbojiang /* Return true if the reply and the C null term string matches. */
TestMatchReply(RedisModuleCallReply * reply,char * str)40*572c4311Sfengbojiang int TestMatchReply(RedisModuleCallReply *reply, char *str) {
41*572c4311Sfengbojiang RedisModuleString *mystr;
42*572c4311Sfengbojiang mystr = RedisModule_CreateStringFromCallReply(reply);
43*572c4311Sfengbojiang if (!mystr) return 0;
44*572c4311Sfengbojiang const char *ptr = RedisModule_StringPtrLen(mystr,NULL);
45*572c4311Sfengbojiang return strcmp(ptr,str) == 0;
46*572c4311Sfengbojiang }
47*572c4311Sfengbojiang
48*572c4311Sfengbojiang /* ------------------------------- Test units ------------------------------- */
49*572c4311Sfengbojiang
50*572c4311Sfengbojiang /* TEST.CALL -- Test Call() API. */
TestCall(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)51*572c4311Sfengbojiang int TestCall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
52*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
53*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
54*572c4311Sfengbojiang
55*572c4311Sfengbojiang RedisModule_AutoMemory(ctx);
56*572c4311Sfengbojiang RedisModuleCallReply *reply;
57*572c4311Sfengbojiang
58*572c4311Sfengbojiang RedisModule_Call(ctx,"DEL","c","mylist");
59*572c4311Sfengbojiang RedisModuleString *mystr = RedisModule_CreateString(ctx,"foo",3);
60*572c4311Sfengbojiang RedisModule_Call(ctx,"RPUSH","csl","mylist",mystr,(long long)1234);
61*572c4311Sfengbojiang reply = RedisModule_Call(ctx,"LRANGE","ccc","mylist","0","-1");
62*572c4311Sfengbojiang long long items = RedisModule_CallReplyLength(reply);
63*572c4311Sfengbojiang if (items != 2) goto fail;
64*572c4311Sfengbojiang
65*572c4311Sfengbojiang RedisModuleCallReply *item0, *item1;
66*572c4311Sfengbojiang
67*572c4311Sfengbojiang item0 = RedisModule_CallReplyArrayElement(reply,0);
68*572c4311Sfengbojiang item1 = RedisModule_CallReplyArrayElement(reply,1);
69*572c4311Sfengbojiang if (!TestMatchReply(item0,"foo")) goto fail;
70*572c4311Sfengbojiang if (!TestMatchReply(item1,"1234")) goto fail;
71*572c4311Sfengbojiang
72*572c4311Sfengbojiang RedisModule_ReplyWithSimpleString(ctx,"OK");
73*572c4311Sfengbojiang return REDISMODULE_OK;
74*572c4311Sfengbojiang
75*572c4311Sfengbojiang fail:
76*572c4311Sfengbojiang RedisModule_ReplyWithSimpleString(ctx,"ERR");
77*572c4311Sfengbojiang return REDISMODULE_OK;
78*572c4311Sfengbojiang }
79*572c4311Sfengbojiang
80*572c4311Sfengbojiang /* TEST.STRING.APPEND -- Test appending to an existing string object. */
TestStringAppend(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)81*572c4311Sfengbojiang int TestStringAppend(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
82*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
83*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
84*572c4311Sfengbojiang
85*572c4311Sfengbojiang RedisModuleString *s = RedisModule_CreateString(ctx,"foo",3);
86*572c4311Sfengbojiang RedisModule_StringAppendBuffer(ctx,s,"bar",3);
87*572c4311Sfengbojiang RedisModule_ReplyWithString(ctx,s);
88*572c4311Sfengbojiang RedisModule_FreeString(ctx,s);
89*572c4311Sfengbojiang return REDISMODULE_OK;
90*572c4311Sfengbojiang }
91*572c4311Sfengbojiang
92*572c4311Sfengbojiang /* TEST.STRING.APPEND.AM -- Test append with retain when auto memory is on. */
TestStringAppendAM(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)93*572c4311Sfengbojiang int TestStringAppendAM(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
94*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
95*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
96*572c4311Sfengbojiang
97*572c4311Sfengbojiang RedisModule_AutoMemory(ctx);
98*572c4311Sfengbojiang RedisModuleString *s = RedisModule_CreateString(ctx,"foo",3);
99*572c4311Sfengbojiang RedisModule_RetainString(ctx,s);
100*572c4311Sfengbojiang RedisModule_StringAppendBuffer(ctx,s,"bar",3);
101*572c4311Sfengbojiang RedisModule_ReplyWithString(ctx,s);
102*572c4311Sfengbojiang RedisModule_FreeString(ctx,s);
103*572c4311Sfengbojiang return REDISMODULE_OK;
104*572c4311Sfengbojiang }
105*572c4311Sfengbojiang
106*572c4311Sfengbojiang /* TEST.STRING.PRINTF -- Test string formatting. */
TestStringPrintf(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)107*572c4311Sfengbojiang int TestStringPrintf(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
108*572c4311Sfengbojiang RedisModule_AutoMemory(ctx);
109*572c4311Sfengbojiang if (argc < 3) {
110*572c4311Sfengbojiang return RedisModule_WrongArity(ctx);
111*572c4311Sfengbojiang }
112*572c4311Sfengbojiang RedisModuleString *s = RedisModule_CreateStringPrintf(ctx,
113*572c4311Sfengbojiang "Got %d args. argv[1]: %s, argv[2]: %s",
114*572c4311Sfengbojiang argc,
115*572c4311Sfengbojiang RedisModule_StringPtrLen(argv[1], NULL),
116*572c4311Sfengbojiang RedisModule_StringPtrLen(argv[2], NULL)
117*572c4311Sfengbojiang );
118*572c4311Sfengbojiang
119*572c4311Sfengbojiang RedisModule_ReplyWithString(ctx,s);
120*572c4311Sfengbojiang
121*572c4311Sfengbojiang return REDISMODULE_OK;
122*572c4311Sfengbojiang }
123*572c4311Sfengbojiang
failTest(RedisModuleCtx * ctx,const char * msg)124*572c4311Sfengbojiang int failTest(RedisModuleCtx *ctx, const char *msg) {
125*572c4311Sfengbojiang RedisModule_ReplyWithError(ctx, msg);
126*572c4311Sfengbojiang return REDISMODULE_ERR;
127*572c4311Sfengbojiang }
128*572c4311Sfengbojiang
TestUnlink(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)129*572c4311Sfengbojiang int TestUnlink(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
130*572c4311Sfengbojiang RedisModule_AutoMemory(ctx);
131*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
132*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
133*572c4311Sfengbojiang
134*572c4311Sfengbojiang RedisModuleKey *k = RedisModule_OpenKey(ctx, RedisModule_CreateStringPrintf(ctx, "unlinked"), REDISMODULE_WRITE | REDISMODULE_READ);
135*572c4311Sfengbojiang if (!k) return failTest(ctx, "Could not create key");
136*572c4311Sfengbojiang
137*572c4311Sfengbojiang if (REDISMODULE_ERR == RedisModule_StringSet(k, RedisModule_CreateStringPrintf(ctx, "Foobar"))) {
138*572c4311Sfengbojiang return failTest(ctx, "Could not set string value");
139*572c4311Sfengbojiang }
140*572c4311Sfengbojiang
141*572c4311Sfengbojiang RedisModuleCallReply *rep = RedisModule_Call(ctx, "EXISTS", "c", "unlinked");
142*572c4311Sfengbojiang if (!rep || RedisModule_CallReplyInteger(rep) != 1) {
143*572c4311Sfengbojiang return failTest(ctx, "Key does not exist before unlink");
144*572c4311Sfengbojiang }
145*572c4311Sfengbojiang
146*572c4311Sfengbojiang if (REDISMODULE_ERR == RedisModule_UnlinkKey(k)) {
147*572c4311Sfengbojiang return failTest(ctx, "Could not unlink key");
148*572c4311Sfengbojiang }
149*572c4311Sfengbojiang
150*572c4311Sfengbojiang rep = RedisModule_Call(ctx, "EXISTS", "c", "unlinked");
151*572c4311Sfengbojiang if (!rep || RedisModule_CallReplyInteger(rep) != 0) {
152*572c4311Sfengbojiang return failTest(ctx, "Could not verify key to be unlinked");
153*572c4311Sfengbojiang }
154*572c4311Sfengbojiang return RedisModule_ReplyWithSimpleString(ctx, "OK");
155*572c4311Sfengbojiang
156*572c4311Sfengbojiang }
157*572c4311Sfengbojiang
NotifyCallback(RedisModuleCtx * ctx,int type,const char * event,RedisModuleString * key)158*572c4311Sfengbojiang int NotifyCallback(RedisModuleCtx *ctx, int type, const char *event,
159*572c4311Sfengbojiang RedisModuleString *key) {
160*572c4311Sfengbojiang /* Increment a counter on the notifications: for each key notified we
161*572c4311Sfengbojiang * increment a counter */
162*572c4311Sfengbojiang RedisModule_Log(ctx, "notice", "Got event type %d, event %s, key %s", type,
163*572c4311Sfengbojiang event, RedisModule_StringPtrLen(key, NULL));
164*572c4311Sfengbojiang
165*572c4311Sfengbojiang RedisModule_Call(ctx, "HINCRBY", "csc", "notifications", key, "1");
166*572c4311Sfengbojiang return REDISMODULE_OK;
167*572c4311Sfengbojiang }
168*572c4311Sfengbojiang
169*572c4311Sfengbojiang /* TEST.NOTIFICATIONS -- Test Keyspace Notifications. */
TestNotifications(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)170*572c4311Sfengbojiang int TestNotifications(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
171*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
172*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
173*572c4311Sfengbojiang
174*572c4311Sfengbojiang #define FAIL(msg, ...) \
175*572c4311Sfengbojiang { \
176*572c4311Sfengbojiang RedisModule_Log(ctx, "warning", "Failed NOTIFY Test. Reason: " #msg, ##__VA_ARGS__); \
177*572c4311Sfengbojiang goto err; \
178*572c4311Sfengbojiang }
179*572c4311Sfengbojiang RedisModule_Call(ctx, "FLUSHDB", "");
180*572c4311Sfengbojiang
181*572c4311Sfengbojiang RedisModule_Call(ctx, "SET", "cc", "foo", "bar");
182*572c4311Sfengbojiang RedisModule_Call(ctx, "SET", "cc", "foo", "baz");
183*572c4311Sfengbojiang RedisModule_Call(ctx, "SADD", "cc", "bar", "x");
184*572c4311Sfengbojiang RedisModule_Call(ctx, "SADD", "cc", "bar", "y");
185*572c4311Sfengbojiang
186*572c4311Sfengbojiang RedisModule_Call(ctx, "HSET", "ccc", "baz", "x", "y");
187*572c4311Sfengbojiang /* LPUSH should be ignored and not increment any counters */
188*572c4311Sfengbojiang RedisModule_Call(ctx, "LPUSH", "cc", "l", "y");
189*572c4311Sfengbojiang RedisModule_Call(ctx, "LPUSH", "cc", "l", "y");
190*572c4311Sfengbojiang
191*572c4311Sfengbojiang size_t sz;
192*572c4311Sfengbojiang const char *rep;
193*572c4311Sfengbojiang RedisModuleCallReply *r = RedisModule_Call(ctx, "HGET", "cc", "notifications", "foo");
194*572c4311Sfengbojiang if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {
195*572c4311Sfengbojiang FAIL("Wrong or no reply for foo");
196*572c4311Sfengbojiang } else {
197*572c4311Sfengbojiang rep = RedisModule_CallReplyStringPtr(r, &sz);
198*572c4311Sfengbojiang if (sz != 1 || *rep != '2') {
199*572c4311Sfengbojiang FAIL("Got reply '%s'. expected '2'", RedisModule_CallReplyStringPtr(r, NULL));
200*572c4311Sfengbojiang }
201*572c4311Sfengbojiang }
202*572c4311Sfengbojiang
203*572c4311Sfengbojiang r = RedisModule_Call(ctx, "HGET", "cc", "notifications", "bar");
204*572c4311Sfengbojiang if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {
205*572c4311Sfengbojiang FAIL("Wrong or no reply for bar");
206*572c4311Sfengbojiang } else {
207*572c4311Sfengbojiang rep = RedisModule_CallReplyStringPtr(r, &sz);
208*572c4311Sfengbojiang if (sz != 1 || *rep != '2') {
209*572c4311Sfengbojiang FAIL("Got reply '%s'. expected '2'", rep);
210*572c4311Sfengbojiang }
211*572c4311Sfengbojiang }
212*572c4311Sfengbojiang
213*572c4311Sfengbojiang r = RedisModule_Call(ctx, "HGET", "cc", "notifications", "baz");
214*572c4311Sfengbojiang if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_STRING) {
215*572c4311Sfengbojiang FAIL("Wrong or no reply for baz");
216*572c4311Sfengbojiang } else {
217*572c4311Sfengbojiang rep = RedisModule_CallReplyStringPtr(r, &sz);
218*572c4311Sfengbojiang if (sz != 1 || *rep != '1') {
219*572c4311Sfengbojiang FAIL("Got reply '%.*s'. expected '1'", sz, rep);
220*572c4311Sfengbojiang }
221*572c4311Sfengbojiang }
222*572c4311Sfengbojiang /* For l we expect nothing since we didn't subscribe to list events */
223*572c4311Sfengbojiang r = RedisModule_Call(ctx, "HGET", "cc", "notifications", "l");
224*572c4311Sfengbojiang if (r == NULL || RedisModule_CallReplyType(r) != REDISMODULE_REPLY_NULL) {
225*572c4311Sfengbojiang FAIL("Wrong reply for l");
226*572c4311Sfengbojiang }
227*572c4311Sfengbojiang
228*572c4311Sfengbojiang RedisModule_Call(ctx, "FLUSHDB", "");
229*572c4311Sfengbojiang
230*572c4311Sfengbojiang return RedisModule_ReplyWithSimpleString(ctx, "OK");
231*572c4311Sfengbojiang err:
232*572c4311Sfengbojiang RedisModule_Call(ctx, "FLUSHDB", "");
233*572c4311Sfengbojiang
234*572c4311Sfengbojiang return RedisModule_ReplyWithSimpleString(ctx, "ERR");
235*572c4311Sfengbojiang }
236*572c4311Sfengbojiang
237*572c4311Sfengbojiang /* TEST.CTXFLAGS -- Test GetContextFlags. */
TestCtxFlags(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)238*572c4311Sfengbojiang int TestCtxFlags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
239*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
240*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
241*572c4311Sfengbojiang
242*572c4311Sfengbojiang RedisModule_AutoMemory(ctx);
243*572c4311Sfengbojiang
244*572c4311Sfengbojiang int ok = 1;
245*572c4311Sfengbojiang const char *errString = NULL;
246*572c4311Sfengbojiang #undef FAIL
247*572c4311Sfengbojiang #define FAIL(msg) \
248*572c4311Sfengbojiang { \
249*572c4311Sfengbojiang ok = 0; \
250*572c4311Sfengbojiang errString = msg; \
251*572c4311Sfengbojiang goto end; \
252*572c4311Sfengbojiang }
253*572c4311Sfengbojiang
254*572c4311Sfengbojiang int flags = RedisModule_GetContextFlags(ctx);
255*572c4311Sfengbojiang if (flags == 0) {
256*572c4311Sfengbojiang FAIL("Got no flags");
257*572c4311Sfengbojiang }
258*572c4311Sfengbojiang
259*572c4311Sfengbojiang if (flags & REDISMODULE_CTX_FLAGS_LUA) FAIL("Lua flag was set");
260*572c4311Sfengbojiang if (flags & REDISMODULE_CTX_FLAGS_MULTI) FAIL("Multi flag was set");
261*572c4311Sfengbojiang
262*572c4311Sfengbojiang if (flags & REDISMODULE_CTX_FLAGS_AOF) FAIL("AOF Flag was set")
263*572c4311Sfengbojiang /* Enable AOF to test AOF flags */
264*572c4311Sfengbojiang RedisModule_Call(ctx, "config", "ccc", "set", "appendonly", "yes");
265*572c4311Sfengbojiang flags = RedisModule_GetContextFlags(ctx);
266*572c4311Sfengbojiang if (!(flags & REDISMODULE_CTX_FLAGS_AOF)) FAIL("AOF Flag not set after config set");
267*572c4311Sfengbojiang
268*572c4311Sfengbojiang if (flags & REDISMODULE_CTX_FLAGS_RDB) FAIL("RDB Flag was set");
269*572c4311Sfengbojiang /* Enable RDB to test RDB flags */
270*572c4311Sfengbojiang RedisModule_Call(ctx, "config", "ccc", "set", "save", "900 1");
271*572c4311Sfengbojiang flags = RedisModule_GetContextFlags(ctx);
272*572c4311Sfengbojiang if (!(flags & REDISMODULE_CTX_FLAGS_RDB)) FAIL("RDB Flag was not set after config set");
273*572c4311Sfengbojiang
274*572c4311Sfengbojiang if (!(flags & REDISMODULE_CTX_FLAGS_MASTER)) FAIL("Master flag was not set");
275*572c4311Sfengbojiang if (flags & REDISMODULE_CTX_FLAGS_SLAVE) FAIL("Slave flag was set");
276*572c4311Sfengbojiang if (flags & REDISMODULE_CTX_FLAGS_READONLY) FAIL("Read-only flag was set");
277*572c4311Sfengbojiang if (flags & REDISMODULE_CTX_FLAGS_CLUSTER) FAIL("Cluster flag was set");
278*572c4311Sfengbojiang
279*572c4311Sfengbojiang if (flags & REDISMODULE_CTX_FLAGS_MAXMEMORY) FAIL("Maxmemory flag was set");
280*572c4311Sfengbojiang
281*572c4311Sfengbojiang RedisModule_Call(ctx, "config", "ccc", "set", "maxmemory", "100000000");
282*572c4311Sfengbojiang flags = RedisModule_GetContextFlags(ctx);
283*572c4311Sfengbojiang if (!(flags & REDISMODULE_CTX_FLAGS_MAXMEMORY))
284*572c4311Sfengbojiang FAIL("Maxmemory flag was not set after config set");
285*572c4311Sfengbojiang
286*572c4311Sfengbojiang if (flags & REDISMODULE_CTX_FLAGS_EVICT) FAIL("Eviction flag was set");
287*572c4311Sfengbojiang RedisModule_Call(ctx, "config", "ccc", "set", "maxmemory-policy", "allkeys-lru");
288*572c4311Sfengbojiang flags = RedisModule_GetContextFlags(ctx);
289*572c4311Sfengbojiang if (!(flags & REDISMODULE_CTX_FLAGS_EVICT)) FAIL("Eviction flag was not set after config set");
290*572c4311Sfengbojiang
291*572c4311Sfengbojiang end:
292*572c4311Sfengbojiang /* Revert config changes */
293*572c4311Sfengbojiang RedisModule_Call(ctx, "config", "ccc", "set", "appendonly", "no");
294*572c4311Sfengbojiang RedisModule_Call(ctx, "config", "ccc", "set", "save", "");
295*572c4311Sfengbojiang RedisModule_Call(ctx, "config", "ccc", "set", "maxmemory", "0");
296*572c4311Sfengbojiang RedisModule_Call(ctx, "config", "ccc", "set", "maxmemory-policy", "noeviction");
297*572c4311Sfengbojiang
298*572c4311Sfengbojiang if (!ok) {
299*572c4311Sfengbojiang RedisModule_Log(ctx, "warning", "Failed CTXFLAGS Test. Reason: %s", errString);
300*572c4311Sfengbojiang return RedisModule_ReplyWithSimpleString(ctx, "ERR");
301*572c4311Sfengbojiang }
302*572c4311Sfengbojiang
303*572c4311Sfengbojiang return RedisModule_ReplyWithSimpleString(ctx, "OK");
304*572c4311Sfengbojiang }
305*572c4311Sfengbojiang
306*572c4311Sfengbojiang /* ----------------------------- Test framework ----------------------------- */
307*572c4311Sfengbojiang
308*572c4311Sfengbojiang /* Return 1 if the reply matches the specified string, otherwise log errors
309*572c4311Sfengbojiang * in the server log and return 0. */
TestAssertStringReply(RedisModuleCtx * ctx,RedisModuleCallReply * reply,char * str,size_t len)310*572c4311Sfengbojiang int TestAssertStringReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, char *str, size_t len) {
311*572c4311Sfengbojiang RedisModuleString *mystr, *expected;
312*572c4311Sfengbojiang
313*572c4311Sfengbojiang if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_STRING) {
314*572c4311Sfengbojiang RedisModule_Log(ctx,"warning","Unexpected reply type %d",
315*572c4311Sfengbojiang RedisModule_CallReplyType(reply));
316*572c4311Sfengbojiang return 0;
317*572c4311Sfengbojiang }
318*572c4311Sfengbojiang mystr = RedisModule_CreateStringFromCallReply(reply);
319*572c4311Sfengbojiang expected = RedisModule_CreateString(ctx,str,len);
320*572c4311Sfengbojiang if (RedisModule_StringCompare(mystr,expected) != 0) {
321*572c4311Sfengbojiang const char *mystr_ptr = RedisModule_StringPtrLen(mystr,NULL);
322*572c4311Sfengbojiang const char *expected_ptr = RedisModule_StringPtrLen(expected,NULL);
323*572c4311Sfengbojiang RedisModule_Log(ctx,"warning",
324*572c4311Sfengbojiang "Unexpected string reply '%s' (instead of '%s')",
325*572c4311Sfengbojiang mystr_ptr, expected_ptr);
326*572c4311Sfengbojiang return 0;
327*572c4311Sfengbojiang }
328*572c4311Sfengbojiang return 1;
329*572c4311Sfengbojiang }
330*572c4311Sfengbojiang
331*572c4311Sfengbojiang /* Return 1 if the reply matches the specified integer, otherwise log errors
332*572c4311Sfengbojiang * in the server log and return 0. */
TestAssertIntegerReply(RedisModuleCtx * ctx,RedisModuleCallReply * reply,long long expected)333*572c4311Sfengbojiang int TestAssertIntegerReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, long long expected) {
334*572c4311Sfengbojiang if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_INTEGER) {
335*572c4311Sfengbojiang RedisModule_Log(ctx,"warning","Unexpected reply type %d",
336*572c4311Sfengbojiang RedisModule_CallReplyType(reply));
337*572c4311Sfengbojiang return 0;
338*572c4311Sfengbojiang }
339*572c4311Sfengbojiang long long val = RedisModule_CallReplyInteger(reply);
340*572c4311Sfengbojiang if (val != expected) {
341*572c4311Sfengbojiang RedisModule_Log(ctx,"warning",
342*572c4311Sfengbojiang "Unexpected integer reply '%lld' (instead of '%lld')",
343*572c4311Sfengbojiang val, expected);
344*572c4311Sfengbojiang return 0;
345*572c4311Sfengbojiang }
346*572c4311Sfengbojiang return 1;
347*572c4311Sfengbojiang }
348*572c4311Sfengbojiang
349*572c4311Sfengbojiang #define T(name,...) \
350*572c4311Sfengbojiang do { \
351*572c4311Sfengbojiang RedisModule_Log(ctx,"warning","Testing %s", name); \
352*572c4311Sfengbojiang reply = RedisModule_Call(ctx,name,__VA_ARGS__); \
353*572c4311Sfengbojiang } while (0);
354*572c4311Sfengbojiang
355*572c4311Sfengbojiang /* TEST.IT -- Run all the tests. */
TestIt(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)356*572c4311Sfengbojiang int TestIt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
357*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
358*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
359*572c4311Sfengbojiang
360*572c4311Sfengbojiang RedisModule_AutoMemory(ctx);
361*572c4311Sfengbojiang RedisModuleCallReply *reply;
362*572c4311Sfengbojiang
363*572c4311Sfengbojiang /* Make sure the DB is empty before to proceed. */
364*572c4311Sfengbojiang T("dbsize","");
365*572c4311Sfengbojiang if (!TestAssertIntegerReply(ctx,reply,0)) goto fail;
366*572c4311Sfengbojiang
367*572c4311Sfengbojiang T("ping","");
368*572c4311Sfengbojiang if (!TestAssertStringReply(ctx,reply,"PONG",4)) goto fail;
369*572c4311Sfengbojiang
370*572c4311Sfengbojiang T("test.call","");
371*572c4311Sfengbojiang if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail;
372*572c4311Sfengbojiang
373*572c4311Sfengbojiang T("test.ctxflags","");
374*572c4311Sfengbojiang if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail;
375*572c4311Sfengbojiang
376*572c4311Sfengbojiang T("test.string.append","");
377*572c4311Sfengbojiang if (!TestAssertStringReply(ctx,reply,"foobar",6)) goto fail;
378*572c4311Sfengbojiang
379*572c4311Sfengbojiang T("test.unlink","");
380*572c4311Sfengbojiang if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail;
381*572c4311Sfengbojiang
382*572c4311Sfengbojiang T("test.string.append.am","");
383*572c4311Sfengbojiang if (!TestAssertStringReply(ctx,reply,"foobar",6)) goto fail;
384*572c4311Sfengbojiang
385*572c4311Sfengbojiang T("test.string.printf", "cc", "foo", "bar");
386*572c4311Sfengbojiang if (!TestAssertStringReply(ctx,reply,"Got 3 args. argv[1]: foo, argv[2]: bar",38)) goto fail;
387*572c4311Sfengbojiang
388*572c4311Sfengbojiang T("test.notify", "");
389*572c4311Sfengbojiang if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail;
390*572c4311Sfengbojiang
391*572c4311Sfengbojiang RedisModule_ReplyWithSimpleString(ctx,"ALL TESTS PASSED");
392*572c4311Sfengbojiang return REDISMODULE_OK;
393*572c4311Sfengbojiang
394*572c4311Sfengbojiang fail:
395*572c4311Sfengbojiang RedisModule_ReplyWithSimpleString(ctx,
396*572c4311Sfengbojiang "SOME TEST NOT PASSED! Check server logs");
397*572c4311Sfengbojiang return REDISMODULE_OK;
398*572c4311Sfengbojiang }
399*572c4311Sfengbojiang
RedisModule_OnLoad(RedisModuleCtx * ctx,RedisModuleString ** argv,int argc)400*572c4311Sfengbojiang int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
401*572c4311Sfengbojiang REDISMODULE_NOT_USED(argv);
402*572c4311Sfengbojiang REDISMODULE_NOT_USED(argc);
403*572c4311Sfengbojiang
404*572c4311Sfengbojiang if (RedisModule_Init(ctx,"test",1,REDISMODULE_APIVER_1)
405*572c4311Sfengbojiang == REDISMODULE_ERR) return REDISMODULE_ERR;
406*572c4311Sfengbojiang
407*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"test.call",
408*572c4311Sfengbojiang TestCall,"write deny-oom",1,1,1) == REDISMODULE_ERR)
409*572c4311Sfengbojiang return REDISMODULE_ERR;
410*572c4311Sfengbojiang
411*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"test.string.append",
412*572c4311Sfengbojiang TestStringAppend,"write deny-oom",1,1,1) == REDISMODULE_ERR)
413*572c4311Sfengbojiang return REDISMODULE_ERR;
414*572c4311Sfengbojiang
415*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"test.string.append.am",
416*572c4311Sfengbojiang TestStringAppendAM,"write deny-oom",1,1,1) == REDISMODULE_ERR)
417*572c4311Sfengbojiang return REDISMODULE_ERR;
418*572c4311Sfengbojiang
419*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"test.string.printf",
420*572c4311Sfengbojiang TestStringPrintf,"write deny-oom",1,1,1) == REDISMODULE_ERR)
421*572c4311Sfengbojiang return REDISMODULE_ERR;
422*572c4311Sfengbojiang
423*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"test.ctxflags",
424*572c4311Sfengbojiang TestCtxFlags,"readonly",1,1,1) == REDISMODULE_ERR)
425*572c4311Sfengbojiang return REDISMODULE_ERR;
426*572c4311Sfengbojiang
427*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"test.unlink",
428*572c4311Sfengbojiang TestUnlink,"write deny-oom",1,1,1) == REDISMODULE_ERR)
429*572c4311Sfengbojiang return REDISMODULE_ERR;
430*572c4311Sfengbojiang
431*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"test.it",
432*572c4311Sfengbojiang TestIt,"readonly",1,1,1) == REDISMODULE_ERR)
433*572c4311Sfengbojiang return REDISMODULE_ERR;
434*572c4311Sfengbojiang
435*572c4311Sfengbojiang RedisModule_SubscribeToKeyspaceEvents(ctx,
436*572c4311Sfengbojiang REDISMODULE_NOTIFY_HASH |
437*572c4311Sfengbojiang REDISMODULE_NOTIFY_SET |
438*572c4311Sfengbojiang REDISMODULE_NOTIFY_STRING,
439*572c4311Sfengbojiang NotifyCallback);
440*572c4311Sfengbojiang if (RedisModule_CreateCommand(ctx,"test.notify",
441*572c4311Sfengbojiang TestNotifications,"write deny-oom",1,1,1) == REDISMODULE_ERR)
442*572c4311Sfengbojiang return REDISMODULE_ERR;
443*572c4311Sfengbojiang
444*572c4311Sfengbojiang return REDISMODULE_OK;
445*572c4311Sfengbojiang }
446