From 8e379e6dc8914a75267a26c923678e1019a9c339 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Fri, 14 Jan 2022 11:13:18 +0300 Subject: [PATCH] test: handle everything with go test Before this patch, it was required to set up test tarantool processes manually (and also handle their dependencies, like making working dir). You can see an example in CI scripts. This patch introduces go helpers for starting a tarantool process and validating Tarantool version. Helpers are based on `os/exec` calls. Retries to connect test tarantool instance handled explicitly, see tarantool/go-tarantool#136. Setup scripts are reworked to use environment variables to configure `box.cfg`. Listen port is set in the end of script so it is possible to connect only if every other thing was set up already. Every test is reworked to start a tarantool process (or processes) in TestMain before test run. To run tests, install all dependencies with running root folder `deps.sh` and then run `go clean -testcache && go test ./... -v -p 1`. Flag `-p 1` means no parallel runs. If you run tests without this flag, several test tarantool instances will try to bind the same port, resulting in run fail. Closes #107 --- .github/workflows/reusable_testing.yml | 36 +--- .github/workflows/testing.yml | 37 +--- .gitignore | 4 +- config.lua | 13 +- deps.sh | 9 + multi/config.lua | 19 ++ multi/config_m1.lua | 14 -- multi/config_m2.lua | 14 -- multi/multi_test.go | 54 ++++++ queue/config.lua | 13 +- queue/deps.sh | 6 + queue/queue_test.go | 33 ++++ tarantool_test.go | 33 ++++ test_helpers/main.go | 234 +++++++++++++++++++++++++ uuid/config.lua | 11 +- uuid/uuid_test.go | 92 +++++++--- 16 files changed, 488 insertions(+), 134 deletions(-) create mode 100755 deps.sh create mode 100644 multi/config.lua delete mode 100644 multi/config_m1.lua delete mode 100644 multi/config_m2.lua create mode 100755 queue/deps.sh create mode 100644 test_helpers/main.go diff --git a/.github/workflows/reusable_testing.yml b/.github/workflows/reusable_testing.yml index 54cd379c6..55fe9d862 100644 --- a/.github/workflows/reusable_testing.yml +++ b/.github/workflows/reusable_testing.yml @@ -38,36 +38,8 @@ jobs: with: go-version: 1.13 - - name: Run base tests - run: | - mkdir snap xlog - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - - - name: Run queue tests - working-directory: ./queue - run: | - mkdir snap xlog - tarantoolctl rocks install queue 1.1.0 - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID + - name: Install test dependencies + run: ./deps.sh - - name: Run uuid tests - working-directory: ./uuid - run: | - mkdir snap xlog - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - if: ${{ !startsWith(env.TNT_VERSION, 'Tarantool 1.10') }} - - - name: Run multi tests - working-directory: ./multi - run: | - mkdir -p m1/{snap,xlog} m2/{snap,xlog} - TNT_PID_1=$(tarantool ./config_m1.lua > tarantool_m1.log 2>&1 & echo $!) - TNT_PID_2=$(tarantool ./config_m2.lua > tarantool_m2.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID_1 $TNT_PID_2 + - name: Run tests + run: go clean -testcache && go test ./... -v -p 1 diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 68cf3d6b0..12b1f1e02 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -39,38 +39,9 @@ jobs: with: go-version: 1.13 - - name: Run base tests - run: | - mkdir snap xlog - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - - - name: Run queue tests + - name: Install test dependencies working-directory: ./queue - run: | - rm -rf snap - rm -rf xlog - mkdir snap xlog - tarantoolctl rocks install queue 1.1.0 - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - - - name: Run uuid tests - working-directory: ./uuid - run: | - mkdir snap xlog - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - if: ${{ matrix.tarantool != 1.10 }} + run: ./deps.sh - - name: Run multi tests - working-directory: ./multi - run: | - mkdir -p m1/{snap,xlog} m2/{snap,xlog} - TNT_PID_1=$(tarantool ./config_m1.lua > tarantool_m1.log 2>&1 & echo $!) - TNT_PID_2=$(tarantool ./config_m2.lua > tarantool_m2.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID_1 $TNT_PID_2 + - name: Run tests + run: go clean -testcache && go test ./... -v -p 1 diff --git a/.gitignore b/.gitignore index 8fcb790f1..8958050e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ *.DS_Store *.swp .idea/ -snap -xlog +work_dir* +.rocks diff --git a/config.lua b/config.lua index c2a9ae966..d06e05395 100644 --- a/config.lua +++ b/config.lua @@ -1,7 +1,7 @@ +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. box.cfg{ - listen = 3013, - wal_dir='xlog', - snap_dir='snap', + work_dir = os.getenv("TEST_TNT_WORK_DIR"), } box.once("init", function() @@ -56,7 +56,10 @@ function simple_incr(a) end box.space.test:truncate() -local console = require 'console' -console.listen '0.0.0.0:33015' --box.schema.user.revoke('guest', 'read,write,execute', 'universe') + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/deps.sh b/deps.sh new file mode 100755 index 000000000..dcd75258c --- /dev/null +++ b/deps.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Call this script to install test dependencies for all subpackages. + +set -e + +# We should install rock dependencies to corresponding directory. +# If you run ./queue/deps.sh instead of cd ./queue; ./deps.sh, +# rock will be installed to current directory instead of ./queue. +cd ./queue; ./deps.sh; cd .. diff --git a/multi/config.lua b/multi/config.lua new file mode 100644 index 000000000..e511cfad9 --- /dev/null +++ b/multi/config.lua @@ -0,0 +1,19 @@ +local nodes_load = require("config_load_nodes") + +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. +box.cfg{ + work_dir = os.getenv("TEST_TNT_WORK_DIR"), +} + +get_cluster_nodes = nodes_load.get_cluster_nodes + +box.once("init", function() + box.schema.user.create('test', { password = 'test' }) + box.schema.user.grant('test', 'read,write,execute', 'universe') +end) + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/multi/config_m1.lua b/multi/config_m1.lua deleted file mode 100644 index 5e364b5cc..000000000 --- a/multi/config_m1.lua +++ /dev/null @@ -1,14 +0,0 @@ -local nodes_load = require("config_load_nodes") - -box.cfg { - listen = 3013, - wal_dir = 'm1/xlog', - snap_dir = 'm1/snap', -} - -get_cluster_nodes = nodes_load.get_cluster_nodes - -box.once("init", function() - box.schema.user.create('test', { password = 'test' }) - box.schema.user.grant('test', 'read,write,execute', 'universe') -end) diff --git a/multi/config_m2.lua b/multi/config_m2.lua deleted file mode 100644 index cf73da319..000000000 --- a/multi/config_m2.lua +++ /dev/null @@ -1,14 +0,0 @@ -local nodes_load = require("config_load_nodes") - -box.cfg { - listen = 3014, - wal_dir = 'm2/xlog', - snap_dir = 'm2/snap', -} - -get_cluster_nodes = nodes_load.get_cluster_nodes - -box.once("init", function() - box.schema.user.create('test', { password = 'test' }) - box.schema.user.grant('test', 'read,write,execute', 'universe') -end) diff --git a/multi/multi_test.go b/multi/multi_test.go index 4515c6232..23a1f7bc6 100644 --- a/multi/multi_test.go +++ b/multi/multi_test.go @@ -1,10 +1,13 @@ package multi import ( + "log" + "os" "testing" "time" "github.com/tarantool/go-tarantool" + "github.com/tarantool/go-tarantool/test_helpers" ) var server1 = "127.0.0.1:3013" @@ -204,3 +207,54 @@ func TestRefresh(t *testing.T) { t.Error("Expect to get data after reconnect") } } + +// runTestMain is a body of TestMain function +// (see https://pkg.go.dev/testing#hdr-Main). +// Using defer + os.Exit is not works so TestMain body +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + initScript := "config.lua" + waitStart := 100 * time.Millisecond + var connectRetry uint = 3 + retryTimeout := 200 * time.Millisecond + + inst1, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: initScript, + Listen: server1, + WorkDir: "work_dir1", + User: connOpts.User, + Pass: connOpts.Pass, + WaitStart: waitStart, + ConnectRetry: connectRetry, + RetryTimeout: retryTimeout, + }) + defer test_helpers.StopTarantoolWithCleanup(inst1) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + inst2, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: initScript, + Listen: server2, + WorkDir: "work_dir2", + User: connOpts.User, + Pass: connOpts.Pass, + WaitStart: waitStart, + ConnectRetry: connectRetry, + RetryTimeout: retryTimeout, + }) + defer test_helpers.StopTarantoolWithCleanup(inst2) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +} diff --git a/queue/config.lua b/queue/config.lua index 57aef4d20..767f259ad 100644 --- a/queue/config.lua +++ b/queue/config.lua @@ -1,9 +1,9 @@ -queue = require 'queue' +queue = require('queue') +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. box.cfg{ - listen = 3013, - wal_dir='xlog', - snap_dir='snap', + work_dir = os.getenv("TEST_TNT_WORK_DIR"), } box.once("init", function() @@ -45,3 +45,8 @@ if box.space._func_index ~= nil then box.schema.user.grant('test', 'read', 'space', '_func_index') end end) + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/queue/deps.sh b/queue/deps.sh new file mode 100755 index 000000000..0c38fc531 --- /dev/null +++ b/queue/deps.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Call this script to install test dependencies. + +set -e + +tarantoolctl rocks install queue 1.1.0 diff --git a/queue/queue_test.go b/queue/queue_test.go index 4419e12eb..3d88450a9 100644 --- a/queue/queue_test.go +++ b/queue/queue_test.go @@ -2,12 +2,15 @@ package queue_test import ( "fmt" + "log" "math" + "os" "testing" "time" . "github.com/tarantool/go-tarantool" "github.com/tarantool/go-tarantool/queue" + "github.com/tarantool/go-tarantool/test_helpers" "gopkg.in/vmihailenco/msgpack.v2" ) @@ -818,3 +821,33 @@ func TestUtube_Put(t *testing.T) { t.Fatalf("Blocking time is less than expected: actual = %.2fs, expected = 1s", end.Sub(start).Seconds()) } } + +// runTestMain is a body of TestMain function +// (see https://pkg.go.dev/testing#hdr-Main). +// Using defer + os.Exit is not works so TestMain body +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: "config.lua", + Listen: server, + WorkDir: "work_dir", + User: opts.User, + Pass: opts.Pass, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 3, + RetryTimeout: 200 * time.Millisecond, + }) + defer test_helpers.StopTarantoolWithCleanup(inst) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +} diff --git a/tarantool_test.go b/tarantool_test.go index ae725fdc7..4f2fb8cd3 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -2,12 +2,15 @@ package tarantool_test import ( "fmt" + "log" + "os" "strings" "sync" "testing" "time" . "github.com/tarantool/go-tarantool" + "github.com/tarantool/go-tarantool/test_helpers" "gopkg.in/vmihailenco/msgpack.v2" ) @@ -1005,3 +1008,33 @@ func TestComplexStructs(t *testing.T) { return } } + +// runTestMain is a body of TestMain function +// (see https://pkg.go.dev/testing#hdr-Main). +// Using defer + os.Exit is not works so TestMain body +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: "config.lua", + Listen: server, + WorkDir: "work_dir", + User: opts.User, + Pass: opts.Pass, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 3, + RetryTimeout: 200 * time.Millisecond, + }) + defer test_helpers.StopTarantoolWithCleanup(inst) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +} diff --git a/test_helpers/main.go b/test_helpers/main.go new file mode 100644 index 000000000..e5c73bfe5 --- /dev/null +++ b/test_helpers/main.go @@ -0,0 +1,234 @@ +package test_helpers + +import ( + "errors" + "fmt" + "log" + "os" + "os/exec" + "regexp" + "strconv" + "time" + + "github.com/tarantool/go-tarantool" +) + +type StartOpts struct { + // InitScript is a Lua script for tarantool to run on start. + InitScript string + + // Listen is box.cfg listen parameter for tarantool. + // Use this address to connect to tarantool after configuration. + // https://www.tarantool.io/en/doc/latest/reference/configuration/#cfg-basic-listen + Listen string + + // WorkDir is box.cfg work_dir parameter for tarantool. + // Specify folder to store tarantool data files. + // Folder must be unique for each tarantool process used simultaneously. + // https://www.tarantool.io/en/doc/latest/reference/configuration/#confval-work_dir + WorkDir string + + // User is a username used to connect to tarantool. + // All required grants must be given in InitScript. + User string + + // Pass is a password for specified User. + Pass string + + // WaitStart is a time to wait before starting to ping tarantool. + WaitStart time.Duration + + // ConnectRetry is a count of attempts to ping tarantool. + ConnectRetry uint + + // RetryTimeout is a time between tarantool ping retries. + RetryTimeout time.Duration +} + +// TarantoolInstance is a data for instance graceful shutdown and cleanup. +type TarantoolInstance struct { + // Cmd is a Tarantool command. Used to kill Tarantool process. + Cmd *exec.Cmd + + // WorkDir is a directory with tarantool data. Cleaned up after run. + WorkDir string +} + +func isReady(server string, opts *tarantool.Opts) error { + var err error + var conn *tarantool.Connection + var resp *tarantool.Response + + conn, err = tarantool.Connect(server, *opts) + if err != nil { + return err + } + if conn == nil { + return errors.New("Conn is nil after connect") + } + defer conn.Close() + + resp, err = conn.Ping() + if err != nil { + return err + } + if resp == nil { + return errors.New("Response is nil after ping") + } + + return nil +} + +var ( + // Used to extract Tarantool version (major.minor.patch). + tarantoolVersionRegexp *regexp.Regexp +) + +func init() { + tarantoolVersionRegexp = regexp.MustCompile(`Tarantool (?:Enterprise )?(\d+)\.(\d+)\.(\d+).*`) +} + +// atoiUint64 parses string to uint64. +func atoiUint64(str string) (uint64, error) { + res, err := strconv.ParseUint(str, 10, 64) + if err != nil { + return 0, fmt.Errorf("cast to number error (%s)", err) + } + return res, nil +} + +// IsTarantoolVersionLess checks if tarantool version is less +// than passed . Returns error if failed +// to extract version. +func IsTarantoolVersionLess(majorMin uint64, minorMin uint64, patchMin uint64) (bool, error) { + var major, minor, patch uint64 + + out, err := exec.Command("tarantool", "--version").Output() + + if err != nil { + return true, err + } + + parsed := tarantoolVersionRegexp.FindStringSubmatch(string(out)) + + if parsed == nil { + return true, errors.New("regexp parse failed") + } + + if major, err = atoiUint64(parsed[1]); err != nil { + return true, fmt.Errorf("failed to parse major: %s", err) + } + + if minor, err = atoiUint64(parsed[2]); err != nil { + return true, fmt.Errorf("failed to parse minor: %s", err) + } + + if patch, err = atoiUint64(parsed[3]); err != nil { + return true, fmt.Errorf("failed to parse patch: %s", err) + } + + if major != majorMin { + return major < majorMin, nil + } else if minor != minorMin { + return minor < minorMin, nil + } else { + return patch < patchMin, nil + } + + return false, nil +} + +// StartTarantool starts a tarantool instance for tests +// with specifies parameters (refer to StartOpts). +// Process must be stopped with StopTarantool. +func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) { + // Prepare tarantool command. + var inst TarantoolInstance + inst.Cmd = exec.Command("tarantool", startOpts.InitScript) + + inst.Cmd.Env = append( + os.Environ(), + fmt.Sprintf("TEST_TNT_WORK_DIR=%s", startOpts.WorkDir), + fmt.Sprintf("TEST_TNT_LISTEN=%s", startOpts.Listen), + ) + + // Clean up existing work_dir. + err := os.RemoveAll(startOpts.WorkDir) + if err != nil { + return inst, err + } + + // Create work_dir. + err = os.Mkdir(startOpts.WorkDir, 0755) + if err != nil { + return inst, err + } + + inst.WorkDir = startOpts.WorkDir + + // Start tarantool. + err = inst.Cmd.Start() + if err != nil { + return inst, err + } + + // Try to connect and ping tarantool. + // Using reconnect opts do not help on Connect, + // see https://github.com/tarantool/go-tarantool/issues/136 + time.Sleep(startOpts.WaitStart) + + opts := tarantool.Opts{ + Timeout: 500 * time.Millisecond, + User: startOpts.User, + Pass: startOpts.Pass, + SkipSchema: true, + } + + var i uint + for i = 0; i <= startOpts.ConnectRetry; i++ { + err = isReady(startOpts.Listen, &opts) + + // Both connect and ping is ok. + if err == nil { + break + } + + if i != startOpts.ConnectRetry { + time.Sleep(startOpts.RetryTimeout) + } + } + + return inst, err +} + +// StopTarantool stops a tarantool instance started +// with StartTarantool. Waits until any resources +// associated with the process is released. If something went wrong, fails. +func StopTarantool(inst TarantoolInstance) { + if inst.Cmd != nil && inst.Cmd.Process != nil { + if err := inst.Cmd.Process.Kill(); err != nil { + log.Fatalf("Failed to kill tarantool (pid %d), got %s", inst.Cmd.Process.Pid, err) + } + + // Wait releases any resources associated with the Process. + if _, err := inst.Cmd.Process.Wait(); err != nil { + log.Fatalf("Failed to wait for Tarantool process to exit, got %s", err) + } + + inst.Cmd = nil + } +} + +// StopTarantoolWithCleanup stops a tarantool instance started +// with StartTarantool. Waits until any resources +// associated with the process is released. +// Cleans work directory after stop. If something went wrong, fails. +func StopTarantoolWithCleanup(inst TarantoolInstance) { + StopTarantool(inst) + + if inst.WorkDir != "" { + if err := os.RemoveAll(inst.WorkDir); err != nil { + log.Fatalf("Failed to clean work directory, got %s", err) + } + } +} diff --git a/uuid/config.lua b/uuid/config.lua index 34b5d26c4..b8fe1fe08 100644 --- a/uuid/config.lua +++ b/uuid/config.lua @@ -1,10 +1,10 @@ local uuid = require('uuid') local msgpack = require('msgpack') +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. box.cfg{ - listen = 3013, - wal_dir = 'xlog', - snap_dir = 'snap', + work_dir = os.getenv("TEST_TNT_WORK_DIR"), } box.schema.user.create('test', { password = 'test' , if_not_exists = true }) @@ -29,3 +29,8 @@ s:truncate() box.schema.user.grant('test', 'read,write', 'space', 'testUUID', { if_not_exists = true }) s:insert({ uuid.fromstr("c8f0fa1f-da29-438c-a040-393f1126ad39") }) + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/uuid/uuid_test.go b/uuid/uuid_test.go index 61eb632d2..813de5bf6 100644 --- a/uuid/uuid_test.go +++ b/uuid/uuid_test.go @@ -2,15 +2,23 @@ package uuid_test import ( "fmt" + "log" + "os" "testing" "time" + "github.com/google/uuid" . "github.com/tarantool/go-tarantool" - _ "github.com/tarantool/go-tarantool/uuid" + "github.com/tarantool/go-tarantool/test_helpers" + _ "github.com/tarantool/go-tarantool/uuid" "gopkg.in/vmihailenco/msgpack.v2" - "github.com/google/uuid" ) +// There is no way to skip tests in testing.M, +// so we use this variable to pass info +// to each testing.T that it should skip. +var isUUIDSupported = false + var server = "127.0.0.1:3013" var opts = Opts{ Timeout: 500 * time.Millisecond, @@ -53,23 +61,6 @@ func connectWithValidation(t *testing.T) *Connection { return conn } -func skipIfUUIDUnsupported(t *testing.T, conn *Connection) { - resp, err := conn.Eval("return pcall(require('msgpack').encode, require('uuid').new())", []interface{}{}) - if err != nil { - t.Errorf("Failed to Eval: %s", err.Error()) - } - if resp == nil { - t.Errorf("Response is nil after Eval") - } - if len(resp.Data) < 1 { - t.Errorf("Response.Data is empty after Eval") - } - val := resp.Data[0].(bool) - if val != true { - t.Skip("Skipping test for Tarantool without UUID support in msgpack") - } -} - func tupleValueIsId(t *testing.T, tuples []interface{}, id uuid.UUID) { if len(tuples) != 1 { t.Errorf("Response Data len != 1") @@ -88,17 +79,19 @@ func tupleValueIsId(t *testing.T, tuples []interface{}, id uuid.UUID) { } func TestSelect(t *testing.T) { + if isUUIDSupported == false { + t.Skip("Skipping test for Tarantool without UUID support in msgpack") + } + conn := connectWithValidation(t) defer conn.Close() - skipIfUUIDUnsupported(t, conn) - id, uuidErr := uuid.Parse("c8f0fa1f-da29-438c-a040-393f1126ad39") if uuidErr != nil { t.Errorf("Failed to prepare test uuid: %s", uuidErr) } - resp, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{ id }) + resp, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{id}) if errSel != nil { t.Errorf("UUID select failed: %s", errSel.Error()) } @@ -108,7 +101,7 @@ func TestSelect(t *testing.T) { tupleValueIsId(t, resp.Data, id) var tuples []TupleUUID - errTyp := conn.SelectTyped(space, index, 0, 1, IterEq, []interface{}{ id }, &tuples) + errTyp := conn.SelectTyped(space, index, 0, 1, IterEq, []interface{}{id}, &tuples) if errTyp != nil { t.Errorf("Failed to SelectTyped: %s", errTyp.Error()) } @@ -121,17 +114,19 @@ func TestSelect(t *testing.T) { } func TestReplace(t *testing.T) { + if isUUIDSupported == false { + t.Skip("Skipping test for Tarantool without UUID support in msgpack") + } + conn := connectWithValidation(t) defer conn.Close() - skipIfUUIDUnsupported(t, conn) - id, uuidErr := uuid.Parse("64d22e4d-ac92-4a23-899a-e59f34af5479") if uuidErr != nil { t.Errorf("Failed to prepare test uuid: %s", uuidErr) } - respRep, errRep := conn.Replace(space, []interface{}{ id }) + respRep, errRep := conn.Replace(space, []interface{}{id}) if errRep != nil { t.Errorf("UUID replace failed: %s", errRep) } @@ -140,7 +135,7 @@ func TestReplace(t *testing.T) { } tupleValueIsId(t, respRep.Data, id) - respSel, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{ id }) + respSel, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{id}) if errSel != nil { t.Errorf("UUID select failed: %s", errSel) } @@ -149,3 +144,46 @@ func TestReplace(t *testing.T) { } tupleValueIsId(t, respSel.Data, id) } + +// runTestMain is a body of TestMain function +// (see https://pkg.go.dev/testing#hdr-Main). +// Using defer + os.Exit is not works so TestMain body +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + isLess, err := test_helpers.IsTarantoolVersionLess(2, 4, 1) + if err != nil { + log.Fatalf("Failed to extract tarantool version: %s", err) + } + + if isLess { + log.Println("Skipping UUID tests...") + isUUIDSupported = false + return m.Run() + } else { + isUUIDSupported = true + } + + inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: "config.lua", + Listen: server, + WorkDir: "work_dir", + User: opts.User, + Pass: opts.Pass, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 3, + RetryTimeout: 200 * time.Millisecond, + }) + defer test_helpers.StopTarantoolWithCleanup(inst) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +}