github.com/cosmos/cosmos-sdk@v0.50.10/x/gov/keeper/proposal_test.go (about)

     1  package keeper_test
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"cosmossdk.io/collections"
    11  
    12  	"github.com/cosmos/cosmos-sdk/testutil/testdata"
    13  	sdk "github.com/cosmos/cosmos-sdk/types"
    14  	"github.com/cosmos/cosmos-sdk/x/gov/types"
    15  	v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
    16  	"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
    17  )
    18  
    19  // TODO(tip): remove this
    20  func (suite *KeeperTestSuite) TestGetSetProposal() {
    21  	testCases := map[string]struct {
    22  		expedited bool
    23  	}{
    24  		"regular proposal": {},
    25  		"expedited proposal": {
    26  			expedited: true,
    27  		},
    28  	}
    29  
    30  	for _, tc := range testCases {
    31  		tp := TestProposal
    32  		proposal, err := suite.govKeeper.SubmitProposal(suite.ctx, tp, "", "test", "summary", suite.addrs[0], tc.expedited)
    33  		suite.Require().NoError(err)
    34  		proposalID := proposal.Id
    35  		suite.govKeeper.SetProposal(suite.ctx, proposal)
    36  
    37  		gotProposal, err := suite.govKeeper.Proposals.Get(suite.ctx, proposalID)
    38  		suite.Require().Nil(err)
    39  		suite.Require().Equal(proposal, gotProposal)
    40  	}
    41  }
    42  
    43  // TODO(tip): remove this
    44  func (suite *KeeperTestSuite) TestDeleteProposal() {
    45  	testCases := map[string]struct {
    46  		expedited bool
    47  	}{
    48  		"regular proposal": {},
    49  		"expedited proposal": {
    50  			expedited: true,
    51  		},
    52  	}
    53  
    54  	for _, tc := range testCases {
    55  		// delete non-existing proposal
    56  		suite.Require().ErrorIs(suite.govKeeper.DeleteProposal(suite.ctx, 10), collections.ErrNotFound)
    57  
    58  		tp := TestProposal
    59  		proposal, err := suite.govKeeper.SubmitProposal(suite.ctx, tp, "", "test", "summary", suite.addrs[0], tc.expedited)
    60  		suite.Require().NoError(err)
    61  		proposalID := proposal.Id
    62  		suite.govKeeper.SetProposal(suite.ctx, proposal)
    63  		suite.Require().NotPanics(func() {
    64  			suite.govKeeper.DeleteProposal(suite.ctx, proposalID)
    65  		}, "")
    66  	}
    67  }
    68  
    69  func (suite *KeeperTestSuite) TestActivateVotingPeriod() {
    70  	testCases := []struct {
    71  		name      string
    72  		expedited bool
    73  	}{
    74  		{name: "regular proposal"},
    75  		{name: "expedited proposal", expedited: true},
    76  	}
    77  
    78  	for _, tc := range testCases {
    79  		tp := TestProposal
    80  		proposal, err := suite.govKeeper.SubmitProposal(suite.ctx, tp, "", "test", "summary", suite.addrs[0], tc.expedited)
    81  		suite.Require().NoError(err)
    82  
    83  		suite.Require().Nil(proposal.VotingStartTime)
    84  
    85  		suite.govKeeper.ActivateVotingPeriod(suite.ctx, proposal)
    86  
    87  		proposal, err = suite.govKeeper.Proposals.Get(suite.ctx, proposal.Id)
    88  		suite.Require().Nil(err)
    89  		suite.Require().True(proposal.VotingStartTime.Equal(suite.ctx.BlockHeader().Time))
    90  
    91  		has, err := suite.govKeeper.ActiveProposalsQueue.Has(suite.ctx, collections.Join(*proposal.VotingEndTime, proposal.Id))
    92  		suite.Require().NoError(err)
    93  		suite.Require().True(has)
    94  		suite.Require().NoError(suite.govKeeper.DeleteProposal(suite.ctx, proposal.Id))
    95  	}
    96  }
    97  
    98  func (suite *KeeperTestSuite) TestDeleteProposalInVotingPeriod() {
    99  	testCases := []struct {
   100  		name      string
   101  		expedited bool
   102  	}{
   103  		{name: "regular proposal"},
   104  		{name: "expedited proposal", expedited: true},
   105  	}
   106  
   107  	for _, tc := range testCases {
   108  		suite.reset()
   109  		tp := TestProposal
   110  		proposal, err := suite.govKeeper.SubmitProposal(suite.ctx, tp, "", "test", "summary", suite.addrs[0], tc.expedited)
   111  		suite.Require().NoError(err)
   112  		suite.Require().Nil(proposal.VotingStartTime)
   113  
   114  		suite.Require().NoError(suite.govKeeper.ActivateVotingPeriod(suite.ctx, proposal))
   115  
   116  		proposal, err = suite.govKeeper.Proposals.Get(suite.ctx, proposal.Id)
   117  		suite.Require().Nil(err)
   118  		suite.Require().True(proposal.VotingStartTime.Equal(suite.ctx.BlockHeader().Time))
   119  
   120  		has, err := suite.govKeeper.ActiveProposalsQueue.Has(suite.ctx, collections.Join(*proposal.VotingEndTime, proposal.Id))
   121  		suite.Require().NoError(err)
   122  		suite.Require().True(has)
   123  
   124  		// add vote
   125  		voteOptions := []*v1.WeightedVoteOption{{Option: v1.OptionYes, Weight: "1.0"}}
   126  		err = suite.govKeeper.AddVote(suite.ctx, proposal.Id, suite.addrs[0], voteOptions, "")
   127  		suite.Require().NoError(err)
   128  
   129  		suite.Require().NoError(suite.govKeeper.DeleteProposal(suite.ctx, proposal.Id))
   130  
   131  		// add vote but proposal is deleted along with its VotingPeriodProposalKey
   132  		err = suite.govKeeper.AddVote(suite.ctx, proposal.Id, suite.addrs[0], voteOptions, "")
   133  		suite.Require().ErrorContains(err, ": inactive proposal")
   134  	}
   135  }
   136  
   137  type invalidProposalRoute struct{ v1beta1.TextProposal }
   138  
   139  func (invalidProposalRoute) ProposalRoute() string { return "nonexistingroute" }
   140  
   141  func (suite *KeeperTestSuite) TestSubmitProposal() {
   142  	govAcct := suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress().String()
   143  	_, _, randomAddr := testdata.KeyTestPubAddr()
   144  	tp := v1beta1.TextProposal{Title: "title", Description: "description"}
   145  
   146  	testCases := []struct {
   147  		content     v1beta1.Content
   148  		authority   string
   149  		metadata    string
   150  		expedited   bool
   151  		expectedErr error
   152  	}{
   153  		{&tp, govAcct, "", false, nil},
   154  		{&tp, govAcct, "", true, nil},
   155  		// Keeper does not check the validity of title and description, no error
   156  		{&v1beta1.TextProposal{Title: "", Description: "description"}, govAcct, "", false, nil},
   157  		{&v1beta1.TextProposal{Title: strings.Repeat("1234567890", 100), Description: "description"}, govAcct, "", false, nil},
   158  		{&v1beta1.TextProposal{Title: "title", Description: ""}, govAcct, "", false, nil},
   159  		{&v1beta1.TextProposal{Title: "title", Description: strings.Repeat("1234567890", 1000)}, govAcct, "", true, nil},
   160  		// error when metadata is too long (>10000)
   161  		{&tp, govAcct, strings.Repeat("a", 100001), true, types.ErrMetadataTooLong},
   162  		// error when signer is not gov acct
   163  		{&tp, randomAddr.String(), "", false, types.ErrInvalidSigner},
   164  		// error only when invalid route
   165  		{&invalidProposalRoute{}, govAcct, "", false, types.ErrNoProposalHandlerExists},
   166  	}
   167  
   168  	for i, tc := range testCases {
   169  		prop, err := v1.NewLegacyContent(tc.content, tc.authority)
   170  		suite.Require().NoError(err)
   171  		_, err = suite.govKeeper.SubmitProposal(suite.ctx, []sdk.Msg{prop}, tc.metadata, "title", "", suite.addrs[0], tc.expedited)
   172  		suite.Require().True(errors.Is(tc.expectedErr, err), "tc #%d; got: %v, expected: %v", i, err, tc.expectedErr)
   173  	}
   174  }
   175  
   176  func (suite *KeeperTestSuite) TestCancelProposal() {
   177  	govAcct := suite.govKeeper.GetGovernanceAccount(suite.ctx).GetAddress().String()
   178  	tp := v1beta1.TextProposal{Title: "title", Description: "description"}
   179  	prop, err := v1.NewLegacyContent(&tp, govAcct)
   180  	suite.Require().NoError(err)
   181  	proposal, err := suite.govKeeper.SubmitProposal(suite.ctx, []sdk.Msg{prop}, "", "title", "summary", suite.addrs[0], false)
   182  	suite.Require().NoError(err)
   183  	proposalID := proposal.Id
   184  
   185  	proposal2, err := suite.govKeeper.SubmitProposal(suite.ctx, []sdk.Msg{prop}, "", "title", "summary", suite.addrs[1], true)
   186  	suite.Require().NoError(err)
   187  	proposal2ID := proposal2.Id
   188  
   189  	// proposal3 is only used to check the votes for proposals which doesn't go through `CancelProposal` are still present in state
   190  	proposal3, err := suite.govKeeper.SubmitProposal(suite.ctx, []sdk.Msg{prop}, "", "title", "summary", suite.addrs[2], false)
   191  	suite.Require().NoError(err)
   192  	proposal3ID := proposal3.Id
   193  
   194  	// add votes for proposal 3
   195  	suite.Require().NoError(suite.govKeeper.ActivateVotingPeriod(suite.ctx, proposal3))
   196  
   197  	proposal3, err = suite.govKeeper.Proposals.Get(suite.ctx, proposal3ID)
   198  	suite.Require().Nil(err)
   199  	suite.Require().True(proposal3.VotingStartTime.Equal(suite.ctx.BlockHeader().Time))
   200  	// add vote
   201  	voteOptions := []*v1.WeightedVoteOption{{Option: v1.OptionYes, Weight: "1.0"}}
   202  	err = suite.govKeeper.AddVote(suite.ctx, proposal3ID, suite.addrs[0], voteOptions, "")
   203  	suite.Require().NoError(err)
   204  
   205  	testCases := []struct {
   206  		name        string
   207  		malleate    func() (proposalID uint64, proposer string)
   208  		proposalID  uint64
   209  		proposer    string
   210  		expectedErr bool
   211  	}{
   212  		{
   213  			name: "without proposer",
   214  			malleate: func() (uint64, string) {
   215  				return 1, ""
   216  			},
   217  			expectedErr: true,
   218  		},
   219  		{
   220  			name: "invalid proposal id",
   221  			malleate: func() (uint64, string) {
   222  				return 1, suite.addrs[1].String()
   223  			},
   224  			expectedErr: true,
   225  		},
   226  		{
   227  			name: "valid proposalID but invalid proposer",
   228  			malleate: func() (uint64, string) {
   229  				return proposalID, suite.addrs[1].String()
   230  			},
   231  			expectedErr: true,
   232  		},
   233  		{
   234  			name: "valid proposalID but invalid proposal which has already passed",
   235  			malleate: func() (uint64, string) {
   236  				// making proposal status pass
   237  				proposal2, err := suite.govKeeper.Proposals.Get(suite.ctx, proposal2ID)
   238  				suite.Require().Nil(err)
   239  
   240  				proposal2.Status = v1.ProposalStatus_PROPOSAL_STATUS_PASSED
   241  				suite.govKeeper.SetProposal(suite.ctx, proposal2)
   242  
   243  				return proposal2ID, suite.addrs[1].String()
   244  			},
   245  			expectedErr: true,
   246  		},
   247  		{
   248  			name: "valid proposer and proposal id",
   249  			malleate: func() (uint64, string) {
   250  				return proposalID, suite.addrs[0].String()
   251  			},
   252  			expectedErr: false,
   253  		},
   254  		{
   255  			name: "valid case with deletion of votes",
   256  			malleate: func() (uint64, string) {
   257  				suite.Require().NoError(suite.govKeeper.ActivateVotingPeriod(suite.ctx, proposal))
   258  
   259  				proposal, err = suite.govKeeper.Proposals.Get(suite.ctx, proposal.Id)
   260  				suite.Require().Nil(err)
   261  				suite.Require().True(proposal.VotingStartTime.Equal(suite.ctx.BlockHeader().Time))
   262  
   263  				// add vote
   264  				voteOptions := []*v1.WeightedVoteOption{{Option: v1.OptionYes, Weight: "1.0"}}
   265  				err = suite.govKeeper.AddVote(suite.ctx, proposalID, suite.addrs[0], voteOptions, "")
   266  				suite.Require().NoError(err)
   267  				vote, err := suite.govKeeper.Votes.Get(suite.ctx, collections.Join(proposalID, suite.addrs[0]))
   268  				suite.Require().NoError(err)
   269  				suite.Require().NotNil(vote)
   270  
   271  				return proposalID, suite.addrs[0].String()
   272  			},
   273  			expectedErr: false,
   274  		},
   275  	}
   276  
   277  	for _, tc := range testCases {
   278  		suite.Run(tc.name, func() {
   279  			pID, proposer := tc.malleate()
   280  			err = suite.govKeeper.CancelProposal(suite.ctx, pID, proposer)
   281  			if tc.expectedErr {
   282  				suite.Require().Error(err)
   283  			} else {
   284  				suite.Require().NoError(err)
   285  			}
   286  		})
   287  	}
   288  	_, err = suite.govKeeper.Votes.Get(suite.ctx, collections.Join(proposalID, suite.addrs[0]))
   289  	suite.Require().ErrorContains(err, collections.ErrNotFound.Error())
   290  
   291  	// check that proposal 3 votes are still present in the state
   292  	votes, err := suite.govKeeper.Votes.Get(suite.ctx, collections.Join(proposal3ID, suite.addrs[0]))
   293  	suite.Require().NoError(err)
   294  	suite.Require().NotNil(votes)
   295  }
   296  
   297  func TestMigrateProposalMessages(t *testing.T) {
   298  	content := v1beta1.NewTextProposal("Test", "description")
   299  	contentMsg, err := v1.NewLegacyContent(content, sdk.AccAddress("test1").String())
   300  	require.NoError(t, err)
   301  	content, err = v1.LegacyContentFromMessage(contentMsg)
   302  	require.NoError(t, err)
   303  	require.Equal(t, "Test", content.GetTitle())
   304  	require.Equal(t, "description", content.GetDescription())
   305  }