From 44d39546290da1986649a8251fa71b14ecc954a1 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 28 Aug 2026 11:17:00 +0200 Subject: [PATCH] test(collections): add list, hash, and set command tests --- test/excache/hash_test.exs | 37 ++++++++++++++++++++++ test/excache/list_test.exs | 36 ++++++++++++++++++++++ test/excache/set_test.exs | 63 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 test/excache/hash_test.exs create mode 100644 test/excache/list_test.exs create mode 100644 test/excache/set_test.exs diff --git a/test/excache/hash_test.exs b/test/excache/hash_test.exs new file mode 100644 index 0000000..9fbf2d5 --- /dev/null +++ b/test/excache/hash_test.exs @@ -0,0 +1,37 @@ +defmodule Excache.HashTest do + use Excache.Test.ServerCase, async: false + + test "hash operations", %{conn: conn} do + assert {2, conn} = Client.command(conn, ["HSET", "profile", "name", "ann", "age", "30"]) + assert {"ann", conn} = Client.command(conn, ["HGET", "profile", "name"]) + assert {2, conn} = Client.command(conn, ["HLEN", "profile"]) + + {keys, conn} = Client.command(conn, ["HKEYS", "profile"]) + assert Enum.sort(keys) == ["age", "name"] + + {all, conn} = Client.command(conn, ["HGETALL", "profile"]) + assert Enum.sort(all) == ["30", "age", "ann", "name"] + + assert {1, conn} = Client.command(conn, ["HDEL", "profile", "age"]) + assert {nil, _conn} = Client.command(conn, ["HGET", "profile", "age"]) + end + + test "HEXISTS reports field presence", %{conn: conn} do + assert {1, conn} = Client.command(conn, ["HSET", "profile", "name", "ben"]) + assert {1, conn} = Client.command(conn, ["HEXISTS", "profile", "name"]) + assert {0, conn} = Client.command(conn, ["HEXISTS", "profile", "missing"]) + assert {0, _conn} = Client.command(conn, ["HEXISTS", "no-such-key", "name"]) + end + + test "HINCRBY increments a numeric field and creates missing ones", %{conn: conn} do + assert {5, conn} = Client.command(conn, ["HINCRBY", "counters", "hits", "5"]) + assert {8, conn} = Client.command(conn, ["HINCRBY", "counters", "hits", "3"]) + assert {2, conn} = Client.command(conn, ["HINCRBY", "counters", "misses", "2"]) + assert {-1, conn} = Client.command(conn, ["HINCRBY", "counters", "hits", "-9"]) + + assert {1, conn} = Client.command(conn, ["HSET", "wrong", "field", "abc"]) + + assert {{:error, "ERR value is not an integer or out of range"}, _conn} = + Client.command(conn, ["HINCRBY", "wrong", "field", "1"]) + end +end diff --git a/test/excache/list_test.exs b/test/excache/list_test.exs new file mode 100644 index 0000000..c38f0e4 --- /dev/null +++ b/test/excache/list_test.exs @@ -0,0 +1,36 @@ +defmodule Excache.ListTest do + use Excache.Test.ServerCase, async: false + + test "list operations", %{conn: conn} do + assert {3, conn} = Client.command(conn, ["RPUSH", "queue", "a", "b", "c"]) + assert {4, conn} = Client.command(conn, ["LPUSH", "queue", "z"]) + assert {4, conn} = Client.command(conn, ["LLEN", "queue"]) + + {range, conn} = Client.command(conn, ["LRANGE", "queue", "0", "-1"]) + assert range == ["z", "a", "b", "c"] + + assert {"z", conn} = Client.command(conn, ["LPOP", "queue"]) + assert {"c", conn} = Client.command(conn, ["RPOP", "queue"]) + assert {"a", conn} = Client.command(conn, ["LINDEX", "queue", "0"]) + assert {nil, _conn} = Client.command(conn, ["LINDEX", "queue", "9"]) + end + + test "LPOP and RPOP with an explicit count", %{conn: conn} do + assert {5, conn} = Client.command(conn, ["RPUSH", "letters", "a", "b", "c", "d", "e"]) + assert {["a", "b"], conn} = Client.command(conn, ["LPOP", "letters", "2"]) + assert {["e", "d"], conn} = Client.command(conn, ["RPOP", "letters", "2"]) + assert {1, conn} = Client.command(conn, ["LLEN", "letters"]) + assert {nil, _conn} = Client.command(conn, ["LPOP", "missing-key", "3"]) + end + + test "LPUSH on an existing non-list key returns WRONGTYPE", %{conn: conn} do + assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "notalist", "v"]) + assert {{:error, "WRONGTYPE" <> _}, _conn} = Client.command(conn, ["LPUSH", "notalist", "x"]) + end + + test "popping a list to empty deletes the key", %{conn: conn} do + assert {1, conn} = Client.command(conn, ["RPUSH", "single", "only"]) + assert {"only", conn} = Client.command(conn, ["LPOP", "single"]) + assert {0, _conn} = Client.command(conn, ["EXISTS", "single"]) + end +end diff --git a/test/excache/set_test.exs b/test/excache/set_test.exs new file mode 100644 index 0000000..6fd8f9d --- /dev/null +++ b/test/excache/set_test.exs @@ -0,0 +1,63 @@ +defmodule Excache.SetTest do + use Excache.Test.ServerCase, async: false + + test "set operations", %{conn: conn} do + assert {3, conn} = Client.command(conn, ["SADD", "tags", "x", "y", "z"]) + assert {0, conn} = Client.command(conn, ["SADD", "tags", "x"]) + assert {3, conn} = Client.command(conn, ["SCARD", "tags"]) + assert {1, conn} = Client.command(conn, ["SISMEMBER", "tags", "y"]) + assert {0, conn} = Client.command(conn, ["SISMEMBER", "tags", "nope"]) + + {members, conn} = Client.command(conn, ["SMEMBERS", "tags"]) + assert Enum.sort(members) == ["x", "y", "z"] + + assert {1, _conn} = Client.command(conn, ["SREM", "tags", "y"]) + end + + test "SINTER, SUNION and SDIFF across multiple sets", %{conn: conn} do + assert {3, conn} = Client.command(conn, ["SADD", "a", "1", "2", "3"]) + assert {3, conn} = Client.command(conn, ["SADD", "b", "2", "3", "4"]) + + {inter, conn} = Client.command(conn, ["SINTER", "a", "b"]) + assert Enum.sort(inter) == ["2", "3"] + + {union, conn} = Client.command(conn, ["SUNION", "a", "b"]) + assert Enum.sort(union) == ["1", "2", "3", "4"] + + {diff, conn} = Client.command(conn, ["SDIFF", "a", "b"]) + assert diff == ["1"] + + {diff_missing, conn} = Client.command(conn, ["SDIFF", "a", "missing-set"]) + assert Enum.sort(diff_missing) == ["1", "2", "3"] + + assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "notaset", "v"]) + assert {{:error, "WRONGTYPE" <> _}, _conn} = Client.command(conn, ["SINTER", "a", "notaset"]) + end + + test "SINTERSTORE, SUNIONSTORE and SDIFFSTORE persist the computed set", %{conn: conn} do + assert {3, conn} = Client.command(conn, ["SADD", "src-a", "1", "2", "3"]) + assert {3, conn} = Client.command(conn, ["SADD", "src-b", "2", "3", "4"]) + + assert {2, conn} = Client.command(conn, ["SINTERSTORE", "dest-inter", "src-a", "src-b"]) + {inter, conn} = Client.command(conn, ["SMEMBERS", "dest-inter"]) + assert Enum.sort(inter) == ["2", "3"] + + assert {4, conn} = Client.command(conn, ["SUNIONSTORE", "dest-union", "src-a", "src-b"]) + {union, conn} = Client.command(conn, ["SMEMBERS", "dest-union"]) + assert Enum.sort(union) == ["1", "2", "3", "4"] + + assert {1, conn} = Client.command(conn, ["SDIFFSTORE", "dest-diff", "src-a", "src-b"]) + assert {["1"], conn} = Client.command(conn, ["SMEMBERS", "dest-diff"]) + + assert {0, conn} = Client.command(conn, ["SDIFFSTORE", "dest-empty", "src-a", "src-a"]) + assert {0, _conn} = Client.command(conn, ["EXISTS", "dest-empty"]) + end + + test "SINTERSTORE overwrites any prior value at the destination key", %{conn: conn} do + assert {{:simple, "OK"}, conn} = Client.command(conn, ["SET", "dest", "stale"]) + assert {2, conn} = Client.command(conn, ["SADD", "src-a", "x", "y"]) + assert {1, conn} = Client.command(conn, ["SADD", "src-b", "x"]) + assert {1, conn} = Client.command(conn, ["SINTERSTORE", "dest", "src-a", "src-b"]) + assert {["x"], _conn} = Client.command(conn, ["SMEMBERS", "dest"]) + end +end