Make system tests extendable for other app binaries

Please enter the commit message for your changes. Lines starting
This commit is contained in:
Alex Peters
2023-07-14 14:30:21 +02:00
parent 52a7a6ad2c
commit d7df23153a
7 changed files with 156 additions and 94 deletions

View File

@@ -40,33 +40,52 @@ type WasmdCli struct {
assertErrorFn RunErrorAssert
awaitNextBlock awaitNextBlock
expTXCommitted bool
execBinary string
}
// NewWasmdCLI constructor
func NewWasmdCLI(t *testing.T, sut *SystemUnderTest, verbose bool) *WasmdCli {
return NewWasmdCLIx(t, sut.rpcAddr, sut.chainID, sut.AwaitNextBlock, filepath.Join(workDir, sut.outputDir), "1"+sdk.DefaultBondDenom, verbose)
return NewWasmdCLIx(
t,
sut.execBinary,
sut.rpcAddr,
sut.chainID,
sut.AwaitNextBlock,
filepath.Join(workDir, sut.outputDir),
"1"+sdk.DefaultBondDenom,
verbose,
assert.NoError,
true,
)
}
// NewWasmdCLIx extended constructor
func NewWasmdCLIx(
t *testing.T,
execBinary string,
nodeAddress string,
chainID string,
awaiter awaitNextBlock,
homeDir string,
fees string,
debug bool,
assertErrorFn RunErrorAssert,
expTXCommitted bool,
) *WasmdCli {
if strings.TrimSpace(execBinary) == "" {
panic("executable binary name must not be empty")
}
return &WasmdCli{
t: t,
execBinary: execBinary,
nodeAddress: nodeAddress,
chainID: chainID,
homeDir: homeDir,
Debug: debug,
assertErrorFn: assert.NoError,
awaitNextBlock: awaiter,
fees: fees,
expTXCommitted: true,
assertErrorFn: assertErrorFn,
expTXCommitted: expTXCommitted,
}
}
@@ -79,31 +98,48 @@ func (c WasmdCli) WithRunErrorsIgnored() WasmdCli {
// WithRunErrorMatcher assert function to ensure run command error value
func (c WasmdCli) WithRunErrorMatcher(f RunErrorAssert) WasmdCli {
return WasmdCli{
t: c.t,
nodeAddress: c.nodeAddress,
chainID: c.chainID,
homeDir: c.homeDir,
Debug: c.Debug,
assertErrorFn: f,
awaitNextBlock: c.awaitNextBlock,
fees: c.fees,
expTXCommitted: c.expTXCommitted,
}
return *NewWasmdCLIx(
c.t,
c.execBinary,
c.nodeAddress,
c.chainID,
c.awaitNextBlock,
c.homeDir,
c.fees,
c.Debug,
f,
c.expTXCommitted,
)
}
func (c WasmdCli) WithNodeAddress(addr string) WasmdCli {
return WasmdCli{
t: c.t,
nodeAddress: addr,
chainID: c.chainID,
homeDir: c.homeDir,
Debug: c.Debug,
assertErrorFn: c.assertErrorFn,
awaitNextBlock: c.awaitNextBlock,
fees: c.fees,
expTXCommitted: c.expTXCommitted,
}
func (c WasmdCli) WithNodeAddress(nodeAddr string) WasmdCli {
return *NewWasmdCLIx(
c.t,
c.execBinary,
nodeAddr,
c.chainID,
c.awaitNextBlock,
c.homeDir,
c.fees,
c.Debug,
c.assertErrorFn,
c.expTXCommitted,
)
}
func (c WasmdCli) WithAssertTXUncommitted() WasmdCli {
return *NewWasmdCLIx(
c.t,
c.execBinary,
c.nodeAddress,
c.chainID,
c.awaitNextBlock,
c.homeDir,
c.fees,
c.Debug,
c.assertErrorFn,
false,
)
}
// CustomCommand main entry for executing wasmd cli commands.
@@ -160,9 +196,8 @@ func (c WasmdCli) CustomQuery(args ...string) string {
// execute shell command
func (c WasmdCli) run(args []string) (output string, ok bool) {
// todo assert error???
if c.Debug {
c.t.Logf("+++ running `wasmd %s`", strings.Join(args, " "))
c.t.Logf("+++ running `%s %s`", c.execBinary, strings.Join(args, " "))
}
gotOut, gotErr := func() (out []byte, err error) {
defer func() {

View File

@@ -94,18 +94,7 @@ func TestVestingAccounts(t *testing.T) {
assert.Equal(t, myStartTimestamp, accounts[0].Get("start_time").Int())
// check accounts have some balances
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100000000))), getGenesisBalance([]byte(raw), vest1Addr))
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100000001))), getGenesisBalance([]byte(raw), vest2Addr))
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(200000002))), getGenesisBalance([]byte(raw), vest3Addr))
}
func getGenesisBalance(raw []byte, addr string) sdk.Coins {
var r []sdk.Coin
balances := gjson.GetBytes(raw, fmt.Sprintf(`app_state.bank.balances.#[address==%q]#.coins`, addr)).Array()
for _, coins := range balances {
for _, coin := range coins.Array() {
r = append(r, sdk.NewCoin(coin.Get("denom").String(), sdk.NewInt(coin.Get("amount").Int())))
}
}
return r
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100000000))), GetGenesisBalance([]byte(raw), vest1Addr))
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(100000001))), GetGenesisBalance([]byte(raw), vest2Addr))
assert.Equal(t, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(200000002))), GetGenesisBalance([]byte(raw), vest3Addr))
}

