/* Some unit tests that don't require Redis to be running. */
#include "command.h"
#include "hiarray.h"
#include "hircluster.h"
#include "test_utils.h"
#include "win32.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Helper for the macro ASSERT_KEYS below. */
void check_keys(char **keys, int numkeys, struct cmd *command, char *file,
int line) {
if (command->result != CMD_PARSE_OK) {
fprintf(stderr, "%s:%d: Command parsing failed: %s\n", file, line,
command->errstr);
assert(0);
}
int actual_numkeys = (int)hiarray_n(command->keys);
if (actual_numkeys != numkeys) {
fprintf(stderr, "%s:%d: Expected %d keys but got %d\n", file, line,
numkeys, actual_numkeys);
assert(actual_numkeys == numkeys);
}
for (int i = 0; i < numkeys; i++) {
struct keypos *kpos = hiarray_get(command->keys, i);
char *actual_key = kpos->start;
int actual_keylen = (int)(kpos->end - kpos->start);
if ((int)strlen(keys[i]) != actual_keylen ||
strncmp(keys[i], actual_key, actual_keylen)) {
fprintf(stderr,
"%s:%d: Expected key %d to be \"%s\" but got \"%.*s\"\n",
file, line, i, keys[i], actual_keylen, actual_key);
assert(0);
}
}
}
/* Checks that a command (struct cmd *) has the given keys (strings). */
#define ASSERT_KEYS(command, ...) \
do { \
char *expected_keys[] = {__VA_ARGS__}; \
size_t n = sizeof(expected_keys) / sizeof(char *); \
check_keys(expected_keys, n, command, __FILE__, __LINE__); \
} while (0)
void test_redis_parse_error_nonresp(void) {
struct cmd *c = command_get();
c->cmd = strdup("+++Not RESP+++\r\n");
c->clen = strlen(c->cmd);
redis_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_ERROR, "Unexpected parse success");
ASSERT_MSG(!strcmp(c->errstr, "Command parse error"), c->errstr);
command_destroy(c);
}
void test_redis_parse_cmd_get(void) {
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "GET foo");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_OK, "Parse not OK");
ASSERT_KEYS(c, "foo");
command_destroy(c);
}
void test_redis_parse_cmd_mset(void) {
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "MSET foo val1 bar val2");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_OK, "Parse not OK");
ASSERT_KEYS(c, "foo", "bar");
command_destroy(c);
}
void test_redis_parse_cmd_eval_1(void) {
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "EVAL dummyscript 1 foo");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_OK, "Parse not OK");
ASSERT_KEYS(c, "foo");
command_destroy(c);
}
void test_redis_parse_cmd_eval_0(void) {
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "EVAL dummyscript 0 foo");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_OK, "Parse not OK");
ASSERT_MSG(hiarray_n(c->keys) == 0, "Nonzero number of keys");
command_destroy(c);
}
void test_redis_parse_cmd_xgroup_no_subcommand(void) {
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "XGROUP");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_ERROR, "Unexpected parse success");
ASSERT_MSG(!strcmp(c->errstr, "Unknown command XGROUP"), c->errstr);
command_destroy(c);
}
void test_redis_parse_cmd_xgroup_destroy_no_key(void) {
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "xgroup destroy");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_ERROR, "Parse not OK");
const char *expected_error =
"Failed to find keys of command XGROUP DESTROY";
ASSERT_MSG(!strncmp(c->errstr, expected_error, strlen(expected_error)),
c->errstr);
command_destroy(c);
}
void test_redis_parse_cmd_xgroup_destroy_ok(void) {
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "xgroup destroy mystream mygroup");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_KEYS(c, "mystream");
command_destroy(c);
}
void test_redis_parse_cmd_xreadgroup_ok(void) {
struct cmd *c = command_get();
/* Use group name and consumer name "streams" just to try to confuse the
* parser. The parser shouldn't mistake those for the STREAMS keyword. */
int len = redisFormatCommand(
&c->cmd, "XREADGROUP GROUP streams streams COUNT 1 streams mystream >");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_KEYS(c, "mystream");
command_destroy(c);
}
void test_redis_parse_cmd_xread_ok(void) {
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd,
"XREAD BLOCK 42 STREAMS mystream another $ $");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_KEYS(c, "mystream");
command_destroy(c);
}
void test_redis_parse_cmd_restore_ok(void) {
/* The ordering of RESTORE and RESTORE-ASKING in the lookup-table was wrong
* in a previous version, leading to the command not being found. */
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "restore k 0 xxx");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_KEYS(c, "k");
command_destroy(c);
}
void test_redis_parse_cmd_restore_asking_ok(void) {
/* The ordering of RESTORE and RESTORE-ASKING in the lookup-table was wrong
* in a previous version, leading to the command not being found. */
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "restore-asking k 0 xxx");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_KEYS(c, "k");
command_destroy(c);
}
void test_redis_parse_cmd_georadius_ro_ok(void) {
/* The position of GEORADIUS_RO was wrong in a previous version of the
* lookup-table, leading to the command not being found. */
struct cmd *c = command_get();
int len = redisFormatCommand(&c->cmd, "georadius_ro k 0 0 0 km");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
redis_parse_cmd(c);
ASSERT_KEYS(c, "k");
command_destroy(c);
}
int main(void) {
test_redis_parse_error_nonresp();
test_redis_parse_cmd_get();
test_redis_parse_cmd_mset();
test_redis_parse_cmd_eval_1();
test_redis_parse_cmd_eval_0();
test_redis_parse_cmd_xgroup_no_subcommand();
test_redis_parse_cmd_xgroup_destroy_no_key();
test_redis_parse_cmd_xgroup_destroy_ok();
test_redis_parse_cmd_xreadgroup_ok();
test_redis_parse_cmd_xread_ok();
test_redis_parse_cmd_restore_ok();
test_redis_parse_cmd_restore_asking_ok();
test_redis_parse_cmd_georadius_ro_ok();
return 0;
}