package testutil import ( "fmt" "strconv" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/testutil/cli" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/group" client "github.com/cosmos/cosmos-sdk/x/group/client/cli" tmcli "github.com/tendermint/tendermint/libs/cli" ) func (s *IntegrationTestSuite) TestQueryGroupInfo() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 }{ { "group not found", []string{"12345", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "group: not found", 0, }, { "group id invalid", []string{"", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "strconv.ParseUint: parsing \"\": invalid syntax", 0, }, { "group found", []string{strconv.FormatUint(s.group.Id, 10), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryGroupInfoCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var g group.GroupInfo s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &g)) s.Require().Equal(s.group.Id, g.Id) s.Require().Equal(s.group.Admin, g.Admin) s.Require().Equal(s.group.TotalWeight, g.TotalWeight) s.Require().Equal(s.group.Metadata, g.Metadata) s.Require().Equal(s.group.Version, g.Version) } }) } } func (s *IntegrationTestSuite) TestQueryGroupsByMembers() { val := s.network.Validators[0] clientCtx := val.ClientCtx require := s.Require() cmd := client.QueryGroupsByAdminCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, []string{val.Address.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}) require.NoError(err) var groups group.QueryGroupsByAdminResponse val.ClientCtx.Codec.MustUnmarshalJSON(out.Bytes(), &groups) require.Len(groups.Groups, 1) cmd = client.QueryGroupMembersCmd() out, err = cli.ExecTestCLICmd(clientCtx, cmd, []string{fmt.Sprintf("%d", groups.Groups[0].Id), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}) require.NoError(err) var members group.QueryGroupMembersResponse val.ClientCtx.Codec.MustUnmarshalJSON(out.Bytes(), &members) require.Len(members.Members, 1) testAddr := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) testCases := []struct { name string args []string expectErr bool expectErrMsg string numItems int expectGroups []*group.GroupInfo }{ { "invalid address", []string{"abcd", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "invalid bech32 string", 0, []*group.GroupInfo{}, }, { "not part of any group", []string{testAddr.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.GroupInfo{}, }, { "expect one group", []string{members.Members[0].Member.Address, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 1, groups.Groups, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryGroupsByMemberCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { require.Contains(out.String(), tc.expectErrMsg) } else { require.NoError(err, out.String()) var resp group.QueryGroupsByMemberResponse val.ClientCtx.Codec.MustUnmarshalJSON(out.Bytes(), &resp) require.Len(resp.Groups, tc.numItems) require.Equal(tc.expectGroups, resp.Groups) } }) } } func (s *IntegrationTestSuite) TestQueryGroupMembers() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 expectMembers []*group.GroupMember }{ { "no group", []string{"12345", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.GroupMember{}, }, { "members found", []string{strconv.FormatUint(s.group.Id, 10), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.GroupMember{ { GroupId: s.group.Id, Member: &group.Member{ Address: val.Address.String(), Weight: "3", Metadata: validMetadata, }, }, }, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryGroupMembersCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var res group.QueryGroupMembersResponse s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) s.Require().Equal(len(res.Members), len(tc.expectMembers)) for i := range res.Members { s.Require().Equal(res.Members[i].GroupId, tc.expectMembers[i].GroupId) s.Require().Equal(res.Members[i].Member.Address, tc.expectMembers[i].Member.Address) s.Require().Equal(res.Members[i].Member.Metadata, tc.expectMembers[i].Member.Metadata) s.Require().Equal(res.Members[i].Member.Weight, tc.expectMembers[i].Member.Weight) } } }) } } func (s *IntegrationTestSuite) TestQueryGroupsByAdmin() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 expectGroups []*group.GroupInfo }{ { "invalid admin address", []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", tmcli.OutputFlag)}, false, "", 0, []*group.GroupInfo{}, }, { "found groups", []string{val.Address.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.GroupInfo{ s.group, }, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryGroupsByAdminCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var res group.QueryGroupsByAdminResponse s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) s.Require().Equal(len(res.Groups), len(tc.expectGroups)) for i := range res.Groups { s.Require().Equal(res.Groups[i].Id, tc.expectGroups[i].Id) s.Require().Equal(res.Groups[i].Metadata, tc.expectGroups[i].Metadata) s.Require().Equal(res.Groups[i].Version, tc.expectGroups[i].Version) s.Require().Equal(res.Groups[i].TotalWeight, tc.expectGroups[i].TotalWeight) s.Require().Equal(res.Groups[i].Admin, tc.expectGroups[i].Admin) } } }) } } func (s *IntegrationTestSuite) TestQueryGroupPolicyInfo() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 }{ { "group policy not found", []string{val.Address.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "group policy: not found", 0, }, { "group policy found", []string{s.groupPolicies[0].Address, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryGroupPolicyInfoCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var g group.GroupPolicyInfo s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &g)) s.Require().Equal(s.groupPolicies[0].GroupId, g.GroupId) s.Require().Equal(s.groupPolicies[0].Address, g.Address) s.Require().Equal(s.groupPolicies[0].Admin, g.Admin) s.Require().Equal(s.groupPolicies[0].Metadata, g.Metadata) s.Require().Equal(s.groupPolicies[0].Version, g.Version) dp1, err := s.groupPolicies[0].GetDecisionPolicy() s.Require().NoError(err) dp2, err := g.GetDecisionPolicy() s.Require().NoError(err) s.Require().Equal(dp1, dp2) } }) } } func (s *IntegrationTestSuite) TestQueryGroupPoliciesByGroup() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 expectGroupPolicies []*group.GroupPolicyInfo }{ { "invalid group id", []string{""}, true, "strconv.ParseUint: parsing \"\": invalid syntax", 0, []*group.GroupPolicyInfo{}, }, { "no group policy", []string{"12345", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.GroupPolicyInfo{}, }, { "found group policies", []string{strconv.FormatUint(s.group.Id, 10), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.GroupPolicyInfo{ s.groupPolicies[0], s.groupPolicies[1], s.groupPolicies[2], s.groupPolicies[3], s.groupPolicies[4], s.groupPolicies[5], }, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryGroupPoliciesByGroupCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var res group.QueryGroupPoliciesByGroupResponse s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) s.Require().Equal(len(res.GroupPolicies), len(tc.expectGroupPolicies)) for i := range res.GroupPolicies { s.Require().Equal(res.GroupPolicies[i].GroupId, tc.expectGroupPolicies[i].GroupId) s.Require().Equal(res.GroupPolicies[i].Metadata, tc.expectGroupPolicies[i].Metadata) s.Require().Equal(res.GroupPolicies[i].Version, tc.expectGroupPolicies[i].Version) s.Require().Equal(res.GroupPolicies[i].Admin, tc.expectGroupPolicies[i].Admin) dp1, err := s.groupPolicies[i].GetDecisionPolicy() s.Require().NoError(err) dp2, err := tc.expectGroupPolicies[i].GetDecisionPolicy() s.Require().NoError(err) s.Require().Equal(dp1, dp2) } } }) } } func (s *IntegrationTestSuite) TestQueryGroupPoliciesByAdmin() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 expectGroupPolicies []*group.GroupPolicyInfo }{ { "invalid admin address", []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", tmcli.OutputFlag)}, false, "", 0, []*group.GroupPolicyInfo{}, }, { "found group policies", []string{val.Address.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.GroupPolicyInfo{ s.groupPolicies[0], s.groupPolicies[1], s.groupPolicies[2], s.groupPolicies[3], s.groupPolicies[4], s.groupPolicies[5], }, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryGroupPoliciesByAdminCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var res group.QueryGroupPoliciesByAdminResponse s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) s.Require().Equal(len(res.GroupPolicies), len(tc.expectGroupPolicies)) for i := range res.GroupPolicies { s.Require().Equal(res.GroupPolicies[i].GroupId, tc.expectGroupPolicies[i].GroupId) s.Require().Equal(res.GroupPolicies[i].Metadata, tc.expectGroupPolicies[i].Metadata) s.Require().Equal(res.GroupPolicies[i].Version, tc.expectGroupPolicies[i].Version) s.Require().Equal(res.GroupPolicies[i].Admin, tc.expectGroupPolicies[i].Admin) dp1, err := s.groupPolicies[i].GetDecisionPolicy() s.Require().NoError(err) dp2, err := tc.expectGroupPolicies[i].GetDecisionPolicy() s.Require().NoError(err) s.Require().Equal(dp1, dp2) } } }) } } func (s *IntegrationTestSuite) TestQueryProposal() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 }{ { "not found", []string{"12345", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "not found", 0, }, { "invalid proposal id", []string{"", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "strconv.ParseUint: parsing \"\": invalid syntax", 0, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryProposalCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) } }) } } func (s *IntegrationTestSuite) TestQueryProposalsByGroupPolicy() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 expectProposals []*group.Proposal }{ { "invalid group policy address", []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", tmcli.OutputFlag)}, false, "", 0, []*group.Proposal{}, }, { "found proposals", []string{s.groupPolicies[0].Address, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.Proposal{ s.proposal, }, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryProposalsByGroupPolicyCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var res group.QueryProposalsByGroupPolicyResponse s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) s.Require().Equal(len(res.Proposals), len(tc.expectProposals)) for i := range res.Proposals { s.Require().Equal(res.Proposals[i], tc.expectProposals[i]) } } }) } } func (s *IntegrationTestSuite) TestQueryVoteByProposalVoter() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 }{ { "invalid voter address", []string{"1", "invalid", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "decoding bech32 failed: invalid bech32", 0, }, { "invalid proposal id", []string{"", val.Address.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "strconv.ParseUint: parsing \"\": invalid syntax", 0, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryVoteByProposalVoterCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) } }) } } func (s *IntegrationTestSuite) TestQueryVotesByProposal() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 expectVotes []*group.Vote }{ { "invalid proposal id", []string{"", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "strconv.ParseUint: parsing \"\": invalid syntax", 0, []*group.Vote{}, }, { "no votes", []string{"12345", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.Vote{}, }, { "found votes", []string{"1", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.Vote{ s.vote, }, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryVotesByProposalCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var res group.QueryVotesByProposalResponse s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) s.Require().Equal(len(res.Votes), len(tc.expectVotes)) for i := range res.Votes { s.Require().Equal(res.Votes[i], tc.expectVotes[i]) } } }) } } func (s *IntegrationTestSuite) TestQueryVotesByVoter() { val := s.network.Validators[0] clientCtx := val.ClientCtx testCases := []struct { name string args []string expectErr bool expectErrMsg string expectedCode uint32 expectVotes []*group.Vote }{ { "invalid voter address", []string{"abcd", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "decoding bech32 failed: invalid bech32", 0, []*group.Vote{}, }, { "no votes", []string{s.groupPolicies[0].Address, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, true, "", 0, []*group.Vote{}, }, { "found votes", []string{val.Address.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, "", 0, []*group.Vote{ s.vote, }, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryVotesByVoterCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var res group.QueryVotesByVoterResponse s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &res)) s.Require().Equal(len(res.Votes), len(tc.expectVotes)) for i := range res.Votes { s.Require().Equal(res.Votes[i], tc.expectVotes[i]) } } }) } } func (s *IntegrationTestSuite) TestTallyResult() { val := s.network.Validators[0] clientCtx := val.ClientCtx member := s.voter commonFlags := []string{ fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), } // create a proposal out, err := cli.ExecTestCLICmd(val.ClientCtx, client.MsgSubmitProposalCmd(), append( []string{ s.createCLIProposal( s.groupPolicies[0].Address, val.Address.String(), s.groupPolicies[0].Address, val.Address.String(), ""), }, commonFlags..., ), ) s.Require().NoError(err, out.String()) var txResp sdk.TxResponse s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txResp), out.String()) s.Require().Equal(uint32(0), txResp.Code, out.String()) proposalID := s.getProposalIDFromTxResponse(txResp) testCases := []struct { name string args []string expectErr bool expTallyResult group.TallyResult expectErrMsg string expectedCode uint32 }{ { "not found", []string{ "12345", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, true, group.TallyResult{}, "not found", 0, }, { "invalid proposal id", []string{ "", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, true, group.TallyResult{}, "strconv.ParseUint: parsing \"\": invalid syntax", 0, }, { "valid proposal id with no votes", []string{ proposalID, fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, false, group.DefaultTallyResult(), "", 0, }, { "valid proposal id", []string{ "1", fmt.Sprintf("--%s=json", tmcli.OutputFlag), }, false, group.TallyResult{ YesCount: member.Weight, AbstainCount: "0", NoCount: "0", NoWithVetoCount: "0", }, "", 0, }, } for _, tc := range testCases { tc := tc s.Run(tc.name, func() { cmd := client.QueryTallyResultCmd() out, err := cli.ExecTestCLICmd(clientCtx, cmd, tc.args) if tc.expectErr { s.Require().Contains(out.String(), tc.expectErrMsg) } else { s.Require().NoError(err, out.String()) var tallyResultRes group.QueryTallyResultResponse s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), &tallyResultRes)) s.Require().NotNil(tallyResultRes) s.Require().Equal(tc.expTallyResult, tallyResultRes.Tally) } }) } }