diff --git a/tests/e2e/distribution/cli_test.go b/tests/e2e/distribution/cli_test.go deleted file mode 100644 index ad8e877d9611..000000000000 --- a/tests/e2e/distribution/cli_test.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build e2e -// +build e2e - -package distribution - -import ( - "testing" - - "github.com/stretchr/testify/suite" -) - -func TestWithdrawAllSuite(t *testing.T) { - suite.Run(t, new(WithdrawAllTestSuite)) -} - -func TestGRPCQueryTestSuite(t *testing.T) { - suite.Run(t, new(GRPCQueryTestSuite)) -} diff --git a/tests/e2e/distribution/grpc_query_suite.go b/tests/e2e/distribution/grpc_query_suite.go deleted file mode 100644 index 991567abfb4e..000000000000 --- a/tests/e2e/distribution/grpc_query_suite.go +++ /dev/null @@ -1,460 +0,0 @@ -package distribution - -import ( - "fmt" - - "github.com/cosmos/gogoproto/proto" - "github.com/stretchr/testify/suite" - - "cosmossdk.io/simapp" - "cosmossdk.io/x/distribution/types" - - sdktestutil "github.com/cosmos/cosmos-sdk/testutil" - "github.com/cosmos/cosmos-sdk/testutil/network" - sdk "github.com/cosmos/cosmos-sdk/types" - grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" - "github.com/cosmos/cosmos-sdk/types/query" -) - -type GRPCQueryTestSuite struct { - suite.Suite - - cfg network.Config - network network.NetworkI -} - -func (s *GRPCQueryTestSuite) SetupSuite() { - s.T().Log("setting up e2e test suite") - - cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) - cfg.NumValidators = 1 - s.cfg = cfg - - var err error - s.network, err = network.New(s.T(), s.T().TempDir(), cfg) - s.Require().NoError(err) - - s.Require().NoError(s.network.WaitForNextBlock()) -} - -// TearDownSuite cleans up the current test network after _each_ test. -func (s *GRPCQueryTestSuite) TearDownSuite() { - s.T().Log("tearing down e2e test suite1") - s.network.Cleanup() -} - -func (s *GRPCQueryTestSuite) TestQueryParamsGRPC() { - val := s.network.GetValidators()[0] - baseURL := val.GetAPIAddress() - - testCases := []struct { - name string - url string - respType proto.Message - expected proto.Message - }{ - { - "gRPC request params", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/params", baseURL), - &types.QueryParamsResponse{}, - &types.QueryParamsResponse{ - Params: types.DefaultParams(), - }, - }, - } - - for _, tc := range testCases { - - resp, err := sdktestutil.GetRequest(tc.url) - s.Run(tc.name, func() { - s.Require().NoError(err) - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - s.Require().Equal(tc.expected, tc.respType) - }) - } -} - -func (s *GRPCQueryTestSuite) TestQueryValidatorDistributionInfoGRPC() { - val := s.network.GetValidators()[0] - baseURL := val.GetAPIAddress() - - testCases := []struct { - name string - url string - expErr bool - respType proto.Message - }{ - { - "gRPC request with wrong validator address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s", baseURL, "wrongAddress"), - true, - &types.QueryValidatorDistributionInfoResponse{}, - }, - { - "gRPC request with valid validator address ", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s", baseURL, val.GetValAddress().String()), - false, - &types.QueryValidatorDistributionInfoResponse{}, - }, - } - - for _, tc := range testCases { - - resp, err := sdktestutil.GetRequest(tc.url) - s.Run(tc.name, func() { - if tc.expErr { - s.Require().Error(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - } else { - s.Require().NoError(err) - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - } - }) - } -} - -func (s *GRPCQueryTestSuite) TestQueryOutstandingRewardsGRPC() { - val := s.network.GetValidators()[0] - baseURL := val.GetAPIAddress() - - rewards, err := sdk.ParseDecCoins("46.06stake") - s.Require().NoError(err) - - testCases := []struct { - name string - url string - headers map[string]string - expErr bool - respType proto.Message - expected proto.Message - }{ - { - "gRPC request params with wrong validator address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/outstanding_rewards", baseURL, "wrongAddress"), - map[string]string{}, - true, - &types.QueryValidatorOutstandingRewardsResponse{}, - &types.QueryValidatorOutstandingRewardsResponse{}, - }, - { - "gRPC request params valid address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/outstanding_rewards", baseURL, val.GetValAddress().String()), - map[string]string{ - grpctypes.GRPCBlockHeightHeader: "2", - }, - false, - &types.QueryValidatorOutstandingRewardsResponse{}, - &types.QueryValidatorOutstandingRewardsResponse{ - Rewards: types.ValidatorOutstandingRewards{ - Rewards: rewards, - }, - }, - }, - } - - for _, tc := range testCases { - - resp, err := sdktestutil.GetRequestWithHeaders(tc.url, tc.headers) - s.Run(tc.name, func() { - if tc.expErr { - s.Require().Error(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - } else { - 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 *GRPCQueryTestSuite) TestQueryValidatorCommissionGRPC() { - val := s.network.GetValidators()[0] - baseURL := val.GetAPIAddress() - - commission, err := sdk.ParseDecCoins("23.03stake") - s.Require().NoError(err) - - testCases := []struct { - name string - url string - headers map[string]string - expErr bool - respType proto.Message - expected proto.Message - }{ - { - "gRPC request params with wrong validator address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/commission", baseURL, "wrongAddress"), - map[string]string{}, - true, - &types.QueryValidatorCommissionResponse{}, - &types.QueryValidatorCommissionResponse{}, - }, - { - "gRPC request params valid address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/commission", baseURL, val.GetValAddress().String()), - map[string]string{ - grpctypes.GRPCBlockHeightHeader: "2", - }, - false, - &types.QueryValidatorCommissionResponse{}, - &types.QueryValidatorCommissionResponse{ - Commission: types.ValidatorAccumulatedCommission{ - Commission: commission, - }, - }, - }, - } - - for _, tc := range testCases { - - resp, err := sdktestutil.GetRequestWithHeaders(tc.url, tc.headers) - s.Run(tc.name, func() { - if tc.expErr { - s.Require().Error(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - } else { - 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 *GRPCQueryTestSuite) TestQuerySlashesGRPC() { - val := s.network.GetValidators()[0] - baseURL := val.GetAPIAddress() - - testCases := []struct { - name string - url string - expErr bool - respType proto.Message - expected proto.Message - }{ - { - "invalid validator address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/slashes", baseURL, ""), - true, - &types.QueryValidatorSlashesResponse{}, - nil, - }, - { - "invalid start height", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/slashes?starting_height=%s&ending_height=%s", baseURL, val.GetValAddress().String(), "-1", "3"), - true, - &types.QueryValidatorSlashesResponse{}, - nil, - }, - { - "invalid start height", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/slashes?starting_height=%s&ending_height=%s", baseURL, val.GetValAddress().String(), "1", "-3"), - true, - &types.QueryValidatorSlashesResponse{}, - nil, - }, - { - "valid request get slashes", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/slashes?starting_height=%s&ending_height=%s", baseURL, val.GetValAddress().String(), "1", "3"), - false, - &types.QueryValidatorSlashesResponse{}, - &types.QueryValidatorSlashesResponse{ - Pagination: &query.PageResponse{}, - }, - }, - } - - for _, tc := range testCases { - - resp, err := sdktestutil.GetRequest(tc.url) - - s.Run(tc.name, func() { - if tc.expErr { - s.Require().Error(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - } else { - 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 *GRPCQueryTestSuite) TestQueryDelegatorRewardsGRPC() { - val := s.network.GetValidators()[0] - baseURL := val.GetAPIAddress() - - rewards, err := sdk.ParseDecCoins("23.03stake") - s.Require().NoError(err) - - testCases := []struct { - name string - url string - headers map[string]string - expErr bool - respType proto.Message - expected proto.Message - }{ - { - "wrong delegator address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/rewards", baseURL, "wrongDelegatorAddress"), - map[string]string{}, - true, - &types.QueryDelegationTotalRewardsResponse{}, - nil, - }, - { - "valid request", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/rewards", baseURL, val.GetAddress().String()), - map[string]string{ - grpctypes.GRPCBlockHeightHeader: "2", - }, - false, - &types.QueryDelegationTotalRewardsResponse{}, - &types.QueryDelegationTotalRewardsResponse{ - Rewards: []types.DelegationDelegatorReward{ - types.NewDelegationDelegatorReward(val.GetValAddress().String(), rewards), - }, - Total: rewards, - }, - }, - { - "wrong validator address(specific validator rewards)", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/rewards/%s", baseURL, val.GetAddress().String(), "wrongValAddress"), - map[string]string{}, - true, - &types.QueryDelegationTotalRewardsResponse{}, - nil, - }, - { - "valid request(specific validator rewards)", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/rewards/%s", baseURL, val.GetAddress().String(), val.GetValAddress().String()), - map[string]string{ - grpctypes.GRPCBlockHeightHeader: "2", - }, - false, - &types.QueryDelegationRewardsResponse{}, - &types.QueryDelegationRewardsResponse{ - Rewards: rewards, - }, - }, - } - - for _, tc := range testCases { - - resp, err := sdktestutil.GetRequestWithHeaders(tc.url, tc.headers) - - s.Run(tc.name, func() { - if tc.expErr { - s.Require().Error(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - } else { - 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 *GRPCQueryTestSuite) TestQueryDelegatorValidatorsGRPC() { - val := s.network.GetValidators()[0] - baseURL := val.GetAPIAddress() - - testCases := []struct { - name string - url string - expErr bool - respType proto.Message - expected proto.Message - }{ - { - "empty delegator address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/validators", baseURL, ""), - true, - &types.QueryDelegatorValidatorsResponse{}, - nil, - }, - { - "wrong delegator address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/validators", baseURL, "wrongDelegatorAddress"), - true, - &types.QueryDelegatorValidatorsResponse{}, - nil, - }, - { - "valid request", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/validators", baseURL, val.GetAddress().String()), - false, - &types.QueryDelegatorValidatorsResponse{}, - &types.QueryDelegatorValidatorsResponse{ - Validators: []string{val.GetValAddress().String()}, - }, - }, - } - - for _, tc := range testCases { - - resp, err := sdktestutil.GetRequest(tc.url) - - s.Run(tc.name, func() { - if tc.expErr { - s.Require().Error(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - } else { - 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 *GRPCQueryTestSuite) TestQueryWithdrawAddressGRPC() { - val := s.network.GetValidators()[0] - baseURL := val.GetAPIAddress() - - testCases := []struct { - name string - url string - expErr bool - respType proto.Message - expected proto.Message - }{ - { - "empty delegator address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/withdraw_address", baseURL, ""), - true, - &types.QueryDelegatorWithdrawAddressResponse{}, - nil, - }, - { - "wrong delegator address", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/withdraw_address", baseURL, "wrongDelegatorAddress"), - true, - &types.QueryDelegatorWithdrawAddressResponse{}, - nil, - }, - { - "valid request", - fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/withdraw_address", baseURL, val.GetAddress().String()), - false, - &types.QueryDelegatorWithdrawAddressResponse{}, - &types.QueryDelegatorWithdrawAddressResponse{ - WithdrawAddress: val.GetAddress().String(), - }, - }, - } - - for _, tc := range testCases { - - resp, err := sdktestutil.GetRequest(tc.url) - - s.Run(tc.name, func() { - if tc.expErr { - s.Require().Error(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - } else { - s.Require().NoError(err) - s.Require().NoError(val.GetClientCtx().Codec.UnmarshalJSON(resp, tc.respType)) - s.Require().Equal(tc.expected.String(), tc.respType.String()) - } - }) - } -} diff --git a/tests/e2e/distribution/withdraw_all_suite.go b/tests/e2e/distribution/withdraw_all_suite.go deleted file mode 100644 index 9dd1888c0468..000000000000 --- a/tests/e2e/distribution/withdraw_all_suite.go +++ /dev/null @@ -1,139 +0,0 @@ -package distribution - -import ( - "fmt" - "strings" - - "github.com/stretchr/testify/suite" - - "cosmossdk.io/math" - "cosmossdk.io/simapp" - banktypes "cosmossdk.io/x/bank/types" - "cosmossdk.io/x/distribution/client/cli" - stakingtypes "cosmossdk.io/x/staking/types" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/testutil/network" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type WithdrawAllTestSuite struct { - suite.Suite - - cfg network.Config - network network.NetworkI -} - -func (s *WithdrawAllTestSuite) SetupSuite() { - cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) - cfg.NumValidators = 2 - s.cfg = cfg - - s.T().Log("setting up e2e test suite") - network, err := network.New(s.T(), s.T().TempDir(), s.cfg) - s.Require().NoError(err) - s.network = network - - s.Require().NoError(s.network.WaitForNextBlock()) -} - -// TearDownSuite cleans up the current test network after _each_ test. -func (s *WithdrawAllTestSuite) TearDownSuite() { - s.T().Log("tearing down e2e test suite") - s.network.Cleanup() -} - -// This test requires multiple validators, if I add this test to `E2ETestSuite` by increasing -// `NumValidators` the existing tests are leading to non-determnism so created new suite for this test. -func (s *WithdrawAllTestSuite) TestNewWithdrawAllRewardsGenerateOnly() { - require := s.Require() - val := s.network.GetValidators()[0] - val1 := s.network.GetValidators()[1] - clientCtx := val.GetClientCtx() - - info, _, err := val.GetClientCtx().Keyring.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - require.NoError(err) - - pubkey, err := info.GetPubKey() - require.NoError(err) - - newAddr := sdk.AccAddress(pubkey.Address()) - - msgSend := &banktypes.MsgSend{ - FromAddress: val.GetAddress().String(), - ToAddress: newAddr.String(), - Amount: sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, math.NewInt(2000))), - } - _, err = clitestutil.SubmitTestTx( - val.GetClientCtx(), - msgSend, - val.GetAddress(), - clitestutil.TestTxConfig{}, - ) - - require.NoError(err) - require.NoError(s.network.WaitForNextBlock()) - - // delegate 500 tokens to validator1 - msg := &stakingtypes.MsgDelegate{ - DelegatorAddress: newAddr.String(), - ValidatorAddress: val.GetValAddress().String(), - Amount: sdk.NewCoin("stake", math.NewInt(500)), - } - - _, err = clitestutil.SubmitTestTx(val.GetClientCtx(), msg, newAddr, clitestutil.TestTxConfig{}) - require.NoError(err) - require.NoError(s.network.WaitForNextBlock()) - - // delegate 500 tokens to validator2 - msg2 := &stakingtypes.MsgDelegate{ - DelegatorAddress: newAddr.String(), - ValidatorAddress: val1.GetValAddress().String(), - Amount: sdk.NewCoin("stake", math.NewInt(500)), - } - - _, err = clitestutil.SubmitTestTx(val.GetClientCtx(), msg2, newAddr, clitestutil.TestTxConfig{}) - require.NoError(err) - require.NoError(s.network.WaitForNextBlock()) - - err = s.network.RetryForBlocks(func() error { - args := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr.String()), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=1", cli.FlagMaxMessagesPerTx), - 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()), - } - cmd := cli.NewWithdrawAllRewardsCmd() - out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) - if err != nil { - return err - } - - // expect 2 transactions in the generated file when --max-msgs in a tx set 1. - txLen := len(strings.Split(strings.Trim(out.String(), "\n"), "\n")) - if txLen != 2 { - return fmt.Errorf("expected 2 transactions in the generated file, got %d", txLen) - } - return nil - }, 3) - require.NoError(err) - - args := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr.String()), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=2", cli.FlagMaxMessagesPerTx), - 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()), - } - cmd := cli.NewWithdrawAllRewardsCmd() - out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) - require.NoError(err) - // expect 1 transaction in the generated file when --max-msgs in a tx set 2, since there are only delegations. - s.Require().Equal(1, len(strings.Split(strings.Trim(out.String(), "\n"), "\n"))) -} diff --git a/tests/systemtests/authz_test.go b/tests/systemtests/authz_test.go index da66aef5d267..f3e0ec748c8b 100644 --- a/tests/systemtests/authz_test.go +++ b/tests/systemtests/authz_test.go @@ -607,7 +607,7 @@ func TestAuthzExecRedelegateAuthorization(t *testing.T) { msgRedelegateTypeURL, granterAddr, val1Addr, val2Addr, testDenom, redelegationAmount) execMsg := WriteToTempJSONFile(t, redelegateTx) - redelegateCmd := []string{"tx", "authz", "exec", execMsg.Name(), "--from=" + granteeAddr, "--gas=auto"} + redelegateCmd := []string{"tx", "authz", "exec", execMsg.Name(), "--from=" + granteeAddr, "--gas=500000", "--fees=10stake"} rsp = cli.RunAndWait(redelegateCmd...) RequireTxSuccess(t, rsp) diff --git a/tests/systemtests/distribution_test.go b/tests/systemtests/distribution_test.go new file mode 100644 index 000000000000..6d8eaf2ae0c0 --- /dev/null +++ b/tests/systemtests/distribution_test.go @@ -0,0 +1,301 @@ +//go:build system_test + +package systemtests + +import ( + "fmt" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +const ( + distrTestDenom = "stake" +) + +func TestWithdrawAllRewardsCmd(t *testing.T) { + // scenario: test distribution withdraw all rewards command + // given a running chain + + sut.ResetChain(t) + cli := NewCLIWrapper(t, sut, verbose) + + newAddr := cli.AddKey("newAddr") + require.NotEmpty(t, newAddr) + + var initialAmount int64 = 10000000 + initialBalance := fmt.Sprintf("%d%s", initialAmount, distrTestDenom) + sut.ModifyGenesisCLI(t, + []string{"genesis", "add-genesis-account", newAddr, initialBalance}, + ) + sut.StartChain(t) + + // query balance + newAddrBal := cli.QueryBalance(newAddr, distrTestDenom) + require.Equal(t, initialAmount, newAddrBal) + + // query validator operator address + rsp := cli.CustomQuery("q", "staking", "validators") + validators := gjson.Get(rsp, "validators.#.operator_address").Array() + require.GreaterOrEqual(t, len(validators), 2) + val1Addr := validators[0].String() + val2Addr := validators[1].String() + + var delegationAmount int64 = 100000 + delegation := fmt.Sprintf("%d%s", delegationAmount, distrTestDenom) + + // delegate tokens to validator1 + rsp = cli.RunAndWait("tx", "staking", "delegate", val1Addr, delegation, "--from="+newAddr, "--fees=1"+distrTestDenom) + RequireTxSuccess(t, rsp) + + // delegate tokens to validator2 + rsp = cli.RunAndWait("tx", "staking", "delegate", val2Addr, delegation, "--from="+newAddr, "--fees=1"+distrTestDenom) + RequireTxSuccess(t, rsp) + + // check updated balance: newAddrBal - delegatedBal - fees + expBal := newAddrBal - (delegationAmount * 2) - 2 + newAddrBal = cli.QueryBalance(newAddr, distrTestDenom) + require.Equal(t, expBal, newAddrBal) + + withdrawCmdArgs := []string{"tx", "distribution", "withdraw-all-rewards", "--from=" + newAddr, "--fees=1" + distrTestDenom} + + // test with --max-msgs + testCases := []struct { + name string + maxMsgs int + expTxLen int + }{ + { + "--max-msgs value is 1", + 1, + 2, + }, + { + "--max-msgs value is 2", + 2, + 1, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assertGenOnlyOutput := func(_ assert.TestingT, gotErr error, gotOutputs ...interface{}) bool { + require.Len(t, gotOutputs, 1) + // gets output combining two objects without any space or new line + splitOutput := strings.Split(gotOutputs[0].(string), "}{") + require.Len(t, splitOutput, tc.expTxLen) + return false + } + cmd := append(withdrawCmdArgs, fmt.Sprintf("--max-msgs=%d", tc.maxMsgs), "--generate-only") + _ = cli.WithRunErrorMatcher(assertGenOnlyOutput).Run(cmd...) + }) + } + + // test withdraw-all-rewards transaction + rsp = cli.RunAndWait(withdrawCmdArgs...) + RequireTxSuccess(t, rsp) +} + +func TestDistrValidatorGRPCQueries(t *testing.T) { + // scenario: test distribution validator grpc gateway queries + // given a running chain + + sut.ResetChain(t) + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + valOperAddr := cli.GetKeyAddrPrefix("node0", "val") + require.NotEmpty(t, valOperAddr) + + sut.StartChain(t) + + sut.AwaitNBlocks(t, 3) + + baseurl := sut.APIAddress() + expectedAmountOutput := fmt.Sprintf(`{"denom":"%s","amount":"203.105000000000000000"}`, distrTestDenom) + + // test params grpc endpoint + paramsURL := baseurl + "/cosmos/distribution/v1beta1/params" + + paramsTestCases := []RestTestCase{ + { + "gRPC request params", + paramsURL, + http.StatusOK, + `{"params":{"community_tax":"0.020000000000000000","base_proposer_reward":"0.000000000000000000","bonus_proposer_reward":"0.000000000000000000","withdraw_addr_enabled":true}}`, + }, + } + RunRestQueries(t, paramsTestCases) + + // test validator distribution info grpc endpoint + validatorsURL := baseurl + `/cosmos/distribution/v1beta1/validators/%s` + validatorsOutput := fmt.Sprintf(`{"operator_address":"%s","self_bond_rewards":[],"commission":[%s]}`, valAddr, expectedAmountOutput) + + validatorsTestCases := []RestTestCase{ + { + "gRPC request validator with valid validator address", + fmt.Sprintf(validatorsURL, valOperAddr), + http.StatusOK, + validatorsOutput, + }, + } + TestRestQueryIgnoreNumbers(t, validatorsTestCases) + + // test outstanding rewards grpc endpoint + outstandingRewardsURL := baseurl + `/cosmos/distribution/v1beta1/validators/%s/outstanding_rewards` + + rewardsTestCases := []RestTestCase{ + { + "gRPC request outstanding rewards with valid validator address", + fmt.Sprintf(outstandingRewardsURL, valOperAddr), + http.StatusOK, + fmt.Sprintf(`{"rewards":{"rewards":[%s]}}`, expectedAmountOutput), + }, + } + TestRestQueryIgnoreNumbers(t, rewardsTestCases) + + // test validator commission grpc endpoint + commissionURL := baseurl + `/cosmos/distribution/v1beta1/validators/%s/commission` + + commissionTestCases := []RestTestCase{ + { + "gRPC request commission with valid validator address", + fmt.Sprintf(commissionURL, valOperAddr), + http.StatusOK, + fmt.Sprintf(`{"commission":{"commission":[%s]}}`, expectedAmountOutput), + }, + } + TestRestQueryIgnoreNumbers(t, commissionTestCases) + + // test validator slashes grpc endpoint + slashURL := baseurl + `/cosmos/distribution/v1beta1/validators/%s/slashes` + invalidHeightOutput := `{"code":3, "message":"strconv.ParseUint: parsing \"-3\": invalid syntax", "details":[]}` + + slashTestCases := []RestTestCase{ + { + "invalid start height", + fmt.Sprintf(slashURL+`?starting_height=%s&ending_height=%s`, valOperAddr, "-3", "3"), + http.StatusBadRequest, + invalidHeightOutput, + }, + { + "invalid end height", + fmt.Sprintf(slashURL+`?starting_height=%s&ending_height=%s`, valOperAddr, "1", "-3"), + http.StatusBadRequest, + invalidHeightOutput, + }, + { + "valid request get slashes", + fmt.Sprintf(slashURL+`?starting_height=%s&ending_height=%s`, valOperAddr, "1", "3"), + http.StatusOK, + `{"slashes":[],"pagination":{"next_key":null,"total":"0"}}`, + }, + } + RunRestQueries(t, slashTestCases) +} + +func TestDistrDelegatorGRPCQueries(t *testing.T) { + // scenario: test distribution validator gsrpc gateway queries + // given a running chain + + sut.ResetChain(t) + cli := NewCLIWrapper(t, sut, verbose) + + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + valOperAddr := cli.GetKeyAddrPrefix("node0", "val") + require.NotEmpty(t, valOperAddr) + + // update commission rate of node0 validator + // generate new gentx and copy it to genesis.json before starting network + rsp := cli.RunCommandWithArgs("genesis", "gentx", "node0", "100000000"+distrTestDenom, "--chain-id="+cli.chainID, "--commission-rate=0.01", "--home", sut.nodePath(0), "--keyring-backend=test") + // extract gentx path from above command output + re := regexp.MustCompile(`"(.*?\.json)"`) + match := re.FindStringSubmatch(rsp) + require.GreaterOrEqual(t, len(match), 1) + + updatedGentx := filepath.Join(WorkDir, match[1]) + updatedGentxBz, err := os.ReadFile(updatedGentx) // #nosec G304 + require.NoError(t, err) + + sut.ModifyGenesisJSON(t, func(genesis []byte) []byte { + state, err := sjson.SetRawBytes(genesis, "app_state.genutil.gen_txs.0", updatedGentxBz) + require.NoError(t, err) + return state + }) + + // create new address which will be used as delegator address + delAddr := cli.AddKey("delAddr") + require.NotEmpty(t, delAddr) + + var initialAmount int64 = 1000000000 + initialBalance := fmt.Sprintf("%d%s", initialAmount, distrTestDenom) + sut.ModifyGenesisCLI(t, + []string{"genesis", "add-genesis-account", delAddr, initialBalance}, + ) + + sut.StartChain(t) + + // delegate some tokens to valOperAddr + rsp = cli.RunAndWait("tx", "staking", "delegate", valOperAddr, "100000000"+distrTestDenom, "--from="+delAddr) + RequireTxSuccess(t, rsp) + + sut.AwaitNBlocks(t, 5) + + baseurl := sut.APIAddress() + + // test delegator rewards grpc endpoint + delegatorRewardsURL := baseurl + `/cosmos/distribution/v1beta1/delegators/%s/rewards` + expectedAmountOutput := `{"denom":"stake","amount":"0.121275000000000000"}` + rewardsOutput := fmt.Sprintf(`{"rewards":[{"validator_address":"%s","reward":[%s]}],"total":[%s]}`, valOperAddr, expectedAmountOutput, expectedAmountOutput) + + delegatorRewardsTestCases := []RestTestCase{ + { + "valid rewards request with valid delegator address", + fmt.Sprintf(delegatorRewardsURL, delAddr), + http.StatusOK, + rewardsOutput, + }, + { + "valid request(specific validator rewards)", + fmt.Sprintf(delegatorRewardsURL+`/%s`, delAddr, valOperAddr), + http.StatusOK, + fmt.Sprintf(`{"rewards":[%s]}`, expectedAmountOutput), + }, + } + TestRestQueryIgnoreNumbers(t, delegatorRewardsTestCases) + + // test delegator validators grpc endpoint + delegatorValsURL := baseurl + `/cosmos/distribution/v1beta1/delegators/%s/validators` + valsTestCases := []RestTestCase{ + { + "gRPC request delegator validators with valid delegator address", + fmt.Sprintf(delegatorValsURL, delAddr), + http.StatusOK, + fmt.Sprintf(`{"validators":["%s"]}`, valOperAddr), + }, + } + RunRestQueries(t, valsTestCases) + + // test withdraw address grpc endpoint + withdrawAddrURL := baseurl + `/cosmos/distribution/v1beta1/delegators/%s/withdraw_address` + withdrawAddrTestCases := []RestTestCase{ + { + "gRPC request withdraw address with valid delegator address", + fmt.Sprintf(withdrawAddrURL, delAddr), + http.StatusOK, + fmt.Sprintf(`{"withdraw_address":"%s"}`, delAddr), + }, + } + RunRestQueries(t, withdrawAddrTestCases) +} diff --git a/tests/systemtests/rest_cli.go b/tests/systemtests/rest_cli.go index 2ae879e1a242..a7609949387f 100644 --- a/tests/systemtests/rest_cli.go +++ b/tests/systemtests/rest_cli.go @@ -3,9 +3,12 @@ package systemtests import ( "io" "net/http" + "regexp" "testing" "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/testutil" ) type RestTestCase struct { @@ -28,6 +31,34 @@ func RunRestQueries(t *testing.T, testCases []RestTestCase) { } } +// TestRestQueryIgnoreNumbers runs given rest testcases by making requests and +// checking response with expected output ignoring number values +// This method is used when number values in response are non-deterministic +func TestRestQueryIgnoreNumbers(t *testing.T, testCases []RestTestCase) { + t.Helper() + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + resp, err := testutil.GetRequest(tc.url) + require.NoError(t, err) + + // regular expression pattern to match any numeric value in the JSON + numberRegexPattern := `"\d+(\.\d+)?"` + + // compile the regex + r, err := regexp.Compile(numberRegexPattern) + require.NoError(t, err) + + // replace all numeric values in the above JSONs with `NUMBER` text + expectedJSON := r.ReplaceAllString(tc.expOut, `"NUMBER"`) + actualJSON := r.ReplaceAllString(string(resp), `"NUMBER"`) + + // compare two jsons + require.JSONEq(t, expectedJSON, actualJSON) + }) + } +} + func GetRequest(t *testing.T, url string) []byte { t.Helper() return GetRequestWithHeaders(t, url, nil, http.StatusOK) diff --git a/tests/systemtests/system.go b/tests/systemtests/system.go index 7aa00f7cfbf3..346f39e761b4 100644 --- a/tests/systemtests/system.go +++ b/tests/systemtests/system.go @@ -132,12 +132,17 @@ func (s *SystemUnderTest) SetupChain() { genesisBz, err = sjson.SetRawBytes(genesisBz, "consensus.params.block.max_gas", []byte(fmt.Sprintf(`"%d"`, 10_000_000))) if err != nil { - panic(fmt.Sprintf("failed set block max gas: %s", err)) + panic(fmt.Sprintf("failed to set block max gas: %s", err)) } // Short period for gov genesisBz, err = sjson.SetRawBytes(genesisBz, "app_state.gov.params.voting_period", []byte(fmt.Sprintf(`"%s"`, "8s"))) if err != nil { - panic(fmt.Sprintf("failed set block max gas: %s", err)) + panic(fmt.Sprintf("failed to set regular voting period: %s", err)) + } + // update expedited voting period to avoid validation error + genesisBz, err = sjson.SetRawBytes(genesisBz, "app_state.gov.params.expedited_voting_period", []byte(fmt.Sprintf(`"%s"`, "7s"))) + if err != nil { + panic(fmt.Sprintf("failed to set expedited voting period: %s", err)) } s.withEachNodeHome(func(i int, home string) { if err := saveGenesis(home, genesisBz); err != nil {