diff --git a/x/staking/client/cli/query_test.go b/x/staking/client/cli/query_test.go new file mode 100644 index 0000000000..0eb1875d48 --- /dev/null +++ b/x/staking/client/cli/query_test.go @@ -0,0 +1,570 @@ +package cli_test + +import ( + "fmt" + "strings" + + "github.com/gogo/protobuf/proto" + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func (s *CLITestSuite) TestGetCmdQueryValidator() { + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "with invalid address ", + []string{"somethinginvalidaddress", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + true, + }, + { + "happy case", + []string{sdk.ValAddress(s.addrs[0]).String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + false, + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidator() + clientCtx := s.clientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + s.Require().NotEqual("internal", err.Error()) + } else { + var result types.Validator + s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &result)) + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryValidators() { + + testCases := []struct { + name string + args []string + minValidatorCount int + }{ + { + "one validator case", + []string{ + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagLimit), + }, + 1, + }, + { + "multi validator case", + []string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidators() + clientCtx := s.clientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + + var result types.QueryValidatorsResponse + s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &result)) + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryDelegation() { + testCases := []struct { + name string + args []string + expErr bool + respType proto.Message + }{ + { + "with wrong delegator address", + []string{ + "wrongDelAddr", + s.addrs[1].String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, nil, + }, + { + "with wrong validator address", + []string{ + s.addrs[0].String(), + "wrongValAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, nil, + }, + { + "with json output", + []string{ + s.addrs[0].String(), + sdk.ValAddress(s.addrs[1]).String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + &types.DelegationResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryDelegation() + clientCtx := s.clientCtx + + _, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().Contains(err.Error(), "Marshal called with nil") + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryDelegations() { + + testCases := []struct { + name string + args []string + expErr bool + respType proto.Message + }{ + { + "with no delegator address", + []string{}, + true, nil, + }, + { + "with wrong delegator address", + []string{"wrongDelAddr"}, + true, nil, + }, + { + "valid request (height specific)", + []string{ + s.addrs[0].String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + false, + &types.QueryDelegatorDelegationsResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryDelegations() + clientCtx := s.clientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryValidatorDelegations() { + + testCases := []struct { + name string + args []string + expErr bool + respType proto.Message + }{ + { + "with no validator address", + []string{}, + true, nil, + }, + { + "wrong validator address", + []string{"wrongValAddr"}, + true, nil, + }, + { + "valid request(height specific)", + []string{ + s.addrs[0].String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + false, + &types.QueryValidatorDelegationsResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryDelegations() + clientCtx := s.clientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryUnbondingDelegations() { + + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong delegator address", + []string{ + "wrongDelAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + s.addrs[0].String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryUnbondingDelegations() + clientCtx := s.clientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var ubds types.QueryDelegatorUnbondingDelegationsResponse + err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &ubds) + + s.Require().NoError(err) + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryUnbondingDelegation() { + + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong delegator address", + []string{ + "wrongDelAddr", + s.addrs[0].String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "wrong validator address", + []string{ + s.addrs[0].String(), + "wrongValAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + s.addrs[0].String(), + sdk.ValAddress(s.addrs[1]).String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryUnbondingDelegation() + clientCtx := s.clientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var ubd types.UnbondingDelegation + + err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &ubd) + s.Require().NoError(err) + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryValidatorUnbondingDelegations() { + + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong validator address", + []string{ + "wrongValAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + sdk.ValAddress(s.addrs[0]).String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidatorUnbondingDelegations() + clientCtx := s.clientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var ubds types.QueryValidatorUnbondingDelegationsResponse + err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &ubds) + s.Require().NoError(err) + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryRedelegations() { + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong delegator address", + []string{ + "wrongdeladdr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + s.addrs[0].String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryRedelegations() + clientCtx := s.clientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var redelegations types.QueryRedelegationsResponse + err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &redelegations) + s.Require().NoError(err) + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryRedelegation() { + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong delegator address", + []string{ + "wrongdeladdr", + sdk.ValAddress(s.addrs[0]).String(), + sdk.ValAddress(s.addrs[1]).String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "wrong source validator address address", + []string{ + s.addrs[0].String(), + "wrongSrcValAddress", + sdk.ValAddress(s.addrs[1]).String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "wrong destination validator address address", + []string{ + s.addrs[0].String(), + sdk.ValAddress(s.addrs[0]).String(), + "wrongDestValAddress", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + s.addrs[0].String(), + sdk.ValAddress(s.addrs[0]).String(), + sdk.ValAddress(s.addrs[1]).String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryRedelegation() + clientCtx := s.clientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var redelegations types.QueryRedelegationsResponse + + err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &redelegations) + s.Require().NoError(err) + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryValidatorRedelegations() { + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong validator address", + []string{ + "wrongValAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + sdk.ValAddress(s.addrs[0]).String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidatorRedelegations() + clientCtx := s.clientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var redelegations types.QueryRedelegationsResponse + err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), &redelegations) + s.Require().NoError(err) + } + }) + } +} + +func (s *CLITestSuite) TestGetCmdQueryPool() { + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "with text", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + `bonded_tokens: "0" +not_bonded_tokens: "0"`, + }, + { + "with json", + []string{ + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + `{"not_bonded_tokens":"0","bonded_tokens":"0"}`, + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryPool() + clientCtx := s.clientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} diff --git a/x/staking/client/cli/tx_test.go b/x/staking/client/cli/tx_test.go index a60a2a3097..644170879e 100644 --- a/x/staking/client/cli/tx_test.go +++ b/x/staking/client/cli/tx_test.go @@ -1,24 +1,117 @@ -package cli +package cli_test import ( + "bytes" + "context" + "fmt" + "io" "testing" + "github.com/gogo/protobuf/proto" "github.com/spf13/pflag" "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + rpcclient "github.com/tendermint/tendermint/rpc/client" + rpcclientmock "github.com/tendermint/tendermint/rpc/client/mock" + coretypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" ) -func TestPrepareConfigForTxCreateValidator(t *testing.T) { +var _ client.TendermintRPC = (*mockTendermintRPC)(nil) + +type mockTendermintRPC struct { + rpcclientmock.Client + + responseQuery abci.ResponseQuery +} + +func newMockTendermintRPC(respQuery abci.ResponseQuery) mockTendermintRPC { + return mockTendermintRPC{responseQuery: respQuery} +} + +func (_ mockTendermintRPC) BroadcastTxCommit(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTxCommit, error) { + return &coretypes.ResultBroadcastTxCommit{}, nil +} + +func (m mockTendermintRPC) ABCIQueryWithOptions( + _ context.Context, + _ string, _ tmbytes.HexBytes, + _ rpcclient.ABCIQueryOptions, +) (*coretypes.ResultABCIQuery, error) { + return &coretypes.ResultABCIQuery{Response: m.responseQuery}, nil +} + +var PKs = simtestutil.CreateTestPubKeys(500) + +type CLITestSuite struct { + suite.Suite + + kr keyring.Keyring + encCfg testutilmod.TestEncodingConfig + baseCtx client.Context + clientCtx client.Context + addrs []sdk.AccAddress +} + +func (s *CLITestSuite) SetupSuite() { + s.encCfg = testutilmod.MakeTestEncodingConfig(staking.AppModuleBasic{}) + s.kr = keyring.NewInMemory(s.encCfg.Codec) + s.baseCtx = client.Context{}. + WithKeyring(s.kr). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Codec). + WithClient(mockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(client.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID("test-chain") + + var outBuf bytes.Buffer + ctxGen := func() client.Context { + bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) + c := newMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + s.addrs = make([]sdk.AccAddress, 0) + for i := 0; i < 3; i++ { + k, _, err := s.clientCtx.Keyring.NewMnemonic("NewValidator", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + s.Require().NoError(err) + + pub, err := k.GetPubKey() + s.Require().NoError(err) + + newAddr := sdk.AccAddress(pub.Address()) + s.addrs = append(s.addrs, newAddr) + } + +} + +func (s *CLITestSuite) TestPrepareConfigForTxCreateValidator() { chainID := "chainID" ip := "1.1.1.1" nodeID := "nodeID" privKey := ed25519.GenPrivKey() valPubKey := privKey.PubKey() moniker := "DefaultMoniker" - mkTxValCfg := func(amount, commission, commissionMax, commissionMaxChange, minSelfDelegation string) TxCreateValidatorConfig { - return TxCreateValidatorConfig{ + mkTxValCfg := func(amount, commission, commissionMax, commissionMaxChange, minSelfDelegation string) cli.TxCreateValidatorConfig { + return cli.TxCreateValidatorConfig{ IP: ip, ChainID: chainID, NodeID: nodeID, @@ -36,64 +129,555 @@ func TestPrepareConfigForTxCreateValidator(t *testing.T) { tests := []struct { name string fsModify func(fs *pflag.FlagSet) - expectedCfg TxCreateValidatorConfig + expectedCfg cli.TxCreateValidatorConfig }{ { - name: "all defaults", - fsModify: func(fs *pflag.FlagSet) { - return - }, - expectedCfg: mkTxValCfg(defaultAmount, "0.1", "0.2", "0.01", "1"), + name: "all defaults", + fsModify: func(fs *pflag.FlagSet) {}, + expectedCfg: mkTxValCfg(cli.DefaultTokens.String()+sdk.DefaultBondDenom, "0.1", "0.2", "0.01", "1"), }, { name: "Custom amount", fsModify: func(fs *pflag.FlagSet) { - fs.Set(FlagAmount, "2000stake") + fs.Set(cli.FlagAmount, "2000stake") }, expectedCfg: mkTxValCfg("2000stake", "0.1", "0.2", "0.01", "1"), }, { name: "Custom commission rate", fsModify: func(fs *pflag.FlagSet) { - fs.Set(FlagCommissionRate, "0.54") + fs.Set(cli.FlagCommissionRate, "0.54") }, - expectedCfg: mkTxValCfg(defaultAmount, "0.54", "0.2", "0.01", "1"), + expectedCfg: mkTxValCfg(cli.DefaultTokens.String()+sdk.DefaultBondDenom, "0.54", "0.2", "0.01", "1"), }, { name: "Custom commission max rate", fsModify: func(fs *pflag.FlagSet) { - fs.Set(FlagCommissionMaxRate, "0.89") + fs.Set(cli.FlagCommissionMaxRate, "0.89") }, - expectedCfg: mkTxValCfg(defaultAmount, "0.1", "0.89", "0.01", "1"), + expectedCfg: mkTxValCfg(cli.DefaultTokens.String()+sdk.DefaultBondDenom, "0.1", "0.89", "0.01", "1"), }, { name: "Custom commission max change rate", fsModify: func(fs *pflag.FlagSet) { - fs.Set(FlagCommissionMaxChangeRate, "0.55") + fs.Set(cli.FlagCommissionMaxChangeRate, "0.55") }, - expectedCfg: mkTxValCfg(defaultAmount, "0.1", "0.2", "0.55", "1"), + expectedCfg: mkTxValCfg(cli.DefaultTokens.String()+sdk.DefaultBondDenom, "0.1", "0.2", "0.55", "1"), }, { name: "Custom min self delegations", fsModify: func(fs *pflag.FlagSet) { - fs.Set(FlagMinSelfDelegation, "0.33") + fs.Set(cli.FlagMinSelfDelegation, "0.33") }, - expectedCfg: mkTxValCfg(defaultAmount, "0.1", "0.2", "0.01", "0.33"), + expectedCfg: mkTxValCfg(cli.DefaultTokens.String()+sdk.DefaultBondDenom, "0.1", "0.2", "0.01", "0.33"), }, } for _, tc := range tests { tc := tc - t.Run(tc.name, func(t *testing.T) { - fs, _ := CreateValidatorMsgFlagSet(ip) + s.Run(tc.name, func() { + fs, _ := cli.CreateValidatorMsgFlagSet(ip) fs.String(flags.FlagName, "", "name of private key with which to sign the gentx") tc.fsModify(fs) - cvCfg, err := PrepareConfigForTxCreateValidator(fs, moniker, nodeID, chainID, valPubKey) - require.NoError(t, err) + cvCfg, err := cli.PrepareConfigForTxCreateValidator(fs, moniker, nodeID, chainID, valPubKey) + require.NoError(s.T(), err) - require.Equal(t, tc.expectedCfg, cvCfg) + require.Equal(s.T(), tc.expectedCfg, cvCfg) }) } } + +func (s *CLITestSuite) TestNewCreateValidatorCmd() { + require := s.Require() + cmd := cli.NewCreateValidatorCmd() + + consPrivKey := ed25519.GenPrivKey() + consPubKeyBz, err := s.encCfg.Codec.MarshalInterfaceJSON(consPrivKey.PubKey()) + require.NoError(err) + require.NotNil(consPubKeyBz) + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + respType proto.Message + }{ + { + "invalid transaction (missing amount)", + []string{ + fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), + fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), + fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), + fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), + fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), + fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), + fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), + fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "invalid transaction (missing pubkey)", + []string{ + fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100), + fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), + fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), + fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), + fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), + fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), + fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), + fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), + fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "invalid transaction (missing moniker)", + []string{ + fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKeyBz), + fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100), + fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), + fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), + fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), + fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), + fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), + fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), + fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), + fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "valid transaction", + []string{ + fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKeyBz), + fmt.Sprintf("--%s=%dstake", cli.FlagAmount, 100), + fmt.Sprintf("--%s=NewValidator", cli.FlagMoniker), + fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), + fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), + fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), + fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), + fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), + fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), + fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), + fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + require.Error(err) + } else { + require.NoError(err, "test: %s\noutput: %s", tc.name, out.String()) + err = s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType) + require.NoError(err, out.String(), "test: %s, output\n:", tc.name, out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + require.Equal(tc.expectedCode, txResp.Code, + "test: %s, output\n:", tc.name, out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestNewEditValidatorCmd() { + cmd := cli.NewEditValidatorCmd() + + details := "bio" + identity := "test identity" + securityContact := "test contact" + website := "https://test.com" + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + respType proto.Message + }{ + { + "with no edit flag (since all are optional)", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, "with wrong from address"), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "with no edit flag (since all are optional)", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + { + "edit validator details", + []string{ + fmt.Sprintf("--details=%s", details), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + { + "edit validator identity", + []string{ + fmt.Sprintf("--identity=%s", identity), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + { + "edit validator security-contact", + []string{ + fmt.Sprintf("--security-contact=%s", securityContact), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + { + "edit validator website", + []string{ + fmt.Sprintf("--website=%s", website), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + { + "with all edit flags", + []string{ + fmt.Sprintf("--details=%s", details), + fmt.Sprintf("--identity=%s", identity), + fmt.Sprintf("--security-contact=%s", securityContact), + fmt.Sprintf("--website=%s", website), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestNewDelegateCmd() { + cmd := cli.NewDelegateCmd() + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + respType proto.Message + }{ + { + "without delegate amount", + []string{ + sdk.ValAddress(s.addrs[0]).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "without validator address", + []string{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(150)).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "valid transaction of delegate", + []string{ + sdk.ValAddress(s.addrs[0]).String(), + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(150)).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestNewRedelegateCmd() { + cmd := cli.NewRedelegateCmd() + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + respType proto.Message + }{ + { + "without amount", + []string{ + sdk.ValAddress(s.addrs[0]).String(), // src-validator-addr + sdk.ValAddress(s.addrs[1]).String(), // dst-validator-addr + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "valid transaction of delegate", + []string{ + sdk.ValAddress(s.addrs[0]).String(), // src-validator-addr + sdk.ValAddress(s.addrs[1]).String(), // dst-validator-addr + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(150)).String(), // amount + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=%d", flags.FlagGas, 300000), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestNewUnbondCmd() { + cmd := cli.NewUnbondCmd() + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + respType proto.Message + }{ + { + "Without unbond amount", + []string{ + sdk.ValAddress(s.addrs[0]).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "Without validator address", + []string{ + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(150)).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "valid transaction of unbond", + []string{ + sdk.ValAddress(s.addrs[0]).String(), + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(150)).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + +func (s *CLITestSuite) TestNewCancelUnbondingDelegationCmd() { + cmd := cli.NewCancelUnbondingDelegation() + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + respType proto.Message + }{ + { + "Without validator address", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "Without canceling unbond delegation amount", + []string{ + sdk.ValAddress(s.addrs[0]).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "Without unbond creation height", + []string{ + sdk.ValAddress(s.addrs[0]).String(), + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(150)).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + true, 0, nil, + }, + { + "valid transaction of canceling unbonding delegation", + []string{ + sdk.ValAddress(s.addrs[0]).String(), + sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5)).String(), + sdk.NewInt(10000).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addrs[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))).String()), + }, + false, 0, &sdk.TxResponse{}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) + } + }) + } +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} diff --git a/x/staking/module.go b/x/staking/module.go index fda2ab1a34..6ffc9bea41 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -18,6 +18,7 @@ import ( modulev1 "cosmossdk.io/api/cosmos/staking/module/v1" "cosmossdk.io/core/appmodule" "cosmossdk.io/depinject" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" cdctypes "github.com/cosmos/cosmos-sdk/codec/types"