-
Notifications
You must be signed in to change notification settings - Fork 328
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ioctl] Build block bucketlist command line into new ioctl (#3469)
* [ioctl] build block bucketlist command line into new ioctl
- Loading branch information
1 parent
e956ca6
commit a20e489
Showing
4 changed files
with
342 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright (c) 2022 IoTeX Foundation | ||
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no | ||
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent | ||
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache | ||
// License 2.0 that can be found in the LICENSE file. | ||
|
||
package bc | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
"github.com/iotexproject/iotex-proto/golang/iotexapi" | ||
"github.com/iotexproject/iotex-proto/golang/iotexapi/mock_iotexapi" | ||
"github.com/iotexproject/iotex-proto/golang/iotextypes" | ||
"github.com/pkg/errors" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient" | ||
) | ||
|
||
func TestGetBucketList(t *testing.T) { | ||
require := require.New(t) | ||
ctrl := gomock.NewController(t) | ||
client := mock_ioctlclient.NewMockClient(ctrl) | ||
apiServiceClient := mock_iotexapi.NewMockAPIServiceClient(ctrl) | ||
request := &iotexapi.ReadStakingDataRequest{} | ||
response := &iotexapi.ReadStateResponse{} | ||
client.EXPECT().APIServiceClient().Return(apiServiceClient, nil).Times(2) | ||
|
||
t.Run("get bucket list", func(t *testing.T) { | ||
expectedValue := &iotextypes.VoteBucketList{ | ||
Buckets: []*iotextypes.VoteBucket{}, | ||
} | ||
apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.Any()).Return(response, nil) | ||
|
||
result, err := GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE, request) | ||
require.NoError(err) | ||
require.Contains(result.String(), expectedValue.String()) | ||
}) | ||
|
||
t.Run("failed to invoke ReadState api", func(t *testing.T) { | ||
expectedErr := errors.New("failed to invoke ReadState api") | ||
apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.Any()).Return(nil, expectedErr) | ||
|
||
_, err := GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER, request) | ||
require.Contains(err.Error(), expectedErr.Error()) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// Copyright (c) 2022 IoTeX Foundation | ||
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no | ||
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent | ||
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache | ||
// License 2.0 that can be found in the LICENSE file. | ||
|
||
package bc | ||
|
||
import ( | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/iotexproject/iotex-proto/golang/iotexapi" | ||
"github.com/iotexproject/iotex-proto/golang/iotextypes" | ||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/iotexproject/iotex-core/ioctl" | ||
"github.com/iotexproject/iotex-core/ioctl/config" | ||
) | ||
|
||
// constants | ||
const ( | ||
MethodVoter = "voter" | ||
MethodCandidate = "cand" | ||
) | ||
|
||
// Multi-language support | ||
var ( | ||
_bcBucketListCmdShorts = map[config.Language]string{ | ||
config.English: "Get bucket list with method and arg(s) on IoTeX blockchain", | ||
config.Chinese: "根据方法和参数在IoTeX区块链上读取投票列表", | ||
} | ||
_bcBucketListCmdUses = map[config.Language]string{ | ||
config.English: "bucketlist <method> [arguments]", | ||
config.Chinese: "bucketlist <方法> [参数]", | ||
} | ||
_bcBucketListCmdLongs = map[config.Language]string{ | ||
config.English: "Read bucket list\nValid methods: [" + | ||
MethodVoter + ", " + MethodCandidate + "]", | ||
config.Chinese: "根据方法和参数在IoTeX区块链上读取投票列表\n可用方法有:" + | ||
MethodVoter + "," + MethodCandidate, | ||
} | ||
) | ||
|
||
// NewBCBucketListCmd represents the bc bucketlist command | ||
func NewBCBucketListCmd(client ioctl.Client) *cobra.Command { | ||
use, _ := client.SelectTranslation(_bcBucketListCmdUses) | ||
short, _ := client.SelectTranslation(_bcBucketListCmdShorts) | ||
long, _ := client.SelectTranslation(_bcBucketListCmdLongs) | ||
|
||
return &cobra.Command{ | ||
Use: use, | ||
Short: short, | ||
Long: long, | ||
Args: cobra.MinimumNArgs(2), | ||
Example: `ioctl bc bucketlist voter [VOTER_ADDRESS] [OFFSET] [LIMIT] | ||
ioctl bc bucketlist cand [CANDIDATE_NAME] [OFFSET] [LIMIT]`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
cmd.SilenceUsage = true | ||
|
||
var ( | ||
bl *iotextypes.VoteBucketList | ||
address string | ||
err error | ||
) | ||
|
||
offset, limit := uint64(0), uint64(1000) | ||
method, addr := args[0], args[1] | ||
s := args[2:] | ||
|
||
if len(s) > 0 { | ||
offset, err = strconv.ParseUint(s[0], 10, 64) | ||
if err != nil { | ||
return errors.Wrap(err, "invalid offset") | ||
} | ||
} | ||
if len(s) > 1 { | ||
limit, err = strconv.ParseUint(s[1], 10, 64) | ||
if err != nil { | ||
return errors.Wrap(err, "invalid limit") | ||
} | ||
} | ||
switch method { | ||
case MethodVoter: | ||
address, err = client.AddressWithDefaultIfNotExist(addr) | ||
if err != nil { | ||
return err | ||
} | ||
bl, err = getBucketListByVoterAddress(client, address, uint32(offset), uint32(limit)) | ||
case MethodCandidate: | ||
bl, err = getBucketListByCandidateName(client, addr, uint32(offset), uint32(limit)) | ||
default: | ||
return errors.New("unknown <method>") | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
var lines []string | ||
if len(bl.Buckets) == 0 { | ||
lines = append(lines, "Empty bucketlist with given address") | ||
} else { | ||
for _, b := range bl.Buckets { | ||
bucket, err := newBucket(b) | ||
if err != nil { | ||
return err | ||
} | ||
lines = append(lines, bucket.String()) | ||
} | ||
} | ||
cmd.Println(strings.Join(lines, "\n")) | ||
return nil | ||
}, | ||
} | ||
} | ||
|
||
func getBucketListByVoterAddress(client ioctl.Client, addr string, offset, limit uint32) (*iotextypes.VoteBucketList, error) { | ||
readStakingdataRequest := &iotexapi.ReadStakingDataRequest{ | ||
Request: &iotexapi.ReadStakingDataRequest_BucketsByVoter{ | ||
BucketsByVoter: &iotexapi.ReadStakingDataRequest_VoteBucketsByVoter{ | ||
VoterAddress: addr, | ||
Pagination: &iotexapi.PaginationParam{ | ||
Offset: offset, | ||
Limit: limit, | ||
}, | ||
}, | ||
}, | ||
} | ||
return GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER, readStakingdataRequest) | ||
} | ||
|
||
func getBucketListByCandidateName(client ioctl.Client, candName string, offset, limit uint32) (*iotextypes.VoteBucketList, error) { | ||
readStakingDataRequest := &iotexapi.ReadStakingDataRequest{ | ||
Request: &iotexapi.ReadStakingDataRequest_BucketsByCandidate{ | ||
BucketsByCandidate: &iotexapi.ReadStakingDataRequest_VoteBucketsByCandidate{ | ||
CandName: candName, | ||
Pagination: &iotexapi.PaginationParam{ | ||
Offset: offset, | ||
Limit: limit, | ||
}, | ||
}, | ||
}, | ||
} | ||
return GetBucketList(client, iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE, readStakingDataRequest) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Copyright (c) 2022 IoTeX Foundation | ||
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no | ||
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent | ||
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache | ||
// License 2.0 that can be found in the LICENSE file. | ||
|
||
package bc | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
"github.com/iotexproject/iotex-proto/golang/iotexapi" | ||
"github.com/iotexproject/iotex-proto/golang/iotexapi/mock_iotexapi" | ||
"github.com/iotexproject/iotex-proto/golang/iotextypes" | ||
"github.com/pkg/errors" | ||
"github.com/stretchr/testify/require" | ||
"google.golang.org/protobuf/proto" | ||
"google.golang.org/protobuf/types/known/timestamppb" | ||
|
||
"github.com/iotexproject/iotex-core/ioctl/config" | ||
"github.com/iotexproject/iotex-core/ioctl/util" | ||
"github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient" | ||
"github.com/iotexproject/iotex-core/testutil" | ||
) | ||
|
||
func TestNewBCBucketListCmd(t *testing.T) { | ||
require := require.New(t) | ||
ctrl := gomock.NewController(t) | ||
client := mock_ioctlclient.NewMockClient(ctrl) | ||
apiServiceClient := mock_iotexapi.NewMockAPIServiceClient(ctrl) | ||
|
||
client.EXPECT().SelectTranslation(gomock.Any()).Return("", config.English).Times(21) | ||
client.EXPECT().APIServiceClient().Return(apiServiceClient, nil).Times(2) | ||
client.EXPECT().Config().Return(config.Config{}).Times(2) | ||
|
||
t.Run("get bucket list by voter", func(t *testing.T) { | ||
client.EXPECT().AddressWithDefaultIfNotExist(gomock.Any()).Return("io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx", nil) | ||
vblist, err := proto.Marshal(&iotextypes.VoteBucketList{ | ||
Buckets: []*iotextypes.VoteBucket{ | ||
{ | ||
Index: 1, | ||
StakedAmount: "10", | ||
UnstakeStartTime: timestamppb.New(testutil.TimestampNow()), | ||
}, | ||
{ | ||
Index: 2, | ||
StakedAmount: "20", | ||
UnstakeStartTime: timestamppb.New(testutil.TimestampNow()), | ||
}, | ||
}, | ||
}) | ||
require.NoError(err) | ||
apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.All()).Return(&iotexapi.ReadStateResponse{ | ||
Data: vblist, | ||
}, nil) | ||
|
||
cmd := NewBCBucketListCmd(client) | ||
result, err := util.ExecuteCmd(cmd, "voter", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx") | ||
require.NoError(err) | ||
require.Contains(result, | ||
"index: 1", | ||
"stakedAmount: 0.00000000000000001 IOTX", | ||
"index: 2", | ||
"stakedAmount: 0.00000000000000002 IOTX") | ||
}) | ||
|
||
t.Run("get bucket list by candidate", func(t *testing.T) { | ||
apiServiceClient.EXPECT().ReadState(gomock.Any(), gomock.All()).Return(&iotexapi.ReadStateResponse{}, nil) | ||
cmd := NewBCBucketListCmd(client) | ||
result, err := util.ExecuteCmd(cmd, "cand", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx") | ||
require.NoError(err) | ||
require.Equal("Empty bucketlist with given address\n", result) | ||
}) | ||
|
||
t.Run("invalid voter address", func(t *testing.T) { | ||
expectedErr := errors.New("cannot find address for alias test") | ||
client.EXPECT().AddressWithDefaultIfNotExist(gomock.Any()).Return("", expectedErr) | ||
cmd := NewBCBucketListCmd(client) | ||
_, err := util.ExecuteCmd(cmd, "voter", "test") | ||
require.Contains(err.Error(), expectedErr.Error()) | ||
}) | ||
|
||
t.Run("unknown method", func(t *testing.T) { | ||
expectedErr := errors.New("unknown <method>") | ||
cmd := NewBCBucketListCmd(client) | ||
_, err := util.ExecuteCmd(cmd, "unknown", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx") | ||
require.Equal(expectedErr.Error(), err.Error()) | ||
}) | ||
|
||
t.Run("invalid offset", func(t *testing.T) { | ||
expectedErr := errors.New("invalid offset") | ||
cmd := NewBCBucketListCmd(client) | ||
_, err := util.ExecuteCmd(cmd, "voter", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx", "test") | ||
require.Contains(err.Error(), expectedErr.Error()) | ||
}) | ||
|
||
t.Run("invalid limit", func(t *testing.T) { | ||
expectedErr := errors.New("invalid limit") | ||
cmd := NewBCBucketListCmd(client) | ||
_, err := util.ExecuteCmd(cmd, "voter", "io1uwnr55vqmhf3xeg5phgurlyl702af6eju542sx", "0", "test") | ||
require.Contains(err.Error(), expectedErr.Error()) | ||
}) | ||
} |