Skip to content

Commit

Permalink
refactor: write integration tests in Golang (#806)
Browse files Browse the repository at this point in the history
Signed-off-by: tison <wander4096@gmail.com>
  • Loading branch information
tisonkun authored Sep 3, 2022
1 parent 2cb5190 commit c9d175a
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 88 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/kvrocks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,27 @@ jobs:
export ${{ matrix.runtime_env_vars }}
./build/unittest
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: 1.19.x

- name: Run Go Integration Cases
working-directory: tests/gocase
run: |
export ${{ matrix.runtime_env_vars }}
export KVROCKS_BIN_PATH="$GITHUB_WORKSPACE/build/kvrocks"
export GO_CASE_WORKSPACE="$GITHUB_WORKSPACE/tests/gocase/workspace"
go test -v ./...
SANITIZER_OUTPUT=$(grep "Sanitizer:" workspace -r || true)
if [[ $SANITIZER_OUTPUT ]]; then
echo "$SANITIZER_OUTPUT"
echo "\ndetail reports:\n"
cat $(find workspace -iname stderr)
echo "sanitizer error was reported, exiting..."
exit 1
fi
- name: Run Redis Tcl Test
run: |
export ${{ matrix.runtime_env_vars }}
Expand Down
16 changes: 16 additions & 0 deletions tests/gocase/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module github.com/apache/incubator-kvrocks/tests/gocase

go 1.19

require (
github.com/go-redis/redis/v9 v9.0.0-beta.2
github.com/stretchr/testify v1.8.0
)

require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
30 changes: 30 additions & 0 deletions tests/gocase/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/go-redis/redis/v9 v9.0.0-beta.2 h1:ZSr84TsnQyKMAg8gnV+oawuQezeJR11/09THcWCQzr4=
github.com/go-redis/redis/v9 v9.0.0-beta.2/go.mod h1:Bldcd/M/bm9HbnNPi/LUtYBSD8ttcZYBMupwMXhdU0o=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
77 changes: 77 additions & 0 deletions tests/gocase/unit/command/command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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 command

import (
"context"
"testing"

"github.com/apache/incubator-kvrocks/tests/gocase/util"
"github.com/stretchr/testify/require"
)

func TestCommand(t *testing.T) {
srv, err := util.StartServer(t, map[string]string{})
require.NoError(t, err)
defer srv.Close()

ctx := context.Background()
rdb := srv.NewClient()
defer func() { require.NoError(t, rdb.Close()) }()

t.Run("Kvrocks supports 180 commands currently", func(t *testing.T) {
r := rdb.Do(ctx, "COMMAND", "COUNT")
v, err := r.Int()
require.NoError(t, err)
require.Equal(t, 180, v)
})

t.Run("acquire GET command info by COMMAND INFO", func(t *testing.T) {
r := rdb.Do(ctx, "COMMAND", "INFO", "GET")
vs, err := r.Slice()
require.NoError(t, err)
require.Len(t, vs, 1)
v := vs[0].([]interface{})
require.Len(t, v, 6)
require.Equal(t, "get", v[0])
require.EqualValues(t, 2, v[1])
require.Equal(t, []interface{}{"readonly"}, v[2])
require.EqualValues(t, 1, v[3])
require.EqualValues(t, 1, v[4])
require.EqualValues(t, 1, v[5])
})

t.Run("command entry length check", func(t *testing.T) {
r := rdb.Do(ctx, "COMMAND")
vs, err := r.Slice()
require.NoError(t, err)
v := vs[0].([]interface{})
require.Len(t, v, 6)
})

t.Run("get keys of commands by COMMAND GETKEYS", func(t *testing.T) {
r := rdb.Do(ctx, "COMMAND", "GETKEYS", "GET", "test")
vs, err := r.Slice()
require.NoError(t, err)
require.Len(t, vs, 1)
require.Equal(t, "test", vs[0])
})
}
103 changes: 103 additions & 0 deletions tests/gocase/unit/info/info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* 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 command

import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
"testing"
"time"

"github.com/apache/incubator-kvrocks/tests/gocase/util"
"github.com/stretchr/testify/require"
)

func TestInfo(t *testing.T) {
srv, err := util.StartServer(t, map[string]string{})
require.NoError(t, err)
defer srv.Close()

ctx := context.Background()
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)
return i
}

