From 939c875f7a6d6b379ae5e8cc43b45eef83af830f Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 3 Jun 2026 13:22:25 +0200 Subject: [PATCH 01/18] Added go runner for yaml tests --- integration_test/runner/runner.go | 289 +++++++++++++++++++++++++ integration_test/runner/runner_test.go | 64 ++++++ 2 files changed, 353 insertions(+) create mode 100644 integration_test/runner/runner.go create mode 100644 integration_test/runner/runner_test.go diff --git a/integration_test/runner/runner.go b/integration_test/runner/runner.go new file mode 100644 index 0000000000..8f7f04b7ae --- /dev/null +++ b/integration_test/runner/runner.go @@ -0,0 +1,289 @@ +// Package runner is a Go-native driver for the YAML integration test suite, +// +// It integrates with standard `go test`, so each test case becomes a subtest +// with proper failure attribution, -run filtering, and -v output. +// +// Requires a running Docker cluster. Start one with `make docker-cluster-start`. +// +// Run a single module: +// +// go test -tags yaml_integration -v ./integration_test/runner/... -run TestBankModule +// +// Run everything: +// +// go test -tags yaml_integration -v ./integration_test/runner/... +package runner + +import ( + "fmt" + "math/big" + "os" + "os/exec" + "regexp" + "strconv" + "strings" + "testing" + + "gopkg.in/yaml.v3" +) + +// TestCase mirrors the YAML schema used by the existing test files. +type TestCase struct { + Name string `yaml:"name"` + Inputs []Input `yaml:"inputs"` + Verifiers []Verifier `yaml:"verifiers"` +} + +// Input is one command step within a test case. +type Input struct { + Cmd string `yaml:"cmd"` + Env string `yaml:"env,omitempty"` // capture trimmed stdout into this name + Node string `yaml:"node,omitempty"` // docker container; defaults to Options.DefaultContainer +} + +// Verifier checks the accumulated env map after all inputs have run. +type Verifier struct { + Type string `yaml:"type"` // "eval" or "regex" + Expr string `yaml:"expr"` + Result string `yaml:"result,omitempty"` // env var to match (regex only) +} + +// Options controls how RunFile executes commands. +type Options struct { + // DefaultContainer is the docker container used when an Input has no Node set. + // If empty, commands run locally (useful for tests that don't need docker). + DefaultContainer string + // ExtraPath is appended to PATH inside docker containers. + // Defaults to /root/go/bin:/root/.foundry/bin (matching runner.py). + ExtraPath string +} + +// RunFile reads path as a YAML list of TestCases and runs each as a subtest of t. +func RunFile(t *testing.T, path string, opts Options) { + t.Helper() + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read %s: %v", path, err) + } + var cases []TestCase + if err = yaml.Unmarshal(data, &cases); err != nil { + t.Fatalf("unmarshal %s: %v", path, err) + } + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + runCase(t, tc, opts) + }) + } +} + +func runCase(t *testing.T, tc TestCase, opts Options) { + t.Helper() + envMap := make(map[string]string) + + for i, inp := range tc.Inputs { + container := inp.Node + if container == "" { + container = opts.DefaultContainer + } + out, err := execCmd(t, inp.Cmd, container, envMap, opts) + t.Logf("[%d] $ %s\n => %s", i, inp.Cmd, out) + if err != nil { + t.Fatalf("input[%d] failed: %v", i, err) + } + if inp.Env != "" { + envMap[inp.Env] = out + } + } + + for _, v := range tc.Verifiers { + if err := verify(v, envMap); err != nil { + t.Errorf("verifier %s %q: %v", v.Type, v.Expr, err) + } + } +} + +// execCmd runs cmd in the given docker container (or locally if container is empty), +// injecting the accumulated envMap. Non-zero exit is logged but not fatal — this +// matches runner.py behaviour where commands that echo error codes exit 0 from +// bash but the captured output is the code. +func execCmd(t *testing.T, cmd, container string, envMap map[string]string, opts Options) (string, error) { + t.Helper() + var c *exec.Cmd + + if container != "" { + extraPath := opts.ExtraPath + if extraPath == "" { + extraPath = "/root/go/bin:/root/.foundry/bin" + } + args := []string{"exec"} + for k, v := range envMap { + args = append(args, "-e", k+"="+v) + } + args = append(args, container, "/bin/bash", "-c", + "export PATH=$PATH:"+extraPath+" && "+cmd) + c = exec.Command("docker", args...) + } else { + c = exec.Command("/bin/bash", "-c", cmd) + c.Env = append(os.Environ(), envMapSlice(envMap)...) + } + + out, err := c.Output() + stdout := strings.TrimSpace(string(out)) + if err != nil { + if exit, ok := err.(*exec.ExitError); ok { + t.Logf(" (exit %d) stderr: %s", exit.ExitCode(), strings.TrimSpace(string(exit.Stderr))) + return stdout, nil + } + return stdout, fmt.Errorf("exec: %w", err) + } + return stdout, nil +} + +// envMapSlice converts envMap to a slice of "K=V" strings suitable for exec.Cmd.Env. +func envMapSlice(envMap map[string]string) []string { + s := make([]string, 0, len(envMap)) + for k, v := range envMap { + s = append(s, k+"="+v) + } + return s +} + +// verify evaluates a single Verifier against the accumulated env map. +func verify(v Verifier, envMap map[string]string) error { + switch v.Type { + case "eval": + ok, err := evalExpr(v.Expr, envMap) + if err != nil { + return err + } + if !ok { + return fmt.Errorf("false: %s%s", v.Expr, fmtRelevant(v.Expr, envMap)) + } + return nil + case "regex": + val := envMap[v.Result] + ok, err := regexp.MatchString(v.Expr, val) + if err != nil { + return fmt.Errorf("bad pattern %q: %w", v.Expr, err) + } + if !ok { + return fmt.Errorf("pattern %q did not match %q", v.Expr, val) + } + return nil + default: + return fmt.Errorf("unknown verifier type %q", v.Type) + } +} + +// evalExpr evaluates a Python-runner-compatible eval expression. +// Handles: VAR op literal, VAR op VAR, expr and expr, expr or expr. +// Operators: ==, !=, >, <, >=, <=. +func evalExpr(expr string, envMap map[string]string) (bool, error) { + if parts := strings.Split(expr, " and "); len(parts) > 1 { + for _, p := range parts { + ok, err := evalExpr(strings.TrimSpace(p), envMap) + if err != nil { + return false, err + } + if !ok { + return false, nil + } + } + return true, nil + } + if parts := strings.Split(expr, " or "); len(parts) > 1 { + for _, p := range parts { + ok, err := evalExpr(strings.TrimSpace(p), envMap) + if err == nil && ok { + return true, nil + } + } + return false, nil + } + return cmpExpr(strings.TrimSpace(expr), envMap) +} + +// cmpExpr evaluates a single "LHS op RHS" comparison. +// Operators are checked longest-first so ">=" is never mis-split by ">". +func cmpExpr(expr string, envMap map[string]string) (bool, error) { + for _, op := range []string{"!=", ">=", "<=", "==", ">", "<"} { + before, after, found := strings.Cut(expr, op) + if !found { + continue + } + lhs := resolve(strings.TrimSpace(before), envMap) + rhs := resolve(strings.TrimSpace(after), envMap) + return cmpValues(lhs, rhs, op) + } + return false, fmt.Errorf("no operator in %q", expr) +} + +// resolve substitutes an env var name with its value, or unquotes a string literal. +func resolve(token string, envMap map[string]string) string { + if len(token) >= 2 && token[0] == '"' && token[len(token)-1] == '"' { + return token[1 : len(token)-1] + } + if v, ok := envMap[token]; ok { + return v + } + return token +} + +// cmpValues compares a and b with op. Tries big.Int (for large integers without +// float64 precision loss), then float64, then string. +func cmpValues(a, b, op string) (bool, error) { + a, b = strings.TrimSpace(a), strings.TrimSpace(b) + + ai, bi := new(big.Int), new(big.Int) + if _, ok1 := ai.SetString(a, 10); ok1 { + if _, ok2 := bi.SetString(b, 10); ok2 { + return applyOp(ai.Cmp(bi), op) + } + } + + fa, errA := strconv.ParseFloat(a, 64) + fb, errB := strconv.ParseFloat(b, 64) + if errA == nil && errB == nil { + c := 0 + if fa < fb { + c = -1 + } else if fa > fb { + c = 1 + } + return applyOp(c, op) + } + + return applyOp(strings.Compare(a, b), op) +} + +func applyOp(cmp int, op string) (bool, error) { + switch op { + case "==": + return cmp == 0, nil + case "!=": + return cmp != 0, nil + case ">": + return cmp > 0, nil + case "<": + return cmp < 0, nil + case ">=": + return cmp >= 0, nil + case "<=": + return cmp <= 0, nil + } + return false, fmt.Errorf("unknown operator %q", op) +} + +// fmtRelevant returns a diagnostic string listing env vars referenced in expr. +func fmtRelevant(expr string, envMap map[string]string) string { + var refs []string + for k, v := range envMap { + if strings.Contains(expr, k) { + refs = append(refs, k+"="+v) + } + } + if len(refs) == 0 { + return "" + } + return " [" + strings.Join(refs, ", ") + "]" +} diff --git a/integration_test/runner/runner_test.go b/integration_test/runner/runner_test.go new file mode 100644 index 0000000000..c518db2efa --- /dev/null +++ b/integration_test/runner/runner_test.go @@ -0,0 +1,64 @@ +//go:build yaml_integration + +// Package runner_test wires the existing YAML test files into standard `go test`. +// +// Requires a running Docker cluster (`make docker-cluster-start`). +// +// Run a single module: +// +// go test -tags yaml_integration -v ./integration_test/runner/... -run TestBankModule +// +// Run all modules: +// +// go test -tags yaml_integration -v ./integration_test/runner/... +// +// Each YAML test case becomes a named subtest, so -run accepts the full path: +// +// -run TestBankModule/Test_sending_funds +package runner_test + +import ( + "testing" + + "github.com/sei-protocol/sei-chain/integration_test/runner" +) + +// opts targets sei-node-0 as the default container, matching runner.py. +var opts = runner.Options{DefaultContainer: "sei-node-0"} + +func TestBankModule(t *testing.T) { + runner.RunFile(t, "../bank_module/send_funds_test.yaml", opts) + runner.RunFile(t, "../bank_module/multi_sig_send_test.yaml", opts) +} + +func TestGovModule(t *testing.T) { + runner.RunFile(t, "../gov_module/gov_proposal_test.yaml", opts) + runner.RunFile(t, "../gov_module/staking_proposal_test.yaml", opts) +} + +func TestOracleModule(t *testing.T) { + runner.RunFile(t, "../oracle_module/set_feeder_test.yaml", opts) + runner.RunFile(t, "../oracle_module/verify_penalty_counts.yaml", opts) +} + +func TestMintModule(t *testing.T) { + runner.RunFile(t, "../mint_module/mint_test.yaml", opts) +} + +func TestStakingModule(t *testing.T) { + runner.RunFile(t, "../staking_module/staking_test.yaml", opts) +} + +func TestDistributionModule(t *testing.T) { + runner.RunFile(t, "../distribution_module/rewards.yaml", opts) +} + +func TestTokenFactoryModule(t *testing.T) { + runner.RunFile(t, "../tokenfactory_module/create_tokenfactory_test.yaml", opts) +} + +func TestAuthzModule(t *testing.T) { + runner.RunFile(t, "../authz_module/staking_authorization_test.yaml", opts) + runner.RunFile(t, "../authz_module/send_authorization_test.yaml", opts) + runner.RunFile(t, "../authz_module/generic_authorization_test.yaml", opts) +} From e246ab16181d198c2c9f4261bf06af6c3680be4f Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 3 Jun 2026 13:27:15 +0200 Subject: [PATCH 02/18] Added rest of the yaml tests to go runner --- integration_test/runner/runner_test.go | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/integration_test/runner/runner_test.go b/integration_test/runner/runner_test.go index c518db2efa..10c38de0fd 100644 --- a/integration_test/runner/runner_test.go +++ b/integration_test/runner/runner_test.go @@ -26,9 +26,14 @@ import ( // opts targets sei-node-0 as the default container, matching runner.py. var opts = runner.Options{DefaultContainer: "sei-node-0"} +func TestStartup(t *testing.T) { + runner.RunFile(t, "../startup/startup_test.yaml", opts) +} + func TestBankModule(t *testing.T) { runner.RunFile(t, "../bank_module/send_funds_test.yaml", opts) runner.RunFile(t, "../bank_module/multi_sig_send_test.yaml", opts) + runner.RunFile(t, "../bank_module/simulation_tx.yaml", opts) } func TestGovModule(t *testing.T) { @@ -51,6 +56,7 @@ func TestStakingModule(t *testing.T) { func TestDistributionModule(t *testing.T) { runner.RunFile(t, "../distribution_module/rewards.yaml", opts) + runner.RunFile(t, "../distribution_module/community_pool.yaml", opts) } func TestTokenFactoryModule(t *testing.T) { @@ -62,3 +68,30 @@ func TestAuthzModule(t *testing.T) { runner.RunFile(t, "../authz_module/send_authorization_test.yaml", opts) runner.RunFile(t, "../authz_module/generic_authorization_test.yaml", opts) } + +func TestWasmModule(t *testing.T) { + runner.RunFile(t, "../wasm_module/timelocked_token_admin_test.yaml", opts) + runner.RunFile(t, "../wasm_module/timelocked_token_delegation_test.yaml", opts) + runner.RunFile(t, "../wasm_module/timelocked_token_emergency_withdraw_test.yaml", opts) + runner.RunFile(t, "../wasm_module/timelocked_token_withdraw_test.yaml", opts) +} + +func TestSeiDB(t *testing.T) { + runner.RunFile(t, "../seidb/flatkv_evm_test.yaml", opts) + runner.RunFile(t, "../seidb/state_store_test.yaml", opts) +} + +func TestChainOperation(t *testing.T) { + runner.RunFile(t, "../chain_operation/snapshot_operation.yaml", opts) + // statesync targets the sei-rpc-node container, which is only present in + // the CI RPC-node cluster (make run-rpc-node-integration-ci). + runner.RunFile(t, "../chain_operation/statesync_operation.yaml", opts) +} + +// TestUpgradeModule covers major and minor upgrade scenarios. +// These require a specially prepared cluster where node binaries can be +// swapped mid-test; they are not suitable for the standard docker-cluster-start setup. +func TestUpgradeModule(t *testing.T) { + runner.RunFile(t, "../upgrade_module/major_upgrade_test.yaml", opts) + runner.RunFile(t, "../upgrade_module/minor_upgrade_test.yaml", opts) +} From 0f87d2c0d849fe194aa8023882b164b253f5e984 Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 3 Jun 2026 13:29:58 +0200 Subject: [PATCH 03/18] Replaced some tests with the go runner --- .github/workflows/integration-test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 11b776014f..77fe24820e 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -85,9 +85,7 @@ jobs: { name: "Mint & Staking & Bank Module", scripts: [ - "python3 integration_test/scripts/runner.py integration_test/staking_module/staking_test.yaml", - "python3 integration_test/scripts/runner.py integration_test/bank_module/send_funds_test.yaml", - "python3 integration_test/scripts/runner.py integration_test/mint_module/mint_test.yaml" + "go test -tags yaml_integration -v -timeout 10m ./integration_test/runner/... -run 'TestMintModule|TestStakingModule|TestBankModule'" ] }, { From 191e9350a3521d7a5d3f04942332bbc04c1f8fad Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 3 Jun 2026 13:54:31 +0200 Subject: [PATCH 04/18] Addressed the evaluation of expression with arithmatic --- integration_test/runner/runner.go | 73 ++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/integration_test/runner/runner.go b/integration_test/runner/runner.go index 8f7f04b7ae..7de00ac03f 100644 --- a/integration_test/runner/runner.go +++ b/integration_test/runner/runner.go @@ -204,29 +204,72 @@ func evalExpr(expr string, envMap map[string]string) (bool, error) { } // cmpExpr evaluates a single "LHS op RHS" comparison. -// Operators are checked longest-first so ">=" is never mis-split by ">". +// It tokenizes by whitespace first and substitutes env var names before +// locating the operator, so arithmetic sub-expressions like EXPECTED_COUNTS + 1 +// are evaluated rather than compared as literal strings. func cmpExpr(expr string, envMap map[string]string) (bool, error) { - for _, op := range []string{"!=", ">=", "<=", "==", ">", "<"} { - before, after, found := strings.Cut(expr, op) - if !found { - continue + tokens := strings.Fields(expr) + for i, t := range tokens { + if len(t) >= 2 && t[0] == '"' && t[len(t)-1] == '"' { + tokens[i] = t[1 : len(t)-1] + } else if v, ok := envMap[t]; ok { + tokens[i] = v + } + } + for i, t := range tokens { + for _, op := range []string{"!=", ">=", "<=", "==", ">", "<"} { + if t == op { + lhs, err := evalArith(tokens[:i]) + if err != nil { + return false, err + } + rhs, err := evalArith(tokens[i+1:]) + if err != nil { + return false, err + } + return cmpValues(lhs, rhs, op) + } } - lhs := resolve(strings.TrimSpace(before), envMap) - rhs := resolve(strings.TrimSpace(after), envMap) - return cmpValues(lhs, rhs, op) } return false, fmt.Errorf("no operator in %q", expr) } -// resolve substitutes an env var name with its value, or unquotes a string literal. -func resolve(token string, envMap map[string]string) string { - if len(token) >= 2 && token[0] == '"' && token[len(token)-1] == '"' { - return token[1 : len(token)-1] +// evalArith evaluates a sequence of tokens as big.Int arithmetic (e.g. ["4", "+", "1"] → "5"). +// Falls back to the raw joined string when the first token is not a valid integer, +// allowing float and string comparisons to fall through to cmpValues. +func evalArith(tokens []string) (string, error) { + if len(tokens) == 0 { + return "", fmt.Errorf("empty side of comparison") + } + if len(tokens) == 1 { + return tokens[0], nil } - if v, ok := envMap[token]; ok { - return v + result := new(big.Int) + if _, ok := result.SetString(tokens[0], 10); !ok { + return strings.Join(tokens, " "), nil + } + for i := 1; i+1 < len(tokens); i += 2 { + operand := new(big.Int) + if _, ok := operand.SetString(tokens[i+1], 10); !ok { + return strings.Join(tokens, " "), nil + } + switch tokens[i] { + case "+": + result.Add(result, operand) + case "-": + result.Sub(result, operand) + case "*": + result.Mul(result, operand) + case "/": + if operand.Sign() == 0 { + return "", fmt.Errorf("division by zero in %v", tokens) + } + result.Div(result, operand) + default: + return strings.Join(tokens, " "), nil + } } - return token + return result.String(), nil } // cmpValues compares a and b with op. Tries big.Int (for large integers without From dc78eed4526e83ffa44538e1e72cea1d43f6344d Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 3 Jun 2026 14:07:10 +0200 Subject: [PATCH 05/18] Fixed test overlap --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 77fe24820e..da618aef78 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -85,7 +85,7 @@ jobs: { name: "Mint & Staking & Bank Module", scripts: [ - "go test -tags yaml_integration -v -timeout 10m ./integration_test/runner/... -run 'TestMintModule|TestStakingModule|TestBankModule'" + "go test -tags yaml_integration -v -timeout 10m ./integration_test/runner/... -run 'TestMintModule|TestStakingModule|TestBankModule/Test_sending_funds'" ] }, { From 4ddd20c2803fbc38480452bce02acdeaefdfe32f Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 3 Jun 2026 15:01:51 +0200 Subject: [PATCH 06/18] Fix CI lint and integration test failures - Run go mod tidy to move gopkg.in/yaml.v3 from indirect to direct deps - Fix shell quoting in CI YAML: replace single quotes in -run regex with escaped double quotes to avoid breaking the echo '...' wrapper in the test runner script Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/integration-test.yml | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index da618aef78..e68a02b5bc 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -85,7 +85,7 @@ jobs: { name: "Mint & Staking & Bank Module", scripts: [ - "go test -tags yaml_integration -v -timeout 10m ./integration_test/runner/... -run 'TestMintModule|TestStakingModule|TestBankModule/Test_sending_funds'" + "go test -tags yaml_integration -v -timeout 10m ./integration_test/runner/... -run \"TestMintModule|TestStakingModule|TestBankModule/Test_sending_funds\"" ] }, { diff --git a/go.mod b/go.mod index 009b009979..35808976e0 100644 --- a/go.mod +++ b/go.mod @@ -107,6 +107,7 @@ require ( google.golang.org/grpc v1.75.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/component-base v0.35.0 pgregory.net/rapid v1.2.0 ) @@ -129,7 +130,6 @@ require ( github.com/skeema/knownhosts v1.3.1 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( From 9b986a602deb4afed0321b3e7ab22e968086d42d Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 3 Jun 2026 15:28:11 +0200 Subject: [PATCH 07/18] Fixing lint issue --- integration_test/runner/runner.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/integration_test/runner/runner.go b/integration_test/runner/runner.go index 7de00ac03f..7c45a51f30 100644 --- a/integration_test/runner/runner.go +++ b/integration_test/runner/runner.go @@ -61,7 +61,7 @@ type Options struct { // RunFile reads path as a YAML list of TestCases and runs each as a subtest of t. func RunFile(t *testing.T, path string, opts Options) { t.Helper() - data, err := os.ReadFile(path) + data, err := os.ReadFile(path) //nolint:gosec if err != nil { t.Fatalf("read %s: %v", path, err) } @@ -115,7 +115,9 @@ func execCmd(t *testing.T, cmd, container string, envMap map[string]string, opts if extraPath == "" { extraPath = "/root/go/bin:/root/.foundry/bin" } - args := []string{"exec"} + // capacity: 1 ("exec") + 2*len(envMap) ("-e" + "k=v" per entry) + 4 (container, "/bin/bash", "-c", cmd) + args := make([]string, 1, 1+2*len(envMap)+4) + args[0] = "exec" for k, v := range envMap { args = append(args, "-e", k+"="+v) } From cac538b6b85fb07c1c2bf5ece3ddbeba0f53f2d1 Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 3 Jun 2026 15:38:18 +0200 Subject: [PATCH 08/18] Fixed lint error --- integration_test/runner/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_test/runner/runner.go b/integration_test/runner/runner.go index 7c45a51f30..cf3576c66c 100644 --- a/integration_test/runner/runner.go +++ b/integration_test/runner/runner.go @@ -123,7 +123,7 @@ func execCmd(t *testing.T, cmd, container string, envMap map[string]string, opts } args = append(args, container, "/bin/bash", "-c", "export PATH=$PATH:"+extraPath+" && "+cmd) - c = exec.Command("docker", args...) + c = exec.Command("docker", args...) //nolint:gosec } else { c = exec.Command("/bin/bash", "-c", cmd) c.Env = append(os.Environ(), envMapSlice(envMap)...) From 4bd1b06b23cfaa5942be92eb7219c6cf718c9ebb Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Tue, 9 Jun 2026 13:39:41 -0700 Subject: [PATCH 09/18] Removed upgrade test from this pr refactor as it requires special handling --- integration_test/runner/runner_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/integration_test/runner/runner_test.go b/integration_test/runner/runner_test.go index 10c38de0fd..99768b735d 100644 --- a/integration_test/runner/runner_test.go +++ b/integration_test/runner/runner_test.go @@ -87,11 +87,3 @@ func TestChainOperation(t *testing.T) { // the CI RPC-node cluster (make run-rpc-node-integration-ci). runner.RunFile(t, "../chain_operation/statesync_operation.yaml", opts) } - -// TestUpgradeModule covers major and minor upgrade scenarios. -// These require a specially prepared cluster where node binaries can be -// swapped mid-test; they are not suitable for the standard docker-cluster-start setup. -func TestUpgradeModule(t *testing.T) { - runner.RunFile(t, "../upgrade_module/major_upgrade_test.yaml", opts) - runner.RunFile(t, "../upgrade_module/minor_upgrade_test.yaml", opts) -} From d5a848dc77d330c1411ba1fea98c7c01001cffd2 Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Tue, 9 Jun 2026 13:51:09 -0700 Subject: [PATCH 10/18] Seperated snapshot operation from statesync operation test --- integration_test/runner/runner_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/integration_test/runner/runner_test.go b/integration_test/runner/runner_test.go index 99768b735d..84a0a5a642 100644 --- a/integration_test/runner/runner_test.go +++ b/integration_test/runner/runner_test.go @@ -83,7 +83,10 @@ func TestSeiDB(t *testing.T) { func TestChainOperation(t *testing.T) { runner.RunFile(t, "../chain_operation/snapshot_operation.yaml", opts) - // statesync targets the sei-rpc-node container, which is only present in - // the CI RPC-node cluster (make run-rpc-node-integration-ci). +} + +// TestStateSync requires the sei-rpc-node container, which is only present in +// the CI RPC-node cluster (make run-rpc-node-integration-ci). +func TestStateSync(t *testing.T) { runner.RunFile(t, "../chain_operation/statesync_operation.yaml", opts) } From eaf12a9f1dc6aa737d5c706505a2373640f712d0 Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Tue, 9 Jun 2026 14:15:40 -0700 Subject: [PATCH 11/18] Addressing feedback --- integration_test/runner/runner.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/integration_test/runner/runner.go b/integration_test/runner/runner.go index cf3576c66c..af48993dbe 100644 --- a/integration_test/runner/runner.go +++ b/integration_test/runner/runner.go @@ -15,6 +15,7 @@ package runner import ( + "errors" "fmt" "math/big" "os" @@ -132,11 +133,12 @@ func execCmd(t *testing.T, cmd, container string, envMap map[string]string, opts out, err := c.Output() stdout := strings.TrimSpace(string(out)) if err != nil { - if exit, ok := err.(*exec.ExitError); ok { + var exit *exec.ExitError + if errors.As(err, &exit) { t.Logf(" (exit %d) stderr: %s", exit.ExitCode(), strings.TrimSpace(string(exit.Stderr))) return stdout, nil } - return stdout, fmt.Errorf("exec: %w", err) + return stdout, err } return stdout, nil } From 8996261dcc4f0f95921f22a3a4bd376888a89e1d Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Tue, 9 Jun 2026 15:22:13 -0700 Subject: [PATCH 12/18] Fixed the bank module yaml tests and enabled in CI --- .github/workflows/integration-test.yml | 2 +- integration_test/bank_module/multi_sig_send_test.yaml | 2 ++ integration_test/bank_module/simulation_tx.yaml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 9d86d8052d..e6586f4622 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -128,7 +128,7 @@ jobs: { name: "Mint & Staking & Bank Module", scripts: [ - "go test -tags yaml_integration -v -timeout 10m ./integration_test/runner/... -run \"TestMintModule|TestStakingModule|TestBankModule/Test_sending_funds\"" + "go test -tags yaml_integration -v -timeout 10m ./integration_test/runner/... -run \"TestMintModule|TestStakingModule|TestBankModule\"" ] }, { diff --git a/integration_test/bank_module/multi_sig_send_test.yaml b/integration_test/bank_module/multi_sig_send_test.yaml index c8341ac070..cdc025c27f 100644 --- a/integration_test/bank_module/multi_sig_send_test.yaml +++ b/integration_test/bank_module/multi_sig_send_test.yaml @@ -21,6 +21,8 @@ - cmd: seid tx sign unsigned-tx.json --multisig=$MULTI_SIG_ACC --keyring-backend test --from=wallet2 --output-document=wallet2.json --chain-id sei -b block --fees 1sei - cmd: seid tx multisign unsigned-tx.json multisig wallet1.json wallet2.json --chain-id sei --keyring-backend test > signed-tx.json - cmd: seid tx broadcast signed-tx.json --chain-id sei -b block -y + # cleanup + - cmd: rm wallet1.json wallet2.json signed-tx.json unsigned-tx.json # Check multi-sig balance - cmd: seid q bank balances $MULTI_SIG_ACC --output json | jq -r .balances[0].amount diff --git a/integration_test/bank_module/simulation_tx.yaml b/integration_test/bank_module/simulation_tx.yaml index d0b138b33f..b4bfb762f4 100644 --- a/integration_test/bank_module/simulation_tx.yaml +++ b/integration_test/bank_module/simulation_tx.yaml @@ -10,7 +10,7 @@ # Send funds - cmd: printf "12345678\n" | seid tx bank send $ADMIN_ACC $SIMULATION_TEST_ACC 1sei -b block --fees 2000usei --chain-id sei -y - - cmd: seid tx bank send $ADMIN_ACC $SIMULATION_TEST_ACC 1000sei --from $ADMIN_ACC --chain-id sei -b block -y --dry-run --keyring-backend test + - cmd: seid tx bank send $ADMIN_ACC $SIMULATION_TEST_ACC 1000sei --from $ADMIN_ACC --chain-id sei -b block -y --dry-run --keyring-backend test 2>&1 env: GAS_ESIMATE # Validate that only the 1sei is sent From 78bd267f2aa7262f736e9e0ee2240b75c058f6ac Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 10 Jun 2026 17:29:50 -0700 Subject: [PATCH 13/18] Using timeout env var --- .github/workflows/integration-test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index e6586f4622..1b21fdbd90 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -106,6 +106,7 @@ jobs: runs-on: ubuntu-large timeout-minutes: 30 env: + JOB_TIMEOUT: 28m AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} DAPP_TESTS_MNEMONIC: ${{ secrets.DAPP_TESTS_MNEMONIC }} @@ -128,7 +129,7 @@ jobs: { name: "Mint & Staking & Bank Module", scripts: [ - "go test -tags yaml_integration -v -timeout 10m ./integration_test/runner/... -run \"TestMintModule|TestStakingModule|TestBankModule\"" + "go test -tags yaml_integration -v -timeout $JOB_TIMEOUT ./integration_test/runner/... -run \"TestMintModule|TestStakingModule|TestBankModule\"" ] }, { From 4a90081412bbd525197776874e930787e4db3ca6 Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 10 Jun 2026 17:35:11 -0700 Subject: [PATCH 14/18] Used testify package --- integration_test/runner/runner.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/integration_test/runner/runner.go b/integration_test/runner/runner.go index af48993dbe..8ff33e5c48 100644 --- a/integration_test/runner/runner.go +++ b/integration_test/runner/runner.go @@ -25,6 +25,7 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) @@ -63,13 +64,9 @@ type Options struct { func RunFile(t *testing.T, path string, opts Options) { t.Helper() data, err := os.ReadFile(path) //nolint:gosec - if err != nil { - t.Fatalf("read %s: %v", path, err) - } + require.NoError(t, err, "read %s: %v", path, err) var cases []TestCase - if err = yaml.Unmarshal(data, &cases); err != nil { - t.Fatalf("unmarshal %s: %v", path, err) - } + require.NoError(t, yaml.Unmarshal(data, &cases), "unmarshal %s: %v", path, err) for _, tc := range cases { t.Run(tc.Name, func(t *testing.T) { runCase(t, tc, opts) @@ -88,9 +85,7 @@ func runCase(t *testing.T, tc TestCase, opts Options) { } out, err := execCmd(t, inp.Cmd, container, envMap, opts) t.Logf("[%d] $ %s\n => %s", i, inp.Cmd, out) - if err != nil { - t.Fatalf("input[%d] failed: %v", i, err) - } + require.NoError(t, err, "input[%d] failed: %v", i, err) if inp.Env != "" { envMap[inp.Env] = out } From 8a56818d2650a09bf58e539f40f30ebc59746998 Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 10 Jun 2026 18:12:25 -0700 Subject: [PATCH 15/18] Used options for defaults --- integration_test/runner/runner.go | 38 ++++++++++++++----- integration_test/runner/runner_test.go | 51 ++++++++++++-------------- 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/integration_test/runner/runner.go b/integration_test/runner/runner.go index 8ff33e5c48..3a243d1c07 100644 --- a/integration_test/runner/runner.go +++ b/integration_test/runner/runner.go @@ -53,23 +53,47 @@ type Verifier struct { // Options controls how RunFile executes commands. type Options struct { // DefaultContainer is the docker container used when an Input has no Node set. - // If empty, commands run locally (useful for tests that don't need docker). DefaultContainer string // ExtraPath is appended to PATH inside docker containers. - // Defaults to /root/go/bin:/root/.foundry/bin (matching runner.py). ExtraPath string } +// Option is a functional option for Options. +type Option func(*Options) + +// WithContainer sets the default docker container for inputs that don't specify one. +func WithContainer(container string) Option { + return func(o *Options) { o.DefaultContainer = container } +} + +// WithExtraPath overrides the PATH suffix injected into docker containers. +func WithExtraPath(path string) Option { + return func(o *Options) { o.ExtraPath = path } +} + +func newOptions(opts []Option) Options { + var o Options + //applying default options + for _, f := range []Option{WithContainer("sei-node-0"), WithExtraPath("/root/go/bin:/root/.foundry/bin")} { + f(&o) + } + for _, opt := range opts { + opt(&o) + } + return o +} + // RunFile reads path as a YAML list of TestCases and runs each as a subtest of t. -func RunFile(t *testing.T, path string, opts Options) { +func RunFile(t *testing.T, path string, opts ...Option) { t.Helper() + o := newOptions(opts) data, err := os.ReadFile(path) //nolint:gosec require.NoError(t, err, "read %s: %v", path, err) var cases []TestCase require.NoError(t, yaml.Unmarshal(data, &cases), "unmarshal %s: %v", path, err) for _, tc := range cases { t.Run(tc.Name, func(t *testing.T) { - runCase(t, tc, opts) + runCase(t, tc, o) }) } } @@ -107,10 +131,6 @@ func execCmd(t *testing.T, cmd, container string, envMap map[string]string, opts var c *exec.Cmd if container != "" { - extraPath := opts.ExtraPath - if extraPath == "" { - extraPath = "/root/go/bin:/root/.foundry/bin" - } // capacity: 1 ("exec") + 2*len(envMap) ("-e" + "k=v" per entry) + 4 (container, "/bin/bash", "-c", cmd) args := make([]string, 1, 1+2*len(envMap)+4) args[0] = "exec" @@ -118,7 +138,7 @@ func execCmd(t *testing.T, cmd, container string, envMap map[string]string, opts args = append(args, "-e", k+"="+v) } args = append(args, container, "/bin/bash", "-c", - "export PATH=$PATH:"+extraPath+" && "+cmd) + "export PATH=$PATH:"+opts.ExtraPath+" && "+cmd) c = exec.Command("docker", args...) //nolint:gosec } else { c = exec.Command("/bin/bash", "-c", cmd) diff --git a/integration_test/runner/runner_test.go b/integration_test/runner/runner_test.go index 84a0a5a642..e1b097bd53 100644 --- a/integration_test/runner/runner_test.go +++ b/integration_test/runner/runner_test.go @@ -23,70 +23,67 @@ import ( "github.com/sei-protocol/sei-chain/integration_test/runner" ) -// opts targets sei-node-0 as the default container, matching runner.py. -var opts = runner.Options{DefaultContainer: "sei-node-0"} - func TestStartup(t *testing.T) { - runner.RunFile(t, "../startup/startup_test.yaml", opts) + runner.RunFile(t, "../startup/startup_test.yaml") } func TestBankModule(t *testing.T) { - runner.RunFile(t, "../bank_module/send_funds_test.yaml", opts) - runner.RunFile(t, "../bank_module/multi_sig_send_test.yaml", opts) - runner.RunFile(t, "../bank_module/simulation_tx.yaml", opts) + runner.RunFile(t, "../bank_module/send_funds_test.yaml") + runner.RunFile(t, "../bank_module/multi_sig_send_test.yaml") + runner.RunFile(t, "../bank_module/simulation_tx.yaml") } func TestGovModule(t *testing.T) { - runner.RunFile(t, "../gov_module/gov_proposal_test.yaml", opts) - runner.RunFile(t, "../gov_module/staking_proposal_test.yaml", opts) + runner.RunFile(t, "../gov_module/gov_proposal_test.yaml") + runner.RunFile(t, "../gov_module/staking_proposal_test.yaml") } func TestOracleModule(t *testing.T) { - runner.RunFile(t, "../oracle_module/set_feeder_test.yaml", opts) - runner.RunFile(t, "../oracle_module/verify_penalty_counts.yaml", opts) + runner.RunFile(t, "../oracle_module/set_feeder_test.yaml") + runner.RunFile(t, "../oracle_module/verify_penalty_counts.yaml") } func TestMintModule(t *testing.T) { - runner.RunFile(t, "../mint_module/mint_test.yaml", opts) + runner.RunFile(t, "../mint_module/mint_test.yaml") } func TestStakingModule(t *testing.T) { - runner.RunFile(t, "../staking_module/staking_test.yaml", opts) + runner.RunFile(t, "../staking_module/staking_test.yaml") } func TestDistributionModule(t *testing.T) { - runner.RunFile(t, "../distribution_module/rewards.yaml", opts) - runner.RunFile(t, "../distribution_module/community_pool.yaml", opts) + runner.RunFile(t, "../distribution_module/rewards.yaml") + runner.RunFile(t, "../distribution_module/community_pool.yaml") } func TestTokenFactoryModule(t *testing.T) { - runner.RunFile(t, "../tokenfactory_module/create_tokenfactory_test.yaml", opts) + runner.RunFile(t, "../tokenfactory_module/create_tokenfactory_test.yaml") } func TestAuthzModule(t *testing.T) { - runner.RunFile(t, "../authz_module/staking_authorization_test.yaml", opts) - runner.RunFile(t, "../authz_module/send_authorization_test.yaml", opts) - runner.RunFile(t, "../authz_module/generic_authorization_test.yaml", opts) + runner.RunFile(t, "../authz_module/staking_authorization_test.yaml") + runner.RunFile(t, "../authz_module/send_authorization_test.yaml") + runner.RunFile(t, "../authz_module/generic_authorization_test.yaml") } func TestWasmModule(t *testing.T) { - runner.RunFile(t, "../wasm_module/timelocked_token_admin_test.yaml", opts) - runner.RunFile(t, "../wasm_module/timelocked_token_delegation_test.yaml", opts) - runner.RunFile(t, "../wasm_module/timelocked_token_emergency_withdraw_test.yaml", opts) - runner.RunFile(t, "../wasm_module/timelocked_token_withdraw_test.yaml", opts) + runner.RunFile(t, "../wasm_module/timelocked_token_admin_test.yaml") + runner.RunFile(t, "../wasm_module/timelocked_token_delegation_test.yaml") + runner.RunFile(t, "../wasm_module/timelocked_token_emergency_withdraw_test.yaml") + runner.RunFile(t, "../wasm_module/timelocked_token_withdraw_test.yaml") } func TestSeiDB(t *testing.T) { - runner.RunFile(t, "../seidb/flatkv_evm_test.yaml", opts) - runner.RunFile(t, "../seidb/state_store_test.yaml", opts) + runner.RunFile(t, "../seidb/flatkv_evm_test.yaml") + runner.RunFile(t, "../seidb/state_store_test.yaml") } func TestChainOperation(t *testing.T) { - runner.RunFile(t, "../chain_operation/snapshot_operation.yaml", opts) + runner.RunFile(t, "../chain_operation/snapshot_operation.yaml") } // TestStateSync requires the sei-rpc-node container, which is only present in // the CI RPC-node cluster (make run-rpc-node-integration-ci). func TestStateSync(t *testing.T) { - runner.RunFile(t, "../chain_operation/statesync_operation.yaml", opts) + runner.RunFile(t, "../chain_operation/statesync_operation.yaml") } From 4375d09c6a2076a8e4e67c0b8c239eecf8ef20d4 Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Wed, 10 Jun 2026 19:17:34 -0700 Subject: [PATCH 16/18] Made shell configurable --- integration_test/runner/runner.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/integration_test/runner/runner.go b/integration_test/runner/runner.go index 3a243d1c07..9020e9e8a6 100644 --- a/integration_test/runner/runner.go +++ b/integration_test/runner/runner.go @@ -56,6 +56,9 @@ type Options struct { DefaultContainer string // ExtraPath is appended to PATH inside docker containers. ExtraPath string + // Shell is the shell used to execute commands (e.g. "sh", "bash"). + // Resolved via PATH at runtime. Defaults to "sh". + Shell string } // Option is a functional option for Options. @@ -71,10 +74,15 @@ func WithExtraPath(path string) Option { return func(o *Options) { o.ExtraPath = path } } +// WithShell overrides the shell used to execute commands. Resolved via PATH at runtime. +func WithShell(shell string) Option { + return func(o *Options) { o.Shell = shell } +} + func newOptions(opts []Option) Options { var o Options //applying default options - for _, f := range []Option{WithContainer("sei-node-0"), WithExtraPath("/root/go/bin:/root/.foundry/bin")} { + for _, f := range []Option{WithContainer("sei-node-0"), WithExtraPath("/root/go/bin:/root/.foundry/bin"), WithShell("sh")} { f(&o) } for _, opt := range opts { @@ -137,11 +145,11 @@ func execCmd(t *testing.T, cmd, container string, envMap map[string]string, opts for k, v := range envMap { args = append(args, "-e", k+"="+v) } - args = append(args, container, "/bin/bash", "-c", + args = append(args, container, opts.Shell, "-c", "export PATH=$PATH:"+opts.ExtraPath+" && "+cmd) c = exec.Command("docker", args...) //nolint:gosec } else { - c = exec.Command("/bin/bash", "-c", cmd) + c = exec.Command(opts.Shell, "-c", cmd) //nolint:gosec c.Env = append(os.Environ(), envMapSlice(envMap)...) } From 867f59cfb7010ece266e520eca3d34ee7e198583 Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Thu, 11 Jun 2026 10:59:21 -0700 Subject: [PATCH 17/18] changed test order to match the old python version --- integration_test/runner/runner_test.go | 29 ++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/integration_test/runner/runner_test.go b/integration_test/runner/runner_test.go index e1b097bd53..a2bbb363c8 100644 --- a/integration_test/runner/runner_test.go +++ b/integration_test/runner/runner_test.go @@ -27,33 +27,36 @@ func TestStartup(t *testing.T) { runner.RunFile(t, "../startup/startup_test.yaml") } +// Tests are declared in the order the CI matrix ran them as Python scripts +// (go test executes tests in declaration order): staking, then bank, then mint. + +func TestStakingModule(t *testing.T) { + runner.RunFile(t, "../staking_module/staking_test.yaml") +} + func TestBankModule(t *testing.T) { runner.RunFile(t, "../bank_module/send_funds_test.yaml") runner.RunFile(t, "../bank_module/multi_sig_send_test.yaml") runner.RunFile(t, "../bank_module/simulation_tx.yaml") } +func TestMintModule(t *testing.T) { + runner.RunFile(t, "../mint_module/mint_test.yaml") +} + func TestGovModule(t *testing.T) { runner.RunFile(t, "../gov_module/gov_proposal_test.yaml") runner.RunFile(t, "../gov_module/staking_proposal_test.yaml") } func TestOracleModule(t *testing.T) { - runner.RunFile(t, "../oracle_module/set_feeder_test.yaml") runner.RunFile(t, "../oracle_module/verify_penalty_counts.yaml") -} - -func TestMintModule(t *testing.T) { - runner.RunFile(t, "../mint_module/mint_test.yaml") -} - -func TestStakingModule(t *testing.T) { - runner.RunFile(t, "../staking_module/staking_test.yaml") + runner.RunFile(t, "../oracle_module/set_feeder_test.yaml") } func TestDistributionModule(t *testing.T) { - runner.RunFile(t, "../distribution_module/rewards.yaml") runner.RunFile(t, "../distribution_module/community_pool.yaml") + runner.RunFile(t, "../distribution_module/rewards.yaml") } func TestTokenFactoryModule(t *testing.T) { @@ -61,16 +64,16 @@ func TestTokenFactoryModule(t *testing.T) { } func TestAuthzModule(t *testing.T) { - runner.RunFile(t, "../authz_module/staking_authorization_test.yaml") runner.RunFile(t, "../authz_module/send_authorization_test.yaml") + runner.RunFile(t, "../authz_module/staking_authorization_test.yaml") runner.RunFile(t, "../authz_module/generic_authorization_test.yaml") } func TestWasmModule(t *testing.T) { - runner.RunFile(t, "../wasm_module/timelocked_token_admin_test.yaml") runner.RunFile(t, "../wasm_module/timelocked_token_delegation_test.yaml") - runner.RunFile(t, "../wasm_module/timelocked_token_emergency_withdraw_test.yaml") + runner.RunFile(t, "../wasm_module/timelocked_token_admin_test.yaml") runner.RunFile(t, "../wasm_module/timelocked_token_withdraw_test.yaml") + runner.RunFile(t, "../wasm_module/timelocked_token_emergency_withdraw_test.yaml") } func TestSeiDB(t *testing.T) { From a4ead107524058719b3ac8904fe4060c27bea6e8 Mon Sep 17 00:00:00 2001 From: Amir Deris Date: Thu, 11 Jun 2026 11:07:07 -0700 Subject: [PATCH 18/18] Fixed the shell issue --- integration_test/runner/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_test/runner/runner.go b/integration_test/runner/runner.go index 9020e9e8a6..94717d2811 100644 --- a/integration_test/runner/runner.go +++ b/integration_test/runner/runner.go @@ -82,7 +82,7 @@ func WithShell(shell string) Option { func newOptions(opts []Option) Options { var o Options //applying default options - for _, f := range []Option{WithContainer("sei-node-0"), WithExtraPath("/root/go/bin:/root/.foundry/bin"), WithShell("sh")} { + for _, f := range []Option{WithContainer("sei-node-0"), WithExtraPath("/root/go/bin:/root/.foundry/bin"), WithShell("bash")} { f(&o) } for _, opt := range opts {