Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,17 @@ jobs:
solaris-developer-studio:
name: Oracle DeveloperStudio
runs-on: ubuntu-24.04

strategy:
fail-fast: false

# Secrets containing certificates are not available in forks or for external PRs.
if: |
github.repository == 'valkey-io/libvalkey' &&
(github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository)
env:
PKG_ORACLE_CERT: ${{ secrets.PKG_ORACLE_CERT }}
PKG_ORACLE_KEY: ${{ secrets.PKG_ORACLE_KEY }}

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0

- name: Transfer Oracle Studio certificates
run: |
Expand All @@ -106,7 +106,7 @@ jobs:
> pkg.oracle.com.key.pem

- name: Build on Solaris
uses: vmactions/solaris-vm@v1.1.8
uses: vmactions/solaris-vm@47bea106d03acaf91084e52548ee460556011602 # v1.1.8
with:
usesh: true
prepare: |
Expand All @@ -130,8 +130,8 @@ jobs:
set -e
PATH=/opt/developerstudio12.6/bin:"$PATH"
export PATH

gmake USE_THREADS=1 USE_TLS=1 -j"$(psrinfo -p)"

build-cross:
name: Cross-compile ${{ matrix.config.target }}
runs-on: ubuntu-22.04
Expand Down
70 changes: 68 additions & 2 deletions src/cmddef.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ COMMAND(ACL_WHOAMI, "ACL", "WHOAMI", 2, NONE, 0)
COMMAND(APPEND, "APPEND", NULL, 3, INDEX, 1)
COMMAND(ASKING, "ASKING", NULL, 1, NONE, 0)
COMMAND(AUTH, "AUTH", NULL, -2, NONE, 0)
COMMAND(BF_ADD, "BF.ADD", NULL, 3, INDEX, 1)
COMMAND(BF_CARD, "BF.CARD", NULL, 2, NONE, 0)
COMMAND(BF_EXISTS, "BF.EXISTS", NULL, 3, INDEX, 1)
COMMAND(BF_INFO, "BF.INFO", NULL, -2, INDEX, 1)
COMMAND(BF_INSERT, "BF.INSERT", NULL, -2, INDEX, 1)
COMMAND(BF_LOAD, "BF.LOAD", NULL, 3, INDEX, 1)
COMMAND(BF_MADD, "BF.MADD", NULL, 3, INDEX, 1)
COMMAND(BF_MEXISTS, "BF.MEXISTS", NULL, 3, INDEX, 1)
COMMAND(BF_RESERVE, "BF.RESERVE", NULL, -4, INDEX, 1)
COMMAND(BGREWRITEAOF, "BGREWRITEAOF", NULL, 1, NONE, 0)
COMMAND(BGSAVE, "BGSAVE", NULL, -1, NONE, 0)
COMMAND(BITCOUNT, "BITCOUNT", NULL, -2, INDEX, 1)
Expand All @@ -33,10 +42,12 @@ COMMAND(BZMPOP, "BZMPOP", NULL, -5, KEYNUM, 2)
COMMAND(BZPOPMAX, "BZPOPMAX", NULL, -3, INDEX, 1)
COMMAND(BZPOPMIN, "BZPOPMIN", NULL, -3, INDEX, 1)
COMMAND(CLIENT_CACHING, "CLIENT", "CACHING", 3, NONE, 0)
COMMAND(CLIENT_CAPA, "CLIENT", "CAPA", -3, NONE, 0)
COMMAND(CLIENT_GETNAME, "CLIENT", "GETNAME", 2, NONE, 0)
COMMAND(CLIENT_GETREDIR, "CLIENT", "GETREDIR", 2, NONE, 0)
COMMAND(CLIENT_HELP, "CLIENT", "HELP", 2, NONE, 0)
COMMAND(CLIENT_ID, "CLIENT", "ID", 2, NONE, 0)
COMMAND(CLIENT_IMPORT_SOURCE, "CLIENT", "IMPORT-SOURCE", 3, NONE, 0)
COMMAND(CLIENT_INFO, "CLIENT", "INFO", 2, NONE, 0)
COMMAND(CLIENT_KILL, "CLIENT", "KILL", -3, NONE, 0)
COMMAND(CLIENT_LIST, "CLIENT", "LIST", -2, NONE, 0)
Expand All @@ -53,31 +64,41 @@ COMMAND(CLIENT_UNPAUSE, "CLIENT", "UNPAUSE", 2, NONE, 0)
COMMAND(CLUSTER_ADDSLOTS, "CLUSTER", "ADDSLOTS", -3, NONE, 0)
COMMAND(CLUSTER_ADDSLOTSRANGE, "CLUSTER", "ADDSLOTSRANGE", -4, NONE, 0)
COMMAND(CLUSTER_BUMPEPOCH, "CLUSTER", "BUMPEPOCH", 2, NONE, 0)
COMMAND(CLUSTER_CANCELSLOTMIGRATIONS, "CLUSTER", "CANCELSLOTMIGRATIONS", 2, NONE, 0)
COMMAND(CLUSTER_COUNT_FAILURE_REPORTS, "CLUSTER", "COUNT-FAILURE-REPORTS", 3, NONE, 0)
COMMAND(CLUSTER_COUNTKEYSINSLOT, "CLUSTER", "COUNTKEYSINSLOT", 3, NONE, 0)
COMMAND(CLUSTER_DELSLOTS, "CLUSTER", "DELSLOTS", -3, NONE, 0)
COMMAND(CLUSTER_DELSLOTSRANGE, "CLUSTER", "DELSLOTSRANGE", -4, NONE, 0)
COMMAND(CLUSTER_FAILOVER, "CLUSTER", "FAILOVER", -2, NONE, 0)
COMMAND(CLUSTER_FLUSHSLOT, "CLUSTER", "FLUSHSLOT", -3, NONE, 0)
COMMAND(CLUSTER_FLUSHSLOTS, "CLUSTER", "FLUSHSLOTS", 2, NONE, 0)
COMMAND(CLUSTER_FORGET, "CLUSTER", "FORGET", 3, NONE, 0)
COMMAND(CLUSTER_GETKEYSINSLOT, "CLUSTER", "GETKEYSINSLOT", 4, NONE, 0)
COMMAND(CLUSTER_GETSLOTMIGRATIONS, "CLUSTER", "GETSLOTMIGRATIONS", 2, NONE, 0)
COMMAND(CLUSTER_HELP, "CLUSTER", "HELP", 2, NONE, 0)
COMMAND(CLUSTER_INFO, "CLUSTER", "INFO", 2, NONE, 0)
COMMAND(CLUSTER_KEYSLOT, "CLUSTER", "KEYSLOT", 3, NONE, 0)
COMMAND(CLUSTER_LINKS, "CLUSTER", "LINKS", 2, NONE, 0)
COMMAND(CLUSTER_MEET, "CLUSTER", "MEET", -4, NONE, 0)
COMMAND(CLUSTER_MIGRATESLOTS, "CLUSTER", "MIGRATESLOTS", -4, NONE, 0)
COMMAND(CLUSTER_MYID, "CLUSTER", "MYID", 2, NONE, 0)
COMMAND(CLUSTER_MYSHARDID, "CLUSTER", "MYSHARDID", 2, NONE, 0)
COMMAND(CLUSTER_NODES, "CLUSTER", "NODES", 2, NONE, 0)
COMMAND(CLUSTER_REPLICAS, "CLUSTER", "REPLICAS", 3, NONE, 0)
COMMAND(CLUSTER_REPLICATE, "CLUSTER", "REPLICATE", 3, NONE, 0)
COMMAND(CLUSTER_REPLICATE, "CLUSTER", "REPLICATE", -3, NONE, 0)
COMMAND(CLUSTER_RESET, "CLUSTER", "RESET", -2, NONE, 0)
COMMAND(CLUSTER_SAVECONFIG, "CLUSTER", "SAVECONFIG", 2, NONE, 0)
COMMAND(CLUSTER_SET_CONFIG_EPOCH, "CLUSTER", "SET-CONFIG-EPOCH", 3, NONE, 0)
COMMAND(CLUSTER_SETSLOT, "CLUSTER", "SETSLOT", -4, NONE, 0)
COMMAND(CLUSTER_SHARDS, "CLUSTER", "SHARDS", 2, NONE, 0)
COMMAND(CLUSTER_SLAVES, "CLUSTER", "SLAVES", 3, NONE, 0)
COMMAND(CLUSTER_SLOT_STATS, "CLUSTER", "SLOT-STATS", -4, NONE, 0)
COMMAND(CLUSTER_SLOTS, "CLUSTER", "SLOTS", 2, NONE, 0)
COMMAND(CLUSTER_SYNCSLOTS, "CLUSTER", "SYNCSLOTS", -3, NONE, 0)
COMMAND(COMMANDLOG_GET, "COMMANDLOG", "GET", 4, NONE, 0)
COMMAND(COMMANDLOG_HELP, "COMMANDLOG", "HELP", 2, NONE, 0)
COMMAND(COMMANDLOG_LEN, "COMMANDLOG", "LEN", 3, NONE, 0)
COMMAND(COMMANDLOG_RESET, "COMMANDLOG", "RESET", 3, NONE, 0)
COMMAND(COMMAND_COUNT, "COMMAND", "COUNT", 2, NONE, 0)
COMMAND(COMMAND_DOCS, "COMMAND", "DOCS", -2, NONE, 0)
COMMAND(COMMAND_GETKEYS, "COMMAND", "GETKEYS", -3, NONE, 0)
Expand All @@ -96,6 +117,7 @@ COMMAND(DEBUG, "DEBUG", NULL, -2, NONE, 0)
COMMAND(DECR, "DECR", NULL, 2, INDEX, 1)
COMMAND(DECRBY, "DECRBY", NULL, 3, INDEX, 1)
COMMAND(DEL, "DEL", NULL, -2, INDEX, 1)
COMMAND(DELIFEQ, "DELIFEQ", NULL, 3, INDEX, 1)
COMMAND(DISCARD, "DISCARD", NULL, 1, NONE, 0)
COMMAND(DUMP, "DUMP", NULL, 2, INDEX, 1)
COMMAND(ECHO, "ECHO", NULL, 2, NONE, 0)
Expand All @@ -113,6 +135,11 @@ COMMAND(FCALL, "FCALL", NULL, -3, KEYNUM, 2)
COMMAND(FCALL_RO, "FCALL_RO", NULL, -3, KEYNUM, 2)
COMMAND(FLUSHALL, "FLUSHALL", NULL, -1, NONE, 0)
COMMAND(FLUSHDB, "FLUSHDB", NULL, -1, NONE, 0)
COMMAND(FT_CREATE, "FT.CREATE", NULL, -1, NONE, 0)
COMMAND(FT_DROPINDEX, "FT.DROPINDEX", NULL, 2, NONE, 0)
COMMAND(FT_INFO, "FT.INFO", NULL, 2, NONE, 0)
COMMAND(FT_SEARCH, "FT.SEARCH", NULL, -3, INDEX, 1)
COMMAND(FT__LIST, "FT._LIST", NULL, 1, NONE, 0)
COMMAND(FUNCTION_DELETE, "FUNCTION", "DELETE", 3, NONE, 0)
COMMAND(FUNCTION_DUMP, "FUNCTION", "DUMP", 2, NONE, 0)
COMMAND(FUNCTION_FLUSH, "FUNCTION", "FLUSH", -2, NONE, 0)
Expand Down Expand Up @@ -141,24 +168,58 @@ COMMAND(GETSET, "GETSET", NULL, 3, INDEX, 1)
COMMAND(HDEL, "HDEL", NULL, -3, INDEX, 1)
COMMAND(HELLO, "HELLO", NULL, -1, NONE, 0)
COMMAND(HEXISTS, "HEXISTS", NULL, 3, INDEX, 1)
COMMAND(HEXPIRE, "HEXPIRE", NULL, -6, INDEX, 1)
COMMAND(HEXPIREAT, "HEXPIREAT", NULL, -6, INDEX, 1)
COMMAND(HEXPIRETIME, "HEXPIRETIME", NULL, -5, INDEX, 1)
COMMAND(HGET, "HGET", NULL, 3, INDEX, 1)
COMMAND(HGETALL, "HGETALL", NULL, 2, INDEX, 1)
COMMAND(HGETEX, "HGETEX", NULL, -5, INDEX, 1)
COMMAND(HINCRBY, "HINCRBY", NULL, 4, INDEX, 1)
COMMAND(HINCRBYFLOAT, "HINCRBYFLOAT", NULL, 4, INDEX, 1)
COMMAND(HKEYS, "HKEYS", NULL, 2, INDEX, 1)
COMMAND(HLEN, "HLEN", NULL, 2, INDEX, 1)
COMMAND(HMGET, "HMGET", NULL, -3, INDEX, 1)
COMMAND(HMSET, "HMSET", NULL, -4, INDEX, 1)
COMMAND(HPERSIST, "HPERSIST", NULL, -5, INDEX, 1)
COMMAND(HPEXPIRE, "HPEXPIRE", NULL, -6, INDEX, 1)
COMMAND(HPEXPIREAT, "HPEXPIREAT", NULL, -6, INDEX, 1)
COMMAND(HPEXPIRETIME, "HPEXPIRETIME", NULL, -5, INDEX, 1)
COMMAND(HPTTL, "HPTTL", NULL, -5, INDEX, 1)
COMMAND(HRANDFIELD, "HRANDFIELD", NULL, -2, INDEX, 1)
COMMAND(HSCAN, "HSCAN", NULL, -3, INDEX, 1)
COMMAND(HSET, "HSET", NULL, -4, INDEX, 1)
COMMAND(HSETEX, "HSETEX", NULL, -6, INDEX, 1)
COMMAND(HSETNX, "HSETNX", NULL, 4, INDEX, 1)
COMMAND(HSTRLEN, "HSTRLEN", NULL, 3, INDEX, 1)
COMMAND(HTTL, "HTTL", NULL, -5, INDEX, 1)
COMMAND(HVALS, "HVALS", NULL, 2, INDEX, 1)
COMMAND(INCR, "INCR", NULL, 2, INDEX, 1)
COMMAND(INCRBY, "INCRBY", NULL, 3, INDEX, 1)
COMMAND(INCRBYFLOAT, "INCRBYFLOAT", NULL, 3, INDEX, 1)
COMMAND(INFO, "INFO", NULL, -1, NONE, 0)
COMMAND(JSON_ARRAPPEND, "JSON.ARRAPPEND", NULL, -4, INDEX, 1)
COMMAND(JSON_ARRINDEX, "JSON.ARRINDEX", NULL, 4, INDEX, 1)
COMMAND(JSON_ARRINSERT, "JSON.ARRINSERT", NULL, -5, INDEX, 1)
COMMAND(JSON_ARRLEN, "JSON.ARRLEN", NULL, 2, INDEX, 1)
COMMAND(JSON_ARRPOP, "JSON.ARRPOP", NULL, 2, INDEX, 1)
COMMAND(JSON_ARRTRIM, "JSON.ARRTRIM", NULL, 5, INDEX, 1)
COMMAND(JSON_CLEAR, "JSON.CLEAR", NULL, 2, INDEX, 1)
COMMAND(JSON_DEBUG, "JSON.DEBUG", NULL, 2, NONE, 0)
COMMAND(JSON_DEL, "JSON.DEL", NULL, 2, INDEX, 1)
COMMAND(JSON_FORGET, "JSON.FORGET", NULL, -1, NONE, 0)
COMMAND(JSON_GET, "JSON.GET", NULL, 2, INDEX, 1)
COMMAND(JSON_MGET, "JSON.MGET", NULL, -3, INDEX, 1)
COMMAND(JSON_MSET, "JSON.MSET", NULL, -4, NONE, 0)
COMMAND(JSON_NUMINCRBY, "JSON.NUMINCRBY", NULL, 4, INDEX, 1)
COMMAND(JSON_NUMMULTBY, "JSON.NUMMULTBY", NULL, 4, INDEX, 1)
COMMAND(JSON_OBJKEYS, "JSON.OBJKEYS", NULL, 2, INDEX, 1)
COMMAND(JSON_OBJLEN, "JSON.OBJLEN", NULL, 2, INDEX, 1)
COMMAND(JSON_RESP, "JSON.RESP", NULL, 2, INDEX, 1)
COMMAND(JSON_SET, "JSON.SET", NULL, 4, INDEX, 1)
COMMAND(JSON_STRAPPEND, "JSON.STRAPPEND", NULL, 3, INDEX, 1)
COMMAND(JSON_STRLEN, "JSON.STRLEN", NULL, 2, INDEX, 1)
COMMAND(JSON_TOGGLE, "JSON.TOGGLE", NULL, 2, INDEX, 1)
COMMAND(JSON_TYPE, "JSON.TYPE", NULL, 2, INDEX, 1)
COMMAND(KEYS, "KEYS", NULL, 2, NONE, 0)
COMMAND(LASTSAVE, "LASTSAVE", NULL, 1, NONE, 0)
COMMAND(LATENCY_DOCTOR, "LATENCY", "DOCTOR", 2, NONE, 0)
Expand Down Expand Up @@ -254,23 +315,28 @@ COMMAND(SCRIPT_FLUSH, "SCRIPT", "FLUSH", -2, NONE, 0)
COMMAND(SCRIPT_HELP, "SCRIPT", "HELP", 2, NONE, 0)
COMMAND(SCRIPT_KILL, "SCRIPT", "KILL", 2, NONE, 0)
COMMAND(SCRIPT_LOAD, "SCRIPT", "LOAD", 3, NONE, 0)
COMMAND(SCRIPT_SHOW, "SCRIPT", "SHOW", 3, NONE, 0)
COMMAND(SDIFF, "SDIFF", NULL, -2, INDEX, 1)
COMMAND(SDIFFSTORE, "SDIFFSTORE", NULL, -3, INDEX, 1)
COMMAND(SELECT, "SELECT", NULL, 2, NONE, 0)
COMMAND(SENTINEL_CKQUORUM, "SENTINEL", "CKQUORUM", 3, NONE, 0)
COMMAND(SENTINEL_CONFIG, "SENTINEL", "CONFIG", -4, NONE, 0)
COMMAND(SENTINEL_DEBUG, "SENTINEL", "DEBUG", -2, NONE, 0)
COMMAND(SENTINEL_FAILOVER, "SENTINEL", "FAILOVER", 3, NONE, 0)
COMMAND(SENTINEL_FAILOVER, "SENTINEL", "FAILOVER", -3, NONE, 0)
COMMAND(SENTINEL_FLUSHCONFIG, "SENTINEL", "FLUSHCONFIG", 2, NONE, 0)
COMMAND(SENTINEL_GET_MASTER_ADDR_BY_NAME, "SENTINEL", "GET-MASTER-ADDR-BY-NAME", 3, NONE, 0)
COMMAND(SENTINEL_GET_PRIMARY_ADDR_BY_NAME, "SENTINEL", "GET-PRIMARY-ADDR-BY-NAME", 3, NONE, 0)
COMMAND(SENTINEL_HELP, "SENTINEL", "HELP", 2, NONE, 0)
COMMAND(SENTINEL_INFO_CACHE, "SENTINEL", "INFO-CACHE", -3, NONE, 0)
COMMAND(SENTINEL_IS_MASTER_DOWN_BY_ADDR, "SENTINEL", "IS-MASTER-DOWN-BY-ADDR", 6, NONE, 0)
COMMAND(SENTINEL_IS_PRIMARY_DOWN_BY_ADDR, "SENTINEL", "IS-PRIMARY-DOWN-BY-ADDR", 6, NONE, 0)
COMMAND(SENTINEL_MASTER, "SENTINEL", "MASTER", 3, NONE, 0)
COMMAND(SENTINEL_MASTERS, "SENTINEL", "MASTERS", 2, NONE, 0)
COMMAND(SENTINEL_MONITOR, "SENTINEL", "MONITOR", 6, NONE, 0)
COMMAND(SENTINEL_MYID, "SENTINEL", "MYID", 2, NONE, 0)
COMMAND(SENTINEL_PENDING_SCRIPTS, "SENTINEL", "PENDING-SCRIPTS", 2, NONE, 0)
COMMAND(SENTINEL_PRIMARIES, "SENTINEL", "PRIMARIES", 2, NONE, 0)
COMMAND(SENTINEL_PRIMARY, "SENTINEL", "PRIMARY", 3, NONE, 0)
COMMAND(SENTINEL_REMOVE, "SENTINEL", "REMOVE", 3, NONE, 0)
COMMAND(SENTINEL_REPLICAS, "SENTINEL", "REPLICAS", 3, NONE, 0)
COMMAND(SENTINEL_RESET, "SENTINEL", "RESET", 3, NONE, 0)
Expand Down
16 changes: 6 additions & 10 deletions src/command.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,6 @@
#include <ctype.h>
#include <errno.h>
#ifndef _WIN32
#if !defined(__FreeBSD__)
#include <alloca.h>
#else
#include <stdlib.h>
#endif
#include <strings.h>
#else
#include <malloc.h>
Expand All @@ -54,6 +49,7 @@
#include <stdio.h>
#include <string.h>

#define MAX_COMMAND_LEN 64
#define LF (uint8_t)10
#define CR (uint8_t)13

Expand Down Expand Up @@ -101,11 +97,13 @@ static inline void to_upper(char *dst, const char *src, uint32_t len) {
* or NULL on failure. */
cmddef *valkey_lookup_cmd(const char *arg0, uint32_t arg0_len, const char *arg1,
uint32_t arg1_len) {
if (arg0_len > MAX_COMMAND_LEN || arg1_len > MAX_COMMAND_LEN)
return NULL;
char cmd[MAX_COMMAND_LEN];
char subcmd[MAX_COMMAND_LEN] = "";
int num_commands = sizeof(server_commands) / sizeof(cmddef);
/* Compare command name in uppercase. */
char *cmd = alloca(arg0_len);
to_upper(cmd, arg0, arg0_len);
char *subcmd = NULL; /* Alloca later on demand. */
/* Find the command using binary search. */
int left = 0, right = num_commands - 1;
while (left <= right) {
Expand All @@ -122,10 +120,8 @@ cmddef *valkey_lookup_cmd(const char *arg0, uint32_t arg0_len, const char *arg1,
/* Command has subcommands, but none given. */
return NULL;
}
if (subcmd == NULL) {
subcmd = alloca(arg1_len);
if (subcmd[0] == '\0')
to_upper(subcmd, arg1, arg1_len);
}
cmp = strncmp(c->subname, subcmd, arg1_len);
if (cmp == 0 && strlen(c->subname) > arg1_len)
cmp = 1;
Expand Down
4 changes: 0 additions & 4 deletions src/win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@
#define strncasecmp _strnicmp
#endif

#ifndef alloca
#define alloca _alloca
#endif

#ifndef va_copy
#define va_copy(d, s) ((d) = (s))
#endif
Expand Down
40 changes: 40 additions & 0 deletions tests/ut_parse_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,32 @@ void test_valkey_parse_error_nonresp(void) {
command_destroy(c);
}

/* Create a 65 bytes command string while limit is 64 bytes */
void test_valkey_parse_too_long_cmd(void) {
char str[66];
memset(str, 'A', 65);
str[65] = '\0';
struct cmd *c = command_get();
int len = valkeyFormatCommand(&c->cmd, str);
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
valkey_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_ERROR, "Unexpected parse success");
ASSERT_MSG(!strncmp(c->errstr, "Unknown command AAAA", 20), c->errstr);
command_destroy(c);
}

void test_valkey_parse_unknown_cmd(void) {
struct cmd *c = command_get();
int len = valkeyFormatCommand(&c->cmd, "OIOIOI");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
valkey_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_ERROR, "Unexpected parse success");
ASSERT_MSG(!strcmp(c->errstr, "Unknown command OIOIOI"), c->errstr);
command_destroy(c);
}

void test_valkey_parse_cmd_get(void) {
struct cmd *c = command_get();
int len = valkeyFormatCommand(&c->cmd, "GET foo");
Expand Down Expand Up @@ -102,6 +128,17 @@ void test_valkey_parse_cmd_xgroup_no_subcommand(void) {
command_destroy(c);
}

void test_valkey_parse_cmd_xgroup_unknown_subcommand(void) {
struct cmd *c = command_get();
int len = valkeyFormatCommand(&c->cmd, "XGROUP OIOIOI");
ASSERT_MSG(len >= 0, "Format command error");
c->clen = len;
valkey_parse_cmd(c);
ASSERT_MSG(c->result == CMD_PARSE_ERROR, "Unexpected parse success");
ASSERT_MSG(!strcmp(c->errstr, "Unknown command XGROUP OIOIOI"), c->errstr);
command_destroy(c);
}

void test_valkey_parse_cmd_xgroup_destroy_no_key(void) {
struct cmd *c = command_get();
int len = valkeyFormatCommand(&c->cmd, "xgroup destroy");
Expand Down Expand Up @@ -188,11 +225,14 @@ void test_valkey_parse_cmd_georadius_ro_ok(void) {

int main(void) {
test_valkey_parse_error_nonresp();
test_valkey_parse_too_long_cmd();
test_valkey_parse_unknown_cmd();
test_valkey_parse_cmd_get();
test_valkey_parse_cmd_mset();
test_valkey_parse_cmd_eval_1();
test_valkey_parse_cmd_eval_0();
test_valkey_parse_cmd_xgroup_no_subcommand();
test_valkey_parse_cmd_xgroup_unknown_subcommand();
test_valkey_parse_cmd_xgroup_destroy_no_key();
test_valkey_parse_cmd_xgroup_destroy_ok();
test_valkey_parse_cmd_xreadgroup_ok();
Expand Down