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 }