test: rewrite group tx e2e tests as unit tests (#15909)
Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
parent
56e0b01f8c
commit
be081b1051
@ -14,6 +14,6 @@ import (
|
||||
|
||||
func TestE2ETestSuite(t *testing.T) {
|
||||
cfg := network.DefaultConfig(simapp.NewTestNetworkFixture)
|
||||
cfg.NumValidators = 2
|
||||
cfg.NumValidators = 1
|
||||
suite.Run(t, NewE2ETestSuite(cfg))
|
||||
}
|
||||
|
||||
@ -21,28 +21,24 @@ func (s *E2ETestSuite) TestQueryGroupInfo() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
}{
|
||||
{
|
||||
"group not found",
|
||||
[]string{"12345", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"group: not found",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"group id invalid",
|
||||
[]string{"", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"strconv.ParseUint: parsing \"\": invalid syntax",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"group found",
|
||||
[]string{strconv.FormatUint(s.group.Id, 10), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
@ -162,55 +158,6 @@ func (s *E2ETestSuite) TestQueryGroupsByMembers() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TestQueryGroups() {
|
||||
val := s.network.Validators[0]
|
||||
clientCtx := val.ClientCtx
|
||||
require := s.Require()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
numItems int
|
||||
expectGroups []*group.GroupInfo
|
||||
}{
|
||||
{
|
||||
name: "valid req",
|
||||
args: []string{fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
expectErr: false,
|
||||
numItems: 5,
|
||||
},
|
||||
{
|
||||
name: "valid req with pagination",
|
||||
args: []string{
|
||||
"--limit=2",
|
||||
fmt.Sprintf("--%s=json", flags.FlagOutput),
|
||||
},
|
||||
expectErr: false,
|
||||
numItems: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
s.Run(tc.name, func() {
|
||||
cmd := client.QueryGroupsCmd()
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
|
||||
if tc.expectErr {
|
||||
require.Contains(out.String(), tc.expectErrMsg)
|
||||
} else {
|
||||
require.NoError(err, out.String())
|
||||
|
||||
var resp group.QueryGroupsResponse
|
||||
val.ClientCtx.Codec.MustUnmarshalJSON(out.Bytes(), &resp)
|
||||
|
||||
require.Len(resp.Groups, tc.numItems)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TestQueryGroupMembers() {
|
||||
val := s.network.Validators[0]
|
||||
clientCtx := val.ClientCtx
|
||||
@ -220,7 +167,6 @@ func (s *E2ETestSuite) TestQueryGroupMembers() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
expectMembers []*group.GroupMember
|
||||
}{
|
||||
{
|
||||
@ -228,7 +174,6 @@ func (s *E2ETestSuite) TestQueryGroupMembers() {
|
||||
[]string{"12345", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupMember{},
|
||||
},
|
||||
{
|
||||
@ -236,7 +181,6 @@ func (s *E2ETestSuite) TestQueryGroupMembers() {
|
||||
[]string{strconv.FormatUint(s.group.Id, 10), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupMember{
|
||||
{
|
||||
GroupId: s.group.Id,
|
||||
@ -257,7 +201,6 @@ func (s *E2ETestSuite) TestQueryGroupMembers() {
|
||||
},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupMember{
|
||||
{
|
||||
GroupId: s.group.Id,
|
||||
@ -306,7 +249,6 @@ func (s *E2ETestSuite) TestQueryGroupsByAdmin() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
expectGroups []*group.GroupInfo
|
||||
}{
|
||||
{
|
||||
@ -314,15 +256,13 @@ func (s *E2ETestSuite) TestQueryGroupsByAdmin() {
|
||||
[]string{"invalid"},
|
||||
true,
|
||||
"decoding bech32 failed: invalid bech32 string",
|
||||
0,
|
||||
[]*group.GroupInfo{},
|
||||
},
|
||||
{
|
||||
"no group",
|
||||
[]string{s.network.Validators[1].Address.String(), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
[]string{"cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupInfo{},
|
||||
},
|
||||
{
|
||||
@ -330,7 +270,6 @@ func (s *E2ETestSuite) TestQueryGroupsByAdmin() {
|
||||
[]string{val.Address.String(), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupInfo{
|
||||
s.group,
|
||||
},
|
||||
@ -344,7 +283,6 @@ func (s *E2ETestSuite) TestQueryGroupsByAdmin() {
|
||||
},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupInfo{
|
||||
s.group,
|
||||
},
|
||||
@ -387,21 +325,18 @@ func (s *E2ETestSuite) TestQueryGroupPolicyInfo() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
}{
|
||||
{
|
||||
"group policy not found",
|
||||
[]string{val.Address.String(), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"not found",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"group policy found",
|
||||
[]string{s.groupPolicies[0].Address, fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
@ -443,7 +378,6 @@ func (s *E2ETestSuite) TestQueryGroupPoliciesByGroup() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
expectGroupPolicies []*group.GroupPolicyInfo
|
||||
}{
|
||||
{
|
||||
@ -451,7 +385,6 @@ func (s *E2ETestSuite) TestQueryGroupPoliciesByGroup() {
|
||||
[]string{""},
|
||||
true,
|
||||
"strconv.ParseUint: parsing \"\": invalid syntax",
|
||||
0,
|
||||
[]*group.GroupPolicyInfo{},
|
||||
},
|
||||
{
|
||||
@ -459,7 +392,6 @@ func (s *E2ETestSuite) TestQueryGroupPoliciesByGroup() {
|
||||
[]string{"12345", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupPolicyInfo{},
|
||||
},
|
||||
{
|
||||
@ -467,7 +399,6 @@ func (s *E2ETestSuite) TestQueryGroupPoliciesByGroup() {
|
||||
[]string{strconv.FormatUint(s.group.Id, 10), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupPolicyInfo{
|
||||
s.groupPolicies[0],
|
||||
s.groupPolicies[1],
|
||||
@ -486,7 +417,6 @@ func (s *E2ETestSuite) TestQueryGroupPoliciesByGroup() {
|
||||
},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupPolicyInfo{
|
||||
s.groupPolicies[0],
|
||||
s.groupPolicies[1],
|
||||
@ -534,7 +464,6 @@ func (s *E2ETestSuite) TestQueryGroupPoliciesByAdmin() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
expectGroupPolicies []*group.GroupPolicyInfo
|
||||
}{
|
||||
{
|
||||
@ -542,15 +471,13 @@ func (s *E2ETestSuite) TestQueryGroupPoliciesByAdmin() {
|
||||
[]string{"invalid"},
|
||||
true,
|
||||
"decoding bech32 failed: invalid bech32 string",
|
||||
0,
|
||||
[]*group.GroupPolicyInfo{},
|
||||
},
|
||||
{
|
||||
"no group policy",
|
||||
[]string{s.network.Validators[1].Address.String(), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
[]string{"cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupPolicyInfo{},
|
||||
},
|
||||
{
|
||||
@ -558,7 +485,6 @@ func (s *E2ETestSuite) TestQueryGroupPoliciesByAdmin() {
|
||||
[]string{val.Address.String(), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.GroupPolicyInfo{
|
||||
s.groupPolicies[0],
|
||||
s.groupPolicies[1],
|
||||
@ -577,7 +503,7 @@ func (s *E2ETestSuite) TestQueryGroupPoliciesByAdmin() {
|
||||
},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
|
||||
[]*group.GroupPolicyInfo{
|
||||
s.groupPolicies[0],
|
||||
s.groupPolicies[1],
|
||||
@ -625,21 +551,18 @@ func (s *E2ETestSuite) TestQueryProposal() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
}{
|
||||
{
|
||||
"not found",
|
||||
[]string{"12345", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"not found",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"invalid proposal id",
|
||||
[]string{"", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"strconv.ParseUint: parsing \"\": invalid syntax",
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
@ -668,7 +591,6 @@ func (s *E2ETestSuite) TestQueryProposalsByGroupPolicy() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
expectProposals []*group.Proposal
|
||||
}{
|
||||
{
|
||||
@ -676,15 +598,13 @@ func (s *E2ETestSuite) TestQueryProposalsByGroupPolicy() {
|
||||
[]string{"invalid"},
|
||||
true,
|
||||
"decoding bech32 failed: invalid bech32 string",
|
||||
0,
|
||||
[]*group.Proposal{},
|
||||
},
|
||||
{
|
||||
"no group policy",
|
||||
[]string{s.network.Validators[1].Address.String(), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
[]string{"cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.Proposal{},
|
||||
},
|
||||
{
|
||||
@ -692,7 +612,6 @@ func (s *E2ETestSuite) TestQueryProposalsByGroupPolicy() {
|
||||
[]string{s.groupPolicies[0].Address, fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.Proposal{
|
||||
s.proposal,
|
||||
},
|
||||
@ -706,7 +625,6 @@ func (s *E2ETestSuite) TestQueryProposalsByGroupPolicy() {
|
||||
},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.Proposal{
|
||||
s.proposal,
|
||||
},
|
||||
@ -745,21 +663,18 @@ func (s *E2ETestSuite) TestQueryVoteByProposalVoter() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
}{
|
||||
{
|
||||
"invalid voter address",
|
||||
[]string{"1", "invalid", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"decoding bech32 failed: invalid bech32",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"invalid proposal id",
|
||||
[]string{"", val.Address.String(), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"strconv.ParseUint: parsing \"\": invalid syntax",
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
@ -788,7 +703,6 @@ func (s *E2ETestSuite) TestQueryVotesByProposal() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
expectVotes []*group.Vote
|
||||
}{
|
||||
{
|
||||
@ -796,7 +710,6 @@ func (s *E2ETestSuite) TestQueryVotesByProposal() {
|
||||
[]string{"", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"strconv.ParseUint: parsing \"\": invalid syntax",
|
||||
0,
|
||||
[]*group.Vote{},
|
||||
},
|
||||
{
|
||||
@ -804,7 +717,6 @@ func (s *E2ETestSuite) TestQueryVotesByProposal() {
|
||||
[]string{"12345", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.Vote{},
|
||||
},
|
||||
{
|
||||
@ -812,7 +724,6 @@ func (s *E2ETestSuite) TestQueryVotesByProposal() {
|
||||
[]string{"1", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.Vote{
|
||||
s.vote,
|
||||
},
|
||||
@ -826,7 +737,6 @@ func (s *E2ETestSuite) TestQueryVotesByProposal() {
|
||||
},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.Vote{
|
||||
s.vote,
|
||||
},
|
||||
@ -865,7 +775,6 @@ func (s *E2ETestSuite) TestQueryVotesByVoter() {
|
||||
args []string
|
||||
expectErr bool
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
expectVotes []*group.Vote
|
||||
}{
|
||||
{
|
||||
@ -873,7 +782,6 @@ func (s *E2ETestSuite) TestQueryVotesByVoter() {
|
||||
[]string{"abcd", fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"decoding bech32 failed: invalid bech32",
|
||||
0,
|
||||
[]*group.Vote{},
|
||||
},
|
||||
{
|
||||
@ -881,7 +789,6 @@ func (s *E2ETestSuite) TestQueryVotesByVoter() {
|
||||
[]string{s.groupPolicies[0].Address, fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
true,
|
||||
"",
|
||||
0,
|
||||
[]*group.Vote{},
|
||||
},
|
||||
{
|
||||
@ -889,7 +796,6 @@ func (s *E2ETestSuite) TestQueryVotesByVoter() {
|
||||
[]string{val.Address.String(), fmt.Sprintf("--%s=json", flags.FlagOutput)},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.Vote{
|
||||
s.vote,
|
||||
},
|
||||
@ -903,7 +809,6 @@ func (s *E2ETestSuite) TestQueryVotesByVoter() {
|
||||
},
|
||||
false,
|
||||
"",
|
||||
0,
|
||||
[]*group.Vote{
|
||||
s.vote,
|
||||
},
|
||||
@ -939,12 +844,6 @@ func (s *E2ETestSuite) TestTallyResult() {
|
||||
|
||||
member := s.voter
|
||||
|
||||
commonFlags := []string{
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
|
||||
}
|
||||
|
||||
// create a proposal
|
||||
out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, client.MsgSubmitProposalCmd(),
|
||||
append(
|
||||
@ -954,7 +853,7 @@ func (s *E2ETestSuite) TestTallyResult() {
|
||||
s.groupPolicies[0].Address, val.Address.String(),
|
||||
"", "title", "summary"),
|
||||
},
|
||||
commonFlags...,
|
||||
s.commonFlags...,
|
||||
),
|
||||
)
|
||||
s.Require().NoError(err, out.String())
|
||||
@ -973,7 +872,6 @@ func (s *E2ETestSuite) TestTallyResult() {
|
||||
expectErr bool
|
||||
expTallyResult group.TallyResult
|
||||
expectErrMsg string
|
||||
expectedCode uint32
|
||||
}{
|
||||
{
|
||||
"not found",
|
||||
@ -984,7 +882,6 @@ func (s *E2ETestSuite) TestTallyResult() {
|
||||
true,
|
||||
group.TallyResult{},
|
||||
"not found",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"invalid proposal id",
|
||||
@ -995,7 +892,6 @@ func (s *E2ETestSuite) TestTallyResult() {
|
||||
true,
|
||||
group.TallyResult{},
|
||||
"strconv.ParseUint: parsing \"\": invalid syntax",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"valid proposal id with no votes",
|
||||
@ -1006,7 +902,6 @@ func (s *E2ETestSuite) TestTallyResult() {
|
||||
false,
|
||||
group.DefaultTallyResult(),
|
||||
"",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"valid proposal id",
|
||||
@ -1022,7 +917,6 @@ func (s *E2ETestSuite) TestTallyResult() {
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
"",
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
// without this import amino json encoding will fail when resolving any types
|
||||
@ -121,7 +119,6 @@ func (s *E2ETestSuite) SetupSuite() {
|
||||
s.Require().NoError(err, out.String())
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
}
|
||||
percentage := 0.5
|
||||
// create group policy with percentage decision policy
|
||||
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, client.MsgCreateGroupPolicyCmd(),
|
||||
append(
|
||||
@ -129,7 +126,7 @@ func (s *E2ETestSuite) SetupSuite() {
|
||||
val.Address.String(),
|
||||
"1",
|
||||
validMetadata,
|
||||
testutil.WriteToNewTempFile(s.T(), fmt.Sprintf(`{"@type":"/cosmos.group.v1.PercentageDecisionPolicy", "percentage":"%f", "windows":{"voting_period":"30000s"}}`, percentage)).Name(),
|
||||
testutil.WriteToNewTempFile(s.T(), fmt.Sprintf(`{"@type":"/cosmos.group.v1.PercentageDecisionPolicy", "percentage":"%f", "windows":{"voting_period":"30000s"}}`, 0.5)).Name(),
|
||||
},
|
||||
s.commonFlags...,
|
||||
),
|
||||
@ -204,212 +201,6 @@ func (s *E2ETestSuite) TearDownSuite() {
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) TestExecProposalsWhenMemberLeavesOrIsUpdated() {
|
||||
val := s.network.Validators[0]
|
||||
clientCtx := val.ClientCtx
|
||||
|
||||
weights := []string{"1", "1", "2"}
|
||||
accounts := s.createAccounts(3)
|
||||
testCases := []struct {
|
||||
name string
|
||||
votes []string
|
||||
members []string
|
||||
malleate func(groupID string) error
|
||||
expectLogErr bool
|
||||
errMsg string
|
||||
respType proto.Message
|
||||
}{
|
||||
{
|
||||
"member leaves while all others vote yes",
|
||||
[]string{"VOTE_OPTION_YES", "VOTE_OPTION_YES", "VOTE_OPTION_YES"},
|
||||
accounts,
|
||||
func(groupID string) error {
|
||||
leavingMemberIdx := 1
|
||||
args := append(
|
||||
[]string{
|
||||
accounts[leavingMemberIdx],
|
||||
groupID,
|
||||
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, accounts[leavingMemberIdx]),
|
||||
},
|
||||
s.commonFlags...,
|
||||
)
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, client.MsgLeaveGroupCmd(), args)
|
||||
s.Require().NoError(err, out.String())
|
||||
var resp sdk.TxResponse
|
||||
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp), out.String())
|
||||
s.Require().NoError(clitestutil.CheckTxCode(s.network, clientCtx, resp.TxHash, 0))
|
||||
|
||||
return err
|
||||
},
|
||||
false,
|
||||
"",
|
||||
&sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"member leaves while all others vote yes and no",
|
||||
[]string{"VOTE_OPTION_NO", "VOTE_OPTION_YES", "VOTE_OPTION_YES"},
|
||||
accounts,
|
||||
func(groupID string) error {
|
||||
leavingMemberIdx := 1
|
||||
args := append(
|
||||
[]string{
|
||||
accounts[leavingMemberIdx],
|
||||
groupID,
|
||||
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, accounts[leavingMemberIdx]),
|
||||
},
|
||||
s.commonFlags...,
|
||||
)
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, client.MsgLeaveGroupCmd(), args)
|
||||
s.Require().NoError(err, out.String())
|
||||
var resp sdk.TxResponse
|
||||
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp), out.String())
|
||||
s.Require().NoError(clitestutil.CheckTxCode(s.network, clientCtx, resp.TxHash, 0))
|
||||
|
||||
return err
|
||||
},
|
||||
true,
|
||||
"PROPOSAL_EXECUTOR_RESULT_NOT_RUN",
|
||||
&sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"member that leaves does affect the threshold policy outcome",
|
||||
[]string{"VOTE_OPTION_YES", "VOTE_OPTION_YES"},
|
||||
accounts,
|
||||
func(groupID string) error {
|
||||
leavingMemberIdx := 2
|
||||
args := append(
|
||||
[]string{
|
||||
accounts[leavingMemberIdx],
|
||||
groupID,
|
||||
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, accounts[leavingMemberIdx]),
|
||||
},
|
||||
s.commonFlags...,
|
||||
)
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, client.MsgLeaveGroupCmd(), args)
|
||||
s.Require().NoError(err, out.String())
|
||||
var resp sdk.TxResponse
|
||||
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp), out.String())
|
||||
s.Require().NoError(clitestutil.CheckTxCode(s.network, clientCtx, resp.TxHash, 0))
|
||||
|
||||
return err
|
||||
},
|
||||
false,
|
||||
"",
|
||||
&sdk.TxResponse{},
|
||||
},
|
||||
{
|
||||
"update group policy voids the proposal",
|
||||
[]string{"VOTE_OPTION_YES", "VOTE_OPTION_NO"},
|
||||
accounts,
|
||||
func(groupID string) error {
|
||||
updateGroup := s.newValidMembers(weights[0:1], accounts[0:1])
|
||||
|
||||
updateGroupByte, err := json.Marshal(updateGroup)
|
||||
s.Require().NoError(err)
|
||||
|
||||
validUpdateMemberFileName := testutil.WriteToNewTempFile(s.T(), string(updateGroupByte)).Name()
|
||||
|
||||
args := append(
|
||||
[]string{
|
||||
accounts[0],
|
||||
groupID,
|
||||
validUpdateMemberFileName,
|
||||
},
|
||||
s.commonFlags...,
|
||||
)
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, client.MsgUpdateGroupMembersCmd(), args)
|
||||
s.Require().NoError(err, out.String())
|
||||
var resp sdk.TxResponse
|
||||
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp), out.String())
|
||||
s.Require().NoError(clitestutil.CheckTxCode(s.network, clientCtx, resp.TxHash, 0))
|
||||
|
||||
return err
|
||||
},
|
||||
true,
|
||||
"PROPOSAL_EXECUTOR_RESULT_NOT_RUN",
|
||||
&sdk.TxResponse{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
s.Run(tc.name, func() {
|
||||
cmdSubmitProposal := client.MsgSubmitProposalCmd()
|
||||
cmdMsgExec := client.MsgExecCmd()
|
||||
|
||||
groupID := s.createGroupWithMembers(weights, accounts)
|
||||
groupPolicyAddress := s.createGroupThresholdPolicyWithBalance(accounts[0], groupID, 3, 100)
|
||||
|
||||
// Submit proposal
|
||||
proposal := s.createCLIProposal(
|
||||
groupPolicyAddress, tc.members[0],
|
||||
groupPolicyAddress, tc.members[0],
|
||||
"", "title", "summary",
|
||||
)
|
||||
submitProposalArgs := append([]string{
|
||||
proposal,
|
||||
},
|
||||
s.commonFlags...,
|
||||
)
|
||||
var submitProposalResp sdk.TxResponse
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmdSubmitProposal, submitProposalArgs)
|
||||
s.Require().NoError(err, out.String())
|
||||
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &submitProposalResp), out.String())
|
||||
submitProposalResp, err = clitestutil.GetTxResponse(s.network, val.ClientCtx, submitProposalResp.TxHash)
|
||||
s.Require().NoError(err)
|
||||
proposalID := s.getProposalIDFromTxResponse(submitProposalResp)
|
||||
|
||||
for i, vote := range tc.votes {
|
||||
memberAddress := tc.members[i]
|
||||
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, client.MsgVoteCmd(),
|
||||
append(
|
||||
[]string{
|
||||
proposalID,
|
||||
memberAddress,
|
||||
vote,
|
||||
"",
|
||||
},
|
||||
s.commonFlags...,
|
||||
),
|
||||
)
|
||||
|
||||
var txResp sdk.TxResponse
|
||||
s.Require().NoError(err, out.String())
|
||||
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txResp), out.String())
|
||||
s.Require().NoError(clitestutil.CheckTxCode(s.network, clientCtx, txResp.TxHash, 0))
|
||||
|
||||
}
|
||||
|
||||
err = tc.malleate(groupID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
|
||||
args := append(
|
||||
[]string{
|
||||
proposalID,
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, tc.members[0]),
|
||||
},
|
||||
s.commonFlags...,
|
||||
)
|
||||
out, err = clitestutil.ExecTestCLICmd(clientCtx, cmdMsgExec, args)
|
||||
s.Require().NoError(err)
|
||||
|
||||
var execResp sdk.TxResponse
|
||||
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &execResp), out.String())
|
||||
execResp, err = clitestutil.GetTxResponse(s.network, val.ClientCtx, execResp.TxHash)
|
||||
s.Require().NoError(err)
|
||||
|
||||
if tc.expectLogErr {
|
||||
s.Require().Contains(execResp.RawLog, tc.errMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) getProposalIDFromTxResponse(txResp sdk.TxResponse) string {
|
||||
s.Require().Greater(len(txResp.Logs), 0)
|
||||
s.Require().NotNil(txResp.Logs[0].Events)
|
||||
@ -425,21 +216,6 @@ func (s *E2ETestSuite) getProposalIDFromTxResponse(txResp sdk.TxResponse) string
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) getGroupIDFromTxResponse(txResp sdk.TxResponse) string {
|
||||
s.Require().Greater(len(txResp.Logs), 0)
|
||||
s.Require().NotNil(txResp.Logs[0].Events)
|
||||
events := txResp.Logs[0].Events
|
||||
createProposalEvent, _ := sdk.TypedEventToEvent(&group.EventCreateGroup{})
|
||||
|
||||
for _, e := range events {
|
||||
if e.Type == createProposalEvent.Type {
|
||||
return strings.ReplaceAll(e.Attributes[0].Value, "\"", "")
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// createCLIProposal writes a CLI proposal with a MsgSend to a file. Returns
|
||||
// the path to the JSON file.
|
||||
func (s *E2ETestSuite) createCLIProposal(groupPolicyAddress, proposer, sendFrom, sendTo, metadata, title, summary string) string {
|
||||
@ -469,69 +245,6 @@ func (s *E2ETestSuite) createCLIProposal(groupPolicyAddress, proposer, sendFrom,
|
||||
return testutil.WriteToNewTempFile(s.T(), string(bz)).Name()
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) createAccounts(quantity int) []string {
|
||||
val := s.network.Validators[0]
|
||||
clientCtx := val.ClientCtx
|
||||
accounts := make([]string, quantity)
|
||||
|
||||
for i := 1; i <= quantity; i++ {
|
||||
memberNumber := uuid.New().String()
|
||||
|
||||
info, _, err := clientCtx.Keyring.NewMnemonic(fmt.Sprintf("member%s", memberNumber), keyring.English, sdk.FullFundraiserPath,
|
||||
keyring.DefaultBIP39Passphrase, hd.Secp256k1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
pk, err := info.GetPubKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
account := sdk.AccAddress(pk.Address())
|
||||
accounts[i-1] = account.String()
|
||||
|
||||
_, err = clitestutil.MsgSendExec(
|
||||
val.ClientCtx,
|
||||
val.Address,
|
||||
account,
|
||||
sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(2000))), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
}
|
||||
|
||||
return accounts
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) createGroupWithMembers(membersWeight, membersAddress []string) string {
|
||||
val := s.network.Validators[0]
|
||||
clientCtx := val.ClientCtx
|
||||
|
||||
s.Require().Equal(len(membersWeight), len(membersAddress))
|
||||
|
||||
membersValid := s.newValidMembers(membersWeight, membersAddress)
|
||||
membersByte, err := json.Marshal(membersValid)
|
||||
|
||||
s.Require().NoError(err)
|
||||
|
||||
validMembersFile := testutil.WriteToNewTempFile(s.T(), string(membersByte))
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, client.MsgCreateGroupCmd(),
|
||||
append(
|
||||
[]string{
|
||||
membersAddress[0],
|
||||
validMetadata,
|
||||
validMembersFile.Name(),
|
||||
},
|
||||
s.commonFlags...,
|
||||
),
|
||||
)
|
||||
s.Require().NoError(err, out.String())
|
||||
var txResp sdk.TxResponse
|
||||
s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txResp), out.String())
|
||||
txResp, err = clitestutil.GetTxResponse(s.network, val.ClientCtx, txResp.TxHash)
|
||||
s.Require().NoError(err)
|
||||
return s.getGroupIDFromTxResponse(txResp)
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) createGroupThresholdPolicyWithBalance(adminAddress, groupID string, threshold int, tokens int64) string {
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
|
||||
@ -570,21 +283,3 @@ func (s *E2ETestSuite) createGroupThresholdPolicyWithBalance(adminAddress, group
|
||||
s.Require().NoError(err)
|
||||
return groupPolicyAddress
|
||||
}
|
||||
|
||||
func (s *E2ETestSuite) newValidMembers(weights, membersAddress []string) struct{ Members []group.MemberRequest } {
|
||||
s.Require().Equal(len(weights), len(membersAddress))
|
||||
membersValid := []group.MemberRequest{}
|
||||
for i, address := range membersAddress {
|
||||
membersValid = append(membersValid, group.MemberRequest{
|
||||
Address: address,
|
||||
Weight: weights[i],
|
||||
Metadata: validMetadata,
|
||||
})
|
||||
}
|
||||
|
||||
return struct {
|
||||
Members []group.MemberRequest
|
||||
}{
|
||||
Members: membersValid,
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ import (
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/group"
|
||||
"github.com/cosmos/cosmos-sdk/x/group/internal/math"
|
||||
"github.com/cosmos/cosmos-sdk/x/group/keeper"
|
||||
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
||||
"github.com/golang/mock/gomock"
|
||||
)
|
||||
@ -3076,3 +3077,224 @@ func (s *TestSuite) TestLeaveGroup() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TestSuite) TestExecProposalsWhenMemberLeavesOrIsUpdated() {
|
||||
proposers := []string{s.addrs[1].String()}
|
||||
|
||||
specs := map[string]struct {
|
||||
votes []group.VoteOption
|
||||
members []group.MemberRequest
|
||||
setupProposal func(ctx context.Context, groupPolicyAddr string) uint64
|
||||
malleate func(ctx context.Context, k keeper.Keeper, groupPolicyAddr string, groupID uint64) error
|
||||
expErrMsg string
|
||||
}{
|
||||
"member leaves while all others vote yes: proposal accepted": {
|
||||
members: []group.MemberRequest{
|
||||
{Address: s.addrs[4].String(), Weight: "1"},
|
||||
{Address: s.addrs[1].String(), Weight: "2"},
|
||||
{Address: s.addrs[3].String(), Weight: "1"},
|
||||
{Address: s.addrs[5].String(), Weight: "2"},
|
||||
{Address: s.addrs[2].String(), Weight: "2"},
|
||||
},
|
||||
votes: []group.VoteOption{
|
||||
group.VOTE_OPTION_YES, group.VOTE_OPTION_YES,
|
||||
group.VOTE_OPTION_YES, group.VOTE_OPTION_YES,
|
||||
group.VOTE_OPTION_YES,
|
||||
},
|
||||
setupProposal: func(ctx context.Context, groupPolicyAddr string) uint64 {
|
||||
msgSend1 := &banktypes.MsgSend{
|
||||
FromAddress: groupPolicyAddr,
|
||||
ToAddress: s.addrs[1].String(),
|
||||
Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)},
|
||||
}
|
||||
|
||||
// the proposal will pass and be executed
|
||||
s.bankKeeper.EXPECT().Send(gomock.Any(), msgSend1).Return(nil, nil).MaxTimes(1)
|
||||
|
||||
msgs := []sdk.Msg{msgSend1}
|
||||
proposalReq := &group.MsgSubmitProposal{
|
||||
GroupPolicyAddress: groupPolicyAddr,
|
||||
Proposers: proposers,
|
||||
}
|
||||
err := proposalReq.SetMsgs(msgs)
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposalRes, err := s.groupKeeper.SubmitProposal(ctx, proposalReq)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return proposalRes.ProposalId
|
||||
},
|
||||
malleate: func(ctx context.Context, k keeper.Keeper, _ string, groupID uint64) error {
|
||||
_, err := k.LeaveGroup(ctx, &group.MsgLeaveGroup{GroupId: groupID, Address: s.addrs[5].String()})
|
||||
return err
|
||||
},
|
||||
},
|
||||
"member leaves while all others vote yes and no: proposal rejected": {
|
||||
members: []group.MemberRequest{
|
||||
{Address: s.addrs[4].String(), Weight: "2"},
|
||||
{Address: s.addrs[1].String(), Weight: "2"},
|
||||
{Address: s.addrs[3].String(), Weight: "2"},
|
||||
{Address: s.addrs[2].String(), Weight: "2"},
|
||||
},
|
||||
votes: []group.VoteOption{
|
||||
group.VOTE_OPTION_NO, group.VOTE_OPTION_NO,
|
||||
group.VOTE_OPTION_YES, group.VOTE_OPTION_YES,
|
||||
},
|
||||
setupProposal: func(ctx context.Context, groupPolicyAddr string) uint64 {
|
||||
msgSend1 := &banktypes.MsgSend{
|
||||
FromAddress: groupPolicyAddr,
|
||||
ToAddress: s.addrs[1].String(),
|
||||
Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)},
|
||||
}
|
||||
msgs := []sdk.Msg{msgSend1, msgSend1}
|
||||
|
||||
proposalReq := &group.MsgSubmitProposal{
|
||||
GroupPolicyAddress: groupPolicyAddr,
|
||||
Proposers: proposers,
|
||||
}
|
||||
err := proposalReq.SetMsgs(msgs)
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposalRes, err := s.groupKeeper.SubmitProposal(ctx, proposalReq)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return proposalRes.ProposalId
|
||||
},
|
||||
malleate: func(ctx context.Context, k keeper.Keeper, _ string, groupID uint64) error {
|
||||
_, err := k.LeaveGroup(ctx, &group.MsgLeaveGroup{GroupId: groupID, Address: s.addrs[3].String()})
|
||||
return err
|
||||
},
|
||||
},
|
||||
"member that leaves does affect the threshold policy outcome": {
|
||||
members: []group.MemberRequest{
|
||||
{Address: s.addrs[3].String(), Weight: "6"},
|
||||
{Address: s.addrs[1].String(), Weight: "1"},
|
||||
{Address: s.addrs[5].String(), Weight: "1"},
|
||||
{Address: s.addrs[2].String(), Weight: "1"},
|
||||
},
|
||||
votes: []group.VoteOption{
|
||||
group.VOTE_OPTION_YES, group.VOTE_OPTION_NO,
|
||||
group.VOTE_OPTION_YES, group.VOTE_OPTION_YES,
|
||||
},
|
||||
setupProposal: func(ctx context.Context, addr string) uint64 {
|
||||
msgSend1 := &banktypes.MsgSend{
|
||||
FromAddress: addr,
|
||||
ToAddress: s.addrs[1].String(),
|
||||
Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)},
|
||||
}
|
||||
msgs := []sdk.Msg{msgSend1, msgSend1}
|
||||
|
||||
proposalReq := &group.MsgSubmitProposal{
|
||||
GroupPolicyAddress: addr,
|
||||
Proposers: proposers,
|
||||
}
|
||||
err := proposalReq.SetMsgs(msgs)
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposalRes, err := s.groupKeeper.SubmitProposal(ctx, proposalReq)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return proposalRes.ProposalId
|
||||
},
|
||||
malleate: func(ctx context.Context, k keeper.Keeper, _ string, groupID uint64) error {
|
||||
_, err := k.LeaveGroup(ctx, &group.MsgLeaveGroup{GroupId: groupID, Address: s.addrs[3].String()})
|
||||
return err
|
||||
},
|
||||
},
|
||||
"update group policy voids the proposal": {
|
||||
members: []group.MemberRequest{
|
||||
{Address: s.addrs[3].String(), Weight: "2"},
|
||||
{Address: s.addrs[2].String(), Weight: "2"},
|
||||
{Address: s.addrs[1].String(), Weight: "2"},
|
||||
{Address: s.addrs[4].String(), Weight: "2"},
|
||||
},
|
||||
votes: []group.VoteOption{
|
||||
group.VOTE_OPTION_YES, group.VOTE_OPTION_NO,
|
||||
group.VOTE_OPTION_YES, group.VOTE_OPTION_NO,
|
||||
},
|
||||
setupProposal: func(ctx context.Context, groupPolicyAddr string) uint64 {
|
||||
msgSend1 := &banktypes.MsgSend{
|
||||
FromAddress: groupPolicyAddr,
|
||||
ToAddress: s.addrs[1].String(),
|
||||
Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)},
|
||||
}
|
||||
msgs := []sdk.Msg{msgSend1, msgSend1}
|
||||
proposalReq := &group.MsgSubmitProposal{
|
||||
GroupPolicyAddress: groupPolicyAddr,
|
||||
Proposers: proposers,
|
||||
}
|
||||
err := proposalReq.SetMsgs(msgs)
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposalRes, err := s.groupKeeper.SubmitProposal(ctx, proposalReq)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return proposalRes.ProposalId
|
||||
},
|
||||
malleate: func(ctx context.Context, k keeper.Keeper, groupPolicyAddr string, groupID uint64) error {
|
||||
newGroupPolicy := &group.MsgUpdateGroupPolicyDecisionPolicy{
|
||||
Admin: s.addrs[0].String(),
|
||||
GroupPolicyAddress: groupPolicyAddr,
|
||||
}
|
||||
newGroupPolicy.SetDecisionPolicy(group.NewThresholdDecisionPolicy("10", time.Second, minExecutionPeriod))
|
||||
|
||||
_, err := k.UpdateGroupPolicyDecisionPolicy(ctx, newGroupPolicy)
|
||||
return err
|
||||
},
|
||||
expErrMsg: "PROPOSAL_STATUS_ABORTED",
|
||||
},
|
||||
}
|
||||
for msg, spec := range specs {
|
||||
spec := spec
|
||||
s.Run(msg, func() {
|
||||
sdkCtx, _ := s.sdkCtx.CacheContext()
|
||||
|
||||
s.setNextAccount()
|
||||
groupRes, err := s.groupKeeper.CreateGroup(s.ctx, &group.MsgCreateGroup{
|
||||
Admin: s.addrs[0].String(),
|
||||
Members: spec.members,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
groupID := groupRes.GroupId
|
||||
|
||||
policy := group.NewThresholdDecisionPolicy("4", time.Second, minExecutionPeriod)
|
||||
policyReq := &group.MsgCreateGroupPolicy{
|
||||
Admin: s.addrs[0].String(),
|
||||
GroupId: groupID,
|
||||
}
|
||||
err = policyReq.SetDecisionPolicy(policy)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.setNextAccount()
|
||||
|
||||
s.groupKeeper.GetGroupSequence(s.sdkCtx)
|
||||
policyRes, err := s.groupKeeper.CreateGroupPolicy(s.ctx, policyReq)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Setup and submit proposal
|
||||
proposalID := spec.setupProposal(sdkCtx, policyRes.Address)
|
||||
|
||||
// vote on the proposals
|
||||
for i, vote := range spec.votes {
|
||||
_, err := s.groupKeeper.Vote(sdkCtx, &group.MsgVote{
|
||||
ProposalId: proposalID,
|
||||
Voter: spec.members[i].Address,
|
||||
Option: vote,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
err = spec.malleate(sdkCtx, s.groupKeeper, policyRes.Address, groupID)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// travel in time
|
||||
sdkCtx = sdkCtx.WithBlockTime(s.blockTime.Add(minExecutionPeriod + 1))
|
||||
_, err = s.groupKeeper.Exec(sdkCtx, &group.MsgExec{Executor: s.addrs[1].String(), ProposalId: proposalID})
|
||||
if spec.expErrMsg != "" {
|
||||
s.Require().Contains(err.Error(), spec.expErrMsg)
|
||||
return
|
||||
}
|
||||
s.Require().NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user