test: migrate e2e/bank to system tests (#21607)
This commit is contained in:
parent
f611150a30
commit
0064ccbce6
@ -1,20 +0,0 @@
|
||||
//go:build e2e
|
||||
// +build e2e
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"cosmossdk.io/simapp"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
)
|
||||
|
||||
func TestE2ETestSuite(t *testing.T) {
|
||||
cfg := network.DefaultConfig(simapp.NewTestNetworkFixture)
|
||||
cfg.NumValidators = 1
|
||||
suite.Run(t, NewE2ETestSuite(cfg))
|
||||
}
|
||||
@ -1,286 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/x/bank/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
)
|
||||
|
||||
func (s *E2ETestSuite) TestTotalSupplyGRPCHandler() {
|
||||
val := s.network.GetValidators()[0]
|
||||
baseURL := val.GetAPIAddress()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
headers map[string]string
|
||||
respType proto.Message
|
||||
expected proto.Message
|
||||
}{
|
||||
{
|
||||
"test GRPC total supply",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/supply", baseURL),
|
||||
map[string]string{
|
||||
grpctypes.GRPCBlockHeightHeader: "1",
|
||||
},
|
||||
&types.QueryTotalSupplyResponse{},
|
||||
&types.QueryTotalSupplyResponse{
|
||||
Supply: sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), s.cfg.AccountTokens),
|
||||
sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(47))),
|
||||
),
|
||||
Pagination: &query.PageResponse{
|
||||
Total: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"GRPC total supply of a specific denom",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/by_denom?denom=%s", baseURL, s.cfg.BondDenom),
|
||||
map[string]string{
|
||||
grpctypes.GRPCBlockHeightHeader: "1",
|
||||
},
|
||||
&types.QuerySupplyOfResponse{},
|
||||
&types.QuerySupplyOfResponse{
|
||||
Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(47))),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Query for `height` > 1",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/by_denom?denom=%s", baseURL, s.cfg.BondDenom),
|
||||
map[string]string{
|
||||
grpctypes.GRPCBlockHeightHeader: "2",
|
||||
},
|
||||
&types.QuerySupplyOfResponse{},
|
||||
&types.QuerySupplyOfResponse{
|
||||
Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(47))),
|
||||
},
|
||||
},
|
||||
{
|
||||
"Query params shouldn't be considered as height",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/by_denom?denom=%s&height=2", baseURL, s.cfg.BondDenom),
|
||||
map[string]string{
|
||||
grpctypes.GRPCBlockHeightHeader: "1",
|
||||
},
|
||||
&types.QuerySupplyOfResponse{},
|
||||
&types.QuerySupplyOfResponse{
|
||||
Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(math.NewInt(47))),
|
||||
},
|
||||
},
|
||||
{
|
||||
"GRPC total supply of a bogus denom",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/by_denom?denom=foobar", baseURL),
|
||||
map[string]string{
|
||||
grpctypes.GRPCBlockHeightHeader: "1",
|
||||
},
|
||||
&types.QuerySupplyOfResponse{},
|
||||
&types.QuerySupplyOfResponse{
|
||||
Amount: sdk.NewCoin("foobar", math.ZeroInt()),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
s.Run(tc.name, func() {
|
||||
resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType))
|
||||
s.Require().Equal(tc.expected.String(), tc.respType.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TestDenomMetadataGRPCHandler() {
|
||||
val := s.network.GetValidators()[0]
|
||||
baseURL := val.GetAPIAddress()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
headers map[string]string
|
||||
expErr bool
|
||||
respType proto.Message
|
||||
expected proto.Message
|
||||
}{
|
||||
{
|
||||
"test GRPC client metadata",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/denoms_metadata", baseURL),
|
||||
map[string]string{
|
||||
grpctypes.GRPCBlockHeightHeader: "1",
|
||||
},
|
||||
false,
|
||||
&types.QueryDenomsMetadataResponse{},
|
||||
&types.QueryDenomsMetadataResponse{
|
||||
Metadatas: []types.Metadata{
|
||||
{
|
||||
Name: "Cosmos Hub Atom",
|
||||
Symbol: "ATOM",
|
||||
Description: "The native staking token of the Cosmos Hub.",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{
|
||||
Denom: "uatom",
|
||||
Exponent: 0,
|
||||
Aliases: []string{"microatom"},
|
||||
},
|
||||
{
|
||||
Denom: "atom",
|
||||
Exponent: 6,
|
||||
Aliases: []string{"ATOM"},
|
||||
},
|
||||
},
|
||||
Base: "uatom",
|
||||
Display: "atom",
|
||||
},
|
||||
{
|
||||
Name: "Ethereum",
|
||||
Symbol: "ETH",
|
||||
Description: "Ethereum mainnet token",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{
|
||||
Denom: "wei",
|
||||
Exponent: 0,
|
||||
},
|
||||
{
|
||||
Denom: "eth",
|
||||
Exponent: 6,
|
||||
Aliases: []string{"ETH"},
|
||||
},
|
||||
},
|
||||
Base: "wei",
|
||||
Display: "eth",
|
||||
},
|
||||
},
|
||||
Pagination: &query.PageResponse{Total: 2},
|
||||
},
|
||||
},
|
||||
{
|
||||
"GRPC client metadata of a specific denom",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/denoms_metadata/uatom", baseURL),
|
||||
map[string]string{
|
||||
grpctypes.GRPCBlockHeightHeader: "1",
|
||||
},
|
||||
false,
|
||||
&types.QueryDenomMetadataResponse{},
|
||||
&types.QueryDenomMetadataResponse{
|
||||
Metadata: types.Metadata{
|
||||
Name: "Cosmos Hub Atom",
|
||||
Symbol: "ATOM",
|
||||
Description: "The native staking token of the Cosmos Hub.",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{
|
||||
Denom: "uatom",
|
||||
Exponent: 0,
|
||||
Aliases: []string{"microatom"},
|
||||
},
|
||||
{
|
||||
Denom: "atom",
|
||||
Exponent: 6,
|
||||
Aliases: []string{"ATOM"},
|
||||
},
|
||||
},
|
||||
Base: "uatom",
|
||||
Display: "atom",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"GRPC client metadata of a bogus denom",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/denoms_metadata/foobar", baseURL),
|
||||
map[string]string{
|
||||
grpctypes.GRPCBlockHeightHeader: "1",
|
||||
},
|
||||
true,
|
||||
&types.QueryDenomMetadataResponse{},
|
||||
&types.QueryDenomMetadataResponse{
|
||||
Metadata: types.Metadata{
|
||||
DenomUnits: []*types.DenomUnit{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
s.Run(tc.name, func() {
|
||||
resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers)
|
||||
s.Require().NoError(err)
|
||||
|
||||
if tc.expErr {
|
||||
s.Require().Error(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType))
|
||||
} else {
|
||||
s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType))
|
||||
s.Require().Equal(tc.expected.String(), tc.respType.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TestBalancesGRPCHandler() {
|
||||
val := s.network.GetValidators()[0]
|
||||
baseURL := val.GetAPIAddress()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
respType proto.Message
|
||||
expected proto.Message
|
||||
}{
|
||||
{
|
||||
"gRPC total account balance",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", baseURL, val.GetAddress().String()),
|
||||
&types.QueryAllBalancesResponse{},
|
||||
&types.QueryAllBalancesResponse{
|
||||
Balances: sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), s.cfg.AccountTokens),
|
||||
sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)),
|
||||
),
|
||||
Pagination: &query.PageResponse{
|
||||
Total: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"gPRC account balance of a denom",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s/by_denom?denom=%s", baseURL, val.GetAddress().String(), s.cfg.BondDenom),
|
||||
&types.QueryBalanceResponse{},
|
||||
&types.QueryBalanceResponse{
|
||||
Balance: &sdk.Coin{
|
||||
Denom: s.cfg.BondDenom,
|
||||
Amount: s.cfg.StakingTokens.Sub(s.cfg.BondedTokens),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"gPRC account balance of a bogus denom",
|
||||
fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s/by_denom?denom=foobar", baseURL, val.GetAddress().String()),
|
||||
&types.QueryBalanceResponse{},
|
||||
&types.QueryBalanceResponse{
|
||||
Balance: &sdk.Coin{
|
||||
Denom: "foobar",
|
||||
Amount: math.NewInt(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
s.Run(tc.name, func() {
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType))
|
||||
s.Require().Equal(tc.expected.String(), tc.respType.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,397 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"cosmossdk.io/core/address"
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/x/bank/client/cli"
|
||||
"cosmossdk.io/x/bank/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
type E2ETestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
cfg network.Config
|
||||
ac address.Codec
|
||||
network network.NetworkI
|
||||
}
|
||||
|
||||
func NewE2ETestSuite(cfg network.Config) *E2ETestSuite {
|
||||
return &E2ETestSuite{cfg: cfg}
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) SetupSuite() {
|
||||
s.T().Log("setting up e2e test suite")
|
||||
|
||||
genesisState := s.cfg.GenesisState
|
||||
var bankGenesis types.GenesisState
|
||||
s.Require().NoError(s.cfg.Codec.UnmarshalJSON(genesisState[types.ModuleName], &bankGenesis))
|
||||
|
||||
bankGenesis.DenomMetadata = []types.Metadata{
|
||||
{
|
||||
Name: "Cosmos Hub Atom",
|
||||
Symbol: "ATOM",
|
||||
Description: "The native staking token of the Cosmos Hub.",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{
|
||||
Denom: "uatom",
|
||||
Exponent: 0,
|
||||
Aliases: []string{"microatom"},
|
||||
},
|
||||
{
|
||||
Denom: "atom",
|
||||
Exponent: 6,
|
||||
Aliases: []string{"ATOM"},
|
||||
},
|
||||
},
|
||||
Base: "uatom",
|
||||
Display: "atom",
|
||||
},
|
||||
{
|
||||
Name: "Ethereum",
|
||||
Symbol: "ETH",
|
||||
Description: "Ethereum mainnet token",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{
|
||||
Denom: "wei",
|
||||
Exponent: 0,
|
||||
},
|
||||
{
|
||||
Denom: "eth",
|
||||
Exponent: 6,
|
||||
Aliases: []string{"ETH"},
|
||||
},
|
||||
},
|
||||
Base: "wei",
|
||||
Display: "eth",
|
||||
},
|
||||
}
|
||||
|
||||
bankGenesisBz, err := s.cfg.Codec.MarshalJSON(&bankGenesis)
|
||||
s.Require().NoError(err)
|
||||
genesisState[types.ModuleName] = bankGenesisBz
|
||||
s.cfg.GenesisState = genesisState
|
||||
|
||||
s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
s.ac = addresscodec.NewBech32Codec("cosmos")
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TearDownSuite() {
|
||||
s.T().Log("tearing down e2e test suite")
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TestNewSendTxCmdGenOnly() {
|
||||
val := s.network.GetValidators()[0]
|
||||
|
||||
from := val.GetAddress()
|
||||
to := val.GetAddress()
|
||||
amount := sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
)
|
||||
fromStr, err := s.ac.BytesToString(from)
|
||||
s.Require().NoError(err)
|
||||
toStr, err := s.ac.BytesToString(to)
|
||||
s.Require().NoError(err)
|
||||
msgSend := &types.MsgSend{
|
||||
FromAddress: fromStr,
|
||||
ToAddress: toStr,
|
||||
Amount: amount,
|
||||
}
|
||||
|
||||
bz, err := clitestutil.SubmitTestTx(
|
||||
val.GetClientCtx(),
|
||||
msgSend,
|
||||
from,
|
||||
clitestutil.TestTxConfig{
|
||||
GenOnly: true,
|
||||
},
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
tx, err := s.cfg.TxConfig.TxJSONDecoder()(bz.Bytes())
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal([]sdk.Msg{types.NewMsgSend(fromStr, toStr, amount)}, tx.GetMsgs())
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TestNewSendTxCmdDryRun() {
|
||||
val := s.network.GetValidators()[0]
|
||||
|
||||
from := val.GetAddress()
|
||||
to := val.GetAddress()
|
||||
amount := sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
)
|
||||
|
||||
msgSend := &types.MsgSend{
|
||||
FromAddress: from.String(),
|
||||
ToAddress: to.String(),
|
||||
Amount: amount,
|
||||
}
|
||||
|
||||
out, err := clitestutil.SubmitTestTx(
|
||||
val.GetClientCtx(),
|
||||
msgSend,
|
||||
from,
|
||||
clitestutil.TestTxConfig{
|
||||
Simulate: true,
|
||||
},
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Regexp("\"gas_info\"", out.String())
|
||||
s.Require().Regexp("\"gas_used\":\"[0-9]+\"", out.String())
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TestNewSendTxCmd() {
|
||||
val := s.network.GetValidators()[0]
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
from, to sdk.AccAddress
|
||||
amount sdk.Coins
|
||||
config clitestutil.TestTxConfig
|
||||
expectErr bool
|
||||
expectedCode uint32
|
||||
respType proto.Message
|
||||
}{
|
||||
{
|
||||
"valid transaction",
|
||||
val.GetAddress(),
|
||||
val.GetAddress(),
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
),
|
||||
clitestutil.TestTxConfig{
|
||||
Fee: sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10))),
|
||||
},
|
||||
false, 0, &sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"not enough fees",
|
||||
val.GetAddress(),
|
||||
val.GetAddress(),
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
),
|
||||
clitestutil.TestTxConfig{
|
||||
Fee: sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(1))),
|
||||
},
|
||||
|
||||
false,
|
||||
sdkerrors.ErrInsufficientFee.ABCICode(),
|
||||
&sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"not enough gas",
|
||||
val.GetAddress(),
|
||||
val.GetAddress(),
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
),
|
||||
clitestutil.TestTxConfig{
|
||||
Gas: 10,
|
||||
Fee: sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10))),
|
||||
},
|
||||
false,
|
||||
sdkerrors.ErrOutOfGas.ABCICode(),
|
||||
&sdk.TxResponse{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
s.Run(tc.name, func() {
|
||||
clientCtx := val.GetClientCtx()
|
||||
|
||||
msgSend := types.MsgSend{
|
||||
FromAddress: tc.from.String(),
|
||||
ToAddress: tc.to.String(),
|
||||
Amount: tc.amount,
|
||||
}
|
||||
bz, err := clitestutil.SubmitTestTx(val.GetClientCtx(), &msgSend, tc.from, tc.config)
|
||||
if tc.expectErr {
|
||||
s.Require().Error(err)
|
||||
} else {
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(bz.Bytes(), tc.respType), bz.String())
|
||||
txResp := tc.respType.(*sdk.TxResponse)
|
||||
s.Require().Equal(tc.expectedCode, txResp.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TestNewMultiSendTxCmd() {
|
||||
val := s.network.GetValidators()[0]
|
||||
testAddr := sdk.AccAddress("cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
from sdk.AccAddress
|
||||
to []sdk.AccAddress
|
||||
amount sdk.Coins
|
||||
args []string
|
||||
expectErr bool
|
||||
expectedCode uint32
|
||||
respType proto.Message
|
||||
}{
|
||||
{
|
||||
"valid transaction",
|
||||
val.GetAddress(),
|
||||
[]sdk.AccAddress{val.GetAddress(), testAddr},
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
),
|
||||
[]string{
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10))).String()),
|
||||
},
|
||||
false, 0, &sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"valid split transaction",
|
||||
val.GetAddress(),
|
||||
[]sdk.AccAddress{val.GetAddress(), testAddr},
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
),
|
||||
[]string{
|
||||
fmt.Sprintf("--%s=true", cli.FlagSplit),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10))).String()),
|
||||
},
|
||||
false, 0, &sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"not enough arguments",
|
||||
val.GetAddress(),
|
||||
[]sdk.AccAddress{val.GetAddress()},
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
),
|
||||
[]string{
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10))).String()),
|
||||
},
|
||||
true, 0, &sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"chain-id shouldn't be used with offline and generate-only flags",
|
||||
val.GetAddress(),
|
||||
[]sdk.AccAddress{val.GetAddress(), testAddr},
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
),
|
||||
[]string{
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10))).String()),
|
||||
fmt.Sprintf("--%s=true", flags.FlagOffline),
|
||||
fmt.Sprintf("--%s=true", flags.FlagGenerateOnly),
|
||||
},
|
||||
true, 0, &sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"not enough fees",
|
||||
val.GetAddress(),
|
||||
[]sdk.AccAddress{val.GetAddress(), testAddr},
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
),
|
||||
[]string{
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(1))).String()),
|
||||
},
|
||||
false,
|
||||
sdkerrors.ErrInsufficientFee.ABCICode(),
|
||||
&sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"not enough gas",
|
||||
val.GetAddress(),
|
||||
[]sdk.AccAddress{val.GetAddress(), testAddr},
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10)),
|
||||
sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10)),
|
||||
),
|
||||
[]string{
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(10))).String()),
|
||||
"--gas=10",
|
||||
},
|
||||
false,
|
||||
sdkerrors.ErrOutOfGas.ABCICode(),
|
||||
&sdk.TxResponse{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
s.Run(tc.name, func() {
|
||||
clientCtx := val.GetClientCtx()
|
||||
|
||||
bz, err := MsgMultiSendExec(clientCtx, tc.from, tc.to, tc.amount, tc.args...)
|
||||
if tc.expectErr {
|
||||
s.Require().Error(err)
|
||||
} else {
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(bz.Bytes(), tc.respType), bz.String())
|
||||
txResp := tc.respType.(*sdk.TxResponse)
|
||||
s.Require().Equal(tc.expectedCode, txResp.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func NewCoin(denom string, amount math.Int) *sdk.Coin {
|
||||
coin := sdk.NewCoin(denom, amount)
|
||||
return &coin
|
||||
}
|
||||
|
||||
func MsgMultiSendExec(clientCtx client.Context, from sdk.AccAddress, to []sdk.AccAddress, amount fmt.Stringer, extraArgs ...string) (testutil.BufferWriter, error) {
|
||||
args := []string{from.String()}
|
||||
for _, addr := range to {
|
||||
args = append(args, addr.String())
|
||||
}
|
||||
|
||||
args = append(args, amount.String())
|
||||
args = append(args, extraArgs...)
|
||||
|
||||
return clitestutil.ExecTestCLICmd(clientCtx, cli.NewMultiSendTxCmd(), args)
|
||||
}
|
||||
349
tests/systemtests/bank_test.go
Normal file
349
tests/systemtests/bank_test.go
Normal file
@ -0,0 +1,349 @@
|
||||
//go:build system_test
|
||||
|
||||
package systemtests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
func TestBankSendTxCmd(t *testing.T) {
|
||||
// scenario: test bank send command
|
||||
// given a running chain
|
||||
|
||||
sut.ResetChain(t)
|
||||
cli := NewCLIWrapper(t, sut, verbose)
|
||||
|
||||
// get validator address
|
||||
valAddr := gjson.Get(cli.Keys("keys", "list"), "1.address").String()
|
||||
require.NotEmpty(t, valAddr)
|
||||
|
||||
// add new key
|
||||
receiverAddr := cli.AddKey("account1")
|
||||
denom := "stake"
|
||||
sut.StartChain(t)
|
||||
|
||||
// query validator balance and make sure it has enough balance
|
||||
var transferAmount int64 = 1000
|
||||
valBalance := cli.QueryBalance(valAddr, denom)
|
||||
require.Greater(t, valBalance, transferAmount, "not enough balance found with validator")
|
||||
|
||||
bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom)}
|
||||
|
||||
// test valid transaction
|
||||
rsp := cli.Run(append(bankSendCmdArgs, "--fees=1stake")...)
|
||||
txResult, found := cli.AwaitTxCommitted(rsp)
|
||||
require.True(t, found)
|
||||
RequireTxSuccess(t, txResult)
|
||||
// check valaddr balance equals to valBalance-(transferedAmount+feeAmount)
|
||||
require.Equal(t, valBalance-(transferAmount+1), cli.QueryBalance(valAddr, denom))
|
||||
// check receiver balance equals to transferAmount
|
||||
require.Equal(t, transferAmount, cli.QueryBalance(receiverAddr, denom))
|
||||
|
||||
// test tx bank send with insufficient funds
|
||||
insufficientCmdArgs := bankSendCmdArgs[0 : len(bankSendCmdArgs)-1]
|
||||
insufficientCmdArgs = append(insufficientCmdArgs, fmt.Sprintf("%d%s", valBalance, denom), "--fees=10stake")
|
||||
rsp = cli.Run(insufficientCmdArgs...)
|
||||
RequireTxFailure(t, rsp)
|
||||
require.Contains(t, rsp, sdkerrors.ErrInsufficientFunds.Error())
|
||||
|
||||
// test tx bank send with unauthorized signature
|
||||
assertUnauthorizedErr := func(_ assert.TestingT, gotErr error, gotOutputs ...interface{}) bool {
|
||||
require.Len(t, gotOutputs, 1)
|
||||
code := gjson.Get(gotOutputs[0].(string), "code")
|
||||
require.True(t, code.Exists())
|
||||
require.Equal(t, int64(sdkerrors.ErrUnauthorized.ABCICode()), code.Int())
|
||||
return false
|
||||
}
|
||||
invalidCli := cli
|
||||
invalidCli.chainID = cli.chainID + "a" // set invalid chain-id
|
||||
rsp = invalidCli.WithRunErrorMatcher(assertUnauthorizedErr).Run(bankSendCmdArgs...)
|
||||
RequireTxFailure(t, rsp)
|
||||
|
||||
// test tx bank send generate only
|
||||
assertGenOnlyOutput := func(_ assert.TestingT, gotErr error, gotOutputs ...interface{}) bool {
|
||||
require.Len(t, gotOutputs, 1)
|
||||
rsp := gotOutputs[0].(string)
|
||||
// get msg from output
|
||||
msgs := gjson.Get(rsp, "body.messages").Array()
|
||||
require.Len(t, msgs, 1)
|
||||
// check from address is equal to account1 address
|
||||
fromAddr := gjson.Get(msgs[0].String(), "from_address").String()
|
||||
require.Equal(t, valAddr, fromAddr)
|
||||
// check to address is equal to account2 address
|
||||
toAddr := gjson.Get(msgs[0].String(), "to_address").String()
|
||||
require.Equal(t, receiverAddr, toAddr)
|
||||
return false
|
||||
}
|
||||
genCmdArgs := append(bankSendCmdArgs, "--generate-only")
|
||||
_ = cli.WithRunErrorMatcher(assertGenOnlyOutput).Run(genCmdArgs...)
|
||||
|
||||
// test tx bank send with dry-run flag
|
||||
assertDryRunOutput := func(_ assert.TestingT, gotErr error, gotOutputs ...interface{}) bool {
|
||||
require.Len(t, gotOutputs, 1)
|
||||
rsp := gotOutputs[0].(string)
|
||||
// check gas estimate value found in output
|
||||
require.Contains(t, rsp, "gas estimate")
|
||||
return false
|
||||
}
|
||||
dryRunCmdArgs := append(bankSendCmdArgs, "--dry-run")
|
||||
_ = cli.WithRunErrorMatcher(assertDryRunOutput).Run(dryRunCmdArgs...)
|
||||
}
|
||||
|
||||
func TestBankMultiSendTxCmd(t *testing.T) {
|
||||
// scenario: test bank multi-send command
|
||||
// given a running chain
|
||||
|
||||
sut.ResetChain(t)
|
||||
cli := NewCLIWrapper(t, sut, verbose)
|
||||
// add genesis account with some tokens
|
||||
account1Addr := cli.AddKey("account1")
|
||||
account2Addr := cli.AddKey("account2")
|
||||
account3Addr := cli.AddKey("account3")
|
||||
require.NotEqual(t, account1Addr, account2Addr)
|
||||
require.NotEqual(t, account1Addr, account3Addr)
|
||||
denom := "stake"
|
||||
var initialAmount int64 = 10000000
|
||||
initialBalance := fmt.Sprintf("%d%s", initialAmount, denom)
|
||||
sut.ModifyGenesisCLI(t,
|
||||
[]string{"genesis", "add-genesis-account", account1Addr, initialBalance},
|
||||
[]string{"genesis", "add-genesis-account", account2Addr, initialBalance},
|
||||
)
|
||||
sut.StartChain(t)
|
||||
|
||||
// query accounts balances
|
||||
account1Bal := cli.QueryBalance(account1Addr, denom)
|
||||
require.Equal(t, initialAmount, account1Bal)
|
||||
account2Bal := cli.QueryBalance(account2Addr, denom)
|
||||
require.Equal(t, initialAmount, account2Bal)
|
||||
var account3Bal int64 = 0
|
||||
|
||||
multiSendCmdArgs := []string{"tx", "bank", "multi-send", account1Addr, account2Addr, account3Addr, "1000stake", "--from=" + account1Addr}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
cmdArgs []string
|
||||
expectErr bool
|
||||
expectedCode uint32
|
||||
expErrMsg string
|
||||
}{
|
||||
{
|
||||
"valid transaction",
|
||||
append(multiSendCmdArgs, "--fees=1stake"),
|
||||
false,
|
||||
0,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"not enough arguments",
|
||||
[]string{"tx", "bank", "multi-send", account1Addr, account2Addr, "1000stake", "--from=" + account1Addr},
|
||||
true,
|
||||
0,
|
||||
"only received 3",
|
||||
},
|
||||
{
|
||||
"chain-id shouldn't be used with offline and generate-only flags",
|
||||
append(multiSendCmdArgs, "--generate-only", "--offline", "-a=0", "-s=4"),
|
||||
true,
|
||||
0,
|
||||
"chain ID cannot be used",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.expectErr {
|
||||
assertErr := func(_ assert.TestingT, gotErr error, gotOutputs ...interface{}) bool {
|
||||
require.Len(t, gotOutputs, 1)
|
||||
output := gotOutputs[0].(string)
|
||||
require.Contains(t, output, tc.expErrMsg)
|
||||
if tc.expectedCode != 0 {
|
||||
code := gjson.Get(output, "code")
|
||||
require.True(t, code.Exists())
|
||||
require.Equal(t, int64(tc.expectedCode), code.Int())
|
||||
}
|
||||
return false // always abort
|
||||
}
|
||||
_ = cli.WithRunErrorMatcher(assertErr).Run(tc.cmdArgs...)
|
||||
} else {
|
||||
rsp := cli.Run(tc.cmdArgs...)
|
||||
txResult, found := cli.AwaitTxCommitted(rsp)
|
||||
require.True(t, found)
|
||||
RequireTxSuccess(t, txResult)
|
||||
// check account1 balance equals to account1Bal - transferredAmount*no_of_accounts - fees
|
||||
expAcc1Balance := account1Bal - (1000 * 2) - 1
|
||||
require.Equal(t, expAcc1Balance, cli.QueryBalance(account1Addr, denom))
|
||||
account1Bal = expAcc1Balance
|
||||
// check account2 balance equals to account2Bal + transferredAmount
|
||||
expAcc2Balance := account2Bal + 1000
|
||||
require.Equal(t, expAcc2Balance, cli.QueryBalance(account2Addr, denom))
|
||||
account2Bal = expAcc2Balance
|
||||
// check account3 balance equals to account3Bal + transferredAmount
|
||||
expAcc3Balance := account3Bal + 1000
|
||||
require.Equal(t, expAcc3Balance, cli.QueryBalance(account3Addr, denom))
|
||||
account3Bal = expAcc3Balance
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBankGRPCQueries(t *testing.T) {
|
||||
// scenario: test bank grpc gateway queries
|
||||
// given a running chain
|
||||
|
||||
sut.ResetChain(t)
|
||||
cli := NewCLIWrapper(t, sut, verbose)
|
||||
|
||||
// update bank denom metadata in genesis
|
||||
atomDenomMetadata := `{"description":"The native staking token of the Cosmos Hub.","denom_units":[{"denom":"uatom","exponent":0,"aliases":["microatom"]},{"denom":"atom","exponent":6,"aliases":["ATOM"]}],"base":"uatom","display":"atom","name":"Cosmos Hub Atom","symbol":"ATOM","uri":"","uri_hash":""}`
|
||||
ethDenomMetadata := `{"description":"Ethereum mainnet token","denom_units":[{"denom":"wei","exponent":0,"aliases":[]},{"denom":"eth","exponent":6,"aliases":["ETH"]}],"base":"wei","display":"eth","name":"Ethereum","symbol":"ETH","uri":"","uri_hash":""}`
|
||||
|
||||
bankDenomMetadata := fmt.Sprintf("[%s,%s]", atomDenomMetadata, ethDenomMetadata)
|
||||
|
||||
sut.ModifyGenesisJSON(t, func(genesis []byte) []byte {
|
||||
state, err := sjson.SetRawBytes(genesis, "app_state.bank.denom_metadata", []byte(bankDenomMetadata))
|
||||
require.NoError(t, err)
|
||||
return state
|
||||
})
|
||||
|
||||
// add genesis account with some tokens
|
||||
account1Addr := cli.AddKey("account1")
|
||||
newDenom := "newdenom"
|
||||
initialAmount := "10000000"
|
||||
sut.ModifyGenesisCLI(t,
|
||||
[]string{"genesis", "add-genesis-account", account1Addr, "10000000stake," + initialAmount + newDenom},
|
||||
)
|
||||
|
||||
// start chain
|
||||
sut.StartChain(t)
|
||||
baseurl := fmt.Sprintf("http://localhost:%d", apiPortStart)
|
||||
|
||||
// test supply grpc endpoint
|
||||
supplyUrl := baseurl + "/cosmos/bank/v1beta1/supply"
|
||||
|
||||
// as supply might change for each block, can't set complete expected output
|
||||
expTotalSupplyOutput := `{"supply":[{"denom":"newdenom","amount":"10000000"},{"denom":"stake","amount"`
|
||||
specificDenomOutput := fmt.Sprintf(`{"denom":"%s","amount":"%s"}`, newDenom, initialAmount)
|
||||
bogusDenomOutput := `{"denom":"foobar","amount":"0"}`
|
||||
|
||||
blockHeightHeader := "x-cosmos-block-height"
|
||||
blockHeight := sut.CurrentHeight()
|
||||
|
||||
supplyTestCases := []struct {
|
||||
name string
|
||||
url string
|
||||
headers map[string]string
|
||||
expOut string
|
||||
}{
|
||||
{
|
||||
"test GRPC total supply",
|
||||
supplyUrl,
|
||||
map[string]string{
|
||||
blockHeightHeader: fmt.Sprintf("%d", blockHeight),
|
||||
},
|
||||
expTotalSupplyOutput,
|
||||
},
|
||||
{
|
||||
"test GRPC total supply of a specific denom",
|
||||
supplyUrl + "/by_denom?denom=" + newDenom,
|
||||
map[string]string{},
|
||||
specificDenomOutput,
|
||||
},
|
||||
{
|
||||
"error when querying supply with height greater than block height",
|
||||
supplyUrl,
|
||||
map[string]string{
|
||||
blockHeightHeader: fmt.Sprintf("%d", blockHeight+5),
|
||||
},
|
||||
"invalid height",
|
||||
},
|
||||
{
|
||||
"test GRPC total supply of a bogus denom",
|
||||
supplyUrl + "/by_denom?denom=foobar",
|
||||
map[string]string{},
|
||||
bogusDenomOutput,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range supplyTestCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(resp), tc.expOut)
|
||||
})
|
||||
}
|
||||
|
||||
// test denom metadata endpoint
|
||||
denomMetadataUrl := baseurl + "/cosmos/bank/v1beta1/denoms_metadata"
|
||||
dmTestCases := []struct {
|
||||
name string
|
||||
url string
|
||||
expOut string
|
||||
}{
|
||||
{
|
||||
"test GRPC client metadata",
|
||||
denomMetadataUrl,
|
||||
bankDenomMetadata,
|
||||
},
|
||||
{
|
||||
"test GRPC client metadata of a specific denom",
|
||||
denomMetadataUrl + "/uatom",
|
||||
atomDenomMetadata,
|
||||
},
|
||||
{
|
||||
"test GRPC client metadata of a bogus denom",
|
||||
denomMetadataUrl + "/foobar",
|
||||
`"details":[]`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range dmTestCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(resp), tc.expOut)
|
||||
})
|
||||
}
|
||||
|
||||
// test bank balances endpoint
|
||||
balanceUrl := baseurl + "/cosmos/bank/v1beta1/balances/"
|
||||
allBalancesOutput := `{"balances":[` + specificDenomOutput + `,{"denom":"stake","amount":"10000000"}],"pagination":{"next_key":null,"total":"2"}}`
|
||||
|
||||
balanceTestCases := []struct {
|
||||
name string
|
||||
url string
|
||||
expOut string
|
||||
}{
|
||||
{
|
||||
"test GRPC total account balance",
|
||||
balanceUrl + account1Addr,
|
||||
allBalancesOutput,
|
||||
},
|
||||
{
|
||||
"test GRPC account balance of a specific denom",
|
||||
fmt.Sprintf("%s%s/by_denom?denom=%s", balanceUrl, account1Addr, newDenom),
|
||||
specificDenomOutput,
|
||||
},
|
||||
{
|
||||
"test GRPC account balance of a bogus denom",
|
||||
fmt.Sprintf("%s%s/by_denom?denom=foobar", balanceUrl, account1Addr),
|
||||
bogusDenomOutput,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range balanceTestCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(resp), tc.expOut)
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user