Skip to content

Commit 52e6388

Browse files
committed
tests: add helpers for TcS
Simple helpers to make easy create tests required Taranatool centralized configuration storage.
1 parent 5368646 commit 52e6388

File tree

4 files changed

+393
-8
lines changed

4 files changed

+393
-8
lines changed

test_helpers/main.go

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ type StartOpts struct {
3333
// InitScript is a Lua script for tarantool to run on start.
3434
InitScript string
3535

36+
// ConfigFile is a path to a configuration file for a Tarantool instance.
37+
// Required in pair with InstanceName.
38+
ConfigFile string
39+
40+
// InstanceName is a name of an instance to run.
41+
// Required in pair with ConfigFile.
42+
InstanceName string
43+
3644
// Listen is box.cfg listen parameter for tarantool.
3745
// Use this address to connect to tarantool after configuration.
3846
// https://www.tarantool.io/en/doc/latest/reference/configuration/#cfg-basic-listen
@@ -77,6 +85,39 @@ type TarantoolInstance struct {
7785

7886
// Dialer to check that connection established.
7987
Dialer tarantool.Dialer
88+
89+
done chan error
90+
is_done bool
91+
result error
92+
is_stopping bool
93+
}
94+
95+
// Status checks if Tarantool instance is still running.
96+
// Return true if it is running, false if it is not.
97+
// If instance was exit and error is nil - process completed success with zero status code.
98+
func (t *TarantoolInstance) Status() (bool, error) {
99+
if t.is_done {
100+
return false, t.result
101+
}
102+
103+
select {
104+
case t.result = <-t.done:
105+
t.is_done = true
106+
return false, t.result
107+
default:
108+
return true, nil
109+
}
110+
}
111+
112+
func (t *TarantoolInstance) checkDone() {
113+
t.is_done = false
114+
t.is_stopping = false
115+
t.done = make(chan error, 1)
116+
t.done <- t.Cmd.Wait()
117+
if !t.is_stopping {
118+
_, err := t.Status()
119+
log.Printf("Tarantool was unexpected terminated: %s", err)
120+
}
80121
}
81122

82123
func isReady(dialer tarantool.Dialer, opts *tarantool.Opts) error {
@@ -108,7 +149,7 @@ var (
108149
)
109150

110151
func init() {
111-
tarantoolVersionRegexp = regexp.MustCompile(`Tarantool (?:Enterprise )?(\d+)\.(\d+)\.(\d+).*`)
152+
tarantoolVersionRegexp = regexp.MustCompile(`Tarantool (Enterprise )?(\d+)\.(\d+)\.(\d+).*`)
112153
}
113154

114155
// atoiUint64 parses string to uint64.
@@ -145,15 +186,15 @@ func IsTarantoolVersionLess(majorMin uint64, minorMin uint64, patchMin uint64) (
145186
return true, fmt.Errorf("failed to parse output %q", out)
146187
}
147188

148-
if major, err = atoiUint64(parsed[1]); err != nil {
189+
if major, err = atoiUint64(parsed[2]); err != nil {
149190
return true, fmt.Errorf("failed to parse major from output %q: %w", out, err)
150191
}
151192

152-
if minor, err = atoiUint64(parsed[2]); err != nil {
193+
if minor, err = atoiUint64(parsed[3]); err != nil {
153194
return true, fmt.Errorf("failed to parse minor from output %q: %w", out, err)
154195
}
155196

156-
if patch, err = atoiUint64(parsed[3]); err != nil {
197+
if patch, err = atoiUint64(parsed[4]); err != nil {
157198
return true, fmt.Errorf("failed to parse patch from output %q: %w", out, err)
158199
}
159200

@@ -166,6 +207,21 @@ func IsTarantoolVersionLess(majorMin uint64, minorMin uint64, patchMin uint64) (
166207
}
167208
}
168209

210+
// IsTarantoolEE checks if Tarantool is Enterprise edition.
211+
func IsTarantoolEE() (bool, error) {
212+
out, err := exec.Command(getTarantoolExec(), "--version").Output()
213+
if err != nil {
214+
return true, err
215+
}
216+
217+
parsed := tarantoolVersionRegexp.FindStringSubmatch(string(out))
218+
if parsed == nil {
219+
return true, fmt.Errorf("failed to parse output %q", out)
220+
}
221+
222+
return parsed[1] != "", nil
223+
}
224+
169225
// RestartTarantool restarts a tarantool instance for tests
170226
// with specifies parameters (refer to StartOpts)
171227
// which were specified in inst parameter.
@@ -209,8 +265,16 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
209265
return inst, err
210266
}
211267
}
268+
args := []string{}
212269

213-
inst.Cmd = exec.Command(getTarantoolExec(), startOpts.InitScript)
270+
if startOpts.InitScript != "" {
271+
args = append(args, startOpts.InitScript)
272+
}
273+
if startOpts.ConfigFile != "" && startOpts.InstanceName != "" {
274+
args = append(args, "--config", startOpts.ConfigFile)
275+
args = append(args, "--name", startOpts.InstanceName)
276+
}
277+
inst.Cmd = exec.Command(getTarantoolExec(), args...)
214278

215279
inst.Cmd.Env = append(
216280
os.Environ(),
@@ -242,6 +306,8 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
242306
// see https://github.com/tarantool/go-tarantool/issues/136
243307
time.Sleep(startOpts.WaitStart)
244308

309+
go inst.checkDone()
310+
245311
opts := tarantool.Opts{
246312
Timeout: 500 * time.Millisecond,
247313
SkipSchema: true,
@@ -261,21 +327,40 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
261327
}
262328
}
263329

264-
return inst, err
330+
working, err_st := inst.Status()
331+
if !working || err_st != nil {
332+
StopTarantool(inst)
333+
return TarantoolInstance{}, fmt.Errorf("unexpected terminated Tarantool: %w", err_st)
334+
}
335+
336+
if err != nil {
337+
StopTarantool(inst)
338+
return TarantoolInstance{}, fmt.Errorf("failed to connect Tarantool: %w", err)
339+
}
340+
341+
return inst, nil
265342
}
266343

267344
// StopTarantool stops a tarantool instance started
268345
// with StartTarantool. Waits until any resources
269346
// associated with the process is released. If something went wrong, fails.
270347
func StopTarantool(inst TarantoolInstance) {
348+
log.Printf("Stopping Tarantool instance")
349+
inst.is_stopping = true
271350
if inst.Cmd != nil && inst.Cmd.Process != nil {
272351
if err := inst.Cmd.Process.Kill(); err != nil {
273-
log.Fatalf("Failed to kill tarantool (pid %d), got %s", inst.Cmd.Process.Pid, err)
352+
is_running, _ := inst.Status()
353+
if is_running {
354+
log.Fatalf("Failed to kill tarantool (pid %d), got %s", inst.Cmd.Process.Pid, err)
355+
}
274356
}
275357

276358
// Wait releases any resources associated with the Process.
277359
if _, err := inst.Cmd.Process.Wait(); err != nil {
278-
log.Fatalf("Failed to wait for Tarantool process to exit, got %s", err)
360+
is_running, _ := inst.Status()
361+
if is_running {
362+
log.Fatalf("Failed to wait for Tarantool process to exit, got %s", err)
363+
}
279364
}
280365

281366
inst.Cmd.Process = nil

test_helpers/tcs/prepare.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package tcs
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"text/template"
9+
"time"
10+
11+
"github.com/tarantool/go-tarantool/v2"
12+
"github.com/tarantool/go-tarantool/v2/test_helpers"
13+
)
14+
15+
const (
16+
waitTimeout = 500 * time.Millisecond
17+
connectRetry = 3
18+
TcsUser = "client"
19+
TcsPassword = "secret"
20+
)
21+
22+
//go:embed testdata/config.yaml
23+
var tcsConfig []byte
24+
25+
func writeConfig(name string, port int) error {
26+
cfg, err := os.Create(name)
27+
if err != nil {
28+
return err
29+
}
30+
defer cfg.Close()
31+
cfg.Chmod(0644)
32+
33+
t := template.Must(template.New("config").Parse(string(tcsConfig)))
34+
return t.Execute(cfg, map[string]interface{}{
35+
"host": "localhost",
36+
"port": port,
37+
})
38+
}
39+
40+
func makeOpts(port int) (test_helpers.StartOpts, error) {
41+
opts := test_helpers.StartOpts{}
42+
dir, err := os.MkdirTemp("", "tcs_dir")
43+
if err != nil {
44+
return opts, err
45+
}
46+
opts.ConfigFile = filepath.Join(dir, "config.yaml")
47+
err = writeConfig(opts.ConfigFile, port)
48+
if err != nil {
49+
return opts, fmt.Errorf("can't save file %q: %w", opts.ConfigFile, err)
50+
}
51+
52+
opts.Listen = fmt.Sprintf("localhost:%d", port)
53+
opts.WaitStart = waitTimeout
54+
opts.ConnectRetry = connectRetry
55+
opts.RetryTimeout = waitTimeout
56+
opts.InstanceName = "master"
57+
opts.Dialer = tarantool.NetDialer{
58+
Address: opts.Listen,
59+
User: TcsUser,
60+
Password: TcsPassword,
61+
}
62+
return opts, nil
63+
}

0 commit comments

Comments
 (0)