use kevy::{Argv, KeyspaceStore, dispatch};
fn argv(parts: &[&[u8]]) -> Argv {
Argv::from(parts.iter().map(|p| p.to_vec()).collect::<Vec<_>>())
}
fn run(store: &mut KeyspaceStore, parts: &[&[u8]]) -> Vec<u8> {
dispatch(store, &argv(parts))
}
fn assert_eq_reply(reply: &[u8], want: &[u8], label: &str) {
assert_eq!(
reply,
want,
"{label}: got {:?}, want {:?}",
std::str::from_utf8(reply).unwrap_or("<binary>"),
std::str::from_utf8(want).unwrap_or("<binary>"),
);
}
fn assert_starts(reply: &[u8], prefix: &[u8], label: &str) {
assert!(
reply.starts_with(prefix),
"{label}: expected prefix {:?}, got {:?}",
std::str::from_utf8(prefix).unwrap_or("<binary>"),
std::str::from_utf8(reply).unwrap_or("<binary>"),
);
}
#[test]
fn conn_and_introspection() {
let mut s = KeyspaceStore::new();
assert_eq_reply(&run(&mut s, &[b"PING"]), b"+PONG\r\n", "PING/0");
assert_eq_reply(
&run(&mut s, &[b"PING", b"hi"]),
b"$2\r\nhi\r\n",
"PING/1",
);
assert_starts(&run(&mut s, &[b"PING", b"a", b"b"]), b"-ERR", "PING/2");
assert_eq_reply(
&run(&mut s, &[b"ECHO", b"hello"]),
b"$5\r\nhello\r\n",
"ECHO/1",
);
assert_starts(&run(&mut s, &[b"ECHO"]), b"-ERR", "ECHO/0");
let cmd_reply = run(&mut s, &[b"COMMAND"]);
assert!(
cmd_reply.starts_with(b"*1") && cmd_reply.len() > 1000,
"COMMAND: expected the full metadata array, got {:?}…",
std::str::from_utf8(&cmd_reply[..40.min(cmd_reply.len())]).unwrap_or("<binary>")
);
assert_eq_reply(&run(&mut s, &[b"QUIT"]), b"+OK\r\n", "QUIT");
assert!(!run(&mut s, &[b"HELLO"]).is_empty(), "HELLO");
assert_eq_reply(
&run(&mut s, &[b"CONFIG", b"GET", b"nonexistent-setting"]),
b"*0\r\n",
"CONFIG GET unknown",
);
assert_starts(
&run(&mut s, &[b"CONFIG", b"SET", b"k", b"v"]),
b"-ERR",
"CONFIG SET",
);
assert_starts(&run(&mut s, &[b"CONFIG"]), b"-ERR", "CONFIG bare");
let empty_argv = Argv::default();
assert_starts(&dispatch(&mut s, &empty_argv), b"-ERR", "empty argv");
assert_starts(
&run(&mut s, &[b"NOPE_NOT_A_CMD"]),
b"-ERR unknown command",
"unknown verb",
);
}
#[test]
fn string_set_get_family() {
let mut s = KeyspaceStore::new();
assert_eq_reply(&run(&mut s, &[b"SET", b"k", b"v"]), b"+OK\r\n", "SET");
assert_eq_reply(&run(&mut s, &[b"GET", b"k"]), b"$1\r\nv\r\n", "GET hit");
assert_eq_reply(&run(&mut s, &[b"GET", b"nope"]), b"$-1\r\n", "GET miss");
assert_starts(&run(&mut s, &[b"GET"]), b"-ERR", "GET/0");
assert_starts(&run(&mut s, &[b"SET", b"k"]), b"-ERR", "SET/1");
assert_eq_reply(
&run(&mut s, &[b"SET", b"k", b"v", b"EX", b"100"]),
b"+OK\r\n",
"SET EX",
);
assert_eq_reply(
&run(&mut s, &[b"SET", b"k", b"v", b"PX", b"123"]),
b"+OK\r\n",
"SET PX",
);
assert_starts(
&run(&mut s, &[b"SET", b"k", b"v", b"EX"]),
b"-ERR",
"SET EX missing arg",
);
assert_starts(
&run(&mut s, &[b"SET", b"k", b"v", b"EX", b"abc"]),
b"-ERR",
"SET EX bad int",
);
assert_starts(
&run(&mut s, &[b"SET", b"k", b"v", b"BOGUS"]),
b"-ERR",
"SET unknown opt",
);
assert_starts(
&run(&mut s, &[b"SET", b"k", b"v", b"NX", b"XX"]),
b"-ERR",
"SET NX+XX",
);
assert_eq_reply(
&run(&mut s, &[b"SET", b"k", b"v2", b"NX"]),
b"$-1\r\n",
"SET NX skip",
);
let _ = run(&mut s, &[b"DEL", b"missing_xx"]);
assert_eq_reply(
&run(&mut s, &[b"SET", b"missing_xx", b"v", b"XX"]),
b"$-1\r\n",
"SET XX skip",
);
assert_eq_reply(&run(&mut s, &[b"SETNX", b"nx", b"v"]), b":1\r\n", "SETNX new");
assert_eq_reply(
&run(&mut s, &[b"SETNX", b"nx", b"w"]),
b":0\r\n",
"SETNX exist",
);
assert_starts(&run(&mut s, &[b"SETNX", b"x"]), b"-ERR", "SETNX/1");
assert_eq_reply(
&run(&mut s, &[b"SETEX", b"se", b"100", b"v"]),
b"+OK\r\n",
"SETEX",
);
assert_eq_reply(
&run(&mut s, &[b"PSETEX", b"pse", b"1000", b"v"]),
b"+OK\r\n",
"PSETEX",
);
assert_starts(
&run(&mut s, &[b"SETEX", b"k", b"v"]),
b"-ERR",
"SETEX/2",
);
assert_starts(
&run(&mut s, &[b"SETEX", b"k", b"abc", b"v"]),
b"-ERR",
"SETEX bad ttl",
);
assert_starts(
&run(&mut s, &[b"SETEX", b"k", b"0", b"v"]),
b"-ERR",
"SETEX zero ttl",
);
let _ = run(&mut s, &[b"DEL", b"ap"]);
assert_eq_reply(&run(&mut s, &[b"APPEND", b"ap", b"foo"]), b":3\r\n", "APPEND new");
assert_eq_reply(
&run(&mut s, &[b"APPEND", b"ap", b"bar"]),
b":6\r\n",
"APPEND ext",
);
assert_starts(&run(&mut s, &[b"APPEND", b"ap"]), b"-ERR", "APPEND/1");
assert_eq_reply(&run(&mut s, &[b"STRLEN", b"ap"]), b":6\r\n", "STRLEN hit");
assert_eq_reply(&run(&mut s, &[b"STRLEN", b"absent"]), b":0\r\n", "STRLEN miss");
assert_starts(&run(&mut s, &[b"STRLEN"]), b"-ERR", "STRLEN/0");
let _ = run(&mut s, &[b"SET", b"gs", b"a"]);
assert_eq_reply(
&run(&mut s, &[b"GETSET", b"gs", b"b"]),
b"$1\r\na\r\n",
"GETSET",
);
assert_eq_reply(
&run(&mut s, &[b"GETSET", b"never", b"v"]),
b"$-1\r\n",
"GETSET miss",
);
assert_starts(&run(&mut s, &[b"GETSET", b"only"]), b"-ERR", "GETSET/1");
let _ = run(&mut s, &[b"SET", b"gd", b"x"]);
assert_eq_reply(
&run(&mut s, &[b"GETDEL", b"gd"]),
b"$1\r\nx\r\n",
"GETDEL",
);
assert_eq_reply(&run(&mut s, &[b"GETDEL", b"gd"]), b"$-1\r\n", "GETDEL miss");
assert_starts(&run(&mut s, &[b"GETDEL"]), b"-ERR", "GETDEL/0");
let _ = run(&mut s, &[b"DEL", b"f"]);
assert!(
run(&mut s, &[b"INCRBYFLOAT", b"f", b"1.5"]).starts_with(b"$"),
"INCRBYFLOAT initial"
);
assert_starts(
&run(&mut s, &[b"INCRBYFLOAT", b"f", b"NaN"]),
b"-ERR",
"INCRBYFLOAT NaN",
);
assert_starts(
&run(&mut s, &[b"INCRBYFLOAT", b"f"]),
b"-ERR",
"INCRBYFLOAT/1",
);
}
#[test]
fn string_counters_and_wrongtype() {
let mut s = KeyspaceStore::new();
assert_eq_reply(&run(&mut s, &[b"INCR", b"n"]), b":1\r\n", "INCR new");
assert_eq_reply(&run(&mut s, &[b"INCR", b"n"]), b":2\r\n", "INCR inc");
assert_eq_reply(&run(&mut s, &[b"DECR", b"n"]), b":1\r\n", "DECR");
assert_eq_reply(&run(&mut s, &[b"INCRBY", b"n", b"10"]), b":11\r\n", "INCRBY");
assert_eq_reply(&run(&mut s, &[b"DECRBY", b"n", b"4"]), b":7\r\n", "DECRBY");
assert_starts(
&run(&mut s, &[b"INCRBY", b"n", b"abc"]),
b"-ERR",
"INCRBY bad int",
);
assert_starts(&run(&mut s, &[b"INCRBY", b"n"]), b"-ERR", "INCRBY/1");
assert_starts(&run(&mut s, &[b"INCR"]), b"-ERR", "INCR/0");
let _ = run(&mut s, &[b"SET", b"str", b"abc"]);
assert_starts(&run(&mut s, &[b"INCR", b"str"]), b"-ERR", "INCR str");
}
#[test]
fn hash_full_surface() {
let mut s = KeyspaceStore::new();
assert_eq_reply(
&run(&mut s, &[b"HSET", b"h", b"f1", b"v1"]),
b":1\r\n",
"HSET new",
);
assert_eq_reply(
&run(&mut s, &[b"HSET", b"h", b"f1", b"v1b", b"f2", b"v2"]),
b":1\r\n",
"HSET upd+new",
);
assert_starts(
&run(&mut s, &[b"HSET", b"h", b"f1"]),
b"-ERR",
"HSET odd pairs",
);
assert_eq_reply(
&run(&mut s, &[b"HSETNX", b"h", b"f3", b"v3"]),
b":1\r\n",
"HSETNX new",
);
assert_eq_reply(
&run(&mut s, &[b"HSETNX", b"h", b"f3", b"v3b"]),
b":0\r\n",
"HSETNX exist",
);
assert_starts(&run(&mut s, &[b"HSETNX", b"h", b"f3"]), b"-ERR", "HSETNX/2");
assert_eq_reply(
&run(&mut s, &[b"HGET", b"h", b"f1"]),
b"$3\r\nv1b\r\n",
"HGET hit",
);
assert_eq_reply(
&run(&mut s, &[b"HGET", b"h", b"absent"]),
b"$-1\r\n",
"HGET miss field",
);
assert_starts(&run(&mut s, &[b"HGET", b"h"]), b"-ERR", "HGET/1");
assert_eq_reply(&run(&mut s, &[b"HEXISTS", b"h", b"f1"]), b":1\r\n", "HEXISTS");
assert_eq_reply(
&run(&mut s, &[b"HEXISTS", b"h", b"x"]),
b":0\r\n",
"HEXISTS miss",
);
assert_starts(&run(&mut s, &[b"HEXISTS", b"h"]), b"-ERR", "HEXISTS/1");
assert!(run(&mut s, &[b"HLEN", b"h"]).starts_with(b":"), "HLEN hit");
assert_eq_reply(&run(&mut s, &[b"HLEN", b"absent"]), b":0\r\n", "HLEN miss");
assert_starts(&run(&mut s, &[b"HLEN"]), b"-ERR", "HLEN/0");
assert!(run(&mut s, &[b"HKEYS", b"h"]).starts_with(b"*"), "HKEYS");
assert!(run(&mut s, &[b"HVALS", b"h"]).starts_with(b"*"), "HVALS");
assert!(
run(&mut s, &[b"HGETALL", b"h"]).starts_with(b"*"),
"HGETALL"
);
assert_eq_reply(&run(&mut s, &[b"HKEYS", b"absent"]), b"*0\r\n", "HKEYS miss");
assert_starts(&run(&mut s, &[b"HKEYS"]), b"-ERR", "HKEYS/0");
assert_starts(&run(&mut s, &[b"HVALS"]), b"-ERR", "HVALS/0");
assert_starts(&run(&mut s, &[b"HGETALL"]), b"-ERR", "HGETALL/0");
let r = run(&mut s, &[b"HMGET", b"h", b"f1", b"absent"]);
assert!(r.starts_with(b"*2"), "HMGET");
assert_starts(&run(&mut s, &[b"HMGET", b"h"]), b"-ERR", "HMGET/1");
assert_eq_reply(
&run(&mut s, &[b"HINCRBY", b"h", b"counter", b"5"]),
b":5\r\n",
"HINCRBY new",
);
assert_eq_reply(
&run(&mut s, &[b"HINCRBY", b"h", b"counter", b"-2"]),
b":3\r\n",
"HINCRBY dec",
);
assert_starts(
&run(&mut s, &[b"HINCRBY", b"h", b"counter", b"oops"]),
b"-ERR",
"HINCRBY bad",
);
assert_starts(
&run(&mut s, &[b"HINCRBY", b"h", b"counter"]),
b"-ERR",
"HINCRBY/2",
);
assert!(
run(&mut s, &[b"HDEL", b"h", b"f1", b"absent"]).starts_with(b":"),
"HDEL",
);
assert_starts(&run(&mut s, &[b"HDEL", b"h"]), b"-ERR", "HDEL/1");
let _ = run(&mut s, &[b"SET", b"str", b"x"]);
assert_starts(
&run(&mut s, &[b"HSET", b"str", b"f", b"v"]),
b"-WRONGTYPE",
"HSET on string",
);
assert_starts(
&run(&mut s, &[b"HGET", b"str", b"f"]),
b"-WRONGTYPE",
"HGET on string",
);
}
#[test]
fn list_full_surface() {
let mut s = KeyspaceStore::new();
assert_eq_reply(
&run(&mut s, &[b"RPUSH", b"l", b"a", b"b", b"c"]),
b":3\r\n",
"RPUSH",
);
assert_eq_reply(
&run(&mut s, &[b"LPUSH", b"l", b"z"]),
b":4\r\n",
"LPUSH",
);
assert_starts(&run(&mut s, &[b"RPUSH", b"l"]), b"-ERR", "RPUSH/1");
assert_starts(&run(&mut s, &[b"LPUSH", b"l"]), b"-ERR", "LPUSH/1");
assert_eq_reply(&run(&mut s, &[b"LLEN", b"l"]), b":4\r\n", "LLEN");
assert_eq_reply(&run(&mut s, &[b"LLEN", b"absent"]), b":0\r\n", "LLEN miss");
assert_starts(&run(&mut s, &[b"LLEN"]), b"-ERR", "LLEN/0");
assert_eq_reply(
&run(&mut s, &[b"LINDEX", b"l", b"0"]),
b"$1\r\nz\r\n",
"LINDEX hit",
);
assert_eq_reply(
&run(&mut s, &[b"LINDEX", b"l", b"99"]),
b"$-1\r\n",
"LINDEX miss",
);
assert_starts(
&run(&mut s, &[b"LINDEX", b"l", b"abc"]),
b"-ERR",
"LINDEX bad int",
);
assert_starts(&run(&mut s, &[b"LINDEX", b"l"]), b"-ERR", "LINDEX/1");
assert!(
run(&mut s, &[b"LRANGE", b"l", b"0", b"-1"]).starts_with(b"*"),
"LRANGE",
);
assert_starts(
&run(&mut s, &[b"LRANGE", b"l", b"a", b"b"]),
b"-ERR",
"LRANGE bad int",
);
assert_starts(&run(&mut s, &[b"LRANGE", b"l"]), b"-ERR", "LRANGE/1");
assert_eq_reply(
&run(&mut s, &[b"LSET", b"l", b"0", b"Z"]),
b"+OK\r\n",
"LSET",
);
assert_starts(
&run(&mut s, &[b"LSET", b"l", b"abc", b"v"]),
b"-ERR",
"LSET bad int",
);
assert_starts(&run(&mut s, &[b"LSET", b"l"]), b"-ERR", "LSET/1");
assert_starts(
&run(&mut s, &[b"LSET", b"l", b"99", b"v"]),
b"-ERR",
"LSET oor",
);
assert!(
run(&mut s, &[b"LREM", b"l", b"0", b"a"]).starts_with(b":"),
"LREM",
);
assert_starts(
&run(&mut s, &[b"LREM", b"l", b"abc", b"x"]),
b"-ERR",
"LREM bad int",
);
assert_starts(&run(&mut s, &[b"LREM", b"l"]), b"-ERR", "LREM/1");
assert_eq_reply(
&run(&mut s, &[b"LTRIM", b"l", b"0", b"-1"]),
b"+OK\r\n",
"LTRIM",
);
assert_starts(
&run(&mut s, &[b"LTRIM", b"l", b"a", b"b"]),
b"-ERR",
"LTRIM bad int",
);
assert_starts(&run(&mut s, &[b"LTRIM", b"l"]), b"-ERR", "LTRIM/1");
assert!(run(&mut s, &[b"LPOP", b"l"]).starts_with(b"$"), "LPOP single");
assert!(run(&mut s, &[b"RPOP", b"l"]).starts_with(b"$"), "RPOP single");
assert!(
run(&mut s, &[b"LPOP", b"l", b"2"]).starts_with(b"*"),
"LPOP count",
);
assert_eq_reply(
&run(&mut s, &[b"LPOP", b"absent", b"5"]),
b"*-1\r\n",
"LPOP count miss",
);
assert_starts(
&run(&mut s, &[b"LPOP", b"l", b"abc"]),
b"-ERR",
"LPOP bad int",
);
assert_starts(
&run(&mut s, &[b"LPOP", b"l", b"-1"]),
b"-ERR",
"LPOP neg",
);
assert_starts(&run(&mut s, &[b"LPOP"]), b"-ERR", "LPOP/0");
let _ = run(&mut s, &[b"SET", b"str", b"x"]);
assert_starts(
&run(&mut s, &[b"LPUSH", b"str", b"v"]),
b"-WRONGTYPE",
"LPUSH on string",
);
}
#[test]
fn set_full_surface() {
let mut s = KeyspaceStore::new();
assert!(
run(&mut s, &[b"SADD", b"S", b"a", b"b", b"c"]).starts_with(b":"),
"SADD",
);
assert_starts(&run(&mut s, &[b"SADD", b"S"]), b"-ERR", "SADD/1");
assert!(run(&mut s, &[b"SCARD", b"S"]).starts_with(b":"), "SCARD");
assert_eq_reply(&run(&mut s, &[b"SCARD", b"absent"]), b":0\r\n", "SCARD miss");
assert_starts(&run(&mut s, &[b"SCARD"]), b"-ERR", "SCARD/0");
assert_eq_reply(
&run(&mut s, &[b"SISMEMBER", b"S", b"a"]),
b":1\r\n",
"SISMEMBER hit",
);
assert_eq_reply(
&run(&mut s, &[b"SISMEMBER", b"S", b"z"]),
b":0\r\n",
"SISMEMBER miss",
);
assert_starts(&run(&mut s, &[b"SISMEMBER", b"S"]), b"-ERR", "SISMEMBER/1");
assert!(run(&mut s, &[b"SMEMBERS", b"S"]).starts_with(b"*"), "SMEMBERS");
assert_starts(&run(&mut s, &[b"SMEMBERS"]), b"-ERR", "SMEMBERS/0");
assert!(run(&mut s, &[b"SREM", b"S", b"a"]).starts_with(b":"), "SREM");
assert_starts(&run(&mut s, &[b"SREM", b"S"]), b"-ERR", "SREM/1");
let _ = run(&mut s, &[b"SADD", b"S", b"x", b"y", b"z"]);
assert!(run(&mut s, &[b"SPOP", b"S"]).starts_with(b"$"), "SPOP single");
assert!(
run(&mut s, &[b"SPOP", b"S", b"2"]).starts_with(b"*"),
"SPOP count",
);
assert!(
run(&mut s, &[b"SRANDMEMBER", b"S"]).starts_with(b"$") ||
run(&mut s, &[b"SRANDMEMBER", b"S"]).starts_with(b"$-1"),
"SRANDMEMBER single",
);
assert!(
run(&mut s, &[b"SRANDMEMBER", b"S", b"2"]).starts_with(b"*"),
"SRANDMEMBER count",
);
assert_starts(
&run(&mut s, &[b"SPOP", b"S", b"-1"]),
b"-ERR",
"SPOP neg",
);
assert_starts(
&run(&mut s, &[b"SPOP", b"S", b"abc"]),
b"-ERR",
"SPOP bad int",
);
assert_starts(&run(&mut s, &[b"SPOP"]), b"-ERR", "SPOP/0");
assert_starts(&run(&mut s, &[b"SRANDMEMBER"]), b"-ERR", "SRANDMEMBER/0");
let _ = run(&mut s, &[b"SET", b"str", b"x"]);
assert_starts(
&run(&mut s, &[b"SADD", b"str", b"v"]),
b"-WRONGTYPE",
"SADD on string",
);
}
#[test]
fn zset_full_surface() {
let mut s = KeyspaceStore::new();
assert_eq_reply(
&run(&mut s, &[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]),
b":3\r\n",
"ZADD",
);
assert_starts(
&run(&mut s, &[b"ZADD", b"z", b"x", b"a"]),
b"-ERR",
"ZADD bad float",
);
assert_starts(
&run(&mut s, &[b"ZADD", b"z", b"1"]),
b"-ERR",
"ZADD/2 odd",
);
assert_starts(&run(&mut s, &[b"ZADD", b"z"]), b"-ERR", "ZADD/1");
assert!(run(&mut s, &[b"ZSCORE", b"z", b"a"]).starts_with(b"$"), "ZSCORE");
assert_eq_reply(
&run(&mut s, &[b"ZSCORE", b"z", b"absent"]),
b"$-1\r\n",
"ZSCORE miss",
);
assert_starts(&run(&mut s, &[b"ZSCORE", b"z"]), b"-ERR", "ZSCORE/1");
assert!(run(&mut s, &[b"ZCARD", b"z"]).starts_with(b":"), "ZCARD");
assert_starts(&run(&mut s, &[b"ZCARD"]), b"-ERR", "ZCARD/0");
assert!(run(&mut s, &[b"ZRANK", b"z", b"a"]).starts_with(b":"), "ZRANK");
assert_eq_reply(
&run(&mut s, &[b"ZRANK", b"z", b"absent"]),
b"$-1\r\n",
"ZRANK miss",
);
assert_starts(&run(&mut s, &[b"ZRANK", b"z"]), b"-ERR", "ZRANK/1");
assert!(
run(&mut s, &[b"ZINCRBY", b"z", b"1.5", b"a"]).starts_with(b"$"),
"ZINCRBY",
);
assert_starts(
&run(&mut s, &[b"ZINCRBY", b"z", b"oops", b"a"]),
b"-ERR",
"ZINCRBY bad float",
);
assert_starts(&run(&mut s, &[b"ZINCRBY", b"z", b"1"]), b"-ERR", "ZINCRBY/2");
assert!(
run(&mut s, &[b"ZRANGE", b"z", b"0", b"-1"]).starts_with(b"*"),
"ZRANGE",
);
assert!(
run(&mut s, &[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]).starts_with(b"*"),
"ZRANGE WS",
);
assert_starts(
&run(&mut s, &[b"ZRANGE", b"z", b"0", b"-1", b"BOGUS"]),
b"-ERR",
"ZRANGE bad opt",
);
assert_starts(
&run(&mut s, &[b"ZRANGE", b"z", b"a", b"b"]),
b"-ERR",
"ZRANGE bad int",
);
assert_starts(&run(&mut s, &[b"ZRANGE", b"z"]), b"-ERR", "ZRANGE/1");
assert_starts(
&run(&mut s, &[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES", b"X"]),
b"-ERR",
"ZRANGE/5",
);
assert!(
run(&mut s, &[b"ZRANGEBYSCORE", b"z", b"-inf", b"+inf"]).starts_with(b"*"),
"ZRANGEBYSCORE",
);
assert!(
run(
&mut s,
&[b"ZRANGEBYSCORE", b"z", b"(1", b"+inf", b"WITHSCORES"],
)
.starts_with(b"*"),
"ZRANGEBYSCORE excl WS",
);
assert_starts(
&run(&mut s, &[b"ZRANGEBYSCORE", b"z", b"foo", b"bar"]),
b"-ERR",
"ZRANGEBYSCORE bad bound",
);
assert_starts(
&run(&mut s, &[b"ZRANGEBYSCORE", b"z", b"0", b"1", b"BOGUS"]),
b"-ERR",
"ZRANGEBYSCORE bad opt",
);
assert_starts(&run(&mut s, &[b"ZRANGEBYSCORE", b"z"]), b"-ERR", "ZRBS/1");
assert!(
run(&mut s, &[b"ZCOUNT", b"z", b"0", b"10"]).starts_with(b":"),
"ZCOUNT",
);
assert_starts(
&run(&mut s, &[b"ZCOUNT", b"z", b"x", b"y"]),
b"-ERR",
"ZCOUNT bad",
);
assert_starts(&run(&mut s, &[b"ZCOUNT", b"z", b"0"]), b"-ERR", "ZCOUNT/2");
assert!(
run(&mut s, &[b"ZREM", b"z", b"a", b"b"]).starts_with(b":"),
"ZREM",
);
assert_starts(&run(&mut s, &[b"ZREM", b"z"]), b"-ERR", "ZREM/1");
let _ = run(&mut s, &[b"SET", b"str", b"x"]);
assert_starts(
&run(&mut s, &[b"ZADD", b"str", b"1", b"a"]),
b"-WRONGTYPE",
"ZADD on string",
);
}
#[test]
fn generic_keyspace() {
let mut s = KeyspaceStore::new();
let _ = run(&mut s, &[b"SET", b"a", b"1"]);
let _ = run(&mut s, &[b"SET", b"b", b"2"]);
assert_eq_reply(&run(&mut s, &[b"DEL", b"a"]), b":1\r\n", "DEL");
assert_eq_reply(
&run(&mut s, &[b"EXISTS", b"b", b"absent"]),
b":1\r\n",
"EXISTS multi",
);
assert_starts(&run(&mut s, &[b"DEL"]), b"-ERR", "DEL/0");
assert_starts(&run(&mut s, &[b"EXISTS"]), b"-ERR", "EXISTS/0");
assert_eq_reply(
&run(&mut s, &[b"EXPIRE", b"b", b"100"]),
b":1\r\n",
"EXPIRE",
);
assert_eq_reply(
&run(&mut s, &[b"PEXPIRE", b"b", b"50000"]),
b":1\r\n",
"PEXPIRE",
);
assert!(
run(&mut s, &[b"EXPIRE", b"absent", b"100"]).starts_with(b":0"),
"EXPIRE miss",
);
assert_starts(
&run(&mut s, &[b"EXPIRE", b"b", b"abc"]),
b"-ERR",
"EXPIRE bad int",
);
assert_starts(&run(&mut s, &[b"EXPIRE", b"b"]), b"-ERR", "EXPIRE/1");
assert!(run(&mut s, &[b"TTL", b"b"]).starts_with(b":"), "TTL");
assert!(run(&mut s, &[b"PTTL", b"b"]).starts_with(b":"), "PTTL");
assert_eq_reply(&run(&mut s, &[b"TTL", b"absent"]), b":-2\r\n", "TTL miss");
assert_eq_reply(&run(&mut s, &[b"PTTL", b"absent"]), b":-2\r\n", "PTTL miss");
assert_starts(&run(&mut s, &[b"TTL"]), b"-ERR", "TTL/0");
assert_starts(&run(&mut s, &[b"PTTL"]), b"-ERR", "PTTL/0");
assert_eq_reply(&run(&mut s, &[b"PERSIST", b"b"]), b":1\r\n", "PERSIST");
assert_eq_reply(&run(&mut s, &[b"PERSIST", b"absent"]), b":0\r\n", "PERSIST miss");
assert_starts(&run(&mut s, &[b"PERSIST"]), b"-ERR", "PERSIST/0");
assert_eq_reply(&run(&mut s, &[b"TYPE", b"b"]), b"+string\r\n", "TYPE str");
assert_eq_reply(
&run(&mut s, &[b"TYPE", b"absent"]),
b"+none\r\n",
"TYPE miss",
);
assert_starts(&run(&mut s, &[b"TYPE"]), b"-ERR", "TYPE/0");
assert!(run(&mut s, &[b"DBSIZE"]).starts_with(b":"), "DBSIZE");
assert_eq_reply(&run(&mut s, &[b"FLUSHDB"]), b"+OK\r\n", "FLUSHDB");
let _ = run(&mut s, &[b"SET", b"x", b"y"]);
assert_eq_reply(&run(&mut s, &[b"FLUSHALL"]), b"+OK\r\n", "FLUSHALL");
}
#[test]
fn multikey_stubs() {
let mut s = KeyspaceStore::new();
for v in [
&b"MSET"[..] as &[u8],
b"MGET",
b"SINTER",
b"SUNION",
b"SDIFF",
b"KEYS",
b"SCAN",
b"RANDOMKEY",
b"SUBSCRIBE",
b"PUBLISH",
] {
assert_starts(&run(&mut s, &[v]), b"-ERR", "stub arity");
}
}