forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x/distribution/simulation/operations.go: add unit tests (cosmos#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 <[email protected]> Co-authored-by: Alexander Bezobchuk <[email protected]>
- Loading branch information
1 parent
8e61ef8
commit 2247465
Showing
1 changed file
with
274 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)) | ||
} |