feat(bank): autocli query support (backport #16899) (#16902)

Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot]
2023-07-10 12:48:24 +00:00
committed by GitHub
co-authored by Julien Robert
parent 3fee9db359
commit c8a49b35cd
13 changed files with 243 additions and 1160 deletions
+91
View File
@@ -0,0 +1,91 @@
package bank
import (
"strings"
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
)
// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
return &autocliv1.ModuleOptions{
Query: &autocliv1.ServiceCommandDescriptor{
Service: bankv1beta1.Query_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "Balance",
Use: "balance [address] [denom]",
Short: "Query an account balance by address and denom",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "address"}, {ProtoField: "denom"}},
},
{
RpcMethod: "AllBalances",
Use: "balances [address]",
Short: "Query for account balances by address",
Long: "Query the total balance of an account or of a specific denomination.",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "address"}},
},
{
RpcMethod: "SpendableBalances",
Use: "spendable-balances [address]",
Short: "Query for account spendable balances by address",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "address"}},
},
{
RpcMethod: "SpendableBalanceByDenom",
Use: "spendable-balances [address] [denom]",
Short: "Query the spendable balance of a single denom for a single account.",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "address"}, {ProtoField: "denom"}},
},
{
RpcMethod: "TotalSupply",
Use: "total-supply",
Alias: []string{"total"},
Short: "Query the total supply of coins of the chain",
Long: "Query total supply of coins that are held by accounts in the chain. To query for the total supply of a specific coin denomination use --denom flag.",
},
{
RpcMethod: "SupplyOf",
Use: "total-supply-of [denom]",
Short: "Query the supply of a single coin denom",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "denom"}},
},
{
RpcMethod: "Params",
Use: "params",
Short: "Query the current bank parameters",
},
{
RpcMethod: "DenomMetadata",
Use: "denom-metadata [denom]",
Short: "Query the client metadata of a given coin denomination",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "denom"}},
},
{
RpcMethod: "DenomsMetadata",
Use: "denoms-metadata",
Short: "Query the client metadata for all registered coin denominations",
},
{
RpcMethod: "DenomOwners",
Use: "denom-owners [denom]",
Short: "Query for all account addresses that own a particular token denomination.",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "denom"}},
},
{
RpcMethod: "SendEnabled",
Use: "send-enabled [denom1 ...]",
Short: "Query for send enabled entries",
Long: strings.TrimSpace(`Query for send enabled entries that have been specifically set.
To look up one or more specific denoms, supply them as arguments to this command.
To look up all denoms, do not provide any arguments.
`,
),
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "denoms"}},
},
},
},
}
}
-350
View File
@@ -1,350 +0,0 @@
package cli
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"cosmossdk.io/core/address"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/bank/types"
)
const (
FlagDenom = "denom"
FlagResolveDenom = "resolve-denom"
)
// GetQueryCmd returns the parent command for all x/bank CLi query commands. The
// provided clientCtx should have, at a minimum, a verifier, CometBFT RPC client,
// and marshaler set.
func GetQueryCmd(ac address.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: types.ModuleName,
Short: "Querying commands for the bank module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
cmd.AddCommand(
GetBalancesCmd(ac),
GetSpendableBalancesCmd(ac),
GetCmdQueryTotalSupply(),
GetCmdDenomsMetadata(),
GetCmdQuerySendEnabled(),
)
return cmd
}
func GetBalancesCmd(ac address.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "balances [address]",
Short: "Query for account balances by address",
Long: strings.TrimSpace(
fmt.Sprintf(`Query the total balance of an account or of a specific denomination.
Example:
$ %s query %s balances [address]
$ %s query %s balances [address] --denom=[denom]
$ %s query %s balances [address] --resolve-denom
`,
version.AppName, types.ModuleName, version.AppName, types.ModuleName, version.AppName, types.ModuleName,
),
),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
denom, err := cmd.Flags().GetString(FlagDenom)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
addr, err := ac.StringToBytes(args[0])
if err != nil {
return err
}
pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err
}
ctx := cmd.Context()
if denom == "" {
resolveDenom, err := cmd.Flags().GetBool(FlagResolveDenom)
if err != nil {
return err
}
params := types.NewQueryAllBalancesRequest(addr, pageReq, resolveDenom)
res, err := queryClient.AllBalances(ctx, params)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
}
params := types.NewQueryBalanceRequest(addr, denom)
res, err := queryClient.Balance(ctx, params)
if err != nil {
return err
}
return clientCtx.PrintProto(res.Balance)
},
}
cmd.Flags().String(FlagDenom, "", "The specific balance denomination to query for")
cmd.Flags().Bool(FlagResolveDenom, false, "Resolve denom to human-readable denom from metadata")
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "all balances")
return cmd
}
func GetSpendableBalancesCmd(ac address.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "spendable-balances [address]",
Short: "Query for account spendable balances by address",
Example: fmt.Sprintf("$ %s query %s spendable-balances [address]", version.AppName, types.ModuleName),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
denom, err := cmd.Flags().GetString(FlagDenom)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
addr, err := ac.StringToBytes(args[0])
if err != nil {
return err
}
pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err
}
ctx := cmd.Context()
if denom == "" {
params := types.NewQuerySpendableBalancesRequest(addr, pageReq)
res, err := queryClient.SpendableBalances(ctx, params)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
}
params := types.NewQuerySpendableBalanceByDenomRequest(addr, denom)
res, err := queryClient.SpendableBalanceByDenom(ctx, params)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}
cmd.Flags().String(FlagDenom, "", "The specific balance denomination to query for")
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "spendable balances")
return cmd
}
// GetCmdDenomsMetadata defines the cobra command to query client denomination metadata.
func GetCmdDenomsMetadata() *cobra.Command {
cmd := &cobra.Command{
Use: "denom-metadata",
Short: "Query the client metadata for coin denominations",
Long: strings.TrimSpace(
fmt.Sprintf(`Query the client metadata for all the registered coin denominations
Example:
To query for the client metadata of all coin denominations use:
$ %s query %s denom-metadata
To query for the client metadata of a specific coin denomination use:
$ %s query %s denom-metadata --denom=[denom]
`,
version.AppName, types.ModuleName, version.AppName, types.ModuleName,
),
),
RunE: func(cmd *cobra.Command, _ []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
denom, err := cmd.Flags().GetString(FlagDenom)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
if denom == "" {
res, err := queryClient.DenomsMetadata(cmd.Context(), &types.QueryDenomsMetadataRequest{})
if err != nil {
return err
}
return clientCtx.PrintProto(res)
}
res, err := queryClient.DenomMetadata(cmd.Context(), &types.QueryDenomMetadataRequest{Denom: denom})
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}
cmd.Flags().String(FlagDenom, "", "The specific denomination to query client metadata for")
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
func GetCmdQueryTotalSupply() *cobra.Command {
cmd := &cobra.Command{
Use: "total",
Short: "Query the total supply of coins of the chain",
Args: cobra.NoArgs,
Long: strings.TrimSpace(
fmt.Sprintf(`Query total supply of coins that are held by accounts in the chain.
Example:
$ %s query %s total
To query for the total supply of a specific coin denomination use:
$ %s query %s total --denom=[denom]
`,
version.AppName, types.ModuleName, version.AppName, types.ModuleName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
denom, err := cmd.Flags().GetString(FlagDenom)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
ctx := cmd.Context()
pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err
}
if denom == "" {
res, err := queryClient.TotalSupply(ctx, &types.QueryTotalSupplyRequest{Pagination: pageReq})
if err != nil {
return err
}
return clientCtx.PrintProto(res)
}
res, err := queryClient.SupplyOf(ctx, &types.QuerySupplyOfRequest{Denom: denom})
if err != nil {
return err
}
return clientCtx.PrintProto(&res.Amount)
},
}
cmd.Flags().String(FlagDenom, "", "The specific balance denomination to query for")
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "all supply totals")
return cmd
}
func GetCmdQuerySendEnabled() *cobra.Command {
cmd := &cobra.Command{
Use: "send-enabled [denom1 ...]",
Short: "Query for send enabled entries",
Long: strings.TrimSpace(`Query for send enabled entries that have been specifically set.
To look up one or more specific denoms, supply them as arguments to this command.
To look up all denoms, do not provide any arguments.
`,
),
Example: strings.TrimSpace(
fmt.Sprintf(`Getting one specific entry:
$ %[1]s query %[2]s send-enabled foocoin
Getting two specific entries:
$ %[1]s query %[2]s send-enabled foocoin barcoin
Getting all entries:
$ %[1]s query %[2]s send-enabled
`,
version.AppName, types.ModuleName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
reqPag, err := client.ReadPageRequest(client.MustFlagSetWithPageKeyDecoded(cmd.Flags()))
if err != nil {
return err
}
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
req := &types.QuerySendEnabledRequest{
Denoms: args,
Pagination: reqPag,
}
res, err := queryClient.SendEnabled(cmd.Context(), req)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}
flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "send enabled entries")
return cmd
}
-428
View File
@@ -1,428 +0,0 @@
package cli_test
import (
"context"
"fmt"
"io"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/gogoproto/proto"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec/address"
svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
"github.com/cosmos/cosmos-sdk/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank/client/cli"
"github.com/cosmos/cosmos-sdk/x/bank/types"
)
func (s *CLITestSuite) TestGetBalancesCmd() {
accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1)
cmd := cli.GetBalancesCmd(address.NewBech32Codec("cosmos"))
cmd.SetOutput(io.Discard)
testCases := []struct {
name string
ctxGen func() client.Context
args []string
expectResult proto.Message
expectErr bool
}{
{
"valid query",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QueryAllBalancesResponse{})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
accounts[0].Address.String(),
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&types.QueryAllBalancesResponse{},
false,
},
{
"valid query with denom",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QueryBalanceResponse{
Balance: &sdk.Coin{},
})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
accounts[0].Address.String(),
fmt.Sprintf("--%s=photon", cli.FlagDenom),
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&sdk.Coin{},
false,
},
{
"invalid Address",
func() client.Context {
return s.baseCtx
},
[]string{
"foo",
},
nil,
true,
},
{
"invalid denom",
func() client.Context {
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Code: 1,
})
return s.baseCtx.WithClient(c)
},
[]string{
accounts[0].Address.String(),
fmt.Sprintf("--%s=foo", cli.FlagDenom),
},
nil,
true,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
ctx := svrcmd.CreateExecuteContext(context.Background())
cmd.SetContext(ctx)
cmd.SetArgs(tc.args)
s.Require().NoError(client.SetCmdClientContextHandler(tc.ctxGen(), cmd))
out, err := clitestutil.ExecTestCLICmd(tc.ctxGen(), cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult))
s.Require().NoError(err)
}
})
}
}
func (s *CLITestSuite) TestGetSpendableBalancesCmd() {
accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1)
cmd := cli.GetSpendableBalancesCmd(address.NewBech32Codec("cosmos"))
cmd.SetOutput(io.Discard)
testCases := []struct {
name string
ctxGen func() client.Context
args []string
expectResult proto.Message
expectErr bool
}{
{
"valid query",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QuerySpendableBalancesResponse{})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
accounts[0].Address.String(),
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&types.QuerySpendableBalancesResponse{},
false,
},
{
"valid query with denom flag",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QuerySpendableBalanceByDenomRequest{})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
accounts[0].Address.String(),
fmt.Sprintf("--%s=json", flags.FlagOutput),
fmt.Sprintf("--%s=photon", cli.FlagDenom),
},
&types.QuerySpendableBalanceByDenomResponse{},
false,
},
{
"invalid Address",
func() client.Context {
return s.baseCtx
},
[]string{
"foo",
},
nil,
true,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
ctx := svrcmd.CreateExecuteContext(context.Background())
cmd.SetContext(ctx)
cmd.SetArgs(tc.args)
s.Require().NoError(client.SetCmdClientContextHandler(tc.ctxGen(), cmd))
out, err := clitestutil.ExecTestCLICmd(tc.ctxGen(), cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult))
s.Require().NoError(err)
}
})
}
}
func (s *CLITestSuite) TestGetCmdDenomsMetadata() {
cmd := cli.GetCmdDenomsMetadata()
cmd.SetOutput(io.Discard)
testCases := []struct {
name string
ctxGen func() client.Context
args []string
expectResult proto.Message
expectErr bool
}{
{
"valid query",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QueryDenomsMetadataResponse{})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&types.QueryDenomsMetadataResponse{},
false,
},
{
"valid query with denom",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QueryDenomMetadataResponse{})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
fmt.Sprintf("--%s=photon", cli.FlagDenom),
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&types.QueryDenomMetadataResponse{},
false,
},
{
"invalid query with denom",
func() client.Context {
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Code: 1,
})
return s.baseCtx.WithClient(c)
},
[]string{
fmt.Sprintf("--%s=foo", cli.FlagDenom),
},
nil,
true,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
ctx := svrcmd.CreateExecuteContext(context.Background())
cmd.SetContext(ctx)
cmd.SetArgs(tc.args)
s.Require().NoError(client.SetCmdClientContextHandler(tc.ctxGen(), cmd))
out, err := clitestutil.ExecTestCLICmd(tc.ctxGen(), cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult))
s.Require().NoError(err)
}
})
}
}
func (s *CLITestSuite) TestGetCmdQueryTotalSupply() {
cmd := cli.GetCmdQueryTotalSupply()
cmd.SetOutput(io.Discard)
testCases := []struct {
name string
ctxGen func() client.Context
args []string
expectResult proto.Message
expectErr bool
}{
{
"valid query",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QueryTotalSupplyResponse{})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&types.QueryTotalSupplyResponse{},
false,
},
{
"valid query with denom",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QuerySupplyOfResponse{
Amount: sdk.Coin{},
})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
fmt.Sprintf("--%s=photon", cli.FlagDenom),
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&sdk.Coin{},
false,
},
{
"invalid query with denom",
func() client.Context {
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Code: 1,
})
return s.baseCtx.WithClient(c)
},
[]string{
fmt.Sprintf("--%s=foo", cli.FlagDenom),
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
nil,
true,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
ctx := svrcmd.CreateExecuteContext(context.Background())
cmd.SetContext(ctx)
cmd.SetArgs(tc.args)
s.Require().NoError(client.SetCmdClientContextHandler(tc.ctxGen(), cmd))
out, err := clitestutil.ExecTestCLICmd(tc.ctxGen(), cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult))
s.Require().NoError(err)
}
})
}
}
func (s *CLITestSuite) TestGetCmdQuerySendEnabled() {
cmd := cli.GetCmdQuerySendEnabled()
cmd.SetOutput(io.Discard)
testCases := []struct {
name string
ctxGen func() client.Context
args []string
expectResult proto.Message
expectErr bool
}{
{
"valid query",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QuerySendEnabledResponse{
SendEnabled: []*types.SendEnabled{},
})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&types.QuerySendEnabledResponse{},
false,
},
{
"valid query with denoms",
func() client.Context {
bz, _ := s.encCfg.Codec.Marshal(&types.QuerySendEnabledResponse{
SendEnabled: []*types.SendEnabled{},
})
c := clitestutil.NewMockCometRPC(abci.ResponseQuery{
Value: bz,
})
return s.baseCtx.WithClient(c)
},
[]string{
"photon",
"stake",
fmt.Sprintf("--%s=json", flags.FlagOutput),
},
&types.QuerySendEnabledResponse{},
false,
},
}
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
ctx := svrcmd.CreateExecuteContext(context.Background())
cmd.SetContext(ctx)
cmd.SetArgs(tc.args)
s.Require().NoError(client.SetCmdClientContextHandler(tc.ctxGen(), cmd))
out, err := clitestutil.ExecTestCLICmd(tc.ctxGen(), cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(s.encCfg.Codec.UnmarshalJSON(out.Bytes(), tc.expectResult))
s.Require().NoError(err)
}
})
}
}
-39
View File
@@ -1,39 +0,0 @@
package cli_test
import (
"io"
"testing"
rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/bank"
)
type CLITestSuite struct {
suite.Suite
kr keyring.Keyring
encCfg testutilmod.TestEncodingConfig
baseCtx client.Context
}
func TestCLITestSuite(t *testing.T) {
suite.Run(t, new(CLITestSuite))
}
func (s *CLITestSuite) SetupSuite() {
s.encCfg = testutilmod.MakeTestEncodingConfig(bank.AppModuleBasic{})
s.kr = keyring.NewInMemory(s.encCfg.Codec)
s.baseCtx = client.Context{}.
WithKeyring(s.kr).
WithTxConfig(s.encCfg.TxConfig).
WithCodec(s.encCfg.Codec).
WithClient(clitestutil.MockCometRPC{Client: rpcclientmock.Client{}}).
WithAccountRetriever(client.MockAccountRetriever{}).
WithOutput(io.Discard)
}
+31
View File
@@ -4,19 +4,50 @@ import (
"context"
"fmt"
"io"
"testing"
rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock"
"github.com/stretchr/testify/suite"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
"github.com/cosmos/cosmos-sdk/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
sdk "github.com/cosmos/cosmos-sdk/types"
testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/bank/client/cli"
)
type CLITestSuite struct {
suite.Suite
kr keyring.Keyring
encCfg testutilmod.TestEncodingConfig
baseCtx client.Context
}
func TestCLITestSuite(t *testing.T) {
suite.Run(t, new(CLITestSuite))
}
func (s *CLITestSuite) SetupSuite() {
s.encCfg = testutilmod.MakeTestEncodingConfig(bank.AppModuleBasic{})
s.kr = keyring.NewInMemory(s.encCfg.Codec)
s.baseCtx = client.Context{}.
WithKeyring(s.kr).
WithTxConfig(s.encCfg.TxConfig).
WithCodec(s.encCfg.Codec).
WithClient(clitestutil.MockCometRPC{Client: rpcclientmock.Client{}}).
WithAccountRetriever(client.MockAccountRetriever{}).
WithOutput(io.Discard)
}
func (s *CLITestSuite) TestSendTxCmd() {
accounts := testutil.CreateKeyringAccounts(s.T(), s.kr, 1)
cmd := cli.NewSendTxCmd(address.NewBech32Codec("cosmos"))
+84 -32
View File
@@ -19,31 +19,81 @@ func (suite *KeeperTestSuite) TestQueryBalance() {
ctx, queryClient := suite.ctx, suite.queryClient
_, _, addr := testdata.KeyTestPubAddr()
_, err := queryClient.Balance(gocontext.Background(), &types.QueryBalanceRequest{})
suite.Require().Error(err)
_, err = queryClient.Balance(gocontext.Background(), &types.QueryBalanceRequest{Address: addr.String()})
suite.Require().Error(err)
req := types.NewQueryBalanceRequest(addr, "0000")
_, err = queryClient.Balance(gocontext.Background(), req)
suite.Require().Error(err)
req = types.NewQueryBalanceRequest(addr, fooDenom)
res, err := queryClient.Balance(gocontext.Background(), req)
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.True(res.Balance.IsZero())
origCoins := sdk.NewCoins(newFooCoin(50), newBarCoin(30))
origCoins := sdk.NewCoins(newBarCoin(30))
suite.mockFundAccount(addr)
suite.Require().NoError(testutil.FundAccount(ctx, suite.bankKeeper, addr, origCoins))
res, err = queryClient.Balance(gocontext.Background(), req)
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.True(res.Balance.IsEqual(newFooCoin(50)))
testCases := []struct {
name string
req *types.QueryBalanceRequest
expectErrMsg string
postFn func(res *types.QueryBalanceResponse)
}{
{
"empty request",
&types.QueryBalanceRequest{},
"invalid denom",
nil,
},
{
"invalid denom",
types.NewQueryBalanceRequest(addr, "0000"),
"invalid denom",
nil,
},
{
"empty address",
types.NewQueryBalanceRequest(sdk.AccAddress{}, barDenom),
"empty address string is not allowed",
nil,
},
{
"invalid address",
&types.QueryBalanceRequest{Address: "foo", Denom: barDenom},
"invalid address",
nil,
},
{
"query missing denom",
&types.QueryBalanceRequest{Address: addr.String()},
"invalid denom",
nil,
},
{
"valid query empty result",
types.NewQueryBalanceRequest(addr, fooDenom),
"",
func(res *types.QueryBalanceResponse) {
suite.True(res.Balance.IsZero())
},
},
{
"valid query",
types.NewQueryBalanceRequest(addr, barDenom),
"",
func(res *types.QueryBalanceResponse) {
suite.True(res.Balance.IsEqual(newBarCoin(30)))
},
},
}
for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
res, err := queryClient.Balance(gocontext.Background(), tc.req)
if tc.expectErrMsg == "" {
suite.Require().NoError(err)
suite.Require().NotNil(res)
} else {
suite.Require().ErrorContains(err, tc.expectErrMsg)
}
if tc.postFn != nil {
tc.postFn(res)
}
})
}
}
func (suite *KeeperTestSuite) TestQueryAllBalances() {
@@ -231,13 +281,12 @@ func (suite *KeeperTestSuite) TestQueryTotalSupply() {
ctx, queryClient := suite.ctx, suite.queryClient
res, err := queryClient.TotalSupply(gocontext.Background(), &types.QueryTotalSupplyRequest{})
suite.Require().NoError(err)
suite.Require().NotNil(res)
genesisSupply := res.Supply
testCoins := sdk.NewCoins(sdk.NewInt64Coin("test", 400000000))
suite.mockMintCoins(mintAcc)
suite.
Require().
NoError(suite.bankKeeper.MintCoins(ctx, minttypes.ModuleName, testCoins))
suite.Require().NoError(suite.bankKeeper.MintCoins(ctx, minttypes.ModuleName, testCoins))
res, err = queryClient.TotalSupply(gocontext.Background(), &types.QueryTotalSupplyRequest{})
suite.Require().NoError(err)
@@ -256,9 +305,7 @@ func (suite *KeeperTestSuite) TestQueryTotalSupplyOf() {
expectedTotalSupply := sdk.NewCoins(test1Supply, test2Supply)
suite.mockMintCoins(mintAcc)
suite.
Require().
NoError(suite.bankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply))
suite.Require().NoError(suite.bankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply))
_, err := queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{})
suite.Require().Error(err)
@@ -266,8 +313,13 @@ func (suite *KeeperTestSuite) TestQueryTotalSupplyOf() {
res, err := queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{Denom: test1Supply.Denom})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(test1Supply, res.Amount)
// total supply bogus denom
res, err = queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{Denom: "bogus"})
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(sdk.NewInt64Coin("bogus", 0), res.Amount)
}
func (suite *KeeperTestSuite) TestQueryParams() {
@@ -277,7 +329,7 @@ func (suite *KeeperTestSuite) TestQueryParams() {
suite.Require().Equal(suite.bankKeeper.GetParams(suite.ctx), res.GetParams())
}
func (suite *KeeperTestSuite) TestQueryDenomsMetadataRequest() {
func (suite *KeeperTestSuite) TestQueryDenomsMetadata() {
var (
req *types.QueryDenomsMetadataRequest
expMetadata = []types.Metadata(nil)
@@ -379,7 +431,7 @@ func (suite *KeeperTestSuite) TestQueryDenomsMetadataRequest() {
}
}
func (suite *KeeperTestSuite) TestQueryDenomMetadataRequest() {
func (suite *KeeperTestSuite) TestQueryDenomMetadata() {
var (
req *types.QueryDenomMetadataRequest
expMetadata = types.Metadata{}
@@ -456,7 +508,7 @@ func (suite *KeeperTestSuite) TestQueryDenomMetadataRequest() {
}
}
func (suite *KeeperTestSuite) TestGRPCDenomOwners() {
func (suite *KeeperTestSuite) TestQueryDenomOwners() {
ctx := suite.ctx
keeper := suite.bankKeeper
+1 -1
View File
@@ -87,7 +87,7 @@ func (ab AppModuleBasic) GetTxCmd() *cobra.Command {
// GetQueryCmd returns no root query command for the bank module.
func (ab AppModuleBasic) GetQueryCmd() *cobra.Command {
return cli.GetQueryCmd(ab.ac)
return nil
}
// RegisterInterfaces registers interfaces and implementations of the bank module.