github.com/cosmos/cosmos-sdk@v0.50.10/x/group/module/abci_test.go (about) 1 package module_test 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" 9 cmttime "github.com/cometbft/cometbft/types/time" 10 "github.com/stretchr/testify/suite" 11 12 "cosmossdk.io/core/address" 13 "cosmossdk.io/depinject" 14 "cosmossdk.io/log" 15 "cosmossdk.io/math" 16 17 codecaddress "github.com/cosmos/cosmos-sdk/codec/address" 18 codectypes "github.com/cosmos/cosmos-sdk/codec/types" 19 "github.com/cosmos/cosmos-sdk/runtime" 20 simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" 21 sdk "github.com/cosmos/cosmos-sdk/types" 22 bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" 23 "github.com/cosmos/cosmos-sdk/x/bank/testutil" 24 banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 25 "github.com/cosmos/cosmos-sdk/x/group" 26 "github.com/cosmos/cosmos-sdk/x/group/keeper" 27 "github.com/cosmos/cosmos-sdk/x/group/module" 28 grouptestutil "github.com/cosmos/cosmos-sdk/x/group/testutil" 29 stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" 30 ) 31 32 type IntegrationTestSuite struct { 33 suite.Suite 34 35 app *runtime.App 36 ctx sdk.Context 37 addrs []sdk.AccAddress 38 groupKeeper keeper.Keeper 39 bankKeeper bankkeeper.Keeper 40 stakingKeeper *stakingkeeper.Keeper 41 interfaceRegistry codectypes.InterfaceRegistry 42 43 addressCodec address.Codec 44 } 45 46 func TestIntegrationTestSuite(t *testing.T) { 47 suite.Run(t, new(IntegrationTestSuite)) 48 } 49 50 func (s *IntegrationTestSuite) SetupTest() { 51 app, err := simtestutil.Setup( 52 depinject.Configs( 53 grouptestutil.AppConfig, 54 depinject.Supply(log.NewNopLogger()), 55 ), 56 &s.interfaceRegistry, 57 &s.bankKeeper, 58 &s.stakingKeeper, 59 &s.groupKeeper, 60 ) 61 s.Require().NoError(err) 62 63 ctx := app.BaseApp.NewContext(false) 64 65 ctx = ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()}) 66 67 s.ctx = ctx 68 69 s.addrs = simtestutil.AddTestAddrsIncremental(s.bankKeeper, s.stakingKeeper, ctx, 4, math.NewInt(30000000)) 70 71 s.addressCodec = codecaddress.NewBech32Codec("cosmos") 72 } 73 74 func (s *IntegrationTestSuite) TestEndBlockerPruning() { 75 ctx := s.ctx 76 addr1 := s.addrs[0] 77 addr2 := s.addrs[1] 78 addr3 := s.addrs[2] 79 80 addr1st, err := s.addressCodec.BytesToString(addr1) 81 s.Require().NoError(err) 82 83 // Initial group, group policy and balance setup 84 members := []group.MemberRequest{ 85 {Address: addr1st, Weight: "1"}, {Address: addr2.String(), Weight: "2"}, 86 } 87 88 groupRes, err := s.groupKeeper.CreateGroup(ctx, &group.MsgCreateGroup{ 89 Admin: addr1st, 90 Members: members, 91 }) 92 s.Require().NoError(err) 93 94 groupRes2, err := s.groupKeeper.CreateGroup(ctx, &group.MsgCreateGroup{ 95 Admin: addr2.String(), 96 Members: members, 97 }) 98 s.Require().NoError(err) 99 100 groupID := groupRes.GroupId 101 groupID2 := groupRes2.GroupId 102 103 policy := group.NewThresholdDecisionPolicy( 104 "2", 105 time.Second, 106 0, 107 ) 108 109 policyReq := &group.MsgCreateGroupPolicy{ 110 Admin: addr1.String(), 111 GroupId: groupID, 112 } 113 114 err = policyReq.SetDecisionPolicy(policy) 115 s.Require().NoError(err) 116 policyRes, err := s.groupKeeper.CreateGroupPolicy(ctx, policyReq) 117 s.Require().NoError(err) 118 119 policy2 := group.NewThresholdDecisionPolicy( 120 "1", 121 time.Second, 122 0, 123 ) 124 125 policyReq2 := &group.MsgCreateGroupPolicy{ 126 Admin: addr2.String(), 127 GroupId: groupID2, 128 } 129 130 err = policyReq2.SetDecisionPolicy(policy2) 131 s.Require().NoError(err) 132 policyRes2, err := s.groupKeeper.CreateGroupPolicy(ctx, policyReq2) 133 s.Require().NoError(err) 134 135 groupPolicyAddr, err := s.addressCodec.StringToBytes(policyRes.Address) 136 s.Require().NoError(err) 137 s.Require().NoError(testutil.FundAccount(ctx, s.bankKeeper, groupPolicyAddr, sdk.Coins{sdk.NewInt64Coin("test", 10000)})) 138 139 groupPolicyAddr2, err := s.addressCodec.StringToBytes(policyRes2.Address) 140 s.Require().NoError(err) 141 s.Require().NoError(testutil.FundAccount(ctx, s.bankKeeper, groupPolicyAddr2, sdk.Coins{sdk.NewInt64Coin("test", 10000)})) 142 143 votingPeriod := policy.GetVotingPeriod() 144 145 msgSend1 := &banktypes.MsgSend{ 146 FromAddress: policyRes.Address, 147 ToAddress: addr2.String(), 148 Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)}, 149 } 150 msgSend2 := &banktypes.MsgSend{ 151 FromAddress: policyRes2.Address, 152 ToAddress: addr2.String(), 153 Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)}, 154 } 155 proposers := []string{addr2.String()} 156 157 specs := map[string]struct { 158 setupProposal func(ctx sdk.Context) uint64 159 expErr bool 160 expErrMsg string 161 newCtx sdk.Context 162 expExecutorResult group.ProposalExecutorResult 163 expStatus group.ProposalStatus 164 }{ 165 "proposal pruned after executor result success": { 166 setupProposal: func(ctx sdk.Context) uint64 { 167 msgs := []sdk.Msg{msgSend1} 168 pID, err := submitProposalAndVote(s, s.app, ctx, msgs, proposers, groupPolicyAddr, group.VOTE_OPTION_YES) 169 s.Require().NoError(err) 170 _, err = s.groupKeeper.Exec(ctx, &group.MsgExec{Executor: addr3.String(), ProposalId: pID}) 171 s.Require().NoError(err) 172 sdkCtx := sdk.UnwrapSDKContext(ctx) 173 s.Require().NoError(testutil.FundAccount(sdkCtx, s.bankKeeper, groupPolicyAddr, sdk.Coins{sdk.NewInt64Coin("test", 10002)})) 174 175 return pID 176 }, 177 expErrMsg: "load proposal: not found", 178 newCtx: ctx, 179 expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_SUCCESS, 180 }, 181 "proposal with multiple messages pruned when executed with result success": { 182 setupProposal: func(ctx sdk.Context) uint64 { 183 msgs := []sdk.Msg{msgSend1, msgSend1} 184 pID, err := submitProposalAndVote(s, s.app, ctx, msgs, proposers, groupPolicyAddr, group.VOTE_OPTION_YES) 185 s.Require().NoError(err) 186 _, err = s.groupKeeper.Exec(ctx, &group.MsgExec{Executor: addr3.String(), ProposalId: pID}) 187 s.Require().NoError(err) 188 sdkCtx := sdk.UnwrapSDKContext(ctx) 189 s.Require().NoError(testutil.FundAccount(sdkCtx, s.bankKeeper, groupPolicyAddr, sdk.Coins{sdk.NewInt64Coin("test", 10002)})) 190 191 return pID 192 }, 193 expErrMsg: "load proposal: not found", 194 newCtx: ctx, 195 expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_SUCCESS, 196 }, 197 "proposal not pruned when not executed and rejected": { 198 setupProposal: func(ctx sdk.Context) uint64 { 199 msgs := []sdk.Msg{msgSend1} 200 pID, err := submitProposalAndVote(s, s.app, ctx, msgs, proposers, groupPolicyAddr, group.VOTE_OPTION_NO) 201 s.Require().NoError(err) 202 _, err = s.groupKeeper.Exec(ctx, &group.MsgExec{Executor: addr3.String(), ProposalId: pID}) 203 s.Require().NoError(err) 204 sdkCtx := sdk.UnwrapSDKContext(ctx) 205 s.Require().NoError(testutil.FundAccount(sdkCtx, s.bankKeeper, groupPolicyAddr, sdk.Coins{sdk.NewInt64Coin("test", 10002)})) 206 207 return pID 208 }, 209 newCtx: ctx, 210 expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN, 211 expStatus: group.PROPOSAL_STATUS_REJECTED, 212 }, 213 "open proposal is not pruned which must not fail ": { 214 setupProposal: func(ctx sdk.Context) uint64 { 215 pID, err := submitProposal(s, s.app, ctx, []sdk.Msg{msgSend1}, proposers, groupPolicyAddr) 216 s.Require().NoError(err) 217 _, err = s.groupKeeper.Exec(ctx, &group.MsgExec{Executor: addr3.String(), ProposalId: pID}) 218 s.Require().NoError(err) 219 sdkCtx := sdk.UnwrapSDKContext(ctx) 220 s.Require().NoError(testutil.FundAccount(sdkCtx, s.bankKeeper, groupPolicyAddr, sdk.Coins{sdk.NewInt64Coin("test", 10002)})) 221 222 return pID 223 }, 224 newCtx: ctx, 225 expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN, 226 expStatus: group.PROPOSAL_STATUS_SUBMITTED, 227 }, 228 "proposal not pruned with group policy modified before tally": { 229 setupProposal: func(ctx sdk.Context) uint64 { 230 pID, err := submitProposal(s, s.app, ctx, []sdk.Msg{msgSend1}, proposers, groupPolicyAddr) 231 s.Require().NoError(err) 232 _, err = s.groupKeeper.UpdateGroupPolicyMetadata(ctx, &group.MsgUpdateGroupPolicyMetadata{ 233 Admin: addr1.String(), 234 GroupPolicyAddress: policyRes.Address, 235 }) 236 s.Require().NoError(err) 237 _, err = s.groupKeeper.Exec(ctx, &group.MsgExec{Executor: addr3.String(), ProposalId: pID}) 238 s.Require().Error(err) // since proposal with status Aborted cannot be executed 239 sdkCtx := sdk.UnwrapSDKContext(ctx) 240 s.Require().NoError(testutil.FundAccount(sdkCtx, s.bankKeeper, groupPolicyAddr, sdk.Coins{sdk.NewInt64Coin("test", 10002)})) 241 242 return pID 243 }, 244 newCtx: ctx, 245 expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN, 246 expStatus: group.PROPOSAL_STATUS_ABORTED, 247 }, 248 "pruned when proposal is executable when failed before": { 249 setupProposal: func(ctx sdk.Context) uint64 { 250 msgs := []sdk.Msg{msgSend1} 251 pID, err := submitProposalAndVote(s, s.app, ctx, msgs, proposers, groupPolicyAddr, group.VOTE_OPTION_YES) 252 s.Require().NoError(err) 253 _, err = s.groupKeeper.Exec(ctx, &group.MsgExec{Executor: s.addrs[2].String(), ProposalId: pID}) 254 s.Require().NoError(err) 255 return pID 256 }, 257 newCtx: ctx, 258 expErrMsg: "load proposal: not found", 259 expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_SUCCESS, 260 }, 261 "proposal with status withdrawn is pruned after voting period end": { 262 setupProposal: func(sdkCtx sdk.Context) uint64 { 263 pID, err := submitProposal(s, s.app, sdkCtx, []sdk.Msg{msgSend1}, proposers, groupPolicyAddr) 264 s.Require().NoError(err) 265 _, err = s.groupKeeper.WithdrawProposal(ctx, &group.MsgWithdrawProposal{ 266 ProposalId: pID, 267 Address: proposers[0], 268 }) 269 s.Require().NoError(err) 270 return pID 271 }, 272 newCtx: ctx.WithBlockTime(ctx.BlockTime().Add(votingPeriod).Add(time.Hour)), 273 expErrMsg: "load proposal: not found", 274 expStatus: group.PROPOSAL_STATUS_WITHDRAWN, 275 }, 276 "proposal with status withdrawn is not pruned (before voting period)": { 277 setupProposal: func(sdkCtx sdk.Context) uint64 { 278 pID, err := submitProposal(s, s.app, sdkCtx, []sdk.Msg{msgSend1}, proposers, groupPolicyAddr) 279 s.Require().NoError(err) 280 _, err = s.groupKeeper.WithdrawProposal(ctx, &group.MsgWithdrawProposal{ 281 ProposalId: pID, 282 Address: proposers[0], 283 }) 284 s.Require().NoError(err) 285 return pID 286 }, 287 newCtx: ctx, 288 expErrMsg: "", 289 expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN, 290 expStatus: group.PROPOSAL_STATUS_WITHDRAWN, 291 }, 292 "proposal with status aborted is pruned after voting period end (due to updated group policy decision policy)": { 293 setupProposal: func(sdkCtx sdk.Context) uint64 { 294 pID, err := submitProposal(s, s.app, sdkCtx, []sdk.Msg{msgSend2}, proposers, groupPolicyAddr2) 295 s.Require().NoError(err) 296 297 policy := group.NewThresholdDecisionPolicy("3", time.Second, 0) 298 msg := &group.MsgUpdateGroupPolicyDecisionPolicy{ 299 Admin: s.addrs[1].String(), 300 GroupPolicyAddress: policyRes2.Address, 301 } 302 err = msg.SetDecisionPolicy(policy) 303 s.Require().NoError(err) 304 _, err = s.groupKeeper.UpdateGroupPolicyDecisionPolicy(ctx, msg) 305 s.Require().NoError(err) 306 307 return pID 308 }, 309 newCtx: ctx.WithBlockTime(ctx.BlockTime().Add(votingPeriod).Add(time.Hour)), 310 expErrMsg: "load proposal: not found", 311 expStatus: group.PROPOSAL_STATUS_ABORTED, 312 expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN, 313 }, 314 "proposal with status aborted is not pruned before voting period end (due to updated group policy)": { 315 setupProposal: func(sdkCtx sdk.Context) uint64 { 316 pID, err := submitProposal(s, s.app, sdkCtx, []sdk.Msg{msgSend2}, proposers, groupPolicyAddr2) 317 s.Require().NoError(err) 318 319 policy := group.NewThresholdDecisionPolicy("3", time.Second, 0) 320 msg := &group.MsgUpdateGroupPolicyDecisionPolicy{ 321 Admin: s.addrs[1].String(), 322 GroupPolicyAddress: policyRes2.Address, 323 } 324 err = msg.SetDecisionPolicy(policy) 325 s.Require().NoError(err) 326 _, err = s.groupKeeper.UpdateGroupPolicyDecisionPolicy(ctx, msg) 327 s.Require().NoError(err) 328 329 return pID 330 }, 331 newCtx: ctx, 332 expErrMsg: "", 333 expStatus: group.PROPOSAL_STATUS_ABORTED, 334 expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN, 335 }, 336 } 337 for msg, spec := range specs { 338 spec := spec 339 s.Run(msg, func() { 340 proposalID := spec.setupProposal(ctx) 341 342 module.EndBlocker(spec.newCtx, s.groupKeeper) 343 344 if spec.expErrMsg != "" && spec.expExecutorResult != group.PROPOSAL_EXECUTOR_RESULT_SUCCESS { 345 _, err = s.groupKeeper.Proposal(spec.newCtx, &group.QueryProposalRequest{ProposalId: proposalID}) 346 s.Require().Error(err) 347 s.Require().Contains(err.Error(), spec.expErrMsg) 348 return 349 } 350 if spec.expExecutorResult == group.PROPOSAL_EXECUTOR_RESULT_SUCCESS { 351 // Make sure proposal is deleted from state 352 _, err = s.groupKeeper.Proposal(spec.newCtx, &group.QueryProposalRequest{ProposalId: proposalID}) 353 s.Require().Contains(err.Error(), spec.expErrMsg) 354 res, err := s.groupKeeper.VotesByProposal(ctx, &group.QueryVotesByProposalRequest{ProposalId: proposalID}) 355 s.Require().NoError(err) 356 s.Require().Empty(res.GetVotes()) 357 } else { 358 // Check that proposal and votes exists 359 res, err := s.groupKeeper.Proposal(spec.newCtx, &group.QueryProposalRequest{ProposalId: proposalID}) 360 s.Require().NoError(err) 361 _, err = s.groupKeeper.VotesByProposal(ctx, &group.QueryVotesByProposalRequest{ProposalId: res.Proposal.Id}) 362 s.Require().NoError(err) 363 s.Require().Equal("", spec.expErrMsg) 364 365 exp := group.ProposalExecutorResult_name[int32(spec.expExecutorResult)] 366 got := group.ProposalExecutorResult_name[int32(res.Proposal.ExecutorResult)] 367 s.Assert().Equal(exp, got) 368 369 s.Require().Equal(res.GetProposal().Status, spec.expStatus) 370 } 371 }) 372 } 373 } 374 375 func (s *IntegrationTestSuite) TestEndBlockerTallying() { 376 app := s.app 377 ctx := s.ctx 378 379 addrs := s.addrs 380 381 // Initial group, group policy and balance setup 382 members := []group.MemberRequest{ 383 {Address: addrs[1].String(), Weight: "1"}, {Address: addrs[2].String(), Weight: "2"}, 384 } 385 386 groupRes, err := s.groupKeeper.CreateGroup(ctx, &group.MsgCreateGroup{ 387 Admin: addrs[0].String(), 388 Members: members, 389 }) 390 s.Require().NoError(err) 391 392 groupID := groupRes.GroupId 393 394 policy := group.NewThresholdDecisionPolicy( 395 "2", 396 time.Second, 397 0, 398 ) 399 400 policyReq := &group.MsgCreateGroupPolicy{ 401 Admin: addrs[0].String(), 402 GroupId: groupID, 403 } 404 405 err = policyReq.SetDecisionPolicy(policy) 406 s.Require().NoError(err) 407 policyRes, err := s.groupKeeper.CreateGroupPolicy(ctx, policyReq) 408 s.Require().NoError(err) 409 410 groupPolicyAddr, err := s.addressCodec.StringToBytes(policyRes.Address) 411 s.Require().NoError(err) 412 413 votingPeriod := policy.GetVotingPeriod() 414 415 msgSend := &banktypes.MsgSend{ 416 FromAddress: policyRes.Address, 417 ToAddress: addrs[3].String(), 418 Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)}, 419 } 420 421 proposers := []string{addrs[2].String()} 422 423 specs := map[string]struct { 424 preRun func(sdkCtx sdk.Context) uint64 425 admin string 426 expErrMsg string 427 newCtx sdk.Context 428 tallyRes group.TallyResult 429 expStatus group.ProposalStatus 430 }{ 431 "tally updated after voting period end": { 432 preRun: func(sdkCtx sdk.Context) uint64 { 433 pID, err := submitProposal(s, app, sdkCtx, []sdk.Msg{msgSend}, proposers, groupPolicyAddr) 434 s.Require().NoError(err) 435 return pID 436 }, 437 admin: proposers[0], 438 newCtx: ctx.WithBlockTime(ctx.BlockTime().Add(votingPeriod).Add(time.Hour)), 439 tallyRes: group.DefaultTallyResult(), 440 expStatus: group.PROPOSAL_STATUS_REJECTED, 441 }, 442 "tally within voting period": { 443 preRun: func(sdkCtx sdk.Context) uint64 { 444 pID, err := submitProposal(s, app, sdkCtx, []sdk.Msg{msgSend}, proposers, groupPolicyAddr) 445 s.Require().NoError(err) 446 447 return pID 448 }, 449 admin: proposers[0], 450 newCtx: ctx, 451 tallyRes: group.DefaultTallyResult(), 452 expStatus: group.PROPOSAL_STATUS_SUBMITTED, 453 }, 454 "tally within voting period(with votes)": { 455 preRun: func(sdkCtx sdk.Context) uint64 { 456 pID, err := submitProposalAndVote(s, app, ctx, []sdk.Msg{msgSend}, proposers, groupPolicyAddr, group.VOTE_OPTION_YES) 457 s.Require().NoError(err) 458 459 return pID 460 }, 461 admin: proposers[0], 462 newCtx: ctx, 463 tallyRes: group.DefaultTallyResult(), 464 expStatus: group.PROPOSAL_STATUS_SUBMITTED, 465 }, 466 "tally after voting period (not passing)": { 467 preRun: func(sdkCtx sdk.Context) uint64 { 468 // `addrs[1]` has weight 1 469 pID, err := submitProposalAndVote(s, app, ctx, []sdk.Msg{msgSend}, []string{addrs[1].String()}, groupPolicyAddr, group.VOTE_OPTION_YES) 470 s.Require().NoError(err) 471 472 return pID 473 }, 474 admin: proposers[0], 475 newCtx: ctx.WithBlockTime(ctx.BlockTime().Add(votingPeriod).Add(time.Hour)), 476 tallyRes: group.TallyResult{ 477 YesCount: "1", 478 NoCount: "0", 479 NoWithVetoCount: "0", 480 AbstainCount: "0", 481 }, 482 expStatus: group.PROPOSAL_STATUS_REJECTED, 483 }, 484 "tally after voting period(with votes)": { 485 preRun: func(sdkCtx sdk.Context) uint64 { 486 pID, err := submitProposalAndVote(s, app, ctx, []sdk.Msg{msgSend}, proposers, groupPolicyAddr, group.VOTE_OPTION_YES) 487 s.Require().NoError(err) 488 489 return pID 490 }, 491 admin: proposers[0], 492 newCtx: ctx.WithBlockTime(ctx.BlockTime().Add(votingPeriod).Add(time.Hour)), 493 tallyRes: group.TallyResult{ 494 YesCount: "2", 495 NoCount: "0", 496 NoWithVetoCount: "0", 497 AbstainCount: "0", 498 }, 499 expStatus: group.PROPOSAL_STATUS_ACCEPTED, 500 }, 501 "tally of withdrawn proposal": { 502 preRun: func(sdkCtx sdk.Context) uint64 { 503 pID, err := submitProposal(s, app, sdkCtx, []sdk.Msg{msgSend}, proposers, groupPolicyAddr) 504 s.Require().NoError(err) 505 506 _, err = s.groupKeeper.WithdrawProposal(ctx, &group.MsgWithdrawProposal{ 507 ProposalId: pID, 508 Address: proposers[0], 509 }) 510 511 s.Require().NoError(err) 512 return pID 513 }, 514 admin: proposers[0], 515 newCtx: ctx, 516 tallyRes: group.DefaultTallyResult(), 517 expStatus: group.PROPOSAL_STATUS_WITHDRAWN, 518 }, 519 "tally of withdrawn proposal (with votes)": { 520 preRun: func(sdkCtx sdk.Context) uint64 { 521 pID, err := submitProposalAndVote(s, app, ctx, []sdk.Msg{msgSend}, proposers, groupPolicyAddr, group.VOTE_OPTION_YES) 522 s.Require().NoError(err) 523 524 _, err = s.groupKeeper.WithdrawProposal(ctx, &group.MsgWithdrawProposal{ 525 ProposalId: pID, 526 Address: proposers[0], 527 }) 528 529 s.Require().NoError(err) 530 return pID 531 }, 532 admin: proposers[0], 533 newCtx: ctx, 534 tallyRes: group.DefaultTallyResult(), 535 expStatus: group.PROPOSAL_STATUS_WITHDRAWN, 536 }, 537 } 538 539 for msg, spec := range specs { 540 s.Run(msg, func() { 541 spec := spec 542 pID := spec.preRun(ctx) 543 544 module.EndBlocker(spec.newCtx, s.groupKeeper) 545 resp, err := s.groupKeeper.Proposal(spec.newCtx, &group.QueryProposalRequest{ 546 ProposalId: pID, 547 }) 548 549 if spec.expErrMsg != "" { 550 s.Require().NoError(err) 551 s.Require().Contains(err.Error(), spec.expErrMsg) 552 return 553 } 554 555 s.Require().NoError(err) 556 s.Require().Equal(resp.GetProposal().FinalTallyResult, spec.tallyRes) 557 s.Require().Equal(resp.GetProposal().Status, spec.expStatus) 558 }) 559 } 560 } 561 562 func submitProposal(s *IntegrationTestSuite, app *runtime.App, ctx context.Context, msgs []sdk.Msg, proposers []string, groupPolicyAddr sdk.AccAddress) (uint64, error) { //nolint:revive // context-as-argument: context.Context should be the first parameter of a function 563 proposalReq := &group.MsgSubmitProposal{ 564 GroupPolicyAddress: groupPolicyAddr.String(), 565 Proposers: proposers, 566 } 567 err := proposalReq.SetMsgs(msgs) 568 if err != nil { 569 return 0, err 570 } 571 572 proposalRes, err := s.groupKeeper.SubmitProposal(ctx, proposalReq) 573 if err != nil { 574 return 0, err 575 } 576 577 return proposalRes.ProposalId, nil 578 } 579 580 func submitProposalAndVote( 581 s *IntegrationTestSuite, app *runtime.App, ctx context.Context, msgs []sdk.Msg, //nolint:revive // context-as-argument: context.Context should be the first parameter of a function 582 proposers []string, groupPolicyAddr sdk.AccAddress, voteOption group.VoteOption, 583 ) (uint64, error) { 584 myProposalID, err := submitProposal(s, app, ctx, msgs, proposers, groupPolicyAddr) 585 if err != nil { 586 return 0, err 587 } 588 _, err = s.groupKeeper.Vote(ctx, &group.MsgVote{ 589 ProposalId: myProposalID, 590 Voter: proposers[0], 591 Option: voteOption, 592 }) 593 if err != nil { 594 return 0, err 595 } 596 return myProposalID, nil 597 }