From 2247465fa3aa63e97597d2f8c07844a914482df7 Mon Sep 17 00:00:00 2001 From: dauTT Date: Mon, 20 Jul 2020 17:05:41 +0200 Subject: [PATCH] x/distribution/simulation/operations.go: add unit tests (#6764) * x/distribution/simulation/operations.go: add unit tests * Use NewDecCoins constructor instead of the DecCoins struct * Refactor code to use a testing suite Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Alexander Bezobchuk --- x/distribution/simulation/operations_test.go | 274 +++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 x/distribution/simulation/operations_test.go diff --git a/x/distribution/simulation/operations_test.go b/x/distribution/simulation/operations_test.go new file mode 100644 index 0000000000..f7aa170197 --- /dev/null +++ b/x/distribution/simulation/operations_test.go @@ -0,0 +1,274 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/suite" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/simapp" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/distribution/simulation" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// TestWeightedOperations tests the weights of the operations. +func (suite *SimTestSuite) TestWeightedOperations() { + cdc := suite.app.Codec() + appParams := make(simtypes.AppParams) + + weightesOps := simulation.WeightedOperations(appParams, cdc, suite.app.AccountKeeper, + suite.app.BankKeeper, suite.app.DistrKeeper, suite.app.StakingKeeper, + ) + + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accs := suite.getTestingAccounts(r, 3) + + expected := []struct { + weight int + opMsgRoute string + opMsgName string + }{ + {simappparams.DefaultWeightMsgSetWithdrawAddress, types.ModuleName, types.TypeMsgSetWithdrawAddress}, + {simappparams.DefaultWeightMsgWithdrawDelegationReward, types.ModuleName, types.TypeMsgWithdrawDelegatorReward}, + {simappparams.DefaultWeightMsgWithdrawValidatorCommission, types.ModuleName, types.TypeMsgWithdrawValidatorCommission}, + {simappparams.DefaultWeightMsgFundCommunityPool, types.ModuleName, types.TypeMsgFundCommunityPool}, + } + + for i, w := range weightesOps { + operationMsg, _, _ := w.Op()(r, suite.app.BaseApp, suite.ctx, accs, "") + // the following checks are very much dependent from the ordering of the output given + // by WeightedOperations. if the ordering in WeightedOperations changes some tests + // will fail + suite.Require().Equal(expected[i].weight, w.Weight(), "weight should be the same") + suite.Require().Equal(expected[i].opMsgRoute, operationMsg.Route, "route should be the same") + suite.Require().Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") + } +} + +// TestSimulateMsgSetWithdrawAddress tests the normal scenario of a valid message of type TypeMsgSetWithdrawAddress. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgSetWithdrawAddress() { + + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // begin a new block + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}}) + + // execute operation + op := simulation.SimulateMsgSetWithdrawAddress(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.DistrKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgSetWithdrawAddress + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.GetDelegatorAddress().String()) + suite.Require().Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.GetWithdrawAddress().String()) + suite.Require().Equal(types.TypeMsgSetWithdrawAddress, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) +} + +// TestSimulateMsgWithdrawDelegatorReward tests the normal scenario of a valid message +// of type TypeMsgWithdrawDelegatorReward. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgWithdrawDelegatorReward() { + // setup 3 accounts + s := rand.NewSource(4) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // setup accounts[0] as validator + validator0 := suite.getTestingValidator0(accounts) + + // setup delegation + delTokens := sdk.TokensFromConsensusPower(2) + validator0, issuedShares := validator0.AddTokensFromDel(delTokens) + delegator := accounts[1] + delegation := stakingtypes.NewDelegation(delegator.Address, validator0.OperatorAddress, issuedShares) + suite.app.StakingKeeper.SetDelegation(suite.ctx, delegation) + suite.app.DistrKeeper.SetDelegatorStartingInfo(suite.ctx, validator0.OperatorAddress, delegator.Address, distrtypes.NewDelegatorStartingInfo(2, sdk.OneDec(), 200)) + + suite.setupValidatorRewards(validator0.OperatorAddress) + + // begin a new block + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}}) + + // execute operation + op := simulation.SimulateMsgWithdrawDelegatorReward(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.DistrKeeper, suite.app.StakingKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgWithdrawDelegatorReward + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("cosmosvaloper1l4s054098kk9hmr5753c6k3m2kw65h686d3mhr", msg.GetValidatorAddress().String()) + suite.Require().Equal("cosmos1d6u7zhjwmsucs678d7qn95uqajd4ucl9jcjt26", msg.GetDelegatorAddress().String()) + suite.Require().Equal(types.TypeMsgWithdrawDelegatorReward, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) + +} + +// TestSimulateMsgWithdrawValidatorCommission tests the normal scenario of a valid message +// of type TypeMsgWithdrawValidatorCommission. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgWithdrawValidatorCommission() { + suite.testSimulateMsgWithdrawValidatorCommission("atoken") + suite.testSimulateMsgWithdrawValidatorCommission("tokenxxx") +} + +// all the checks in this function should not fail if we change the tokenName +func (suite *SimTestSuite) testSimulateMsgWithdrawValidatorCommission(tokenName string) { + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // setup accounts[0] as validator + validator0 := suite.getTestingValidator0(accounts) + + // set module account coins + distrAcc := suite.app.DistrKeeper.GetDistributionAccount(suite.ctx) + err := suite.app.BankKeeper.SetBalances(suite.ctx, distrAcc.GetAddress(), sdk.NewCoins( + sdk.NewCoin(tokenName, sdk.NewInt(10)), + sdk.NewCoin("stake", sdk.NewInt(5)), + )) + suite.Require().NoError(err) + suite.app.AccountKeeper.SetModuleAccount(suite.ctx, distrAcc) + + // set outstanding rewards + valCommission := sdk.NewDecCoins( + sdk.NewDecCoinFromDec(tokenName, sdk.NewDec(5).Quo(sdk.NewDec(2))), + sdk.NewDecCoinFromDec("stake", sdk.NewDec(1).Quo(sdk.NewDec(1))), + ) + + suite.app.DistrKeeper.SetValidatorOutstandingRewards(suite.ctx, validator0.OperatorAddress, types.ValidatorOutstandingRewards{Rewards: valCommission}) + + // setup validator accumulated commission + suite.app.DistrKeeper.SetValidatorAccumulatedCommission(suite.ctx, validator0.OperatorAddress, types.ValidatorAccumulatedCommission{Commission: valCommission}) + + // begin a new block + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}}) + + // execute operation + op := simulation.SimulateMsgWithdrawValidatorCommission(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.DistrKeeper, suite.app.StakingKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgWithdrawValidatorCommission + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.GetValidatorAddress().String()) + suite.Require().Equal(types.TypeMsgWithdrawValidatorCommission, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) +} + +// TestSimulateMsgFundCommunityPool tests the normal scenario of a valid message of type TypeMsgFundCommunityPool. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgFundCommunityPool() { + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // begin a new block + suite.app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}}) + + // execute operation + op := simulation.SimulateMsgFundCommunityPool(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.DistrKeeper, suite.app.StakingKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgFundCommunityPool + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("4896096stake", msg.GetAmount().String()) + suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Depositor.String()) + suite.Require().Equal(types.TypeMsgFundCommunityPool, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) +} + +type SimTestSuite struct { + suite.Suite + + ctx sdk.Context + app *simapp.SimApp +} + +func (suite *SimTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + suite.app = app + suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{}) +} + +func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Account { + accounts := simtypes.RandomAccounts(r, n) + + initAmt := sdk.TokensFromConsensusPower(200) + initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) + + // add coins to the accounts + for _, account := range accounts { + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, account.Address) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + err := suite.app.BankKeeper.SetBalances(suite.ctx, account.Address, initCoins) + suite.Require().NoError(err) + } + + return accounts +} + +func (suite *SimTestSuite) getTestingValidator0(accounts []simtypes.Account) stakingtypes.Validator { + commission0 := stakingtypes.NewCommission(sdk.ZeroDec(), sdk.OneDec(), sdk.OneDec()) + return suite.getTestingValidator(accounts, commission0, 0) +} + +func (suite *SimTestSuite) getTestingValidator(accounts []simtypes.Account, commission stakingtypes.Commission, n int) stakingtypes.Validator { + account := accounts[n] + valPubKey := account.PubKey + valAddr := sdk.ValAddress(account.PubKey.Address().Bytes()) + validator := stakingtypes.NewValidator(valAddr, valPubKey, stakingtypes.Description{}) + validator, err := validator.SetInitialCommission(commission) + suite.Require().NoError(err) + + validator.DelegatorShares = sdk.NewDec(100) + validator.Tokens = sdk.NewInt(1000000) + + suite.app.StakingKeeper.SetValidator(suite.ctx, validator) + + return validator +} + +func (suite *SimTestSuite) setupValidatorRewards(valAddress sdk.ValAddress) { + decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.OneDec())} + historicalRewards := distrtypes.NewValidatorHistoricalRewards(decCoins, 2) + suite.app.DistrKeeper.SetValidatorHistoricalRewards(suite.ctx, valAddress, 2, historicalRewards) + // setup current revards + currentRewards := distrtypes.NewValidatorCurrentRewards(decCoins, 3) + suite.app.DistrKeeper.SetValidatorCurrentRewards(suite.ctx, valAddress, currentRewards) + +} + +func TestSimTestSuite(t *testing.T) { + suite.Run(t, new(SimTestSuite)) +}