-- SPDX-FileCopyrightText: © 2026 Vladimir Zorin -- SPDX-License-Identifier: LicenseRef-OWL-1.0-or-later -- Licensed under OWL v1.0+. See LICENSE. local testimony = require("testimony") local lev = require("lev") local testify = testimony.new("== Redis ==") local PREFIX = "__LILUSH_TEST:" -- Helper: connect, run fn, clean up local function with_redis(fn) lev.run(function() local redis = require("redis") local client, err = redis.connect("127.0.0.1:6379") if not client then error("Redis not available: " .. tostring(err)) end local ok, test_err = pcall(fn, client) -- Clean up test keys local keys = client:cmd("KEYS", PREFIX .. "*") if keys and type(keys) == "table" then for _, key in ipairs(keys) do client:cmd("DEL", key) end end client:close(true) if not ok then error(test_err) end end) end -- ========================================================================= -- cmd (basic sanity) -- ========================================================================= testify:that("cmd: SET and GET round-trip", function() with_redis(function(r) local key = PREFIX .. "cmd:basic" local ok = r:cmd("SET", key, "hello") testimony.assert_equal("OK", ok) local val = r:cmd("GET", key) testimony.assert_equal("hello", val) end) end) -- ========================================================================= -- pipeline -- ========================================================================= testify:that("pipeline: returns results for multiple commands", function() with_redis(function(r) local k1 = PREFIX .. "pipe:k1" local k2 = PREFIX .. "pipe:k2" local results = r:pipeline({ { "SET", k1, "aaa" }, { "SET", k2, "bbb" }, { "GET", k1 }, { "GET", k2 }, }) testimony.assert_equal(4, #results) testimony.assert_equal("OK", results[1][1]) testimony.assert_equal("OK", results[2][1]) testimony.assert_equal("aaa", results[3][1]) testimony.assert_equal("bbb", results[4][1]) end) end) testify:that("pipeline: handles errors in individual commands", function() with_redis(function(r) local key = PREFIX .. "pipe:str" r:cmd("SET", key, "not_a_list") local results = r:pipeline({ { "GET", key }, { "LPUSH", key, "value" }, -- wrong type error { "GET", key }, }) testimony.assert_equal(3, #results) testimony.assert_equal("not_a_list", results[1][1]) -- Second command should have an error testimony.assert_nil(results[2][1]) testimony.assert_not_nil(results[2][2]) -- Third command should still succeed testimony.assert_equal("not_a_list", results[3][1]) end) end) testify:that("pipeline: returns empty array for empty input", function() with_redis(function(r) local results = r:pipeline({}) testimony.assert_equal(0, #results) end) end) testify:that("pipeline: works with HINCRBY", function() with_redis(function(r) local key = PREFIX .. "pipe:hash" local results = r:pipeline({ { "HINCRBY", key, "count_a", "1" }, { "HINCRBY", key, "count_b", "5" }, { "HINCRBY", key, "count_a", "1" }, }) testimony.assert_equal(3, #results) testimony.assert_equal(1, results[1][1]) testimony.assert_equal(5, results[2][1]) testimony.assert_equal(2, results[3][1]) end) end) testify:that("pipeline: works with HINCRBYFLOAT", function() with_redis(function(r) local key = PREFIX .. "pipe:floats" local results = r:pipeline({ { "HINCRBYFLOAT", key, "duration_sum", "0.0423" }, { "HINCRBYFLOAT", key, "duration_sum", "0.1000" }, }) testimony.assert_equal(2, #results) -- HINCRBYFLOAT returns a bulk string local first = tonumber(results[1][1]) local second = tonumber(results[2][1]) testimony.assert_true(first ~= nil, "first result should be a number") testimony.assert_true(math.abs(first - 0.0423) < 0.0001, "first increment") testimony.assert_true(math.abs(second - 0.1423) < 0.0001, "second increment") end) end) testify:that("pipeline: mixed with EXPIRE for TTL bounding", function() with_redis(function(r) local key = PREFIX .. "pipe:ttl" local results = r:pipeline({ { "HINCRBY", key, "hits", "1" }, { "EXPIRE", key, "86400" }, }) testimony.assert_equal(2, #results) testimony.assert_equal(1, results[1][1]) testimony.assert_equal(1, results[2][1]) -- EXPIRE returns 1 on success local ttl = r:cmd("TTL", key) testimony.assert_true(ttl > 0 and ttl <= 86400, "TTL should be set") end) end) testify:conclude()