View File

@@ -41,8 +41,10 @@ func TestRecursiveMsgsExternalTrigger(t *testing.T) {
fees = calcMinFeeRequired(t, gas)
}
for _, n := range sut.AllNodes(t) {
clix := cli.WithRunErrorMatcher(spec.expErrMatcher).WithNodeAddress(n.RPCAddr())
clix.expTXCommitted = false
clix := cli.
WithRunErrorMatcher(spec.expErrMatcher).
WithNodeAddress(n.RPCAddr()).
WithAssertTXUncommitted()
clix.WasmExecute(contractAddr, execMsg, defaultSrcAddr, "--gas="+gas, "--broadcast-mode=sync", "--fees="+fees)
}
sut.AwaitNextBlock(t)

View File

@@ -0,0 +1,33 @@
package system
import (
"fmt"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
// SetConsensusMaxGas max gas that can be consumed in a block
func SetConsensusMaxGas(t *testing.T, max int) GenesisMutator {
return func(genesis []byte) []byte {
t.Helper()
state, err := sjson.SetRawBytes(genesis, "consensus_params.block.max_gas", []byte(fmt.Sprintf(`"%d"`, max)))
require.NoError(t, err)
return state
}
}
// GetGenesisBalance return the balance amount for an address from the given genesis json
func GetGenesisBalance(rawGenesis []byte, addr string) sdk.Coins {
var r []sdk.Coin
balances := gjson.GetBytes(rawGenesis, fmt.Sprintf(`app_state.bank.balances.#[address==%q]#.coins`, addr)).Array()
for _, coins := range balances {
for _, coin := range coins.Array() {
r = append(r, sdk.NewCoin(coin.Get("denom").String(), sdk.NewInt(coin.Get("amount").Int())))
}
}
return r
}

View File

@@ -1,19 +0,0 @@
package system
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/tidwall/sjson"
)
// SetConsensusMaxGas max gas that can be consumed in a block
func SetConsensusMaxGas(t *testing.T, max int) GenesisMutator {
return func(genesis []byte) []byte {
t.Helper()
state, err := sjson.SetRawBytes(genesis, "consensus_params.block.max_gas", []byte(fmt.Sprintf(`"%d"`, max)))
require.NoError(t, err)
return state
}
}

View File

