cosmos-sdk/cmd/gaia/cli_test/test_helpers.go

559 lines
20 KiB
Go
Raw Normal View History

package clitest
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
)
const (
denom = "stake"
keyFoo = "foo"
keyBar = "bar"
fooDenom = "footoken"
feeDenom = "feetoken"
)
var startCoins = sdk.Coins{
sdk.NewInt64Coin(feeDenom, 1000),
sdk.NewInt64Coin(fooDenom, 1000),
sdk.NewInt64Coin(denom, 150),
}
//___________________________________________________________________________________
// Fixtures
// Fixtures is used to setup the testing environment
type Fixtures struct {
ChainID string
RPCAddr string
Port string
GDHome string
GCLIHome string
P2PAddr string
T *testing.T
}
// NewFixtures creates a new instance of Fixtures with many vars set
func NewFixtures(t *testing.T) *Fixtures {
tmpDir := os.TempDir()
gaiadHome := fmt.Sprintf("%s%s%s%s.test_gaiad", tmpDir, string(os.PathSeparator), t.Name(), string(os.PathSeparator))
gaiacliHome := fmt.Sprintf("%s%s%s%s.test_gaiacli", tmpDir, string(os.PathSeparator), t.Name(), string(os.PathSeparator))
servAddr, port, err := server.FreeTCPAddr()
require.NoError(t, err)
p2pAddr, _, err := server.FreeTCPAddr()
require.NoError(t, err)
return &Fixtures{
T: t,
GDHome: gaiadHome,
GCLIHome: gaiacliHome,
RPCAddr: servAddr,
P2PAddr: p2pAddr,
Port: port,
}
}
// InitFixtures is called at the beginning of a test
// and initializes a chain with 1 validator
func InitFixtures(t *testing.T) (f *Fixtures) {
f = NewFixtures(t)
// Reset test state
f.UnsafeResetAll()
// Ensure keystore has foo and bar keys
f.KeysDelete(keyFoo)
f.KeysDelete(keyBar)
f.KeysAdd(keyFoo)
f.KeysAdd(keyBar)
// Ensure that CLI output is in JSON format
f.CLIConfig("output", "json")
// NOTE: GDInit sets the ChainID
f.GDInit(keyFoo)
// Start an account with tokens
f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins)
f.GenTx(keyFoo)
f.CollectGenTxs()
return
}
// Cleanup is meant to be run at the end of a test to clean up an remaining test state
func (f *Fixtures) Cleanup(dirs ...string) {
clean := append(dirs, f.GDHome, f.GCLIHome)
for _, d := range clean {
err := os.RemoveAll(d)
require.NoError(f.T, err)
}
}
// Flags returns the flags necessary for making most CLI calls
func (f *Fixtures) Flags() string {
return fmt.Sprintf("--home=%s --node=%s --chain-id=%s", f.GCLIHome, f.RPCAddr, f.ChainID)
}
//___________________________________________________________________________________
// gaiad
// UnsafeResetAll is gaiad unsafe-reset-all
func (f *Fixtures) UnsafeResetAll(flags ...string) {
cmd := fmt.Sprintf("gaiad --home=%s unsafe-reset-all", f.GDHome)
executeWrite(f.T, addFlags(cmd, flags))
err := os.RemoveAll(filepath.Join(f.GDHome, "config", "gentx"))
require.NoError(f.T, err)
}
// GDInit is gaiad init
// NOTE: GDInit sets the ChainID for the Fixtures instance
func (f *Fixtures) GDInit(moniker string, flags ...string) {
cmd := fmt.Sprintf("gaiad init -o --moniker=%s --home=%s", moniker, f.GDHome)
_, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
var chainID string
var initRes map[string]json.RawMessage
err := json.Unmarshal([]byte(stderr), &initRes)
require.NoError(f.T, err)
err = json.Unmarshal(initRes["chain_id"], &chainID)
require.NoError(f.T, err)
f.ChainID = chainID
}
// AddGenesisAccount is gaiad add-genesis-account
func (f *Fixtures) AddGenesisAccount(address sdk.AccAddress, coins sdk.Coins, flags ...string) {
cmd := fmt.Sprintf("gaiad add-genesis-account %s %s --home=%s", address, coins, f.GDHome)
executeWriteCheckErr(f.T, addFlags(cmd, flags))
}
// GenTx is gaiad gentx
func (f *Fixtures) GenTx(name string, flags ...string) {
cmd := fmt.Sprintf("gaiad gentx --name=%s --home=%s --home-client=%s", name, f.GDHome, f.GCLIHome)
executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
// CollectGenTxs is gaiad collect-gentxs
func (f *Fixtures) CollectGenTxs(flags ...string) {
cmd := fmt.Sprintf("gaiad collect-gentxs --home=%s", f.GDHome)
executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
// GDStart runs gaiad start with the appropriate flags and returns a process
func (f *Fixtures) GDStart(flags ...string) *tests.Process {
cmd := fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.GDHome, f.RPCAddr, f.P2PAddr)
proc := tests.GoExecuteTWithStdout(f.T, addFlags(cmd, flags))
tests.WaitForTMStart(f.Port)
tests.WaitForNextNBlocksTM(1, f.Port)
return proc
}
//___________________________________________________________________________________
// gaiacli keys
// KeysDelete is gaiacli keys delete
func (f *Fixtures) KeysDelete(name string, flags ...string) {
cmd := fmt.Sprintf("gaiacli keys delete --home=%s %s", f.GCLIHome, name)
executeWrite(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
// KeysAdd is gaiacli keys add
func (f *Fixtures) KeysAdd(name string, flags ...string) {
cmd := fmt.Sprintf("gaiacli keys add --home=%s %s", f.GCLIHome, name)
executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
// KeysShow is gaiacli keys show
func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput {
cmd := fmt.Sprintf("gaiacli keys show --home=%s %s", f.GCLIHome, name)
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var ko keys.KeyOutput
err := keys.UnmarshalJSON([]byte(out), &ko)
require.NoError(f.T, err)
return ko
}
// KeyAddress returns the SDK account address from the key
func (f *Fixtures) KeyAddress(name string) sdk.AccAddress {
ko := f.KeysShow(name)
accAddr, err := sdk.AccAddressFromBech32(ko.Address)
require.NoError(f.T, err)
return accAddr
}
//___________________________________________________________________________________
// gaiacli config
// CLIConfig is gaiacli config
func (f *Fixtures) CLIConfig(key, value string, flags ...string) {
cmd := fmt.Sprintf("gaiacli config --home=%s %s %s", f.GCLIHome, key, value)
executeWriteCheckErr(f.T, addFlags(cmd, flags))
}
//___________________________________________________________________________________
// gaiacli tx send/sign/broadcast
// TxSend is gaiacli tx send
func (f *Fixtures) TxSend(from string, to sdk.AccAddress, amount sdk.Coin, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx send %v --amount=%s --to=%s --from=%s", f.Flags(), amount, to, from)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
// TxSign is gaiacli tx sign
func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx sign %v --name=%s %v", f.Flags(), signer, fileName)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
// TxBroadcast is gaiacli tx sign
func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx broadcast %v --json %v", f.Flags(), fileName)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
//___________________________________________________________________________________
// gaiacli tx stake
// TxStakeCreateValidator is gaiacli tx stake create-validator
func (f *Fixtures) TxStakeCreateValidator(from, consPubKey string, amount sdk.Coin, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx stake create-validator %v --from=%s --pubkey=%s", f.Flags(), from, consPubKey)
cmd += fmt.Sprintf(" --amount=%v --moniker=%v --commission-rate=%v", amount, from, "0.05")
cmd += fmt.Sprintf(" --commission-max-rate=%v --commission-max-change-rate=%v", "0.20", "0.10")
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
// TxStakeUnbond is gaiacli tx stake unbond
func (f *Fixtures) TxStakeUnbond(from, shares string, validator sdk.ValAddress, flags ...string) bool {
cmd := fmt.Sprintf("gaiacli tx stake unbond %v --from=%s --validator=%s --shares-amount=%v", f.Flags(), from, validator, shares)
return executeWrite(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
//___________________________________________________________________________________
// gaiacli tx gov
// TxGovSubmitProposal is gaiacli tx gov submit-proposal
func (f *Fixtures) TxGovSubmitProposal(from, typ, title, description string, deposit sdk.Coin, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx gov submit-proposal %v --from=%s --type=%s", f.Flags(), from, typ)
cmd += fmt.Sprintf(" --title=%s --description=%s --deposit=%s", title, description, deposit)
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
// TxGovDeposit is gaiacli tx gov deposit
func (f *Fixtures) TxGovDeposit(proposalID int, from string, amount sdk.Coin, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx gov deposit %d %s --from=%s %v", proposalID, amount, from, f.Flags())
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
// TxGovVote is gaiacli tx gov vote
func (f *Fixtures) TxGovVote(proposalID int, option gov.VoteOption, from string, flags ...string) (bool, string, string) {
cmd := fmt.Sprintf("gaiacli tx gov vote %d %s --from=%s %v", proposalID, option, from, f.Flags())
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
}
//___________________________________________________________________________________
// gaiacli query account
// QueryAccount is gaiacli query account
func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) auth.BaseAccount {
cmd := fmt.Sprintf("gaiacli query account %s %v", address, f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var initRes map[string]json.RawMessage
err := json.Unmarshal([]byte(out), &initRes)
require.NoError(f.T, err, "out %v, err %v", out, err)
value := initRes["value"]
var acc auth.BaseAccount
cdc := codec.New()
codec.RegisterCrypto(cdc)
err = cdc.UnmarshalJSON(value, &acc)
require.NoError(f.T, err, "value %v, err %v", string(value), err)
return acc
}
//___________________________________________________________________________________
// gaiacli query txs
// QueryTxs is gaiacli query txs
func (f *Fixtures) QueryTxs(tags ...string) []tx.Info {
cmd := fmt.Sprintf("gaiacli query txs --tags='%s' %v", queryTags(tags), f.Flags())
out, _ := tests.ExecuteT(f.T, cmd, "")
var txs []tx.Info
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &txs)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return txs
}
//___________________________________________________________________________________
// gaiacli query stake
// QueryStakeValidator is gaiacli query stake validator
func (f *Fixtures) QueryStakeValidator(valAddr sdk.ValAddress, flags ...string) stake.Validator {
cmd := fmt.Sprintf("gaiacli query stake validator %s %v", valAddr, f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var validator stake.Validator
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &validator)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return validator
}
// QueryStakeUnbondingDelegationsFrom is gaiacli query stake unbonding-delegations-from
func (f *Fixtures) QueryStakeUnbondingDelegationsFrom(valAddr sdk.ValAddress, flags ...string) []stake.UnbondingDelegation {
cmd := fmt.Sprintf("gaiacli query stake unbonding-delegations-from %s %v", valAddr, f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var ubds []stake.UnbondingDelegation
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &ubds)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return ubds
}
// QueryStakeDelegationsTo is gaiacli query stake delegations-to
func (f *Fixtures) QueryStakeDelegationsTo(valAddr sdk.ValAddress, flags ...string) []stake.Delegation {
cmd := fmt.Sprintf("gaiacli query stake delegations-to %s %v", valAddr, f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var delegations []stake.Delegation
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &delegations)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return delegations
}
// QueryStakePool is gaiacli query stake pool
func (f *Fixtures) QueryStakePool(flags ...string) stake.Pool {
cmd := fmt.Sprintf("gaiacli query stake pool %v", f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var pool stake.Pool
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &pool)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return pool
}
// QueryStakeParameters is gaiacli query stake parameters
func (f *Fixtures) QueryStakeParameters(flags ...string) stake.Params {
cmd := fmt.Sprintf("gaiacli query stake parameters %v", f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var params stake.Params
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &params)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return params
}
//___________________________________________________________________________________
// gaiacli query gov
// QueryGovParamDeposit is gaiacli query gov param deposit
func (f *Fixtures) QueryGovParamDeposit() gov.DepositParams {
cmd := fmt.Sprintf("gaiacli query gov param deposit %s", f.Flags())
out, _ := tests.ExecuteT(f.T, cmd, "")
var depositParam gov.DepositParams
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &depositParam)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return depositParam
}
// QueryGovParamVoting is gaiacli query gov param voting
func (f *Fixtures) QueryGovParamVoting() gov.VotingParams {
cmd := fmt.Sprintf("gaiacli query gov param voting %s", f.Flags())
out, _ := tests.ExecuteT(f.T, cmd, "")
var votingParam gov.VotingParams
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &votingParam)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return votingParam
}
// QueryGovParamTallying is gaiacli query gov param tallying
func (f *Fixtures) QueryGovParamTallying() gov.TallyParams {
cmd := fmt.Sprintf("gaiacli query gov param tallying %s", f.Flags())
out, _ := tests.ExecuteT(f.T, cmd, "")
var tallyingParam gov.TallyParams
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &tallyingParam)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return tallyingParam
}
// QueryGovProposals is gaiacli query gov proposals
func (f *Fixtures) QueryGovProposals(flags ...string) string {
cmd := fmt.Sprintf("gaiacli query gov proposals %v", f.Flags())
stdout, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
require.Empty(f.T, stderr)
return stdout
}
// QueryGovProposal is gaiacli query gov proposal
func (f *Fixtures) QueryGovProposal(proposalID int, flags ...string) gov.Proposal {
cmd := fmt.Sprintf("gaiacli query gov proposal %d %v", proposalID, f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var proposal gov.Proposal
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &proposal)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return proposal
}
// QueryGovVote is gaiacli query gov vote
func (f *Fixtures) QueryGovVote(proposalID int, voter sdk.AccAddress, flags ...string) gov.Vote {
cmd := fmt.Sprintf("gaiacli query gov vote %d %s %v", proposalID, voter, f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var vote gov.Vote
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &vote)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return vote
}
// QueryGovVotes is gaiacli query gov votes
func (f *Fixtures) QueryGovVotes(proposalID int, flags ...string) []gov.Vote {
cmd := fmt.Sprintf("gaiacli query gov votes %d %v", proposalID, f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var votes []gov.Vote
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &votes)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return votes
}
// QueryGovDeposit is gaiacli query gov deposit
func (f *Fixtures) QueryGovDeposit(proposalID int, depositor sdk.AccAddress, flags ...string) gov.Deposit {
cmd := fmt.Sprintf("gaiacli query gov deposit %d %s %v", proposalID, depositor, f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var deposit gov.Deposit
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &deposit)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return deposit
}
// QueryGovDeposits is gaiacli query gov deposits
func (f *Fixtures) QueryGovDeposits(propsalID int, flags ...string) []gov.Deposit {
cmd := fmt.Sprintf("gaiacli query gov deposits %d %v", propsalID, f.Flags())
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
var deposits []gov.Deposit
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &deposits)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return deposits
}
//___________________________________________________________________________________
// query slashing
// QuerySlashingParams is gaiacli query slashing params
func (f *Fixtures) QuerySlashingParams() slashing.Params {
cmd := fmt.Sprintf("gaiacli query slashing params %s", f.Flags())
res, errStr := tests.ExecuteT(f.T, cmd, "")
require.Empty(f.T, errStr)
cdc := app.MakeCodec()
var params slashing.Params
err := cdc.UnmarshalJSON([]byte(res), &params)
require.NoError(f.T, err)
return params
}
//___________________________________________________________________________________
// executors
func executeWriteCheckErr(t *testing.T, cmdStr string, writes ...string) {
require.True(t, executeWrite(t, cmdStr, writes...))
}
func executeWrite(t *testing.T, cmdStr string, writes ...string) (exitSuccess bool) {
exitSuccess, _, _ = executeWriteRetStdStreams(t, cmdStr, writes...)
return
}
func executeWriteRetStdStreams(t *testing.T, cmdStr string, writes ...string) (bool, string, string) {
proc := tests.GoExecuteT(t, cmdStr)
// Enables use of interactive commands
for _, write := range writes {
_, err := proc.StdinPipe.Write([]byte(write + "\n"))
require.NoError(t, err)
}
// Read both stdout and stderr from the process
stdout, stderr, err := proc.ReadAll()
if err != nil {
fmt.Println("Err on proc.ReadAll()", err, cmdStr)
}
// Log output.
if len(stdout) > 0 {
t.Log("Stdout:", cmn.Green(string(stdout)))
}
if len(stderr) > 0 {
t.Log("Stderr:", cmn.Red(string(stderr)))
}
// Wait for process to exit
proc.Wait()
// Return succes, stdout, stderr
return proc.ExitState.Success(), string(stdout), string(stderr)
}
//___________________________________________________________________________________
// utils
func addFlags(cmd string, flags []string) string {
for _, f := range flags {
cmd += " " + f
}
return strings.TrimSpace(cmd)
}
func queryTags(tags []string) (out string) {
for _, tag := range tags {
out += tag + "&"
}
return strings.TrimSuffix(out, "&")
}
func writeToNewTempFile(t *testing.T, s string) *os.File {
fp, err := ioutil.TempFile(os.TempDir(), "cosmos_cli_test_")
require.Nil(t, err)
_, err = fp.WriteString(s)
require.Nil(t, err)
return fp
}
func marshalStdTx(t *testing.T, stdTx auth.StdTx) []byte {
cdc := app.MakeCodec()
bz, err := cdc.MarshalBinaryBare(stdTx)
require.NoError(t, err)
return bz
}
func unmarshalStdTx(t *testing.T, s string) (stdTx auth.StdTx) {
cdc := app.MakeCodec()
require.Nil(t, cdc.UnmarshalJSON([]byte(s), &stdTx))
return
}