You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kvrocks.apache.org by ti...@apache.org on 2022/09/21 06:19:39 UTC
[incubator-kvrocks] branch unstable updated: Move TCL test unit/scripting to Go case (#900)
This is an automated email from the ASF dual-hosted git repository.
tison pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/incubator-kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new e8721a2 Move TCL test unit/scripting to Go case (#900)
e8721a2 is described below
commit e8721a270d23345f385af6db0617c3549d6e6812
Author: tison <wa...@gmail.com>
AuthorDate: Wed Sep 21 14:19:34 2022 +0800
Move TCL test unit/scripting to Go case (#900)
---
tests/gocase/unit/info/info_test.go | 34 +-
tests/gocase/unit/scripting/scripting_test.go | 505 ++++++++++++++++++++++++++
tests/gocase/util/conditions.go | 55 +++
tests/gocase/util/constants.go | 1 +
tests/gocase/util/server.go | 10 +-
tests/tcl/tests/test_helper.tcl | 1 -
tests/tcl/tests/unit/scripting.tcl | 436 ----------------------
7 files changed, 582 insertions(+), 460 deletions(-)
diff --git a/tests/gocase/unit/info/info_test.go b/tests/gocase/unit/info/info_test.go
index 1fde8cf..8aab000 100644
--- a/tests/gocase/unit/info/info_test.go
+++ b/tests/gocase/unit/info/info_test.go
@@ -22,9 +22,7 @@ package command
import (
"context"
"fmt"
- "regexp"
"strconv"
- "strings"
"testing"
"time"
@@ -40,14 +38,6 @@ func TestInfo(t *testing.T) {
rdb := srv.NewClient()
defer func() { require.NoError(t, rdb.Close()) }()
- FindInfoEntry := func(t *testing.T, section string, key string) string {
- r := rdb.Info(ctx, section)
- p := regexp.MustCompile(fmt.Sprintf("%s:(.+)", key))
- ms := p.FindStringSubmatch(r.Val())
- require.Len(t, ms, 2)
- return strings.TrimSpace(ms[1])
- }
-
MustAtoi := func(t *testing.T, s string) int {
i, err := strconv.Atoi(s)
require.NoError(t, err)
@@ -65,21 +55,21 @@ func TestInfo(t *testing.T) {
time.Sleep(time.Second)
}
- r := FindInfoEntry(t, "rocksdb", "put_per_sec")
+ r := util.FindInfoEntry(t, ctx, rdb, "put_per_sec", "rocksdb")
require.Greater(t, MustAtoi(t, r), 0)
- r = FindInfoEntry(t, "rocksdb", "get_per_sec")
+ r = util.FindInfoEntry(t, ctx, rdb, "get_per_sec", "rocksdb")
require.Greater(t, MustAtoi(t, r), 0)
- r = FindInfoEntry(t, "rocksdb", "seek_per_sec")
+ r = util.FindInfoEntry(t, ctx, rdb, "seek_per_sec", "rocksdb")
require.Greater(t, MustAtoi(t, r), 0)
- r = FindInfoEntry(t, "rocksdb", "next_per_sec")
+ r = util.FindInfoEntry(t, ctx, rdb, "next_per_sec", "rocksdb")
require.Greater(t, MustAtoi(t, r), 0)
})
t.Run("get bgsave information by INFO", func(t *testing.T) {
- require.Equal(t, "0", FindInfoEntry(t, "persistence", "bgsave_in_progress"))
- require.Equal(t, "-1", FindInfoEntry(t, "persistence", "last_bgsave_time"))
- require.Equal(t, "ok", FindInfoEntry(t, "persistence", "last_bgsave_status"))
- require.Equal(t, "-1", FindInfoEntry(t, "persistence", "last_bgsave_time_sec"))
+ require.Equal(t, "0", util.FindInfoEntry(t, ctx, rdb, "bgsave_in_progress", "persistence"))
+ require.Equal(t, "-1", util.FindInfoEntry(t, ctx, rdb, "last_bgsave_time", "persistence"))
+ require.Equal(t, "ok", util.FindInfoEntry(t, ctx, rdb, "last_bgsave_status", "persistence"))
+ require.Equal(t, "-1", util.FindInfoEntry(t, ctx, rdb, "last_bgsave_time_sec", "persistence"))
r := rdb.Do(ctx, "bgsave")
v, err := r.Text()
@@ -87,14 +77,14 @@ func TestInfo(t *testing.T) {
require.Equal(t, "OK", v)
require.Eventually(t, func() bool {
- e := MustAtoi(t, FindInfoEntry(t, "persistence", "bgsave_in_progress"))
+ e := MustAtoi(t, util.FindInfoEntry(t, ctx, rdb, "bgsave_in_progress", "persistence"))
return e == 0
}, 5*time.Second, 100*time.Millisecond)
- lastBgsaveTime := MustAtoi(t, FindInfoEntry(t, "persistence", "last_bgsave_time"))
+ lastBgsaveTime := MustAtoi(t, util.FindInfoEntry(t, ctx, rdb, "last_bgsave_time", "persistence"))
require.Greater(t, lastBgsaveTime, 1640507660)
- require.Equal(t, "ok", FindInfoEntry(t, "persistence", "last_bgsave_status"))
- lastBgsaveTimeSec := MustAtoi(t, FindInfoEntry(t, "persistence", "last_bgsave_time_sec"))
+ require.Equal(t, "ok", util.FindInfoEntry(t, ctx, rdb, "last_bgsave_status", "persistence"))
+ lastBgsaveTimeSec := MustAtoi(t, util.FindInfoEntry(t, ctx, rdb, "last_bgsave_time_sec", "persistence"))
require.GreaterOrEqual(t, lastBgsaveTimeSec, 0)
require.Less(t, lastBgsaveTimeSec, 3)
})
diff --git a/tests/gocase/unit/scripting/scripting_test.go b/tests/gocase/unit/scripting/scripting_test.go
new file mode 100644
index 0000000..c5e7aba
--- /dev/null
+++ b/tests/gocase/unit/scripting/scripting_test.go
@@ -0,0 +1,505 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package scripting
+
+import (
+ "context"
+ "fmt"
+ "testing"
+
+ "github.com/apache/incubator-kvrocks/tests/gocase/util"
+ "github.com/stretchr/testify/require"
+)
+
+func TestScripting(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ t.Run("EVAL - Does Lua interpreter replies to our requests?", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return 'hello'`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "hello", r.Val())
+ })
+
+ t.Run("EVAL - Lua integer -> Redis protocol type conversion", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return 100.5`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, int64(100), r.Val())
+ })
+
+ t.Run("EVAL - Lua string -> Redis protocol type conversion", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return 'hello world'`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "hello world", r.Val())
+ })
+
+ t.Run("EVAL - Lua true boolean -> Redis protocol type conversion", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return true`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, int64(1), r.Val())
+ })
+
+ t.Run("EVAL - Lua false boolean -> Redis protocol type conversion", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return false`, []string{})
+ require.EqualError(t, r.Err(), util.ErrRedisNil)
+ require.Nil(t, r.Val())
+ })
+
+ t.Run("EVAL - Lua status code reply -> Redis protocol type conversion", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return {ok='fine'}`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "fine", r.Val())
+ })
+
+ t.Run("EVAL - Lua error reply -> Redis protocol type conversion", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return {err='this is an error'}`, []string{})
+ require.EqualError(t, r.Err(), "this is an error")
+ require.Nil(t, r.Val())
+ })
+
+ t.Run("Script return recursive object", func(t *testing.T) {
+ c := srv.NewTCPClient()
+ defer func() { require.NoError(t, c.Close()) }()
+ require.NoError(t, c.WriteArgs("EVAL", `return "hello"`, "0"))
+ r, err := c.ReadLine()
+ require.NoError(t, err)
+ require.Equal(t, "$5", r)
+ r, err = c.ReadLine()
+ require.NoError(t, err)
+ require.Equal(t, "hello", r)
+ })
+
+ t.Run("EVAL - Lua table -> Redis protocol type conversion", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return {1,2,3,'ciao',{1,2}}`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[1 2 3 ciao [1 2]]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - Are the KEYS and ARGV arrays populated correctly?", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}`, []string{"a", "b"}, "c", "d")
+ require.NoError(t, r.Err())
+ require.Equal(t, "[a b c d]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - is Lua able to call Redis API?", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "mykey", "myval", 0).Err())
+ r := rdb.Eval(ctx, `return redis.call('get',KEYS[1])`, []string{"mykey"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "myval", r.Val())
+ })
+
+ t.Run("EVALSHA - Can we call a SHA1 if already defined?", func(t *testing.T) {
+ r := rdb.EvalSha(ctx, "fd758d1589d044dd850a6f05d52f2eefd27f033f", []string{"mykey"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "myval", r.Val())
+ })
+
+ t.Run("EVALSHA - Can we call a SHA1 in uppercase?", func(t *testing.T) {
+ r := rdb.EvalSha(ctx, "FD758D1589D044DD850A6F05D52F2EEFD27F033F", []string{"mykey"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "myval", r.Val())
+ })
+
+ t.Run("EVALSHA - Do we get an error on invalid SHA1?", func(t *testing.T) {
+ r := rdb.EvalSha(ctx, "NotValidShaSUM", []string{})
+ util.ErrorRegexp(t, r.Err(), "ERR NOSCRIPT.*")
+ require.Nil(t, r.Val())
+ })
+
+ t.Run("EVALSHA - Do we get an error on non defined SHA1?", func(t *testing.T) {
+ r := rdb.EvalSha(ctx, "ffd632c7d33e571e9f24556ebed26c3479a87130", []string{})
+ util.ErrorRegexp(t, r.Err(), "ERR NOSCRIPT.*")
+ require.Nil(t, r.Val())
+ })
+
+ t.Run("EVAL - Redis integer -> Lua type conversion", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "x", 0, 0).Err())
+ r := rdb.Eval(ctx, `
+local foo = redis.pcall('incr',KEYS[1])
+return {type(foo),foo}
+`, []string{"x"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[number 1]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - Redis bulk -> Lua type conversion", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "mykey", "myval", 0).Err())
+ r := rdb.Eval(ctx, `
+local foo = redis.pcall('get',KEYS[1])
+return {type(foo),foo}
+`, []string{"mykey"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[string myval]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - Redis multi bulk -> Lua type conversion", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "mylist").Err())
+ require.NoError(t, rdb.RPush(ctx, "mylist", "a", "b", "c").Err())
+ r := rdb.Eval(ctx, `
+local foo = redis.pcall('lrange',KEYS[1],0,-1)
+return {type(foo),foo[1],foo[2],foo[3],# foo}
+`, []string{"mylist"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[table a b c 3]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - Redis status reply -> Lua type conversion", func(t *testing.T) {
+ r := rdb.Eval(ctx, `
+local foo = redis.pcall('set',KEYS[1],'myval')
+return {type(foo),foo['ok']}
+`, []string{"mykey"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[table OK]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - Redis error reply -> Lua type conversion", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "mykey", "myval", 0).Err())
+ r := rdb.Eval(ctx, `
+local foo = redis.pcall('incr',KEYS[1])
+return {type(foo),foo['err']}
+`, []string{"mykey"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[table Invalid argument: value is not an integer or out of range]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - Redis nil bulk reply -> Lua type conversion", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "mykey").Err())
+ r := rdb.Eval(ctx, `
+local foo = redis.pcall('get',KEYS[1])
+return {type(foo),foo == false}
+`, []string{"mykey"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[boolean 1]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - Scripts can't run certain commands", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return redis.pcall('blpop','x',0)`, []string{})
+ require.ErrorContains(t, r.Err(), "not allowed")
+ })
+
+ t.Run("EVAL - Scripts can run certain commands", func(t *testing.T) {
+ r := rdb.Eval(ctx, `redis.pcall('randomkey'); return redis.pcall('set','x','ciao')`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "OK", r.Val())
+ })
+
+ t.Run("EVAL - No arguments to redis.call/pcall is considered an error", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return redis.call()`, []string{})
+ require.ErrorContains(t, r.Err(), "one argument")
+ })
+
+ t.Run("EVAL - redis.call variant raises a Lua error on Redis cmd error", func(t *testing.T) {
+ r := rdb.Eval(ctx, `redis.call('nosuchcommand')`, []string{})
+ require.ErrorContains(t, r.Err(), "Unknown Redis")
+ r = rdb.Eval(ctx, `redis.call('get','a','b','c')`, []string{})
+ require.ErrorContains(t, r.Err(), "number of args")
+ require.NoError(t, rdb.Set(ctx, "foo", "bar", 0).Err())
+ r = rdb.Eval(ctx, `redis.call('lpush',KEYS[1],'val')`, []string{"foo"})
+ require.ErrorContains(t, r.Err(), "against a key")
+ })
+
+ t.Run("EVAL - JSON numeric decoding", func(t *testing.T) {
+ r := rdb.Eval(ctx, `
+return
+ table.concat(
+ cjson.decode(
+ "[0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10]"), " ")
+`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "0 -5000 -1 0.0003 1023.2 0", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - JSON string decoding", func(t *testing.T) {
+ r := rdb.Eval(ctx, `
+local decoded = cjson.decode('{"keya": "a", "keyb": "b"}')
+return {decoded.keya, decoded.keyb}
+`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[a b]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - cmsgpack can pack double?", func(t *testing.T) {
+ r := rdb.Eval(ctx, `
+local encoded = cmsgpack.pack(0.1)
+local h = ""
+for i = 1, #encoded do
+ h = h .. string.format("%02x",string.byte(encoded,i))
+end
+return h
+`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "cb3fb999999999999a", r.Val())
+ })
+
+ t.Run("EVAL - cmsgpack can pack negative int64?", func(t *testing.T) {
+ r := rdb.Eval(ctx, `
+local encoded = cmsgpack.pack(-1099511627776)
+local h = ""
+for i = 1, #encoded do
+ h = h .. string.format("%02x",string.byte(encoded,i))
+end
+return h
+`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "d3ffffff0000000000", r.Val())
+ })
+
+ t.Run("EVAL - cmsgpack can pack and unpack circular references?", func(t *testing.T) {
+ r := rdb.Eval(ctx, `
+local a = {x=nil,y=5}
+local b = {x=a}
+a['x'] = b
+local encoded = cmsgpack.pack(a)
+local h = ""
+-- cmsgpack encodes to a depth of 16, but can't encode
+-- references, so the encoded object has a deep copy recursive
+-- depth of 16.
+for i = 1, #encoded do
+ h = h .. string.format("%02x",string.byte(encoded,i))
+end
+-- when unpacked, re.x.x != re because the unpack creates
+-- individual tables down to a depth of 16.
+-- (that's why the encoded output is so large)
+local re = cmsgpack.unpack(encoded)
+assert(re)
+assert(re.x)
+assert(re.x.x.y == re.y)
+assert(re.x.x.x.x.y == re.y)
+assert(re.x.x.x.x.x.x.y == re.y)
+assert(re.x.x.x.x.x.x.x.x.x.x.y == re.y)
+-- maximum working depth:
+assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.y == re.y)
+-- now the last x would be b above and has no y
+assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x)
+-- so, the final x.x is at the depth limit and was assigned nil
+assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x == nil)
+assert(h == "82a17881a17882a17881a17882a17881a17882a17881a17882a17881a17882a17881a17882a17881a17882a17881a178c0a17905a17905a17905a17905a17905a17905a17905a17905" or h == "82a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a178c0")
+return {re.x.x.x.x.x.x.x.x.y == re.y, re.y == 5}
+`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[1 1]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("EVAL - Numerical sanity check from bitop", func(t *testing.T) {
+ r := rdb.Eval(ctx, `
+assert(0x7fffffff == 2147483647, "broken hex literals");
+assert(0xffffffff == -1 or 0xffffffff == 2^32-1,
+ "broken hex literals");
+assert(tostring(-1) == "-1", "broken tostring()");
+assert(tostring(0xffffffff) == "-1" or
+ tostring(0xffffffff) == "4294967295",
+ "broken tostring()")
+`, []string{})
+ require.EqualError(t, r.Err(), util.ErrRedisNil)
+ require.Nil(t, r.Val())
+ })
+
+ t.Run("EVAL - Verify minimal bitop functionality", func(t *testing.T) {
+ r := rdb.Eval(ctx, `
+assert(bit.tobit(1) == 1);
+assert(bit.band(1) == 1);
+assert(bit.bxor(1,2) == 3);
+assert(bit.bor(1,2,4,8,16,32,64,128) == 255)
+`, []string{})
+ require.EqualError(t, r.Err(), util.ErrRedisNil)
+ require.Nil(t, r.Val())
+ })
+
+ t.Run("EVAL - Able to parse trailing comments", func(t *testing.T) {
+ r := rdb.Eval(ctx, `return 'hello' --trailing comment`, []string{})
+ require.NoError(t, r.Err())
+ require.Equal(t, "hello", r.Val())
+ })
+
+ t.Run("EVAL does not leak in the Lua stack", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "x", 0, 0).Err())
+
+ // use a non-blocking client to speed up the loop.
+ c := srv.NewTCPClient()
+ defer func() { require.NoError(t, c.Close()) }()
+
+ for i := 0; i < 10000; i++ {
+ require.NoError(t, c.WriteArgs("EVAL", `return redis.call("incr",KEYS[1])`, "1", "x"))
+ }
+ for i := 0; i < 10000; i++ {
+ _, err := c.ReadLine()
+ require.NoError(t, err)
+ }
+
+ require.EqualValues(t, "10000", rdb.Get(ctx, "x").Val())
+ })
+
+ t.Run("SCRIPTING FLUSH - is able to clear the scripts cache?", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "mykey", "myval", 0).Err())
+ r := rdb.EvalSha(ctx, "fd758d1589d044dd850a6f05d52f2eefd27f033f", []string{"mykey"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "myval", r.Val())
+ require.NoError(t, rdb.ScriptFlush(ctx).Err())
+ r = rdb.EvalSha(ctx, "fd758d1589d044dd850a6f05d52f2eefd27f033f", []string{"mykey"})
+ util.ErrorRegexp(t, r.Err(), "ERR NOSCRIPT.*")
+ })
+
+ t.Run("SCRIPT EXISTS - can detect already defined scripts?", func(t *testing.T) {
+ r1 := rdb.Eval(ctx, "return 1+1", []string{})
+ require.NoError(t, r1.Err())
+ require.Equal(t, int64(2), r1.Val())
+ r2 := rdb.ScriptExists(ctx, "a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bd9", "a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bda")
+ require.NoError(t, r2.Err())
+ require.Equal(t, []bool{true, false}, r2.Val())
+ })
+
+ t.Run("SCRIPT LOAD - is able to register scripts in the scripting cache", func(t *testing.T) {
+ r1 := rdb.ScriptLoad(ctx, "return 'loaded'")
+ require.NoError(t, r1.Err())
+ require.Equal(t, "b534286061d4b9e4026607613b95c06c06015ae8", r1.Val())
+ r2 := rdb.EvalSha(ctx, "b534286061d4b9e4026607613b95c06c06015ae8", []string{})
+ require.NoError(t, r2.Err())
+ require.Equal(t, "loaded", r2.Val())
+ })
+
+ t.Run("Globals protection reading an undeclared global variable", func(t *testing.T) {
+ r2 := rdb.Eval(ctx, `return a`, []string{})
+ util.ErrorRegexp(t, r2.Err(), ".*ERR.*attempted to access .* global.*")
+ })
+
+ t.Run("Globals protection setting an undeclared global variable", func(t *testing.T) {
+ r2 := rdb.Eval(ctx, `a=10`, []string{})
+ util.ErrorRegexp(t, r2.Err(), ".*ERR.*attempted to create global.*")
+ })
+
+ t.Run("Test an example script DECR_IF_GT", func(t *testing.T) {
+ scriptDecrIfGt := `
+local current
+
+current = redis.call('get',KEYS[1])
+if not current then return nil end
+if current > ARGV[1] then
+ return redis.call('decr',KEYS[1])
+else
+ return redis.call('get',KEYS[1])
+end
+`
+ require.NoError(t, rdb.Set(ctx, "foo", 5, 0).Err())
+ r := rdb.Eval(ctx, scriptDecrIfGt, []string{"foo"}, 2)
+ require.NoError(t, r.Err())
+ require.Equal(t, int64(4), r.Val())
+ r = rdb.Eval(ctx, scriptDecrIfGt, []string{"foo"}, 2)
+ require.NoError(t, r.Err())
+ require.Equal(t, int64(3), r.Val())
+ r = rdb.Eval(ctx, scriptDecrIfGt, []string{"foo"}, 2)
+ require.NoError(t, r.Err())
+ require.Equal(t, int64(2), r.Val())
+ r = rdb.Eval(ctx, scriptDecrIfGt, []string{"foo"}, 2)
+ require.NoError(t, r.Err())
+ require.Equal(t, "2", r.Val())
+ r = rdb.Eval(ctx, scriptDecrIfGt, []string{"foo"}, 2)
+ require.NoError(t, r.Err())
+ require.Equal(t, "2", r.Val())
+ })
+
+ t.Run("Scripting engine PRNG can be seeded correctly", func(t *testing.T) {
+ rand1 := rdb.Eval(ctx, `
+math.randomseed(ARGV[1]); return tostring(math.random())
+`, []string{}, 10).Val()
+ rand2 := rdb.Eval(ctx, `
+math.randomseed(ARGV[1]); return tostring(math.random())
+`, []string{}, 10).Val()
+ rand3 := rdb.Eval(ctx, `
+math.randomseed(ARGV[1]); return tostring(math.random())
+`, []string{}, 20).Val()
+ require.Equal(t, rand1, rand2)
+ require.NotEqual(t, rand2, rand3)
+ })
+
+ t.Run("In the context of Lua the output of random commands gets ordered", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "myset").Err())
+ require.NoError(t, rdb.SAdd(ctx, "myset", "a", "b", "c", "d", "e", "f", "g", "h", "i", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "z", "aa", "aaa", "azz").Err())
+ r := rdb.Eval(ctx, `return redis.call('smembers',KEYS[1])`, []string{"myset"})
+ require.NoError(t, r.Err())
+ require.Equal(t, "[a aa aaa azz b c d e f g h i l m n o p q r s t u v z]", fmt.Sprintf("%v", r.Val()))
+ })
+
+ t.Run("Make sure redis.log() works", func(t *testing.T) {
+ require.EqualError(t, rdb.Eval(ctx, `return redis.log(redis.LOG_DEBUG, 'debug level');`, []string{}).Err(), util.ErrRedisNil)
+ require.EqualError(t, rdb.Eval(ctx, `return redis.log(redis.LOG_VERBOSE, 'debug level');`, []string{}).Err(), util.ErrRedisNil)
+ require.EqualError(t, rdb.Eval(ctx, `return redis.log(redis.LOG_NOTICE, 'debug level');`, []string{}).Err(), util.ErrRedisNil)
+ require.EqualError(t, rdb.Eval(ctx, `return redis.log(redis.LOG_WARNING, 'debug level');`, []string{}).Err(), util.ErrRedisNil)
+ })
+
+ t.Run("EVAL_RO - successful case", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "foo", "bar", 0).Err())
+ r := rdb.Do(ctx, "EVAL_RO", `return redis.call('get', KEYS[1]);`, "1", "foo")
+ require.NoError(t, r.Err())
+ require.Equal(t, "bar", r.Val())
+ })
+
+ t.Run("EVALSHA_RO - successful case", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "foo", "bar", 0).Err())
+ r := rdb.Do(ctx, "EVALSHA_RO", "796941151549c416aa77522fb347487236c05e46", "1", "foo")
+ require.NoError(t, r.Err())
+ require.Equal(t, "bar", r.Val())
+ })
+
+ t.Run("EVAL_RO - cannot run write commands", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "foo", "bar", 0).Err())
+ r := rdb.Do(ctx, "EVAL_RO", `redis.call('del', KEYS[1]);`, "1", "foo")
+ util.ErrorRegexp(t, r.Err(), "ERR .* Write commands are not allowed from read-only scripts")
+ })
+
+ t.Run("EVALSHA_RO - cannot run write commands", func(t *testing.T) {
+ require.NoError(t, rdb.Set(ctx, "foo", "bar", 0).Err())
+ r := rdb.Do(ctx, "EVALSHA_RO", "a1e63e1cd1bd1d5413851949332cfb9da4ee6dc0", "1", "foo")
+ util.ErrorRegexp(t, r.Err(), "ERR .* Write commands are not allowed from read-only scripts")
+ })
+}
+
+func TestScriptingMasterSlave(t *testing.T) {
+ master := util.StartServer(t, map[string]string{})
+ defer master.Close()
+ masterClient := master.NewClient()
+ defer func() { require.NoError(t, masterClient.Close()) }()
+
+ slave := util.StartServer(t, map[string]string{})
+ defer slave.Close()
+ slaveClient := slave.NewClient()
+ defer func() { require.NoError(t, slaveClient.Close()) }()
+
+ ctx := context.Background()
+
+ require.NoError(t, slaveClient.SlaveOf(ctx, master.Host(), fmt.Sprintf("%d", master.Port())).Err())
+ util.WaitForSync(t, ctx, slaveClient)
+
+ t.Run("SCRIPTING: script load on master, read on slave", func(t *testing.T) {
+ sha := masterClient.ScriptLoad(ctx, `return 'script loaded'`).Val()
+ require.Equal(t, "4167ea82ed9c381c7659f7cf93f394219147e8c4", sha)
+ util.WaitForOffsetSync(t, ctx, masterClient, slaveClient)
+ require.Equal(t, []bool{true}, masterClient.ScriptExists(ctx, sha).Val())
+ require.Equal(t, []bool{true}, slaveClient.ScriptExists(ctx, sha).Val())
+
+ require.NoError(t, masterClient.ScriptFlush(ctx).Err())
+ util.WaitForOffsetSync(t, ctx, masterClient, slaveClient)
+ require.Equal(t, []bool{false}, masterClient.ScriptExists(ctx, sha).Val())
+ require.Equal(t, []bool{false}, slaveClient.ScriptExists(ctx, sha).Val())
+ })
+}
diff --git a/tests/gocase/util/conditions.go b/tests/gocase/util/conditions.go
new file mode 100644
index 0000000..d6ac7a8
--- /dev/null
+++ b/tests/gocase/util/conditions.go
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package util
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/go-redis/redis/v9"
+ "github.com/stretchr/testify/require"
+)
+
+func FindInfoEntry(t *testing.T, ctx context.Context, rdb *redis.Client, key string, section ...string) string {
+ r := rdb.Info(ctx, section...)
+ p := regexp.MustCompile(fmt.Sprintf("%s:(.+)", key))
+ ms := p.FindStringSubmatch(r.Val())
+ require.Len(t, ms, 2)
+ return strings.TrimSpace(ms[1])
+}
+
+func WaitForSync(t *testing.T, ctx context.Context, slave *redis.Client) {
+ require.Eventually(t, func() bool {
+ r := FindInfoEntry(t, ctx, slave, "master_link_status")
+ return r == "up"
+ }, 5*time.Second, 100*time.Millisecond)
+}
+
+func WaitForOffsetSync(t *testing.T, ctx context.Context, master, slave *redis.Client) {
+ require.Eventually(t, func() bool {
+ o1 := FindInfoEntry(t, ctx, master, "master_repl_offset")
+ o2 := FindInfoEntry(t, ctx, slave, "master_repl_offset")
+ return o1 == o2
+ }, 5*time.Second, 100*time.Millisecond)
+}
diff --git a/tests/gocase/util/constants.go b/tests/gocase/util/constants.go
index 9c90e7e..cdb8bf4 100644
--- a/tests/gocase/util/constants.go
+++ b/tests/gocase/util/constants.go
@@ -20,3 +20,4 @@
package util
const DefaultDelta = 0.000001
+const ErrRedisNil = "redis: nil"
diff --git a/tests/gocase/util/server.go b/tests/gocase/util/server.go
index 136e60a..10cca14 100644
--- a/tests/gocase/util/server.go
+++ b/tests/gocase/util/server.go
@@ -36,11 +36,19 @@ import (
type KvrocksServer struct {
t testing.TB
cmd *exec.Cmd
- addr net.Addr
+ addr *net.TCPAddr
clean func()
}
+func (s *KvrocksServer) Host() string {
+ return s.addr.AddrPort().Addr().String()
+}
+
+func (s *KvrocksServer) Port() uint16 {
+ return s.addr.AddrPort().Port()
+}
+
func (s *KvrocksServer) NewClient() *redis.Client {
return s.NewClientWithOption(&redis.Options{Addr: s.addr.String()})
}
diff --git a/tests/tcl/tests/test_helper.tcl b/tests/tcl/tests/test_helper.tcl
index 24fc3ad..d16a59c 100644
--- a/tests/tcl/tests/test_helper.tcl
+++ b/tests/tcl/tests/test_helper.tcl
@@ -45,7 +45,6 @@ set ::all_tests {
unit/pubsub
unit/introspection
unit/geo
- unit/scripting
integration/slotmigrate
integration/slotimport
integration/redis-cli
diff --git a/tests/tcl/tests/unit/scripting.tcl b/tests/tcl/tests/unit/scripting.tcl
deleted file mode 100644
index 41268be..0000000
--- a/tests/tcl/tests/unit/scripting.tcl
+++ /dev/null
@@ -1,436 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-# Copyright (c) 2006-2020, Salvatore Sanfilippo
-# See bundled license file licenses/LICENSE.redis for details.
-
-# This file is copied and modified from the Redis project,
-# which started out as: https://github.com/redis/redis/blob/dbcc0a8/tests/unit/scripting.tcl
-
-start_server {tags {"scripting"}} {
- test {EVAL - Does Lua interpreter replies to our requests?} {
- r eval {return 'hello'} 0
- } {hello}
- test {EVAL - Lua integer -> Redis protocol type conversion} {
- r eval {return 100.5} 0
- } {100}
-
- test {EVAL - Lua string -> Redis protocol type conversion} {
- r eval {return 'hello world'} 0
- } {hello world}
-
- test {EVAL - Lua true boolean -> Redis protocol type conversion} {
- r eval {return true} 0
- } {1}
-
- test {EVAL - Lua false boolean -> Redis protocol type conversion} {
- r eval {return false} 0
- } {}
-
- test {EVAL - Lua status code reply -> Redis protocol type conversion} {
- r eval {return {ok='fine'}} 0
- } {fine}
-
- test {EVAL - Lua error reply -> Redis protocol type conversion} {
- catch {
- r eval {return {err='this is an error'}} 0
- } e
- set _ $e
- } {this is an error}
-
- test {Script return recursive object} {
- r readraw 1
- set bulk_len [r eval {return "hello"} 0]
- set bulk [r read]
- r readraw 0
- assert_equal $bulk_len {$5}
- assert_equal $bulk hello
- } {}
-
- test {EVAL - Lua table -> Redis protocol type conversion} {
- r eval {return {1,2,3,'ciao',{1,2}}} 0
- } {1 2 3 ciao {1 2}}
-
- test {EVAL - Are the KEYS and ARGV arrays populated correctly?} {
- r eval {return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}} 2 a b c d
- } {a b c d}
-
- test {EVAL - is Lua able to call Redis API?} {
- r set mykey myval
- r eval {return redis.call('get',KEYS[1])} 1 mykey
- } {myval}
-
- test {EVALSHA - Can we call a SHA1 if already defined?} {
- r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey
- } {myval}
-
- test {EVALSHA - Can we call a SHA1 in uppercase?} {
- r evalsha FD758D1589D044DD850A6F05D52F2EEFD27F033F 1 mykey
- } {myval}
-
- test {EVALSHA - Do we get an error on invalid SHA1?} {
- catch {r evalsha NotValidShaSUM 0} e
- set _ $e
- } {ERR NOSCRIPT*}
-
- test {EVALSHA - Do we get an error on non defined SHA1?} {
- catch {r evalsha ffd632c7d33e571e9f24556ebed26c3479a87130 0} e
- set _ $e
- } {ERR NOSCRIPT*}
-
- test {EVAL - Redis integer -> Lua type conversion} {
- r set x 0
- r eval {
- local foo = redis.pcall('incr',KEYS[1])
- return {type(foo),foo}
- } 1 x
- } {number 1}
-
- test {EVAL - Redis bulk -> Lua type conversion} {
- r set mykey myval
- r eval {
- local foo = redis.pcall('get',KEYS[1])
- return {type(foo),foo}
- } 1 mykey
- } {string myval}
-
- test {EVAL - Redis integer -> Lua type conversion} {
- r set x 0
- r eval {
- local foo = redis.pcall('incr',KEYS[1])
- return {type(foo),foo}
- } 1 x
- } {number 1}
-
- test {EVAL - Redis bulk -> Lua type conversion} {
- r set mykey myval
- r eval {
- local foo = redis.pcall('get',KEYS[1])
- return {type(foo),foo}
- } 1 mykey
- } {string myval}
- test {EVAL - Redis multi bulk -> Lua type conversion} {
- r del mylist
- r rpush mylist a
- r rpush mylist b
- r rpush mylist c
- r eval {
- local foo = redis.pcall('lrange',KEYS[1],0,-1)
- return {type(foo),foo[1],foo[2],foo[3],# foo}
- } 1 mylist
- } {table a b c 3}
-
- test {EVAL - Redis status reply -> Lua type conversion} {
- r eval {
- local foo = redis.pcall('set',KEYS[1],'myval')
- return {type(foo),foo['ok']}
- } 1 mykey
- } {table OK}
-
- test {EVAL - Redis error reply -> Lua type conversion} {
- r set mykey myval
- r eval {
- local foo = redis.pcall('incr',KEYS[1])
- return {type(foo),foo['err']}
- } 1 mykey
- } {table {Invalid argument: value is not an integer or out of range}}
- test {EVAL - Redis nil bulk reply -> Lua type conversion} {
- r del mykey
- r eval {
- local foo = redis.pcall('get',KEYS[1])
- return {type(foo),foo == false}
- } 1 mykey
- } {boolean 1}
- test {EVAL - Scripts can't run certain commands} {
- set e {}
- catch {r eval {return redis.pcall('blpop','x',0)} 0} e
- set e
- } {*not allowed*}
-
- test {EVAL - Scripts can't run certain commands} {
- set e {}
- catch {
- r eval "redis.pcall('randomkey'); return redis.pcall('set','x','ciao')" 0
- } e
- set e
- } {OK}
-
- test {EVAL - No arguments to redis.call/pcall is considered an error} {
- set e {}
- catch {r eval {return redis.call()} 0} e
- set e
- } {*one argument*}
-
- test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} {
- set e {}
- catch {
- r eval "redis.call('nosuchcommand')" 0
- } e
- set e
- } {*Unknown Redis*}
- test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} {
- set e {}
- catch {
- r eval "redis.call('get','a','b','c')" 0
- } e
- set e
- } {*number of args*}
-
- test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} {
- set e {}
- r set foo bar
- catch {
- r eval {redis.call('lpush',KEYS[1],'val')} 1 foo
- } e
- set e
- } {*against a key*}
-
- test {EVAL - JSON numeric decoding} {
- # We must return the table as a string because otherwise
- # Redis converts floats to ints and we get 0 and 1023 instead
- # of 0.0003 and 1023.2 as the parsed output.
- r eval {return
- table.concat(
- cjson.decode(
- "[0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10]"), " ")
- } 0
- } {0 -5000 -1 0.0003 1023.2 0}
-
- test {EVAL - JSON string decoding} {
- r eval {local decoded = cjson.decode('{"keya": "a", "keyb": "b"}')
- return {decoded.keya, decoded.keyb}
- } 0
- } {a b}
-
- test {EVAL - cmsgpack can pack double?} {
- r eval {local encoded = cmsgpack.pack(0.1)
- local h = ""
- for i = 1, #encoded do
- h = h .. string.format("%02x",string.byte(encoded,i))
- end
- return h
- } 0
- } {cb3fb999999999999a}
-
- test {EVAL - cmsgpack can pack negative int64?} {
- r eval {local encoded = cmsgpack.pack(-1099511627776)
- local h = ""
- for i = 1, #encoded do
- h = h .. string.format("%02x",string.byte(encoded,i))
- end
- return h
- } 0
- } {d3ffffff0000000000}
- test {EVAL - cmsgpack can pack and unpack circular references?} {
- r eval {local a = {x=nil,y=5}
- local b = {x=a}
- a['x'] = b
- local encoded = cmsgpack.pack(a)
- local h = ""
- -- cmsgpack encodes to a depth of 16, but can't encode
- -- references, so the encoded object has a deep copy recursive
- -- depth of 16.
- for i = 1, #encoded do
- h = h .. string.format("%02x",string.byte(encoded,i))
- end
- -- when unpacked, re.x.x != re because the unpack creates
- -- individual tables down to a depth of 16.
- -- (that's why the encoded output is so large)
- local re = cmsgpack.unpack(encoded)
- assert(re)
- assert(re.x)
- assert(re.x.x.y == re.y)
- assert(re.x.x.x.x.y == re.y)
- assert(re.x.x.x.x.x.x.y == re.y)
- assert(re.x.x.x.x.x.x.x.x.x.x.y == re.y)
- -- maximum working depth:
- assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.y == re.y)
- -- now the last x would be b above and has no y
- assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x)
- -- so, the final x.x is at the depth limit and was assigned nil
- assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x == nil)
- assert(h == "82a17881a17882a17881a17882a17881a17882a17881a17882a17881a17882a17881a17882a17881a17882a17881a178c0a17905a17905a17905a17905a17905a17905a17905a17905" or h == "82a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a178c0")
- return {re.x.x.x.x.x.x.x.x.y == re.y, re.y == 5}
- } 0
- } {1 1}
- test {EVAL - Numerical sanity check from bitop} {
- r eval {assert(0x7fffffff == 2147483647, "broken hex literals");
- assert(0xffffffff == -1 or 0xffffffff == 2^32-1,
- "broken hex literals");
- assert(tostring(-1) == "-1", "broken tostring()");
- assert(tostring(0xffffffff) == "-1" or
- tostring(0xffffffff) == "4294967295",
- "broken tostring()")
- } 0
- } {}
-
- test {EVAL - Verify minimal bitop functionality} {
- r eval {assert(bit.tobit(1) == 1);
- assert(bit.band(1) == 1);
- assert(bit.bxor(1,2) == 3);
- assert(bit.bor(1,2,4,8,16,32,64,128) == 255)
- } 0
- } {}
-
- test {EVAL - Able to parse trailing comments} {
- r eval {return 'hello' --trailing comment} 0
- } {hello}
-
- test {EVAL does not leak in the Lua stack} {
- r set x 0
- # Use a non blocking client to speedup the loop.
- set rd [redis_deferring_client]
- for {set j 0} {$j < 10000} {incr j} {
- $rd eval {return redis.call("incr",KEYS[1])} 1 x
- }
- for {set j 0} {$j < 10000} {incr j} {
- $rd read
- }
- assert {[s used_memory_lua] < 1024*100}
- $rd close
- r get x
- } {10000}
-
- test {SCRIPTING FLUSH - is able to clear the scripts cache?} {
- r set mykey myval
- set v [r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey]
- assert_equal $v myval
- set e ""
- r script flush
- catch {r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey} e
- set e
- } {ERR NOSCRIPT*}
-
- test {SCRIPT EXISTS - can detect already defined scripts?} {
- r eval "return 1+1" 0
- r script exists a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bd9 a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bda
- } {1 0}
-
- test {SCRIPT LOAD - is able to register scripts in the scripting cache} {
- list \
- [r script load "return 'loaded'"] \
- [r evalsha b534286061d4b9e4026607613b95c06c06015ae8 0]
- } {b534286061d4b9e4026607613b95c06c06015ae8 loaded}
-
- test {Globals protection reading an undeclared global variable} {
- catch {r eval {return a} 0} e
- set e
- } {*ERR*attempted to access * global*}
-
- test {Globals protection setting an undeclared global*} {
- catch {r eval {a=10} 0} e
- set e
- } {*ERR*attempted to create global*}
-
- test {Test an example script DECR_IF_GT} {
- set decr_if_gt {
- local current
-
- current = redis.call('get',KEYS[1])
- if not current then return nil end
- if current > ARGV[1] then
- return redis.call('decr',KEYS[1])
- else
- return redis.call('get',KEYS[1])
- end
- }
- r set foo 5
- set res {}
- lappend res [r eval $decr_if_gt 1 foo 2]
- lappend res [r eval $decr_if_gt 1 foo 2]
- lappend res [r eval $decr_if_gt 1 foo 2]
- lappend res [r eval $decr_if_gt 1 foo 2]
- lappend res [r eval $decr_if_gt 1 foo 2]
- set res
- } {4 3 2 2 2}
-
- test {Scripting engine PRNG can be seeded correctly} {
- set rand1 [r eval {
- math.randomseed(ARGV[1]); return tostring(math.random())
- } 0 10]
- set rand2 [r eval {
- math.randomseed(ARGV[1]); return tostring(math.random())
- } 0 10]
- set rand3 [r eval {
- math.randomseed(ARGV[1]); return tostring(math.random())
- } 0 20]
- assert_equal $rand1 $rand2
- assert {$rand2 ne $rand3}
- }
- test "In the context of Lua the output of random commands gets ordered" {
- r del myset
- r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz
- r eval {return redis.call('smembers',KEYS[1])} 1 myset
- } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}
-
- test "Make sure redis.log() works" {
- set v [r eval { return redis.log(redis.LOG_DEBUG, 'debug level'); } 0]
- assert_equal "" $v
- set v [r eval { return redis.log(redis.LOG_VERBOSE, 'verbose level'); } 0]
- assert_equal "" $v
- set v [r eval { return redis.log(redis.LOG_NOTICE, 'notice level'); } 0]
- assert_equal "" $v
- set v [r eval { return redis.log(redis.LOG_WARNING, 'warning level'); } 0]
- assert_equal "" $v
- } {}
-
- test {EVAL_RO - Successful case} {
- r set foo bar
- assert_equal bar [r eval_ro {return redis.call('get', KEYS[1]);} 1 foo]
- }
-
- test {EVALSHA_RO - Successful case} {
- r set foo bar
- assert_equal bar [r evalsha_ro 796941151549c416aa77522fb347487236c05e46 1 foo]
- }
-
- test {EVAL_RO - Cannot run write commands} {
- r set foo bar
- catch {r eval_ro {redis.call('del', KEYS[1]);} 1 foo} e
- set e
- } {ERR * Write commands are not allowed from read-only scripts}
-
- test {EVALSHA_RO - Cannot run write commands} {
- r set foo bar
- catch {r evalsha_ro a1e63e1cd1bd1d5413851949332cfb9da4ee6dc0 1 foo} e
- set e
- } {ERR * Write commands are not allowed from read-only scripts}
-}
-
-start_server {tags {"repl"}} {
- start_server {} {
- set master [srv -1 client]
- set master_host [srv -1 host]
- set master_port [srv -1 port]
- set slave [srv 0 client]
-
- $slave slaveof $master_host $master_port
- wait_for_sync $slave
-
- test {SCRIPTING: script load on master, read on slave} {
- set sha [$master script load "return 'script loaded'"]
- assert_equal 4167ea82ed9c381c7659f7cf93f394219147e8c4 $sha
- wait_for_ofs_sync $master $slave
- assert_equal 1 [$master script exists $sha]
- assert_equal 1 [$slave script exists $sha]
-
- $master script flush
- wait_for_ofs_sync $master $slave
- assert_equal 0 [$slave script exists $sha]
- }
- }
-}