cosmos-sdk/x/nft/keeper/grpc_query_test.go

589 lines
15 KiB
Go
Raw Normal View History

feat: implement nft module query server (#10462) <!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## Description QueryServer implementation and corresponding keeper methods,refer #9826 <!-- Add a description of the changes that this PR introduces and the files that are the most critical to review. --> --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2021-11-06 16:05:45 -07:00
package keeper_test
import (
gocontext "context"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/x/nft"
)
func TestGRPCQuery(t *testing.T) {
suite.Run(t, new(TestSuite))
}
func (suite *TestSuite) TestBalance() {
var (
req *nft.QueryBalanceRequest
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
balance uint64
postTest func(index int, require *require.Assertions, res *nft.QueryBalanceResponse, expBalance uint64)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryBalanceRequest{}
},
"invalid class id",
0,
func(index int, require *require.Assertions, res *nft.QueryBalanceResponse, expBalance uint64) {},
},
{
"fail invalid Owner addr",
func(index int, require *require.Assertions) {
req = &nft.QueryBalanceRequest{
ClassId: testClassID,
Owner: "owner",
}
},
"decoding bech32 failed",
0,
func(index int, require *require.Assertions, res *nft.QueryBalanceResponse, expBalance uint64) {},
},
{
"Success",
func(index int, require *require.Assertions) {
suite.TestMint()
req = &nft.QueryBalanceRequest{
ClassId: testClassID,
Owner: suite.addrs[0].String(),
}
},
"",
fix: update nft storekey `nftOfClassByOwnerStoreKey` (#10682) <!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> The length of `nftOfClassByOwnerStoreKey` is incorrect, and` len(classIDBz)` is not included. As a result, when querying nft according to `owner` and `classID`, the nft that does not belong to the classID will be returned. <!-- Add a description of the changes that this PR introduces and the files that are the most critical to review. --> --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2021-12-07 04:59:51 -08:00
2,
feat: implement nft module query server (#10462) <!-- The default pull request template is for types feat, fix, or refactor. For other templates, add one of the following parameters to the url: - template=docs.md - template=other.md --> ## Description QueryServer implementation and corresponding keeper methods,refer #9826 <!-- Add a description of the changes that this PR introduces and the files that are the most critical to review. --> --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2021-11-06 16:05:45 -07:00
func(index int, require *require.Assertions, res *nft.QueryBalanceResponse, expBalance uint64) {
require.Equal(res.Amount, expBalance, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Balance(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result, tc.balance)
})
}
}
func (suite *TestSuite) TestOwner() {
var (
req *nft.QueryOwnerRequest
owner string
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryOwnerResponse)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryOwnerRequest{
Id: testID,
}
},
"invalid class id",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {},
},
{
"fail empty nft id",
func(index int, require *require.Assertions) {
req = &nft.QueryOwnerRequest{
ClassId: testClassID,
}
},
"invalid nft id",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {},
},
{
"success but nft id not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryOwnerRequest{
ClassId: testClassID,
Id: "kitty2",
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {
require.Equal(res.Owner, owner, "the error occurred on:%d", index)
},
},
{
"success but class id not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryOwnerRequest{
ClassId: "kitty1",
Id: testID,
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {
require.Equal(res.Owner, owner, "the error occurred on:%d", index)
},
},
{
"Success",
func(index int, require *require.Assertions) {
suite.TestMint()
req = &nft.QueryOwnerRequest{
ClassId: testClassID,
Id: testID,
}
owner = suite.addrs[0].String()
},
"",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {
require.Equal(res.Owner, owner, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Owner(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}
func (suite *TestSuite) TestSupply() {
var (
req *nft.QuerySupplyRequest
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
supply uint64
postTest func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QuerySupplyRequest{}
},
"invalid class id",
0,
func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64) {},
},
{
"success but class id not exist",
func(index int, require *require.Assertions) {
req = &nft.QuerySupplyRequest{
ClassId: "kitty1",
}
},
"",
0,
func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64) {
require.Equal(res.Amount, supply, "the error occurred on:%d", index)
},
},
{
"success but supply equal zero",
func(index int, require *require.Assertions) {
req = &nft.QuerySupplyRequest{
ClassId: testClassID,
}
suite.TestSaveClass()
},
"",
0,
func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64) {
require.Equal(res.Amount, supply, "the error occurred on:%d", index)
},
},
{
"Success",
func(index int, require *require.Assertions) {
n := nft.NFT{
ClassId: testClassID,
Id: testID,
Uri: testURI,
}
err := suite.app.NFTKeeper.Mint(suite.ctx, n, suite.addrs[0])
require.NoError(err, "the error occurred on:%d", index)
req = &nft.QuerySupplyRequest{
ClassId: testClassID,
}
},
"",
1,
func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64) {
require.Equal(res.Amount, supply, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Supply(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result, tc.supply)
})
}
}
func (suite *TestSuite) TestNFTsOfClass() {
var (
req *nft.QueryNFTsOfClassRequest
nfts []*nft.NFT
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{}
},
"invalid class id",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {},
},
{
"success, no nft",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: testClassID,
}
suite.TestSaveClass()
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Len(res.Nfts, 0, "the error occurred on:%d", index)
},
},
{
"success, class id not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: "kitty1",
}
n := nft.NFT{
ClassId: testClassID,
Id: testID,
Uri: testURI,
}
err := suite.app.NFTKeeper.Mint(suite.ctx, n, suite.addrs[0])
require.NoError(err, "the error occurred on:%d", index)
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Len(res.Nfts, 0, "the error occurred on:%d", index)
},
},
{
"success, owner not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: testClassID,
Owner: suite.addrs[1].String(),
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Len(res.Nfts, 0, "the error occurred on:%d", index)
},
},
{
"Success, query by classId",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: testClassID,
}
nfts = []*nft.NFT{
{
ClassId: testClassID,
Id: testID,
Uri: testURI,
},
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Equal(res.Nfts, nfts, "the error occurred on:%d", index)
},
},
{
"Success,query by classId and owner",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: testClassID,
Owner: suite.addrs[0].String(),
}
nfts = []*nft.NFT{
{
ClassId: testClassID,
Id: testID,
Uri: testURI,
},
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Equal(res.Nfts, nfts, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.NFTsOfClass(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}
func (suite *TestSuite) TestNFT() {
var (
req *nft.QueryNFTRequest
expNFT nft.NFT
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryNFTResponse)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{}
},
"invalid class id",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {},
},
{
"fail empty nft id",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{
ClassId: testClassID,
}
},
"invalid nft id",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {},
},
{
"fail ClassId not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{
ClassId: "kitty1",
Id: testID,
}
suite.TestMint()
},
"not found nft",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {},
},
{
"fail nft id not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{
ClassId: testClassID,
Id: "kitty2",
}
},
"not found nft",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {},
},
{
"success",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{
ClassId: testClassID,
Id: testID,
}
expNFT = nft.NFT{
ClassId: testClassID,
Id: testID,
Uri: testURI,
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {
require.Equal(*res.Nft, expNFT, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.NFT(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}
func (suite *TestSuite) TestClass() {
var (
req *nft.QueryClassRequest
class nft.Class
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryClassResponse)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryClassRequest{}
},
"invalid class id",
func(index int, require *require.Assertions, res *nft.QueryClassResponse) {},
},
{
"fail ClassId not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryClassRequest{
ClassId: "kitty1",
}
suite.TestSaveClass()
},
"not found class",
func(index int, require *require.Assertions, res *nft.QueryClassResponse) {},
},
{
"success",
func(index int, require *require.Assertions) {
class = nft.Class{
Id: testClassID,
Name: testClassName,
Symbol: testClassSymbol,
Description: testClassDescription,
Uri: testClassURI,
UriHash: testClassURIHash,
}
req = &nft.QueryClassRequest{
ClassId: testClassID,
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryClassResponse) {
require.Equal(*res.Class, class, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Class(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}
func (suite *TestSuite) TestClasses() {
var (
req *nft.QueryClassesRequest
classes []nft.Class
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryClassesResponse)
}{
{
"success Class not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryClassesRequest{}
},
"",
func(index int, require *require.Assertions, res *nft.QueryClassesResponse) {
require.Len(res.Classes, 0)
},
},
{
"success",
func(index int, require *require.Assertions) {
req = &nft.QueryClassesRequest{}
classes = []nft.Class{
{
Id: testClassID,
Name: testClassName,
Symbol: testClassSymbol,
Description: testClassDescription,
Uri: testClassURI,
UriHash: testClassURIHash,
},
}
suite.TestSaveClass()
},
"",
func(index int, require *require.Assertions, res *nft.QueryClassesResponse) {
require.Len(res.Classes, 1, "the error occurred on:%d", index)
require.Equal(*res.Classes[0], classes[0], "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Classes(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}