549 lines
26 KiB
Elixir
549 lines
26 KiB
Elixir
defmodule Excache.KeyspaceTest do
|
|
use Excache.Test.ServerCase, async: false
|
|
|
|
test "STRLEN, GETRANGE and SETRANGE follow Redis offsets", %{conn: conn} do
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "greeting", "Hello World"])
|
|
assert {11, conn} = Client.command(conn, ["STRLEN", "greeting"])
|
|
assert {0, conn} = Client.command(conn, ["STRLEN", "absent"])
|
|
|
|
assert {"Hello", conn} = Client.command(conn, ["GETRANGE", "greeting", "0", "4"])
|
|
assert {"World", conn} = Client.command(conn, ["GETRANGE", "greeting", "-5", "-1"])
|
|
assert {"", conn} = Client.command(conn, ["GETRANGE", "greeting", "10", "4"])
|
|
assert {"", conn} = Client.command(conn, ["GETRANGE", "absent", "0", "-1"])
|
|
|
|
assert {11, conn} = Client.command(conn, ["SETRANGE", "greeting", "6", "Redis"])
|
|
assert {"Hello Redis", conn} = Client.command(conn, ["GETRANGE", "greeting", "0", "-1"])
|
|
|
|
assert {8, conn} = Client.command(conn, ["SETRANGE", "padded", "5", "abc"])
|
|
{padded, conn} = Client.command(conn, ["GET", "padded"])
|
|
assert padded == :binary.copy(<<0>>, 5) <> "abc"
|
|
|
|
assert {0, conn} = Client.command(conn, ["SETRANGE", "untouched", "0", ""])
|
|
assert {0, conn} = Client.command(conn, ["EXISTS", "untouched"])
|
|
|
|
assert {{:error, "ERR offset is out of range"}, _conn} =
|
|
Client.command(conn, ["SETRANGE", "greeting", "-1", "x"])
|
|
end
|
|
|
|
test "SETNX and GETDEL", %{conn: conn} do
|
|
assert {1, conn} = Client.command(conn, ["SETNX", "slot", "first"])
|
|
assert {0, conn} = Client.command(conn, ["SETNX", "slot", "second"])
|
|
assert {"first", conn} = Client.command(conn, ["GET", "slot"])
|
|
|
|
assert {"first", conn} = Client.command(conn, ["GETDEL", "slot"])
|
|
assert {0, conn} = Client.command(conn, ["EXISTS", "slot"])
|
|
assert {nil, conn} = Client.command(conn, ["GETDEL", "slot"])
|
|
|
|
assert {1, conn} = Client.command(conn, ["RPUSH", "queue", "x"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, _conn} = Client.command(conn, ["GETDEL", "queue"])
|
|
end
|
|
|
|
test "INCRBYFLOAT accumulates and rejects non numeric values", %{conn: conn} do
|
|
assert {"10.5", conn} = Client.command(conn, ["INCRBYFLOAT", "balance", "10.5"])
|
|
assert {"10.6", conn} = Client.command(conn, ["INCRBYFLOAT", "balance", "0.1"])
|
|
assert {"8", conn} = Client.command(conn, ["INCRBYFLOAT", "balance", "-2.6"])
|
|
assert {"5", conn} = Client.command(conn, ["INCRBYFLOAT", "counter", "5"])
|
|
|
|
assert {{:error, "ERR value is not a valid float"}, conn} =
|
|
Client.command(conn, ["INCRBYFLOAT", "balance", "abc"])
|
|
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "word", "hello"])
|
|
|
|
assert {{:error, "ERR value is not a valid float"}, _conn} =
|
|
Client.command(conn, ["INCRBYFLOAT", "word", "1"])
|
|
end
|
|
|
|
test "HSETNX and HMGET", %{conn: conn} do
|
|
assert {1, conn} = Client.command(conn, ["HSETNX", "profile", "name", "ann"])
|
|
assert {0, conn} = Client.command(conn, ["HSETNX", "profile", "name", "ben"])
|
|
assert {"ann", conn} = Client.command(conn, ["HGET", "profile", "name"])
|
|
assert {1, conn} = Client.command(conn, ["HSETNX", "profile", "city", "gdansk"])
|
|
|
|
{values, conn} = Client.command(conn, ["HMGET", "profile", "name", "ghost", "city"])
|
|
assert values == ["ann", nil, "gdansk"]
|
|
|
|
{absent, conn} = Client.command(conn, ["HMGET", "nohash", "a", "b"])
|
|
assert absent == [nil, nil]
|
|
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "plain", "v"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, _conn} = Client.command(conn, ["HMGET", "plain", "a"])
|
|
end
|
|
|
|
test "SMISMEMBER and SINTERCARD", %{conn: conn} do
|
|
assert {3, conn} = Client.command(conn, ["SADD", "left", "a", "b", "c"])
|
|
assert {3, conn} = Client.command(conn, ["SADD", "right", "b", "c", "d"])
|
|
|
|
{flags, conn} = Client.command(conn, ["SMISMEMBER", "left", "a", "zz", "c"])
|
|
assert flags == [1, 0, 1]
|
|
|
|
{none, conn} = Client.command(conn, ["SMISMEMBER", "noset", "a"])
|
|
assert none == [0]
|
|
|
|
assert {2, conn} = Client.command(conn, ["SINTERCARD", "2", "left", "right"])
|
|
assert {2, conn} = Client.command(conn, ["SINTERCARD", "2", "left", "right", "LIMIT", "0"])
|
|
assert {1, conn} = Client.command(conn, ["SINTERCARD", "2", "left", "right", "LIMIT", "1"])
|
|
|
|
assert {{:error, "ERR numkeys should be greater than 0"}, conn} =
|
|
Client.command(conn, ["SINTERCARD", "0", "left"])
|
|
|
|
assert {{:error, "ERR Number of keys can't be greater than number of args"}, conn} =
|
|
Client.command(conn, ["SINTERCARD", "3", "left"])
|
|
|
|
assert {{:error, "ERR LIMIT can't be negative"}, conn} =
|
|
Client.command(conn, ["SINTERCARD", "1", "left", "LIMIT", "-1"])
|
|
|
|
assert {{:error, "ERR syntax error"}, _conn} =
|
|
Client.command(conn, ["SINTERCARD", "1", "left", "BOUND", "1"])
|
|
end
|
|
|
|
test "SPOP replies with a bulk without count and an array with count", %{conn: conn} do
|
|
assert {3, conn} = Client.command(conn, ["SADD", "bag", "a", "b", "c"])
|
|
|
|
{single, conn} = Client.command(conn, ["SPOP", "bag"])
|
|
assert single in ["a", "b", "c"]
|
|
|
|
{batch, conn} = Client.command(conn, ["SPOP", "bag", "5"])
|
|
assert length(batch) == 2
|
|
assert {0, conn} = Client.command(conn, ["EXISTS", "bag"])
|
|
|
|
assert {nil, conn} = Client.command(conn, ["SPOP", "bag"])
|
|
assert {[], conn} = Client.command(conn, ["SPOP", "bag", "3"])
|
|
|
|
assert {5, conn} = Client.command(conn, ["SADD", "bag", "a", "b", "c", "d", "e"])
|
|
assert {[], conn} = Client.command(conn, ["SPOP", "bag", "0"])
|
|
assert {5, conn} = Client.command(conn, ["SCARD", "bag"])
|
|
|
|
assert {{:error, "ERR value is out of range, must be positive"}, _conn} =
|
|
Client.command(conn, ["SPOP", "bag", "-1"])
|
|
end
|
|
|
|
test "SRANDMEMBER repeats members for a negative count", %{conn: conn} do
|
|
assert {2, conn} = Client.command(conn, ["SADD", "colors", "red", "green"])
|
|
|
|
{single, conn} = Client.command(conn, ["SRANDMEMBER", "colors"])
|
|
assert single in ["red", "green"]
|
|
|
|
{repeats, conn} = Client.command(conn, ["SRANDMEMBER", "colors", "-5"])
|
|
assert length(repeats) == 5
|
|
assert Enum.all?(repeats, &(&1 in ["red", "green"]))
|
|
|
|
{capped, conn} = Client.command(conn, ["SRANDMEMBER", "colors", "5"])
|
|
assert Enum.sort(capped) == ["green", "red"]
|
|
|
|
assert {2, conn} = Client.command(conn, ["SCARD", "colors"])
|
|
assert {nil, conn} = Client.command(conn, ["SRANDMEMBER", "nothing"])
|
|
assert {[], _conn} = Client.command(conn, ["SRANDMEMBER", "nothing", "3"])
|
|
end
|
|
|
|
test "SMOVE moves a member and refuses a destination of the wrong type", %{conn: conn} do
|
|
assert {2, conn} = Client.command(conn, ["SADD", "source", "x", "y"])
|
|
assert {1, conn} = Client.command(conn, ["SADD", "target", "z"])
|
|
|
|
assert {1, conn} = Client.command(conn, ["SMOVE", "source", "target", "x"])
|
|
assert {0, conn} = Client.command(conn, ["SMOVE", "source", "target", "ghost"])
|
|
|
|
{members, conn} = Client.command(conn, ["SMEMBERS", "target"])
|
|
assert Enum.sort(members) == ["x", "z"]
|
|
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "notaset", "v"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} =
|
|
Client.command(conn, ["SMOVE", "source", "notaset", "y"])
|
|
|
|
{left, _conn} = Client.command(conn, ["SMEMBERS", "source"])
|
|
assert left == ["y"]
|
|
end
|
|
|
|
test "LREM counts from the tail for a negative count", %{conn: conn} do
|
|
assert {5, conn} = Client.command(conn, ["RPUSH", "log", "a", "b", "a", "c", "a"])
|
|
assert {2, conn} = Client.command(conn, ["LREM", "log", "-2", "a"])
|
|
|
|
{rest, conn} = Client.command(conn, ["LRANGE", "log", "0", "-1"])
|
|
assert rest == ["a", "b", "c"]
|
|
|
|
assert {1, conn} = Client.command(conn, ["LREM", "log", "0", "a"])
|
|
assert {0, conn} = Client.command(conn, ["LREM", "log", "3", "zz"])
|
|
assert {0, conn} = Client.command(conn, ["LREM", "absent", "0", "a"])
|
|
|
|
assert {{:error, "ERR value is not an integer or out of range"}, _conn} =
|
|
Client.command(conn, ["LREM", "log", "many", "a"])
|
|
end
|
|
|
|
test "LSET reports missing keys and out of range indexes", %{conn: conn} do
|
|
assert {{:error, "ERR no such key"}, conn} =
|
|
Client.command(conn, ["LSET", "nolist", "0", "v"])
|
|
|
|
assert {3, conn} = Client.command(conn, ["RPUSH", "items", "a", "b", "c"])
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["LSET", "items", "-1", "z"])
|
|
|
|
{items, conn} = Client.command(conn, ["LRANGE", "items", "0", "-1"])
|
|
assert items == ["a", "b", "z"]
|
|
|
|
assert {{:error, "ERR index out of range"}, conn} =
|
|
Client.command(conn, ["LSET", "items", "9", "v"])
|
|
|
|
assert {{:error, "ERR index out of range"}, _conn} =
|
|
Client.command(conn, ["LSET", "items", "-9", "v"])
|
|
end
|
|
|
|
test "LTRIM keeps an inclusive window and drops an empty list", %{conn: conn} do
|
|
assert {5, conn} = Client.command(conn, ["RPUSH", "queue", "a", "b", "c", "d", "e"])
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["LTRIM", "queue", "1", "-2"])
|
|
|
|
{kept, conn} = Client.command(conn, ["LRANGE", "queue", "0", "-1"])
|
|
assert kept == ["b", "c", "d"]
|
|
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["LTRIM", "queue", "5", "10"])
|
|
assert {0, conn} = Client.command(conn, ["EXISTS", "queue"])
|
|
assert {{:simple, "OK"}, _conn} = Client.command(conn, ["LTRIM", "absent", "0", "-1"])
|
|
end
|
|
|
|
test "LINSERT places an element around a pivot", %{conn: conn} do
|
|
assert {2, conn} = Client.command(conn, ["RPUSH", "plan", "one", "three"])
|
|
assert {3, conn} = Client.command(conn, ["LINSERT", "plan", "BEFORE", "three", "two"])
|
|
assert {4, conn} = Client.command(conn, ["LINSERT", "plan", "AFTER", "three", "four"])
|
|
|
|
{plan, conn} = Client.command(conn, ["LRANGE", "plan", "0", "-1"])
|
|
assert plan == ["one", "two", "three", "four"]
|
|
|
|
assert {-1, conn} = Client.command(conn, ["LINSERT", "plan", "BEFORE", "ghost", "x"])
|
|
assert {0, conn} = Client.command(conn, ["LINSERT", "absent", "BEFORE", "a", "b"])
|
|
|
|
assert {{:error, "ERR syntax error"}, _conn} =
|
|
Client.command(conn, ["LINSERT", "plan", "SIDEWAYS", "one", "x"])
|
|
end
|
|
|
|
test "RPOPLPUSH moves, rotates and protects the destination", %{conn: conn} do
|
|
assert {3, conn} = Client.command(conn, ["RPUSH", "source", "a", "b", "c"])
|
|
assert {"c", conn} = Client.command(conn, ["RPOPLPUSH", "source", "target"])
|
|
|
|
{target, conn} = Client.command(conn, ["LRANGE", "target", "0", "-1"])
|
|
assert target == ["c"]
|
|
|
|
assert {"b", conn} = Client.command(conn, ["RPOPLPUSH", "source", "source"])
|
|
{rotated, conn} = Client.command(conn, ["LRANGE", "source", "0", "-1"])
|
|
assert rotated == ["b", "a"]
|
|
|
|
assert {nil, conn} = Client.command(conn, ["RPOPLPUSH", "absent", "target"])
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "text", "v"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} =
|
|
Client.command(conn, ["RPOPLPUSH", "source", "text"])
|
|
|
|
{intact, _conn} = Client.command(conn, ["LRANGE", "source", "0", "-1"])
|
|
assert intact == ["b", "a"]
|
|
end
|
|
|
|
test "LPOS honours RANK, COUNT and MAXLEN", %{conn: conn} do
|
|
assert {8, conn} =
|
|
Client.command(conn, ["RPUSH", "mylist", "a", "b", "c", "1", "2", "3", "c", "c"])
|
|
|
|
assert {2, conn} = Client.command(conn, ["LPOS", "mylist", "c"])
|
|
assert {6, conn} = Client.command(conn, ["LPOS", "mylist", "c", "RANK", "2"])
|
|
assert {7, conn} = Client.command(conn, ["LPOS", "mylist", "c", "RANK", "-1"])
|
|
assert {6, conn} = Client.command(conn, ["LPOS", "mylist", "c", "RANK", "-2"])
|
|
|
|
{all, conn} = Client.command(conn, ["LPOS", "mylist", "c", "COUNT", "0"])
|
|
assert all == [2, 6, 7]
|
|
|
|
{two, conn} = Client.command(conn, ["LPOS", "mylist", "c", "COUNT", "2"])
|
|
assert two == [2, 6]
|
|
|
|
{backwards, conn} = Client.command(conn, ["LPOS", "mylist", "c", "RANK", "-1", "COUNT", "0"])
|
|
assert backwards == [7, 6, 2]
|
|
|
|
assert {nil, conn} = Client.command(conn, ["LPOS", "mylist", "ghost"])
|
|
{empty, conn} = Client.command(conn, ["LPOS", "mylist", "ghost", "COUNT", "0"])
|
|
assert empty == []
|
|
|
|
assert {nil, conn} = Client.command(conn, ["LPOS", "mylist", "c", "MAXLEN", "2"])
|
|
assert {2, conn} = Client.command(conn, ["LPOS", "mylist", "c", "MAXLEN", "3"])
|
|
assert {nil, conn} = Client.command(conn, ["LPOS", "absent", "c"])
|
|
|
|
assert {{:error, "ERR RANK can't be zero." <> _}, conn} =
|
|
Client.command(conn, ["LPOS", "mylist", "c", "RANK", "0"])
|
|
|
|
assert {{:error, "ERR COUNT can't be negative"}, conn} =
|
|
Client.command(conn, ["LPOS", "mylist", "c", "COUNT", "-1"])
|
|
|
|
assert {{:error, "ERR MAXLEN can't be negative"}, conn} =
|
|
Client.command(conn, ["LPOS", "mylist", "c", "MAXLEN", "-1"])
|
|
|
|
assert {{:error, "ERR syntax error"}, _conn} =
|
|
Client.command(conn, ["LPOS", "mylist", "c", "NEAR", "1"])
|
|
end
|
|
|
|
test "ZCOUNT handles exclusive and infinite bounds", %{conn: conn} do
|
|
assert {4, conn} =
|
|
Client.command(conn, ["ZADD", "prices", "10", "a", "20", "b", "30", "c", "40", "d"])
|
|
|
|
assert {4, conn} = Client.command(conn, ["ZCOUNT", "prices", "-inf", "+inf"])
|
|
assert {3, conn} = Client.command(conn, ["ZCOUNT", "prices", "20", "40"])
|
|
assert {2, conn} = Client.command(conn, ["ZCOUNT", "prices", "(20", "40"])
|
|
assert {1, conn} = Client.command(conn, ["ZCOUNT", "prices", "(20", "(40"])
|
|
assert {2, conn} = Client.command(conn, ["ZCOUNT", "prices", "-inf", "20"])
|
|
assert {0, conn} = Client.command(conn, ["ZCOUNT", "absent", "-inf", "+inf"])
|
|
|
|
assert {{:error, "ERR min or max is not a float"}, _conn} =
|
|
Client.command(conn, ["ZCOUNT", "prices", "abc", "10"])
|
|
end
|
|
|
|
test "ZREVRANGE, ZREVRANK and ZMSCORE", %{conn: conn} do
|
|
assert {3, conn} = Client.command(conn, ["ZADD", "board", "1", "a", "2", "b", "3.5", "c"])
|
|
|
|
{desc, conn} = Client.command(conn, ["ZREVRANGE", "board", "0", "-1"])
|
|
assert desc == ["c", "b", "a"]
|
|
|
|
{top, conn} = Client.command(conn, ["ZREVRANGE", "board", "0", "0"])
|
|
assert top == ["c"]
|
|
|
|
{tail, conn} = Client.command(conn, ["ZREVRANGE", "board", "-2", "-1"])
|
|
assert tail == ["b", "a"]
|
|
|
|
{scored, conn} = Client.command(conn, ["ZREVRANGE", "board", "0", "-1", "WITHSCORES"])
|
|
assert scored == ["c", "3.5", "b", "2", "a", "1"]
|
|
|
|
assert {{:error, "ERR syntax error"}, conn} =
|
|
Client.command(conn, ["ZREVRANGE", "board", "0", "-1", "WITHVALUES"])
|
|
|
|
assert {0, conn} = Client.command(conn, ["ZREVRANK", "board", "c"])
|
|
assert {2, conn} = Client.command(conn, ["ZREVRANK", "board", "a"])
|
|
assert {nil, conn} = Client.command(conn, ["ZREVRANK", "board", "ghost"])
|
|
|
|
{scores, conn} = Client.command(conn, ["ZMSCORE", "board", "a", "ghost", "c"])
|
|
assert scores == ["1", nil, "3.5"]
|
|
|
|
{absent, _conn} = Client.command(conn, ["ZMSCORE", "nozset", "a"])
|
|
assert absent == [nil]
|
|
end
|
|
|
|
test "RENAME and RENAMENX", %{conn: conn} do
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "old", "payload"])
|
|
assert {1, conn} = Client.command(conn, ["EXPIRE", "old", "100"])
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["RENAME", "old", "fresh"])
|
|
assert {"payload", conn} = Client.command(conn, ["GET", "fresh"])
|
|
assert {0, conn} = Client.command(conn, ["EXISTS", "old"])
|
|
|
|
{ttl, conn} = Client.command(conn, ["TTL", "fresh"])
|
|
assert ttl > 0
|
|
|
|
assert {{:error, "ERR no such key"}, conn} = Client.command(conn, ["RENAME", "old", "other"])
|
|
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "taken", "t"])
|
|
assert {0, conn} = Client.command(conn, ["RENAMENX", "fresh", "taken"])
|
|
assert {"payload", conn} = Client.command(conn, ["GET", "fresh"])
|
|
assert {1, conn} = Client.command(conn, ["RENAMENX", "fresh", "free"])
|
|
assert {"payload", conn} = Client.command(conn, ["GET", "free"])
|
|
|
|
assert {{:error, "ERR no such key"}, _conn} = Client.command(conn, ["RENAMENX", "ghost", "x"])
|
|
end
|
|
|
|
test "COPY with REPLACE and DB", %{conn: conn} do
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "origin", "payload"])
|
|
assert {1, conn} = Client.command(conn, ["COPY", "origin", "clone"])
|
|
assert {"payload", conn} = Client.command(conn, ["GET", "clone"])
|
|
assert {0, conn} = Client.command(conn, ["COPY", "origin", "clone"])
|
|
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "origin", "changed"])
|
|
assert {1, conn} = Client.command(conn, ["COPY", "origin", "clone", "REPLACE"])
|
|
assert {"changed", conn} = Client.command(conn, ["GET", "clone"])
|
|
|
|
assert {1, conn} = Client.command(conn, ["COPY", "origin", "sameDb", "DB", "0"])
|
|
assert {"changed", conn} = Client.command(conn, ["GET", "sameDb"])
|
|
|
|
assert {{:error, "ERR DB index is out of range"}, conn} =
|
|
Client.command(conn, ["COPY", "origin", "elsewhere", "DB", "3"])
|
|
|
|
assert {{:error, "ERR source and destination objects are the same"}, conn} =
|
|
Client.command(conn, ["COPY", "origin", "origin"])
|
|
|
|
assert {0, conn} = Client.command(conn, ["COPY", "ghost", "anywhere"])
|
|
|
|
assert {{:error, "ERR syntax error"}, _conn} =
|
|
Client.command(conn, ["COPY", "origin", "clone", "FORCE"])
|
|
end
|
|
|
|
test "TOUCH and UNLINK count existing keys", %{conn: conn} do
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["MSET", "t1", "1", "t2", "2"])
|
|
assert {2, conn} = Client.command(conn, ["TOUCH", "t1", "t2", "t3"])
|
|
assert {2, conn} = Client.command(conn, ["UNLINK", "t1", "t2", "t3"])
|
|
assert {0, conn} = Client.command(conn, ["EXISTS", "t1"])
|
|
assert {0, _conn} = Client.command(conn, ["TOUCH", "t1"])
|
|
end
|
|
|
|
test "EXPIREAT, EXPIRETIME and PEXPIRETIME", %{conn: conn} do
|
|
future = System.system_time(:second) + 100
|
|
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "session", "v"])
|
|
assert {-1, conn} = Client.command(conn, ["EXPIRETIME", "session"])
|
|
assert {-1, conn} = Client.command(conn, ["PEXPIRETIME", "session"])
|
|
assert {-2, conn} = Client.command(conn, ["EXPIRETIME", "ghost"])
|
|
assert {-2, conn} = Client.command(conn, ["PEXPIRETIME", "ghost"])
|
|
|
|
assert {0, conn} = Client.command(conn, ["EXPIREAT", "ghost", Integer.to_string(future)])
|
|
assert {1, conn} = Client.command(conn, ["EXPIREAT", "session", Integer.to_string(future)])
|
|
assert {^future, conn} = Client.command(conn, ["EXPIRETIME", "session"])
|
|
|
|
{millis, conn} = Client.command(conn, ["PEXPIRETIME", "session"])
|
|
assert millis == future * 1000
|
|
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "doomed", "v"])
|
|
assert {1, conn} = Client.command(conn, ["EXPIREAT", "doomed", "1000"])
|
|
assert {0, conn} = Client.command(conn, ["EXISTS", "doomed"])
|
|
|
|
assert {{:error, "ERR value is not an integer or out of range"}, _conn} =
|
|
Client.command(conn, ["EXPIREAT", "session", "soon"])
|
|
end
|
|
|
|
test "EXPIREAT respects NX, XX, GT and LT", %{conn: conn} do
|
|
base = System.system_time(:second) + 100
|
|
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "lease", "v"])
|
|
assert {0, conn} = Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(base), "XX"])
|
|
assert {0, conn} = Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(base), "GT"])
|
|
assert {1, conn} = Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(base), "NX"])
|
|
assert {^base, conn} = Client.command(conn, ["EXPIRETIME", "lease"])
|
|
|
|
assert {0, conn} =
|
|
Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(base + 50), "NX"])
|
|
|
|
later = base + 50
|
|
assert {1, conn} = Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(later), "GT"])
|
|
assert {^later, conn} = Client.command(conn, ["EXPIRETIME", "lease"])
|
|
|
|
assert {0, conn} = Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(base), "GT"])
|
|
assert {1, conn} = Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(base), "LT"])
|
|
assert {^base, conn} = Client.command(conn, ["EXPIRETIME", "lease"])
|
|
assert {1, conn} = Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(later), "XX"])
|
|
|
|
assert {{:error, "ERR NX and XX, GT or LT options at the same time are not compatible"}, conn} =
|
|
Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(base), "NX", "XX"])
|
|
|
|
assert {{:error, "ERR Unsupported option SOON"}, _conn} =
|
|
Client.command(conn, ["EXPIREAT", "lease", Integer.to_string(base), "soon"])
|
|
end
|
|
|
|
test "OBJECT reports encoding, refcount, idletime and help", %{conn: conn} do
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "number", "12345"])
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "shared", "42"])
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "padded", "007"])
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "short", "hello"])
|
|
|
|
assert {{:simple, "OK"}, conn} =
|
|
Client.command(conn, ["SET", "long", String.duplicate("x", 60)])
|
|
|
|
assert {"int", conn} = Client.command(conn, ["OBJECT", "ENCODING", "number"])
|
|
assert {"embstr", conn} = Client.command(conn, ["OBJECT", "ENCODING", "padded"])
|
|
assert {"embstr", conn} = Client.command(conn, ["OBJECT", "ENCODING", "short"])
|
|
assert {"raw", conn} = Client.command(conn, ["OBJECT", "ENCODING", "long"])
|
|
|
|
assert {1, conn} = Client.command(conn, ["RPUSH", "smallList", "a"])
|
|
assert {"listpack", conn} = Client.command(conn, ["OBJECT", "ENCODING", "smallList"])
|
|
|
|
bigList = ["RPUSH", "bigList"] ++ Enum.map(1..200, &Integer.to_string/1)
|
|
assert {200, conn} = Client.command(conn, bigList)
|
|
assert {"quicklist", conn} = Client.command(conn, ["OBJECT", "ENCODING", "bigList"])
|
|
|
|
assert {2, conn} = Client.command(conn, ["SADD", "ints", "1", "2"])
|
|
assert {"intset", conn} = Client.command(conn, ["OBJECT", "ENCODING", "ints"])
|
|
|
|
assert {2, conn} = Client.command(conn, ["SADD", "words", "alpha", "beta"])
|
|
assert {"listpack", conn} = Client.command(conn, ["OBJECT", "ENCODING", "words"])
|
|
|
|
assert {1, conn} = Client.command(conn, ["HSET", "hash", "field", "value"])
|
|
assert {"listpack", conn} = Client.command(conn, ["OBJECT", "ENCODING", "hash"])
|
|
|
|
assert {1, conn} = Client.command(conn, ["ZADD", "zset", "1", "a"])
|
|
assert {"listpack", conn} = Client.command(conn, ["OBJECT", "ENCODING", "zset"])
|
|
|
|
assert {1, conn} = Client.command(conn, ["OBJECT", "REFCOUNT", "short"])
|
|
assert {2_147_483_647, conn} = Client.command(conn, ["OBJECT", "REFCOUNT", "shared"])
|
|
assert {0, conn} = Client.command(conn, ["OBJECT", "IDLETIME", "short"])
|
|
|
|
assert {{:error, "ERR no such key"}, conn} =
|
|
Client.command(conn, ["OBJECT", "ENCODING", "ghost"])
|
|
|
|
assert {{:error, "ERR no such key"}, conn} =
|
|
Client.command(conn, ["OBJECT", "REFCOUNT", "ghost"])
|
|
|
|
{help, conn} = Client.command(conn, ["OBJECT", "HELP"])
|
|
assert Enum.any?(help, &match?({:simple, "ENCODING <key>"}, &1))
|
|
|
|
assert {{:error, "ERR Unknown subcommand" <> _}, _conn} =
|
|
Client.command(conn, ["OBJECT", "SIZE", "short"])
|
|
end
|
|
|
|
test "wrong arity is reported per command", %{conn: conn} do
|
|
assert {{:error, "ERR wrong number of arguments for 'strlen' command"}, conn} =
|
|
Client.command(conn, ["STRLEN"])
|
|
|
|
assert {{:error, "ERR wrong number of arguments for 'lset' command"}, conn} =
|
|
Client.command(conn, ["LSET", "key"])
|
|
|
|
assert {{:error, "ERR wrong number of arguments for 'hmget' command"}, conn} =
|
|
Client.command(conn, ["HMGET", "key"])
|
|
|
|
assert {{:error, "ERR wrong number of arguments for 'copy' command"}, conn} =
|
|
Client.command(conn, ["COPY", "key"])
|
|
|
|
assert {{:error, "ERR wrong number of arguments for 'unlink' command"}, conn} =
|
|
Client.command(conn, ["UNLINK"])
|
|
|
|
assert {{:error, "ERR wrong number of arguments for 'expiretime' command"}, _conn} =
|
|
Client.command(conn, ["EXPIRETIME", "a", "b"])
|
|
end
|
|
|
|
test "wrong types and non integer arguments are rejected", %{conn: conn} do
|
|
assert {1, conn} = Client.command(conn, ["RPUSH", "listkey", "x"])
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "stringkey", "x"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} = Client.command(conn, ["STRLEN", "listkey"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} =
|
|
Client.command(conn, ["GETRANGE", "listkey", "0", "-1"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} = Client.command(conn, ["LPOS", "stringkey", "x"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} =
|
|
Client.command(conn, ["ZCOUNT", "stringkey", "-inf", "+inf"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} =
|
|
Client.command(conn, ["ZREVRANGE", "stringkey", "0", "-1"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} =
|
|
Client.command(conn, ["ZMSCORE", "stringkey", "a"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} =
|
|
Client.command(conn, ["LTRIM", "stringkey", "0", "-1"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} = Client.command(conn, ["SPOP", "stringkey"])
|
|
|
|
assert {{:error, "WRONGTYPE" <> _}, conn} =
|
|
Client.command(conn, ["SMISMEMBER", "stringkey", "a"])
|
|
|
|
assert {{:error, "ERR value is not an integer or out of range"}, conn} =
|
|
Client.command(conn, ["GETRANGE", "stringkey", "a", "1"])
|
|
|
|
assert {{:error, "ERR value is not an integer or out of range"}, conn} =
|
|
Client.command(conn, ["SETRANGE", "stringkey", "here", "v"])
|
|
|
|
assert {{:error, "ERR value is not an integer or out of range"}, _conn} =
|
|
Client.command(conn, ["SRANDMEMBER", "stringkey", "few"])
|
|
end
|
|
|
|
test "keyspace commands are queued and replayed inside MULTI", %{conn: conn} do
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "counter", "1"])
|
|
assert {{:simple, "OK"}, conn} = Client.command(conn, ["MULTI"])
|
|
assert {{:simple, "QUEUED"}, conn} = Client.command(conn, ["STRLEN", "counter"])
|
|
assert {{:simple, "QUEUED"}, conn} = Client.command(conn, ["COPY", "counter", "backup"])
|
|
assert {{:simple, "QUEUED"}, conn} = Client.command(conn, ["TOUCH", "counter", "backup"])
|
|
|
|
{replies, conn} = Client.command(conn, ["EXEC"])
|
|
assert replies == [1, 1, 2]
|
|
|
|
assert {"1", _conn} = Client.command(conn, ["GET", "backup"])
|
|
end
|
|
end
|