@@ -23,22 +23,13 @@ var (
verbose bool
)
func init() {
InitSDKConfig("wasm")
}
func InitSDKConfig(bech32Prefix string) {
config := sdk.GetConfig()
config.SetBech32PrefixForAccount(bech32Prefix, bech32Prefix+sdk.PrefixPublic)
config.SetBech32PrefixForValidator(bech32Prefix+sdk.PrefixValidator+sdk.PrefixOperator, bech32Prefix+sdk.PrefixValidator+sdk.PrefixOperator+sdk.PrefixPublic)
config.SetBech32PrefixForConsensusNode(bech32Prefix+sdk.PrefixValidator+sdk.PrefixConsensus, bech32Prefix+sdk.PrefixValidator+sdk.PrefixConsensus+sdk.PrefixPublic)
}
func TestMain(m *testing.M) {
rebuild := flag.Bool("rebuild", false, "rebuild artifacts")
waitTime := flag.Duration("wait-time", defaultWaitTime, "time to wait for chain events")
nodesCount := flag.Int("nodes-count", 4, "number of nodes in the cluster")
blockTime := flag.Duration("block-time", 1000*time.Millisecond, "block creation time")
execBinary := flag.String("binary", "wasmd", "executable binary for server/ client side")
bech32Prefix := flag.String("bech32", "wasm", "bech32 prefix to be used with addresses")
flag.BoolVar(&verbose, "verbose", false, "verbose output")
flag.Parse()
@@ -53,8 +44,13 @@ func TestMain(m *testing.M) {
if verbose {
println("Work dir: ", workDir)
}
initSDKConfig(*bech32Prefix)
defaultWaitTime = *waitTime
sut = NewSystemUnderTest(verbose, *nodesCount, *blockTime)
if *execBinary == "" {
panic("executable binary name must not be empty")
}
sut = NewSystemUnderTest(*execBinary, verbose, *nodesCount, *blockTime)
if *rebuild {
sut.BuildNewBinary()
}
@@ -98,6 +94,13 @@ func requireEnoughFileHandlers(nodesCount int) {
return
}
func initSDKConfig(bech32Prefix string) {
config := sdk.GetConfig()
config.SetBech32PrefixForAccount(bech32Prefix, bech32Prefix+sdk.PrefixPublic)
config.SetBech32PrefixForValidator(bech32Prefix+sdk.PrefixValidator+sdk.PrefixOperator, bech32Prefix+sdk.PrefixValidator+sdk.PrefixOperator+sdk.PrefixPublic)
config.SetBech32PrefixForConsensusNode(bech32Prefix+sdk.PrefixValidator+sdk.PrefixConsensus, bech32Prefix+sdk.PrefixValidator+sdk.PrefixConsensus+sdk.PrefixPublic)
}
const (
successFlag = `
___ _ _ ___ ___ ___ ___ ___

View File

@@ -33,6 +33,7 @@ var workDir string
// SystemUnderTest blockchain provisioning
type SystemUnderTest struct {
execBinary string
blockListener *EventListener
currentHeight int64
chainID string
@@ -51,9 +52,13 @@ type SystemUnderTest struct {
dirty bool // requires full reset when marked dirty
}
func NewSystemUnderTest(verbose bool, nodesCount int, blockTime time.Duration) *SystemUnderTest {
func NewSystemUnderTest(execBinary string, verbose bool, nodesCount int, blockTime time.Duration) *SystemUnderTest {
if execBinary == "" {
panic("executable binary name must not be empty")
}
return &SystemUnderTest{
chainID: "testing",
execBinary: execBinary,
outputDir: "./testnet",
blockTime: blockTime,
rpcAddr: "tcp://localhost:26657",
@@ -84,9 +89,9 @@ func (s *SystemUnderTest) SetupChain() {
"--starting-ip-address", "", // empty to use host systems
"--single-host",
}
println("+++ wasmd " + strings.Join(args, " "))
fmt.Printf("+++ %s %s", s.execBinary, strings.Join(args, " "))
cmd := exec.Command( //nolint:gosec
locateExecutable("wasmd"),
locateExecutable(s.execBinary),
args...,
)
cmd.Dir = workDir
@@ -193,7 +198,7 @@ func appendToBuf(r io.Reader, b *ring.Ring, stop <-chan struct{}) {
}
text := scanner.Text()
// filter out noise
if strings.Contains(text, "module=rpc-server protocol=websocket") {
if isLogNoise(text) {
continue
}
b.Value = text
@@ -201,6 +206,17 @@ func appendToBuf(r io.Reader, b *ring.Ring, stop <-chan struct{}) {
}
}
func isLogNoise(text string) bool {
for _, v := range []string{
"\x1b[36mmodule=\x1b[0mrpc-server", // "module=rpc-server",
} {
if strings.Contains(text, v) {
return true
}
}
return false
}
// AwaitNodeUp ensures the node is running
func (s *SystemUnderTest) AwaitNodeUp(t *testing.T, rpcAddr string) {
t.Helper()
@@ -249,7 +265,7 @@ func (s *SystemUnderTest) StopChain() {
}
s.cleanupFn = nil
// send SIGTERM
cmd := exec.Command(locateExecutable("pkill"), "-15", "wasmd") //nolint:gosec
cmd := exec.Command(locateExecutable("pkill"), "-15", s.execBinary) //nolint:gosec
cmd.Dir = workDir
if _, err := cmd.CombinedOutput(); err != nil {
s.Logf("failed to stop chain: %s\n", err)
@@ -260,14 +276,14 @@ func (s *SystemUnderTest) StopChain() {
select {
case <-timeout:
s.Log("killing nodes now")
cmd = exec.Command(locateExecutable("pkill"), "-9", "wasmd") //nolint:gosec
cmd = exec.Command(locateExecutable("pkill"), "-9", s.execBinary) //nolint:gosec
cmd.Dir = workDir
if _, err := cmd.CombinedOutput(); err != nil {
s.Logf("failed to kill process: %s\n", err)
}
shutdown = true
default:
if err := exec.Command(locateExecutable("pgrep"), "wasmd").Run(); err != nil { //nolint:gosec
if err := exec.Command(locateExecutable("pgrep"), s.execBinary).Run(); err != nil { //nolint:gosec
shutdown = true
}
}
@@ -290,7 +306,7 @@ func (s SystemUnderTest) PrintBuffer() {
})
}
// BuildNewBinary builds and installs new wasmd binary
// BuildNewBinary builds and installs new executable binary
func (s SystemUnderTest) BuildNewBinary() {
s.Log("Install binaries\n")
makePath := locateExecutable("make")
@@ -431,7 +447,7 @@ func saveGenesis(home string, content []byte) error {
return nil
}
// ForEachNodeExecAndWait runs the given wasmd commands for all cluster nodes synchronously
// ForEachNodeExecAndWait runs the given app executable commands for all cluster nodes synchronously
// The commands output is returned for each node.
func (s *SystemUnderTest) ForEachNodeExecAndWait(t *testing.T, cmds ...[]string) [][]string {
result := make([][]string, s.nodesCount)
@@ -439,9 +455,9 @@ func (s *SystemUnderTest) ForEachNodeExecAndWait(t *testing.T, cmds ...[]string)
result[i] = make([]string, len(cmds))
for j, xargs := range cmds {
xargs = append(xargs, "--home", home)
s.Logf("Execute `wasmd %s`\n", strings.Join(xargs, " "))
s.Logf("Execute `%s %s`\n", s.execBinary, strings.Join(xargs, " "))
cmd := exec.Command( //nolint:gosec
locateExecutable("wasmd"),
locateExecutable(s.execBinary),
xargs...,
)
cmd.Dir = workDir
@@ -454,14 +470,14 @@ func (s *SystemUnderTest) ForEachNodeExecAndWait(t *testing.T, cmds ...[]string)
return result
}
// forEachNodesExecAsync runs the given wasmd command for all cluster nodes and returns without waiting
// forEachNodesExecAsync runs the given app cli command for all cluster nodes and returns without waiting
func (s *SystemUnderTest) forEachNodesExecAsync(t *testing.T, xargs ...string) []func() error {
r := make([]func() error, s.nodesCount)
s.withEachNodeHome(func(i int, home string) {
args := append(xargs, "--home", home) //nolint:gocritic
s.Logf("Execute `wasmd %s`\n", strings.Join(args, " "))
s.Logf("Execute `%s %s`\n", s.execBinary, strings.Join(args, " "))
cmd := exec.Command( //nolint:gosec
locateExecutable("wasmd"),
locateExecutable(s.execBinary),
args...,
)
cmd.Dir = workDir
@@ -480,7 +496,7 @@ func (s SystemUnderTest) withEachNodeHome(cb func(i int, home string)) {
// nodePath returns the path of the node within the work dir. not absolute
func (s SystemUnderTest) nodePath(i int) string {
return fmt.Sprintf("%s/node%d/wasmd", s.outputDir, i)
return fmt.Sprintf("%s/node%d/%s", s.outputDir, i, s.execBinary)
}
func (s SystemUnderTest) Log(msg string) {
@@ -538,9 +554,9 @@ func (s *SystemUnderTest) AddFullnode(t *testing.T, beforeStart ...func(nodeNumb
// prepare new node
moniker := fmt.Sprintf("node%d", nodeNumber)
args := []string{"init", moniker, "--home", nodePath, "--overwrite"}
s.Logf("Execute `wasmd %s`\n", strings.Join(args, " "))
s.Logf("Execute `%s %s`\n", s.execBinary, strings.Join(args, " "))
cmd := exec.Command( //nolint:gosec
locateExecutable("wasmd"),
locateExecutable(s.execBinary),
args...,
)
cmd.Dir = workDir
@@ -575,9 +591,9 @@ func (s *SystemUnderTest) AddFullnode(t *testing.T, beforeStart ...func(nodeNumb
"--log_level=info",
"--home", nodePath,
}
s.Logf("Execute `wasmd %s`\n", strings.Join(args, " "))
s.Logf("Execute `%s %s`\n", s.execBinary, strings.Join(args, " "))
cmd = exec.Command( //nolint:gosec
locateExecutable("wasmd"),
locateExecutable(s.execBinary),
args...,
)
cmd.Dir = workDir
@@ -608,6 +624,9 @@ func (n Node) RPCAddr() string {
// locateExecutable looks up the binary on the OS path.
func locateExecutable(file string) string {
if strings.TrimSpace(file) == "" {
panic("executable binary name must not be empty")
}
path, err := exec.LookPath(file)
if err != nil {
panic(fmt.Sprintf("unexpected error %s", err.Error()))