test(gov): add test for Tally function (#17142)
This commit is contained in:
parent
56ba5e288a
commit
7daf40a270
@ -54,13 +54,38 @@ func getTestProposal() []sdk.Msg {
|
||||
}
|
||||
}
|
||||
|
||||
type mocks struct {
|
||||
acctKeeper *govtestutil.MockAccountKeeper
|
||||
bankKeeper *govtestutil.MockBankKeeper
|
||||
stakingKeeper *govtestutil.MockStakingKeeper
|
||||
distributionKeeper *govtestutil.MockDistributionKeeper
|
||||
}
|
||||
|
||||
func mockAccountKeeperExpectations(ctx sdk.Context, m mocks) {
|
||||
m.acctKeeper.EXPECT().GetModuleAddress(types.ModuleName).Return(govAcct).AnyTimes()
|
||||
m.acctKeeper.EXPECT().GetModuleAddress(disttypes.ModuleName).Return(distAcct).AnyTimes()
|
||||
m.acctKeeper.EXPECT().GetModuleAccount(gomock.Any(), types.ModuleName).Return(authtypes.NewEmptyModuleAccount(types.ModuleName)).AnyTimes()
|
||||
m.acctKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()
|
||||
}
|
||||
|
||||
func mockDefaultExpectations(ctx sdk.Context, m mocks) {
|
||||
mockAccountKeeperExpectations(ctx, m)
|
||||
trackMockBalances(m.bankKeeper, m.distributionKeeper)
|
||||
m.stakingKeeper.EXPECT().TokensFromConsensusPower(ctx, gomock.Any()).DoAndReturn(func(ctx sdk.Context, power int64) math.Int {
|
||||
return sdk.TokensFromConsensusPower(power, math.NewIntFromUint64(1000000))
|
||||
}).AnyTimes()
|
||||
|
||||
m.stakingKeeper.EXPECT().BondDenom(ctx).Return("stake", nil).AnyTimes()
|
||||
m.stakingKeeper.EXPECT().IterateBondedValidatorsByPower(gomock.Any(), gomock.Any()).AnyTimes()
|
||||
m.stakingKeeper.EXPECT().IterateDelegations(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
m.stakingKeeper.EXPECT().TotalBondedTokens(gomock.Any()).Return(math.NewInt(10000000), nil).AnyTimes()
|
||||
m.distributionKeeper.EXPECT().FundCommunityPool(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
||||
}
|
||||
|
||||
// setupGovKeeper creates a govKeeper as well as all its dependencies.
|
||||
func setupGovKeeper(t *testing.T) (
|
||||
func setupGovKeeper(t *testing.T, expectations ...func(sdk.Context, mocks)) (
|
||||
*keeper.Keeper,
|
||||
*govtestutil.MockAccountKeeper,
|
||||
*govtestutil.MockBankKeeper,
|
||||
*govtestutil.MockStakingKeeper,
|
||||
*govtestutil.MockDistributionKeeper,
|
||||
mocks,
|
||||
moduletestutil.TestEncodingConfig,
|
||||
sdk.Context,
|
||||
) {
|
||||
@ -80,30 +105,23 @@ func setupGovKeeper(t *testing.T) (
|
||||
|
||||
// gomock initializations
|
||||
ctrl := gomock.NewController(t)
|
||||
acctKeeper := govtestutil.NewMockAccountKeeper(ctrl)
|
||||
bankKeeper := govtestutil.NewMockBankKeeper(ctrl)
|
||||
stakingKeeper := govtestutil.NewMockStakingKeeper(ctrl)
|
||||
distributionKeeper := govtestutil.NewMockDistributionKeeper(ctrl)
|
||||
|
||||
acctKeeper.EXPECT().GetModuleAddress(types.ModuleName).Return(govAcct).AnyTimes()
|
||||
acctKeeper.EXPECT().GetModuleAddress(disttypes.ModuleName).Return(distAcct).AnyTimes()
|
||||
acctKeeper.EXPECT().GetModuleAccount(gomock.Any(), types.ModuleName).Return(authtypes.NewEmptyModuleAccount(types.ModuleName)).AnyTimes()
|
||||
acctKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()
|
||||
|
||||
trackMockBalances(bankKeeper, distributionKeeper)
|
||||
stakingKeeper.EXPECT().TokensFromConsensusPower(ctx, gomock.Any()).DoAndReturn(func(ctx sdk.Context, power int64) math.Int {
|
||||
return sdk.TokensFromConsensusPower(power, math.NewIntFromUint64(1000000))
|
||||
}).AnyTimes()
|
||||
|
||||
stakingKeeper.EXPECT().BondDenom(ctx).Return("stake", nil).AnyTimes()
|
||||
stakingKeeper.EXPECT().IterateBondedValidatorsByPower(gomock.Any(), gomock.Any()).AnyTimes()
|
||||
stakingKeeper.EXPECT().IterateDelegations(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
||||
stakingKeeper.EXPECT().TotalBondedTokens(gomock.Any()).Return(math.NewInt(10000000), nil).AnyTimes()
|
||||
distributionKeeper.EXPECT().FundCommunityPool(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
||||
m := mocks{
|
||||
acctKeeper: govtestutil.NewMockAccountKeeper(ctrl),
|
||||
bankKeeper: govtestutil.NewMockBankKeeper(ctrl),
|
||||
stakingKeeper: govtestutil.NewMockStakingKeeper(ctrl),
|
||||
distributionKeeper: govtestutil.NewMockDistributionKeeper(ctrl),
|
||||
}
|
||||
if len(expectations) == 0 {
|
||||
mockDefaultExpectations(ctx, m)
|
||||
} else {
|
||||
for _, exp := range expectations {
|
||||
exp(ctx, m)
|
||||
}
|
||||
}
|
||||
|
||||
// Gov keeper initializations
|
||||
|
||||
govKeeper := keeper.NewKeeper(encCfg.Codec, storeService, acctKeeper, bankKeeper, stakingKeeper, distributionKeeper, msr, types.DefaultConfig(), govAcct.String())
|
||||
govKeeper := keeper.NewKeeper(encCfg.Codec, storeService, m.acctKeeper, m.bankKeeper, m.stakingKeeper, m.distributionKeeper, msr, types.DefaultConfig(), govAcct.String())
|
||||
require.NoError(t, govKeeper.ProposalID.Set(ctx, 1))
|
||||
govRouter := v1beta1.NewRouter() // Also register legacy gov handlers to test them too.
|
||||
govRouter.AddRoute(types.RouterKey, v1beta1.ProposalHandler)
|
||||
@ -118,7 +136,7 @@ func setupGovKeeper(t *testing.T) (
|
||||
v1.RegisterMsgServer(msr, keeper.NewMsgServerImpl(govKeeper))
|
||||
banktypes.RegisterMsgServer(msr, nil) // Nil is fine here as long as we never execute the proposal's Msgs.
|
||||
|
||||
return govKeeper, acctKeeper, bankKeeper, stakingKeeper, distributionKeeper, encCfg, ctx
|
||||
return govKeeper, m, encCfg, ctx
|
||||
}
|
||||
|
||||
// trackMockBalances sets up expected calls on the Mock BankKeeper, and also
|
||||
|
||||
@ -38,7 +38,8 @@ func TestDeposits(t *testing.T) {
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
govKeeper, authKeeper, bankKeeper, stakingKeeper, distKeeper, _, ctx := setupGovKeeper(t)
|
||||
govKeeper, mocks, _, ctx := setupGovKeeper(t)
|
||||
authKeeper, bankKeeper, stakingKeeper, distKeeper := mocks.acctKeeper, mocks.bankKeeper, mocks.stakingKeeper, mocks.distributionKeeper
|
||||
trackMockBalances(bankKeeper, distKeeper)
|
||||
|
||||
// With expedited proposals the minimum deposit is higher, so we must
|
||||
@ -249,7 +250,7 @@ func TestValidateInitialDeposit(t *testing.T) {
|
||||
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
govKeeper, _, _, _, _, _, ctx := setupGovKeeper(t)
|
||||
govKeeper, _, _, ctx := setupGovKeeper(t)
|
||||
|
||||
params := v1.DefaultParams()
|
||||
if tc.expedited {
|
||||
@ -312,7 +313,8 @@ func TestChargeDeposit(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run(testName(i), func(t *testing.T) {
|
||||
govKeeper, authKeeper, bankKeeper, stakingKeeper, _, _, ctx := setupGovKeeper(t)
|
||||
govKeeper, mocks, _, ctx := setupGovKeeper(t)
|
||||
authKeeper, bankKeeper, stakingKeeper := mocks.acctKeeper, mocks.bankKeeper, mocks.stakingKeeper
|
||||
params := v1.DefaultParams()
|
||||
params.ProposalCancelRatio = tc.proposalCancelRatio
|
||||
TestAddrs := simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, ctx, 2, sdkmath.NewInt(10000000000))
|
||||
|
||||
@ -49,7 +49,8 @@ func (h *MockGovHooksReceiver) AfterProposalVotingPeriodEnded(ctx context.Contex
|
||||
|
||||
func TestHooks(t *testing.T) {
|
||||
minDeposit := v1.DefaultParams().MinDeposit
|
||||
govKeeper, authKeeper, bankKeeper, stakingKeeper, _, _, ctx := setupGovKeeper(t)
|
||||
govKeeper, mocks, _, ctx := setupGovKeeper(t)
|
||||
authKeeper, bankKeeper, stakingKeeper := mocks.acctKeeper, mocks.bankKeeper, mocks.stakingKeeper
|
||||
addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 1, minDeposit[0].Amount)
|
||||
|
||||
authKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()
|
||||
|
||||
@ -45,7 +45,8 @@ func (suite *KeeperTestSuite) SetupSuite() {
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) reset() {
|
||||
govKeeper, acctKeeper, bankKeeper, stakingKeeper, distKeeper, encCfg, ctx := setupGovKeeper(suite.T())
|
||||
govKeeper, mocks, encCfg, ctx := setupGovKeeper(suite.T())
|
||||
acctKeeper, bankKeeper, stakingKeeper, distKeeper := mocks.acctKeeper, mocks.bankKeeper, mocks.stakingKeeper, mocks.distributionKeeper
|
||||
|
||||
// Populate the gov account with some coins, as the TestProposal we have
|
||||
// is a MsgSend from the gov account.
|
||||
@ -80,7 +81,8 @@ func (suite *KeeperTestSuite) reset() {
|
||||
}
|
||||
|
||||
func TestIncrementProposalNumber(t *testing.T) {
|
||||
govKeeper, authKeeper, _, _, _, _, ctx := setupGovKeeper(t)
|
||||
govKeeper, mocks, _, ctx := setupGovKeeper(t)
|
||||
authKeeper := mocks.acctKeeper
|
||||
|
||||
authKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()
|
||||
|
||||
@ -106,7 +108,8 @@ func TestIncrementProposalNumber(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProposalQueues(t *testing.T) {
|
||||
govKeeper, authKeeper, _, _, _, _, ctx := setupGovKeeper(t)
|
||||
govKeeper, mocks, _, ctx := setupGovKeeper(t)
|
||||
authKeeper := mocks.acctKeeper
|
||||
|
||||
ac := address.NewBech32Codec("cosmos")
|
||||
addrBz, err := ac.StringToBytes(address1)
|
||||
@ -133,7 +136,7 @@ func TestProposalQueues(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetHooks(t *testing.T) {
|
||||
govKeeper, _, _, _, _, _, _ := setupGovKeeper(t)
|
||||
govKeeper, _, _, _ := setupGovKeeper(t)
|
||||
require.Empty(t, govKeeper.Hooks())
|
||||
|
||||
govHooksReceiver := MockGovHooksReceiver{}
|
||||
@ -145,7 +148,8 @@ func TestSetHooks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetGovGovernanceAndModuleAccountAddress(t *testing.T) {
|
||||
govKeeper, authKeeper, _, _, _, _, ctx := setupGovKeeper(t)
|
||||
govKeeper, mocks, _, ctx := setupGovKeeper(t)
|
||||
authKeeper := mocks.acctKeeper
|
||||
mAcc := authKeeper.GetModuleAccount(ctx, "gov")
|
||||
require.Equal(t, mAcc, govKeeper.GetGovernanceAccount(ctx))
|
||||
mAddr := authKeeper.GetModuleAddress("gov")
|
||||
|
||||
417
x/gov/keeper/tally_test.go
Normal file
417
x/gov/keeper/tally_test.go
Normal file
@ -0,0 +1,417 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
|
||||
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
func TestTally(t *testing.T) {
|
||||
type suite struct {
|
||||
t *testing.T
|
||||
proposal v1.Proposal
|
||||
valAddrs []sdk.ValAddress
|
||||
delAddrs []sdk.AccAddress
|
||||
keeper *keeper.Keeper
|
||||
ctx sdk.Context
|
||||
mocks mocks
|
||||
}
|
||||
|
||||
var (
|
||||
// handy functions
|
||||
setTotalBonded = func(s suite, n int64) {
|
||||
s.mocks.stakingKeeper.EXPECT().TotalBondedTokens(gomock.Any()).
|
||||
Return(sdkmath.NewInt(n), nil)
|
||||
}
|
||||
delegatorVote = func(s suite, voter sdk.AccAddress, delegations []stakingtypes.Delegation, vote v1.VoteOption) {
|
||||
err := s.keeper.AddVote(s.ctx, s.proposal.Id, voter, v1.NewNonSplitVoteOption(vote), "")
|
||||
require.NoError(s.t, err)
|
||||
s.mocks.stakingKeeper.EXPECT().
|
||||
IterateDelegations(s.ctx, voter, gomock.Any()).
|
||||
DoAndReturn(
|
||||
func(ctx context.Context, voter sdk.AccAddress, fn func(index int64, d stakingtypes.DelegationI) bool) error {
|
||||
for i, d := range delegations {
|
||||
fn(int64(i), d)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
validatorVote = func(s suite, voter sdk.ValAddress, vote v1.VoteOption) {
|
||||
// validatorVote is like delegatorVote but without delegations
|
||||
delegatorVote(s, sdk.AccAddress(voter), nil, vote)
|
||||
}
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
expedited bool
|
||||
setup func(suite)
|
||||
expectedPass bool
|
||||
expectedBurn bool
|
||||
expectedTally v1.TallyResult
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "no votes, no bonded tokens: prop fails",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 0)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: false,
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "0",
|
||||
AbstainCount: "0",
|
||||
NoCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no votes: prop fails/burn deposit",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: true, // burn because quorum not reached
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "0",
|
||||
AbstainCount: "0",
|
||||
NoCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "one validator votes: prop fails/burn deposit",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_NO)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: true, // burn because quorum not reached
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "0",
|
||||
AbstainCount: "0",
|
||||
NoCount: "1000000",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "one account votes without delegation: prop fails/burn deposit",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
delegatorVote(s, s.delAddrs[0], nil, v1.VoteOption_VOTE_OPTION_YES)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: true, // burn because quorum not reached
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "0",
|
||||
AbstainCount: "0",
|
||||
NoCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "one delegator votes: prop fails/burn deposit",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
delegations := []stakingtypes.Delegation{{
|
||||
DelegatorAddress: s.delAddrs[0].String(),
|
||||
ValidatorAddress: s.valAddrs[0].String(),
|
||||
Shares: sdkmath.LegacyNewDec(42),
|
||||
}}
|
||||
delegatorVote(s, s.delAddrs[0], delegations, v1.VoteOption_VOTE_OPTION_YES)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: true, // burn because quorum not reached
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "42",
|
||||
AbstainCount: "0",
|
||||
NoCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "one delegator votes yes, validator votes also yes: prop fails/burn deposit",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
delegations := []stakingtypes.Delegation{{
|
||||
DelegatorAddress: s.delAddrs[0].String(),
|
||||
ValidatorAddress: s.valAddrs[0].String(),
|
||||
Shares: sdkmath.LegacyNewDec(42),
|
||||
}}
|
||||
delegatorVote(s, s.delAddrs[0], delegations, v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: true, // burn because quorum not reached
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "1000000",
|
||||
AbstainCount: "0",
|
||||
NoCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "one delegator votes yes, validator votes no: prop fails/burn deposit",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
delegations := []stakingtypes.Delegation{{
|
||||
DelegatorAddress: s.delAddrs[0].String(),
|
||||
ValidatorAddress: s.valAddrs[0].String(),
|
||||
Shares: sdkmath.LegacyNewDec(42),
|
||||
}}
|
||||
delegatorVote(s, s.delAddrs[0], delegations, v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_NO)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: true, // burn because quorum not reached
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "42",
|
||||
AbstainCount: "0",
|
||||
NoCount: "999958",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
// one delegator delegates 42 shares to 2 different validators (21 each)
|
||||
// delegator votes yes
|
||||
// first validator votes yes
|
||||
// second validator votes no
|
||||
// third validator (no delegation) votes abstain
|
||||
name: "delegator with mixed delegations: prop fails/burn deposit",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
delegations := []stakingtypes.Delegation{
|
||||
{
|
||||
DelegatorAddress: s.delAddrs[0].String(),
|
||||
ValidatorAddress: s.valAddrs[0].String(),
|
||||
Shares: sdkmath.LegacyNewDec(21),
|
||||
},
|
||||
{
|
||||
DelegatorAddress: s.delAddrs[0].String(),
|
||||
ValidatorAddress: s.valAddrs[1].String(),
|
||||
Shares: sdkmath.LegacyNewDec(21),
|
||||
},
|
||||
}
|
||||
delegatorVote(s, s.delAddrs[0], delegations, v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_NO)
|
||||
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_ABSTAIN)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: true, // burn because quorum not reached
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "1000021",
|
||||
AbstainCount: "1000000",
|
||||
NoCount: "999979",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "quorum reached with only abstain: prop fails",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ABSTAIN)
|
||||
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ABSTAIN)
|
||||
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_ABSTAIN)
|
||||
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_ABSTAIN)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: false,
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "0",
|
||||
AbstainCount: "4000000",
|
||||
NoCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "quorum reached with veto>1/3: prop fails/burn deposit",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_NO_WITH_VETO)
|
||||
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_NO_WITH_VETO)
|
||||
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_NO_WITH_VETO)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: true,
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "4000000",
|
||||
AbstainCount: "0",
|
||||
NoCount: "0",
|
||||
NoWithVetoCount: "3000000",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "quorum reached with yes<=.5: prop fails",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_NO)
|
||||
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_NO)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: false,
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "2000000",
|
||||
AbstainCount: "0",
|
||||
NoCount: "2000000",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "quorum reached with yes>.5: prop succeeds",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_NO)
|
||||
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_NO)
|
||||
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_NO_WITH_VETO)
|
||||
},
|
||||
expectedPass: true,
|
||||
expectedBurn: false,
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "4000000",
|
||||
AbstainCount: "0",
|
||||
NoCount: "2000000",
|
||||
NoWithVetoCount: "1000000",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "quorum reached thanks to abstain, yes>.5: prop succeeds",
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_NO)
|
||||
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_ABSTAIN)
|
||||
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_ABSTAIN)
|
||||
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_ABSTAIN)
|
||||
},
|
||||
expectedPass: true,
|
||||
expectedBurn: false,
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "2000000",
|
||||
AbstainCount: "3000000",
|
||||
NoCount: "1000000",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "quorum reached with yes<=.667: expedited prop fails",
|
||||
expedited: true,
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_NO)
|
||||
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_NO)
|
||||
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_NO_WITH_VETO)
|
||||
},
|
||||
expectedPass: false,
|
||||
expectedBurn: false,
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "4000000",
|
||||
AbstainCount: "0",
|
||||
NoCount: "2000000",
|
||||
NoWithVetoCount: "1000000",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "quorum reached with yes>.667: expedited prop succeeds",
|
||||
expedited: true,
|
||||
setup: func(s suite) {
|
||||
setTotalBonded(s, 10000000)
|
||||
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_YES)
|
||||
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_NO)
|
||||
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_NO_WITH_VETO)
|
||||
},
|
||||
expectedPass: true,
|
||||
expectedBurn: false,
|
||||
expectedTally: v1.TallyResult{
|
||||
YesCount: "5000000",
|
||||
AbstainCount: "0",
|
||||
NoCount: "1000000",
|
||||
NoWithVetoCount: "1000000",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
govKeeper, mocks, _, ctx := setupGovKeeper(t, mockAccountKeeperExpectations)
|
||||
params := v1.DefaultParams()
|
||||
// Ensure params value are different than false
|
||||
params.BurnVoteQuorum = true
|
||||
params.BurnVoteVeto = true
|
||||
err := govKeeper.Params.Set(ctx, params)
|
||||
require.NoError(t, err)
|
||||
var (
|
||||
numVals = 10
|
||||
numDelegators = 5
|
||||
addrs = simtestutil.CreateRandomAccounts(numVals + numDelegators)
|
||||
valAddrs = simtestutil.ConvertAddrsToValAddrs(addrs[:numVals])
|
||||
delAddrs = addrs[numVals:]
|
||||
)
|
||||
// Mocks a bunch of validators
|
||||
mocks.stakingKeeper.EXPECT().
|
||||
IterateBondedValidatorsByPower(ctx, gomock.Any()).
|
||||
DoAndReturn(
|
||||
func(ctx context.Context, fn func(index int64, validator stakingtypes.ValidatorI) bool) error {
|
||||
for i := int64(0); i < int64(numVals); i++ {
|
||||
fn(i, stakingtypes.Validator{
|
||||
OperatorAddress: valAddrs[i].String(),
|
||||
Status: stakingtypes.Bonded,
|
||||
Tokens: sdkmath.NewInt(1000000),
|
||||
DelegatorShares: sdkmath.LegacyNewDec(1000000),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Submit and activate a proposal
|
||||
proposal, err := govKeeper.SubmitProposal(ctx, TestProposal, "", "title", "summary", delAddrs[0], tt.expedited)
|
||||
require.NoError(t, err)
|
||||
err = govKeeper.ActivateVotingPeriod(ctx, proposal)
|
||||
require.NoError(t, err)
|
||||
suite := suite{
|
||||
t: t,
|
||||
proposal: proposal,
|
||||
valAddrs: valAddrs,
|
||||
delAddrs: delAddrs,
|
||||
ctx: ctx,
|
||||
keeper: govKeeper,
|
||||
mocks: mocks,
|
||||
}
|
||||
tt.setup(suite)
|
||||
|
||||
pass, burn, tally, err := govKeeper.Tally(ctx, proposal)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedPass, pass, "wrong pass")
|
||||
assert.Equal(t, tt.expectedBurn, burn, "wrong burn")
|
||||
assert.Equal(t, tt.expectedTally, tally)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,8 @@ import (
|
||||
)
|
||||
|
||||
func TestVotes(t *testing.T) {
|
||||
govKeeper, authKeeper, bankKeeper, stakingKeeper, _, _, ctx := setupGovKeeper(t)
|
||||
govKeeper, mocks, _, ctx := setupGovKeeper(t)
|
||||
authKeeper, bankKeeper, stakingKeeper := mocks.acctKeeper, mocks.bankKeeper, mocks.stakingKeeper
|
||||
addrs := simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, ctx, 2, sdkmath.NewInt(10000000))
|
||||
authKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user