t.Run("get rocksdb ops by INFO", func(t *testing.T) {
for i := 0; i < 2; i++ {
k := fmt.Sprintf("key%d", i)
v := fmt.Sprintf("value%d", i)
for j := 0; j < 500; j++ {
rdb.LPush(ctx, k, v)
rdb.LRange(ctx, k, 0, 1)
}
time.Sleep(time.Second)
}

r := FindInfoEntry(t, "rocksdb", "put_per_sec")
require.Greater(t, MustAtoi(t, r), 0)
r = FindInfoEntry(t, "rocksdb", "get_per_sec")
require.Greater(t, MustAtoi(t, r), 0)
r = FindInfoEntry(t, "rocksdb", "seek_per_sec")
require.Greater(t, MustAtoi(t, r), 0)
r = FindInfoEntry(t, "rocksdb", "next_per_sec")
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"))

r := rdb.Do(ctx, "bgsave")
v, err := r.Text()
require.NoError(t, err)
require.Equal(t, "OK", v)

require.Eventually(t, func() bool {
e := MustAtoi(t, FindInfoEntry(t, "persistence", "bgsave_in_progress"))
return e == 0
}, 5*time.Second, 100*time.Millisecond)

lastBgsaveTime := MustAtoi(t, FindInfoEntry(t, "persistence", "last_bgsave_time"))
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.GreaterOrEqual(t, lastBgsaveTimeSec, 0)
require.Less(t, lastBgsaveTimeSec, 3)
})
}
122 changes: 122 additions & 0 deletions tests/gocase/util/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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"
"net"
"os"
"os/exec"
"path/filepath"
"testing"
"time"

"github.com/go-redis/redis/v9"
"github.com/stretchr/testify/require"
)

type KvrocksServer struct {
t *testing.T
cmd *exec.Cmd
addr net.Addr

clean func()
}

func (s *KvrocksServer) NewClient() *redis.Client {
return redis.NewClient(&redis.Options{Addr: s.addr.String()})
}

func (s *KvrocksServer) Close() {
require.NoError(s.t, s.cmd.Process.Kill())
require.EqualError(s.t, s.cmd.Wait(), "signal: killed")
s.clean()
}

func StartServer(t *testing.T, configs map[string]string) (*KvrocksServer, error) {
b := os.Getenv("KVROCKS_BIN_PATH")
cmd := exec.Command(b)

addr, err := findFreePort()
if err != nil {
return nil, err
}
configs["bind"] = addr.IP.String()
configs["port"] = fmt.Sprintf("%d", addr.Port)

dir := os.Getenv("GO_CASE_WORKSPACE")
require.NoError(t, err)
dir, err = os.MkdirTemp(dir, "Server-*")
require.NoError(t, err)
configs["dir"] = dir

f, err := os.Create(filepath.Join(dir, "kvrocks.conf"))
if err != nil {
return nil, err
}

for k := range configs {
_, err := f.WriteString(fmt.Sprintf("%s %s\n", k, configs[k]))
if err != nil {
return nil, err
}
}

cmd.Args = append(cmd.Args, "-c", f.Name())

stdout, err := os.Create(filepath.Join(dir, "stdout"))
require.NoError(t, err)
cmd.Stdout = stdout
stderr, err := os.Create(filepath.Join(dir, "stderr"))
require.NoError(t, err)
cmd.Stderr = stderr

if err := cmd.Start(); err != nil {
return nil, err
}

c := redis.NewClient(&redis.Options{Addr: addr.String()})
defer func() { require.NoError(t, c.Close()) }()
require.Eventually(t, func() bool {
return c.Ping(context.Background()).Err() == nil
}, time.Minute, time.Second)

return &KvrocksServer{
t: t,
cmd: cmd,
addr: addr,
clean: func() {
require.NoError(t, stdout.Close())
require.NoError(t, stderr.Close())
},
}, nil
}

func findFreePort() (*net.TCPAddr, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
return nil, err
}
lis, err := net.ListenTCP("tcp", addr)
defer func() { _ = lis.Close() }()
return lis.Addr().(*net.TCPAddr), nil
}
1 change: 1 addition & 0 deletions tests/gocase/workspace/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*
1 change: 0 additions & 1 deletion tests/tcl/tests/test_helper.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ set ::all_tests {
unit/introspection
unit/limits
unit/geo
unit/command
unit/config
unit/scripting
integration/slotmigrate
Expand Down
Loading

0 comments on commit c9d175a

Please sign in to comment.