From d346b95234710ceabca96130c9a5d8864b2fe6a3 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Mon, 4 Mar 2024 11:16:09 +0000 Subject: [PATCH] Add e2e tests for gRPC requests and CLI commands (#13) - Add E2E tests following pattern suggested in cosmos-sdk docs: https://docs.cosmos.network/v0.50/build/building-modules/testing#end-to-end-tests - Tests for gRPC requests - Tests for manually configured CLI commands - Add a CI workflow to run these E2E tests Reviewed-on: https://git.vdb.to/deep-stack/laconic2d/pulls/13 Co-authored-by: Prathamesh Musale Co-committed-by: Prathamesh Musale --- .gitea/workflows/test-e2e.yml | 19 + Makefile | 3 + README.md | 6 +- api/cerc/bond/v1/genesis.pulsar.go | 1 - proto/cerc/auction/v1/query.proto | 2 - proto/cerc/bond/v1/genesis.proto | 1 - scripts/protocgen.sh | 1 - tests/Makefile | 3 + .../data/examples/example1.yml | 0 .../data/examples/general_record_example.yml | 0 .../examples/service_provider_example.yml | 0 .../examples/website_registration_example.yml | 0 tests/e2e/auction/cli_test.go | 17 + tests/e2e/auction/grpc.go | 263 +++++++++++ tests/e2e/auction/query.go | 45 ++ tests/e2e/auction/suite.go | 148 ++++++ tests/e2e/auction/tx.go | 105 +++++ tests/e2e/bond/cli_test.go | 17 + tests/e2e/bond/grpc.go | 183 ++++++++ tests/e2e/bond/query.go | 49 ++ tests/e2e/bond/suite.go | 123 +++++ tests/e2e/bond/tx.go | 63 +++ tests/e2e/common.go | 67 +++ tests/e2e/registry/cli_test.go | 17 + tests/e2e/registry/grpc.go | 429 ++++++++++++++++++ tests/e2e/registry/suite.go | 282 ++++++++++++ tests/e2e/registry/tx.go | 70 +++ .../registry/keeper/query_server_test.go | 14 +- x/auction/client/cli/query.go | 35 ++ x/auction/client/cli/tx.go | 58 +++ x/auction/msgs.go | 21 + x/bond/client/cli/query.go | 48 ++ x/bond/client/cli/tx.go | 38 ++ x/bond/genesis.pb.go | 1 - x/bond/keeper/msg_server.go | 5 +- x/bond/msgs.go | 17 + x/registry/client/cli/query.go | 58 +++ x/registry/client/cli/tx.go | 112 +++++ x/registry/msgs.go | 57 +++ 39 files changed, 2362 insertions(+), 16 deletions(-) create mode 100644 .gitea/workflows/test-e2e.yml rename tests/{integration => }/data/examples/example1.yml (100%) rename tests/{integration => }/data/examples/general_record_example.yml (100%) rename tests/{integration => }/data/examples/service_provider_example.yml (100%) rename tests/{integration => }/data/examples/website_registration_example.yml (100%) create mode 100644 tests/e2e/auction/cli_test.go create mode 100644 tests/e2e/auction/grpc.go create mode 100644 tests/e2e/auction/query.go create mode 100644 tests/e2e/auction/suite.go create mode 100644 tests/e2e/auction/tx.go create mode 100644 tests/e2e/bond/cli_test.go create mode 100644 tests/e2e/bond/grpc.go create mode 100644 tests/e2e/bond/query.go create mode 100644 tests/e2e/bond/suite.go create mode 100644 tests/e2e/bond/tx.go create mode 100644 tests/e2e/common.go create mode 100644 tests/e2e/registry/cli_test.go create mode 100644 tests/e2e/registry/grpc.go create mode 100644 tests/e2e/registry/suite.go create mode 100644 tests/e2e/registry/tx.go create mode 100644 x/auction/client/cli/query.go create mode 100644 x/bond/client/cli/query.go create mode 100644 x/bond/client/cli/tx.go create mode 100644 x/bond/msgs.go create mode 100644 x/registry/client/cli/query.go diff --git a/.gitea/workflows/test-e2e.yml b/.gitea/workflows/test-e2e.yml new file mode 100644 index 00000000..fe288a48 --- /dev/null +++ b/.gitea/workflows/test-e2e.yml @@ -0,0 +1,19 @@ +name: E2E Tests +on: + pull_request: + push: + branches: + - main + +jobs: + test-e2e: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v3 + with: + go-version: 1.21 + check-latest: true + - uses: actions/checkout@v3 + - name: Test + run: | + make test-e2e diff --git a/Makefile b/Makefile index 6ce6a9da..6617ec11 100644 --- a/Makefile +++ b/Makefile @@ -88,3 +88,6 @@ lint-fix: test-integration: $(MAKE) -C tests test-integration + +test-e2e: + $(MAKE) -C tests test-e2e diff --git a/README.md b/README.md index ab165b07..6648efc8 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,15 @@ Install and run `laconic2d`: make init # start the chain - laconic2d start + laconic2d start --gql-playground --gql-server ``` Run tests: ```bash + # integration tests make test-integration + + # e2e tests + make test-e2e ``` diff --git a/api/cerc/bond/v1/genesis.pulsar.go b/api/cerc/bond/v1/genesis.pulsar.go index 93d1c38e..608c6c79 100644 --- a/api/cerc/bond/v1/genesis.pulsar.go +++ b/api/cerc/bond/v1/genesis.pulsar.go @@ -608,7 +608,6 @@ type GenesisState struct { // params defines all the parameters of the module. Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` // bonds defines all the bonds - // TODO: Add nullable = false ? Bonds []*Bond `protobuf:"bytes,2,rep,name=bonds,proto3" json:"bonds,omitempty"` } diff --git a/proto/cerc/auction/v1/query.proto b/proto/cerc/auction/v1/query.proto index cd88fc20..d4afe85b 100644 --- a/proto/cerc/auction/v1/query.proto +++ b/proto/cerc/auction/v1/query.proto @@ -123,8 +123,6 @@ message QueryAuctionsByBidderRequest { message QueryAuctionsByBidderResponse { // List of auctions Auctions auctions = 1; - - // TODO: Add pagination? } // AuctionsByOwnerRequest is the format for querying all auctions created by an owner diff --git a/proto/cerc/bond/v1/genesis.proto b/proto/cerc/bond/v1/genesis.proto index c37ba129..b6f922fb 100644 --- a/proto/cerc/bond/v1/genesis.proto +++ b/proto/cerc/bond/v1/genesis.proto @@ -13,6 +13,5 @@ message GenesisState { Params params = 1 [(gogoproto.nullable) = false]; // bonds defines all the bonds - // TODO: Add nullable = false ? repeated Bond bonds = 2 [(gogoproto.moretags) = "json:\"bonds\" yaml:\"bonds\""]; } diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index a18fcb37..6acf722a 100755 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -16,7 +16,6 @@ for dir in $proto_dirs; do done done -# TODO: Check if required echo "Generating pulsar proto code" buf generate --template buf.gen.pulsar.yaml diff --git a/tests/Makefile b/tests/Makefile index 3ed7cfec..0349dfc1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,2 +1,5 @@ test-integration: go test -mod=readonly ./integration/... -test.v -timeout 10m + +test-e2e: + go test ./e2e/... -mod=readonly -test.v -timeout 10m diff --git a/tests/integration/data/examples/example1.yml b/tests/data/examples/example1.yml similarity index 100% rename from tests/integration/data/examples/example1.yml rename to tests/data/examples/example1.yml diff --git a/tests/integration/data/examples/general_record_example.yml b/tests/data/examples/general_record_example.yml similarity index 100% rename from tests/integration/data/examples/general_record_example.yml rename to tests/data/examples/general_record_example.yml diff --git a/tests/integration/data/examples/service_provider_example.yml b/tests/data/examples/service_provider_example.yml similarity index 100% rename from tests/integration/data/examples/service_provider_example.yml rename to tests/data/examples/service_provider_example.yml diff --git a/tests/integration/data/examples/website_registration_example.yml b/tests/data/examples/website_registration_example.yml similarity index 100% rename from tests/integration/data/examples/website_registration_example.yml rename to tests/data/examples/website_registration_example.yml diff --git a/tests/e2e/auction/cli_test.go b/tests/e2e/auction/cli_test.go new file mode 100644 index 00000000..aac55bcc --- /dev/null +++ b/tests/e2e/auction/cli_test.go @@ -0,0 +1,17 @@ +package auction + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/stretchr/testify/suite" + + "git.vdb.to/cerc-io/laconic2d/tests/e2e" +) + +func TestAuctionE2ETestSuite(t *testing.T) { + cfg := network.DefaultConfig(e2e.NewTestNetworkFixture) + cfg.NumValidators = 1 + + suite.Run(t, NewE2ETestSuite(cfg)) +} diff --git a/tests/e2e/auction/grpc.go b/tests/e2e/auction/grpc.go new file mode 100644 index 00000000..1f9c4ade --- /dev/null +++ b/tests/e2e/auction/grpc.go @@ -0,0 +1,263 @@ +package auction + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/testutil" + + auctiontypes "git.vdb.to/cerc-io/laconic2d/x/auction" +) + +const ( + randomAuctionId = "randomAuctionId" + randomBidderAddress = "randomBidderAddress" + randomOwnerAddress = "randomOwnerAddress" +) + +func (ets *E2ETestSuite) TestQueryParamsGrpc() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/auction/v1/params", val.APIAddress) + + ets.Run("valid request to get auction params", func() { + resp, err := testutil.GetRequest(reqURL) + ets.Require().NoError(err) + + var params auctiontypes.QueryParamsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, ¶ms) + + sr.NoError(err) + sr.Equal(*params.GetParams(), auctiontypes.DefaultParams()) + }) +} + +func (ets *E2ETestSuite) TestGetAllAuctionsGrpc() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/auction/v1/auctions", val.APIAddress) + + testCases := []struct { + msg string + url string + errorMsg string + isErrorExpected bool + }{ + { + "invalid request to get all auctions", + reqURL + randomAuctionId, + "", + true, + }, + { + "valid request to get all auctions", + reqURL, + "", + false, + }, + } + for _, tc := range testCases { + ets.Run(tc.msg, func() { + resp, err := testutil.GetRequest(tc.url) + if tc.isErrorExpected { + sr.Contains(string(resp), tc.errorMsg) + } else { + sr.NoError(err) + var auctions auctiontypes.QueryAuctionsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, &auctions) + sr.NoError(err) + sr.NotZero(len(auctions.Auctions.Auctions)) + } + }) + } +} + +func (ets *E2ETestSuite) TestGetAuctionGrpc() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/auction/v1/auctions/", val.APIAddress) + + testCases := []struct { + msg string + url string + errorMsg string + isErrorExpected bool + preRun func() string + }{ + { + "invalid request to get an auction", + reqURL + randomAuctionId, + "", + true, + func() string { return "" }, + }, + { + "valid request to get an auction", + reqURL, + "", + false, + func() string { return ets.defaultAuctionId }, + }, + } + for _, tc := range testCases { + ets.Run(tc.msg, func() { + auctionId := tc.preRun() + resp, err := testutil.GetRequest(tc.url + auctionId) + if tc.isErrorExpected { + sr.Contains(string(resp), tc.errorMsg) + } else { + sr.NoError(err) + var auction auctiontypes.QueryAuctionResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, &auction) + sr.NoError(err) + sr.Equal(auctionId, auction.Auction.Id) + } + }) + } +} + +func (ets *E2ETestSuite) TestGetBidsGrpc() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/auction/v1/bids/", val.APIAddress) + testCases := []struct { + msg string + url string + errorMsg string + isErrorExpected bool + preRun func() string + }{ + { + "invalid request to get all bids", + reqURL, + "", + true, + func() string { return "" }, + }, + { + "valid request to get all bids", + reqURL, + "", + false, + func() string { return ets.createAuctionAndBid(false, true) }, + }, + } + + for _, tc := range testCases { + ets.Run(tc.msg, func() { + auctionId := tc.preRun() + tc.url += auctionId + resp, err := testutil.GetRequest(tc.url) + if tc.isErrorExpected { + sr.Contains(string(resp), tc.errorMsg) + } else { + sr.NoError(err) + var bids auctiontypes.QueryBidsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, &bids) + sr.NoError(err) + sr.Equal(auctionId, bids.Bids[0].AuctionId) + } + }) + } +} + +func (ets *E2ETestSuite) TestGetBidGrpc() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/auction/v1/bids/", val.APIAddress) + testCases := []struct { + msg string + url string + errorMsg string + isErrorExpected bool + preRun func() string + }{ + { + "invalid request to get bid", + reqURL, + "", + true, + func() string { return randomAuctionId }, + }, + { + "valid request to get bid", + reqURL, + "", + false, + func() string { return ets.createAuctionAndBid(false, true) }, + }, + } + for _, tc := range testCases { + ets.Run(tc.msg, func() { + auctionId := tc.preRun() + tc.url += auctionId + "/" + bidderAddress + resp, err := testutil.GetRequest(tc.url) + + if tc.isErrorExpected { + sr.Contains(string(resp), tc.errorMsg) + } else { + sr.NoError(err) + var bid auctiontypes.QueryBidResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, &bid) + sr.NoError(err) + } + }) + } +} + +func (ets *E2ETestSuite) TestGetAuctionsByOwnerGrpc() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/auction/v1/by-owner/", val.APIAddress) + testCases := []struct { + msg string + url string + errorMsg string + isErrorExpected bool + }{ + { + "invalid request to get auctions by owner", + reqURL, + "", + true, + }, + { + "valid request to get auctions by owner", + fmt.Sprintf("%s/%s", reqURL, ownerAddress), + "", + false, + }, + } + for _, tc := range testCases { + ets.Run(tc.msg, func() { + resp, err := testutil.GetRequest(tc.url) + if tc.isErrorExpected { + sr.Contains(string(resp), tc.errorMsg) + } else { + sr.NoError(err) + var auctions auctiontypes.QueryAuctionsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, &auctions) + sr.NoError(err) + } + }) + } +} + +func (ets *E2ETestSuite) TestQueryBalanceGrpc() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/auction/v1/balance", val.APIAddress) + msg := "valid request to get the auction module balance" + + ets.createAuctionAndBid(false, true) + + ets.Run(msg, func() { + resp, err := testutil.GetRequest(reqURL) + sr.NoError(err) + + var response auctiontypes.QueryGetAuctionModuleBalanceResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + + sr.NoError(err) + sr.NotZero(len(response.GetBalance())) + }) +} diff --git a/tests/e2e/auction/query.go b/tests/e2e/auction/query.go new file mode 100644 index 00000000..fcca2de1 --- /dev/null +++ b/tests/e2e/auction/query.go @@ -0,0 +1,45 @@ +package auction + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + + types "git.vdb.to/cerc-io/laconic2d/x/auction" + "git.vdb.to/cerc-io/laconic2d/x/auction/client/cli" +) + +var queryJSONFlag = []string{fmt.Sprintf("--%s=json", flags.FlagOutput)} + +func (ets *E2ETestSuite) TestGetCmdList() { + val := ets.network.Validators[0] + sr := ets.Require() + + testCases := []struct { + msg string + createAuction bool + }{ + { + "list auctions when no auctions exist", + false, + }, + { + "list auctions after creating an auction", + true, + }, + } + + for _, test := range testCases { + ets.Run(fmt.Sprintf("Case %s", test.msg), func() { + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.GetCmdList(), queryJSONFlag) + sr.NoError(err) + var auctions types.QueryAuctionsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &auctions) + sr.NoError(err) + if test.createAuction { + sr.NotZero(len(auctions.Auctions.Auctions)) + } + }) + } +} diff --git a/tests/e2e/auction/suite.go b/tests/e2e/auction/suite.go new file mode 100644 index 00000000..ce322258 --- /dev/null +++ b/tests/e2e/auction/suite.go @@ -0,0 +1,148 @@ +package auction + +import ( + "fmt" + "os" + "path/filepath" + + "cosmossdk.io/math" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client/flags" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + + types "git.vdb.to/cerc-io/laconic2d/x/auction" + "git.vdb.to/cerc-io/laconic2d/x/auction/client/cli" +) + +var ( + ownerAccount = "owner" + bidderAccount = "bidder" + ownerAddress string + bidderAddress string +) + +type E2ETestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + + defaultAuctionId string +} + +func NewE2ETestSuite(cfg network.Config) *E2ETestSuite { + return &E2ETestSuite{cfg: cfg} +} + +func (ets *E2ETestSuite) SetupSuite() { //nolint: all + sr := ets.Require() + ets.T().Log("setting up e2e test suite") + + var err error + + ets.network, err = network.New(ets.T(), ets.T().TempDir(), ets.cfg) + sr.NoError(err) + + _, err = ets.network.WaitForHeight(1) + sr.NoError(err) + + // setting up random owner and bidder accounts + ets.createAccountWithBalance(ownerAccount, &ownerAddress) + ets.createAccountWithBalance(bidderAccount, &bidderAddress) + + ets.defaultAuctionId = ets.createAuctionAndBid(true, false) +} + +func (ets *E2ETestSuite) TearDownSuite() { + ets.T().Log("tearing down integration test suite") + ets.network.Cleanup() + + ets.cleanupBidFiles() +} + +func (ets *E2ETestSuite) createAccountWithBalance(accountName string, accountAddress *string) { + val := ets.network.Validators[0] + sr := ets.Require() + + info, _, err := val.ClientCtx.Keyring.NewMnemonic(accountName, keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + sr.NoError(err) + + newAddr, _ := info.GetAddress() + _, err = clitestutil.MsgSendExec( + val.ClientCtx, + val.Address, + newAddr, + sdk.NewCoins(sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(200000))), + addresscodec.NewBech32Codec("laconic"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(10))).String()), + ) + sr.NoError(err) + *accountAddress = newAddr.String() + + // wait for tx to take effect + err = ets.network.WaitForNextBlock() + sr.NoError(err) +} + +func (ets *E2ETestSuite) createAuctionAndBid(createAuction, createBid bool) string { + val := ets.network.Validators[0] + sr := ets.Require() + auctionId := "" + + if createAuction { + auctionArgs := []string{ + sampleCommitTime, sampleRevealTime, + fmt.Sprintf("10%s", ets.cfg.BondDenom), + fmt.Sprintf("10%s", ets.cfg.BondDenom), + fmt.Sprintf("100%s", ets.cfg.BondDenom), + } + + resp, err := ets.executeTx(cli.GetCmdCreateAuction(), auctionArgs, ownerAccount) + sr.NoError(err) + sr.Zero(resp.Code) + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.GetCmdList(), queryJSONFlag) + sr.NoError(err) + var queryResponse types.QueryAuctionsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &queryResponse) + sr.NoError(err) + auctionId = queryResponse.Auctions.Auctions[0].Id + } else { + auctionId = ets.defaultAuctionId + } + + if createBid { + bidArgs := []string{auctionId, fmt.Sprintf("200%s", ets.cfg.BondDenom)} + resp, err := ets.executeTx(cli.GetCmdCommitBid(), bidArgs, bidderAccount) + sr.NoError(err) + sr.Zero(resp.Code) + } + + return auctionId +} + +func (ets *E2ETestSuite) cleanupBidFiles() error { + matches, err := filepath.Glob(fmt.Sprintf("%s-*.json", bidderAccount)) + if err != nil { + ets.T().Errorf("Error matching bidder files: %v\n", err) + return err + } + + for _, match := range matches { + err := os.Remove(match) + if err != nil { + return err + } + } + + return nil +} diff --git a/tests/e2e/auction/tx.go b/tests/e2e/auction/tx.go new file mode 100644 index 00000000..1675d25e --- /dev/null +++ b/tests/e2e/auction/tx.go @@ -0,0 +1,105 @@ +package auction + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/cobra" + + auctiontypes "git.vdb.to/cerc-io/laconic2d/x/auction" + "git.vdb.to/cerc-io/laconic2d/x/auction/client/cli" +) + +const ( + sampleCommitTime = "90s" + sampleRevealTime = "5s" + placeholderAuctionId = "placeholder_auction_id" +) + +func (ets *E2ETestSuite) TestTxCommitBid() { + val := ets.network.Validators[0] + sr := ets.Require() + testCases := []struct { + msg string + args []string + createAuction bool + }{ + { + "commit bid with missing args", + []string{fmt.Sprintf("200%s", ets.cfg.BondDenom)}, + false, + }, + { + "commit bid with valid args", + []string{ + placeholderAuctionId, + fmt.Sprintf("200%s", ets.cfg.BondDenom), + }, + true, + }, + } + + for _, test := range testCases { + ets.Run(fmt.Sprintf("Case %s", test.msg), func() { + if test.createAuction { + auctionArgs := []string{ + sampleCommitTime, sampleRevealTime, + fmt.Sprintf("10%s", ets.cfg.BondDenom), + fmt.Sprintf("10%s", ets.cfg.BondDenom), + fmt.Sprintf("100%s", ets.cfg.BondDenom), + } + + _, err := ets.executeTx(cli.GetCmdCreateAuction(), auctionArgs, ownerAccount) + sr.NoError(err) + + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.GetCmdList(), + []string{fmt.Sprintf("--%s=json", flags.FlagOutput)}) + sr.NoError(err) + var queryResponse auctiontypes.QueryAuctionsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &queryResponse) + sr.NoError(err) + sr.NotNil(queryResponse.GetAuctions()) + test.args[0] = queryResponse.GetAuctions().Auctions[0].Id + } + + resp, err := ets.executeTx(cli.GetCmdCommitBid(), test.args, bidderAccount) + if test.createAuction { + sr.NoError(err) + sr.Zero(resp.Code) + } else { + sr.Error(err) + } + }) + } +} + +func (ets *E2ETestSuite) executeTx(cmd *cobra.Command, args []string, caller string) (sdk.TxResponse, error) { + val := ets.network.Validators[0] + additionalArgs := []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, caller), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + } + args = append(args, additionalArgs...) + + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) + if err != nil { + return sdk.TxResponse{}, err + } + + var resp sdk.TxResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resp) + if err != nil { + return sdk.TxResponse{}, err + } + + err = ets.network.WaitForNextBlock() + if err != nil { + return sdk.TxResponse{}, err + } + + return resp, nil +} diff --git a/tests/e2e/bond/cli_test.go b/tests/e2e/bond/cli_test.go new file mode 100644 index 00000000..c5e5a9f8 --- /dev/null +++ b/tests/e2e/bond/cli_test.go @@ -0,0 +1,17 @@ +package bond + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/stretchr/testify/suite" + + "git.vdb.to/cerc-io/laconic2d/tests/e2e" +) + +func TestBondE2ETestSuite(t *testing.T) { + cfg := network.DefaultConfig(e2e.NewTestNetworkFixture) + cfg.NumValidators = 1 + + suite.Run(t, NewE2ETestSuite(cfg)) +} diff --git a/tests/e2e/bond/grpc.go b/tests/e2e/bond/grpc.go new file mode 100644 index 00000000..b326a51d --- /dev/null +++ b/tests/e2e/bond/grpc.go @@ -0,0 +1,183 @@ +package bond + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/testutil" + + bondtypes "git.vdb.to/cerc-io/laconic2d/x/bond" +) + +func (ets *E2ETestSuite) TestGRPCGetParams() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/bond/v1/params", val.APIAddress) + + resp, err := testutil.GetRequest(reqURL) + ets.Require().NoError(err) + + var params bondtypes.QueryParamsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, ¶ms) + + sr.NoError(err) + sr.Equal(params.GetParams().MaxBondAmount, bondtypes.DefaultParams().MaxBondAmount) +} + +func (ets *E2ETestSuite) TestGRPCGetBonds() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/bond/v1/bonds", val.APIAddress) + + testCases := []struct { + name string + url string + expErr bool + errorMsg string + preRun func() string + }{ + { + "invalid request with headers", + reqURL + "asdasdas", + true, + "", + func() string { return "" }, + }, + { + "valid request", + reqURL, + false, + "", + func() string { return ets.createBond() }, + }, + } + for _, tc := range testCases { + ets.Run(tc.name, func() { + tc.preRun() + + resp, _ := testutil.GetRequest(tc.url) + if tc.expErr { + sr.Contains(string(resp), tc.errorMsg) + } else { + var response bondtypes.QueryGetBondsResponse + err := val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + sr.NoError(err) + sr.NotZero(len(response.GetBonds())) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCGetBondsByOwner() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := val.APIAddress + "/cerc/bond/v1/by-owner/%s" + + testCases := []struct { + name string + url string + expErr bool + preRun func() string + }{ + { + "empty list", + fmt.Sprintf(reqURL, "asdasd"), + true, + func() string { return "" }, + }, + { + "valid request", + fmt.Sprintf(reqURL, ets.accountAddress), + false, + func() string { return ets.createBond() }, + }, + } + + for _, tc := range testCases { + ets.Run(tc.name, func() { + tc.preRun() + + resp, err := testutil.GetRequest(tc.url) + ets.Require().NoError(err) + + var bonds bondtypes.QueryGetBondsByOwnerResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, &bonds) + sr.NoError(err) + if tc.expErr { + sr.Empty(bonds.GetBonds()) + } else { + bondsList := bonds.GetBonds() + sr.NotZero(len(bondsList)) + sr.Equal(ets.accountAddress, bondsList[0].GetOwner()) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCGetBondById() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := val.APIAddress + "/cerc/bond/v1/bonds/%s" + + testCases := []struct { + name string + url string + expErr bool + preRun func() string + }{ + { + "invalid request", + fmt.Sprintf(reqURL, "asdadad"), + true, + func() string { return "" }, + }, + { + "valid request", + reqURL, + false, + func() string { return ets.createBond() }, + }, + } + for _, tc := range testCases { + ets.Run(tc.name, func() { + var bondId string + if !tc.expErr { + bondId = tc.preRun() + tc.url = fmt.Sprintf(reqURL, bondId) + } + + resp, err := testutil.GetRequest(tc.url) + ets.Require().NoError(err) + + var bonds bondtypes.QueryGetBondByIdResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, &bonds) + + if tc.expErr { + sr.Empty(bonds.GetBond().GetId()) + } else { + sr.NoError(err) + sr.NotZero(bonds.GetBond().GetId()) + sr.Equal(bonds.GetBond().GetId(), bondId) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCGetBondModuleBalance() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := fmt.Sprintf("%s/cerc/bond/v1/balance", val.APIAddress) + + // creating the bond + ets.createBond() + + ets.Run("valid request", func() { + resp, err := testutil.GetRequest(reqURL) + sr.NoError(err) + + var response bondtypes.QueryGetBondModuleBalanceResponse + err = val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + + sr.NoError(err) + sr.False(response.GetBalance().IsZero()) + }) +} diff --git a/tests/e2e/bond/query.go b/tests/e2e/bond/query.go new file mode 100644 index 00000000..8c48b6e8 --- /dev/null +++ b/tests/e2e/bond/query.go @@ -0,0 +1,49 @@ +package bond + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + + bondtypes "git.vdb.to/cerc-io/laconic2d/x/bond" + "git.vdb.to/cerc-io/laconic2d/x/bond/client/cli" +) + +func (ets *E2ETestSuite) TestGetQueryBondList() { + val := ets.network.Validators[0] + sr := ets.Require() + + testCases := []struct { + name string + args []string + createBond bool + preRun func() + }{ + { + "create and get bond lists", + []string{fmt.Sprintf("--%s=json", flags.FlagOutput)}, + true, + func() { + ets.createBond() + }, + }, + } + + for _, tc := range testCases { + ets.Run(fmt.Sprintf("Case %s", tc.name), func() { + clientCtx := val.ClientCtx + if tc.createBond { + tc.preRun() + } + + cmd := cli.GetQueryBondList() + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + sr.NoError(err) + var queryResponse bondtypes.QueryGetBondsResponse + err = clientCtx.Codec.UnmarshalJSON(out.Bytes(), &queryResponse) + sr.NoError(err) + sr.NotZero(len(queryResponse.GetBonds())) + }) + } +} diff --git a/tests/e2e/bond/suite.go b/tests/e2e/bond/suite.go new file mode 100644 index 00000000..67f435d9 --- /dev/null +++ b/tests/e2e/bond/suite.go @@ -0,0 +1,123 @@ +package bond + +import ( + "fmt" + + "cosmossdk.io/math" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client/flags" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + + bondtypes "git.vdb.to/cerc-io/laconic2d/x/bond" + "git.vdb.to/cerc-io/laconic2d/x/bond/client/cli" +) + +type E2ETestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + + accountName string + accountAddress string +} + +func NewE2ETestSuite(cfg network.Config) *E2ETestSuite { + return &E2ETestSuite{cfg: cfg} +} + +func (ets *E2ETestSuite) SetupSuite() { //nolint: all + sr := ets.Require() + ets.T().Log("setting up e2e test suite") + + var err error + + ets.network, err = network.New(ets.T(), ets.T().TempDir(), ets.cfg) + sr.NoError(err) + + _, err = ets.network.WaitForHeight(1) + sr.NoError(err) + + // setting up random account + ets.accountName = "accountName" + ets.createAccountWithBalance(ets.accountName, &ets.accountAddress) +} + +func (ets *E2ETestSuite) TearDownSuite() { + ets.T().Log("tearing down e2e test suite") + ets.network.Cleanup() +} + +func (ets *E2ETestSuite) createAccountWithBalance(accountName string, accountAddress *string) { + val := ets.network.Validators[0] + sr := ets.Require() + + info, _, err := val.ClientCtx.Keyring.NewMnemonic(accountName, keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + sr.NoError(err) + + newAddr, _ := info.GetAddress() + _, err = clitestutil.MsgSendExec( + val.ClientCtx, + val.Address, + newAddr, + sdk.NewCoins(sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(200000))), + addresscodec.NewBech32Codec("laconic"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(10))).String()), + ) + sr.NoError(err) + *accountAddress = newAddr.String() + + // wait for tx to take effect + err = ets.network.WaitForNextBlock() + sr.NoError(err) +} + +func (ets *E2ETestSuite) createBond() string { + val := ets.network.Validators[0] + sr := ets.Require() + createBondCmd := cli.NewCreateBondCmd() + args := []string{ + fmt.Sprintf("10%s", ets.cfg.BondDenom), + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + } + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, createBondCmd, args) + sr.NoError(err) + var d sdk.TxResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.NoError(err) + sr.Zero(d.Code) + + // wait for tx to take effect + err = ets.network.WaitForNextBlock() + sr.NoError(err) + + // getting the bonds list and returning the bond-id + clientCtx := val.ClientCtx + cmd := cli.GetQueryBondList() + args = []string{ + fmt.Sprintf("--%s=json", flags.FlagOutput), + } + out, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + sr.NoError(err) + var queryResponse bondtypes.QueryGetBondsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &queryResponse) + sr.NoError(err) + + // extract bond id from bonds list + bond := queryResponse.GetBonds()[0] + return bond.GetId() +} diff --git a/tests/e2e/bond/tx.go b/tests/e2e/bond/tx.go new file mode 100644 index 00000000..edb3a031 --- /dev/null +++ b/tests/e2e/bond/tx.go @@ -0,0 +1,63 @@ +package bond + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + + "git.vdb.to/cerc-io/laconic2d/x/bond/client/cli" +) + +func (ets *E2ETestSuite) TestTxCreateBond() { + val := ets.network.Validators[0] + sr := ets.Require() + + testCases := []struct { + name string + args []string + err bool + }{ + { + "without deposit", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + }, + true, + }, + { + "create bond", + []string{ + fmt.Sprintf("10%s", ets.cfg.BondDenom), + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + }, + false, + }, + } + + for _, tc := range testCases { + ets.Run(fmt.Sprintf("Case %s", tc.name), func() { + clientCtx := val.ClientCtx + cmd := cli.NewCreateBondCmd() + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.err { + sr.Error(err) + } else { + sr.NoError(err) + var d sdk.TxResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.Nil(err) + sr.NoError(err) + sr.Zero(d.Code) + } + }) + } +} diff --git a/tests/e2e/common.go b/tests/e2e/common.go new file mode 100644 index 00000000..7a746eb1 --- /dev/null +++ b/tests/e2e/common.go @@ -0,0 +1,67 @@ +package e2e + +import ( + "fmt" + "os" + + "cosmossdk.io/log" + pruningtypes "cosmossdk.io/store/pruning/types" + + dbm "github.com/cosmos/cosmos-db" + bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/flags" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/testutil/network" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/staking" + + laconicApp "git.vdb.to/cerc-io/laconic2d/app" + auctionmodule "git.vdb.to/cerc-io/laconic2d/x/auction/module" + bondmodule "git.vdb.to/cerc-io/laconic2d/x/bond/module" + registrymodule "git.vdb.to/cerc-io/laconic2d/x/registry/module" + + _ "git.vdb.to/cerc-io/laconic2d/app/params" // import for side-effects (see init) +) + +// NewTestNetworkFixture returns a new LaconicApp AppConstructor for network simulation tests +func NewTestNetworkFixture() network.TestFixture { + dir, err := os.MkdirTemp("", "laconic") + if err != nil { + panic(fmt.Sprintf("failed creating temporary directory: %v", err)) + } + defer os.RemoveAll(dir) + + app, err := laconicApp.NewLaconicApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(dir)) + if err != nil { + panic(fmt.Sprintf("failed to create laconic app: %v", err)) + } + + appCtr := func(val network.ValidatorI) servertypes.Application { + app, err := laconicApp.NewLaconicApp( + val.GetCtx().Logger, dbm.NewMemDB(), nil, true, + simtestutil.NewAppOptionsWithFlagHome(val.GetCtx().Config.RootDir), + bam.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), + bam.SetMinGasPrices(val.GetAppConfig().MinGasPrices), + bam.SetChainID(val.GetCtx().Viper.GetString(flags.FlagChainID)), + ) + if err != nil { + panic(fmt.Sprintf("failed creating temporary directory: %v", err)) + } + + return app + } + + return network.TestFixture{ + AppConstructor: appCtr, + GenesisState: app.DefaultGenesis(), + EncodingConfig: testutil.MakeTestEncodingConfig( + auth.AppModuleBasic{}, + staking.AppModuleBasic{}, + auctionmodule.AppModule{}, + bondmodule.AppModule{}, + registrymodule.AppModule{}, + ), + } +} diff --git a/tests/e2e/registry/cli_test.go b/tests/e2e/registry/cli_test.go new file mode 100644 index 00000000..198962fa --- /dev/null +++ b/tests/e2e/registry/cli_test.go @@ -0,0 +1,17 @@ +package registry + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/stretchr/testify/suite" + + "git.vdb.to/cerc-io/laconic2d/tests/e2e" +) + +func TestRegistryE2ETestSuite(t *testing.T) { + cfg := network.DefaultConfig(e2e.NewTestNetworkFixture) + cfg.NumValidators = 1 + + suite.Run(t, NewE2ETestSuite(cfg)) +} diff --git a/tests/e2e/registry/grpc.go b/tests/e2e/registry/grpc.go new file mode 100644 index 00000000..f050b05a --- /dev/null +++ b/tests/e2e/registry/grpc.go @@ -0,0 +1,429 @@ +package registry + +import ( + "encoding/json" + "fmt" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + + registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry" + "git.vdb.to/cerc-io/laconic2d/x/registry/client/cli" +) + +const badPath = "/asdasd" + +func (ets *E2ETestSuite) TestGRPCQueryParams() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := val.APIAddress + "/cerc/registry/v1/params" + + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + }{ + { + "invalid request", + reqURL + badPath, + true, + "", + }, + { + "valid request", + reqURL, + false, + "", + }, + } + + for _, tc := range testCases { + ets.Run(tc.name, func() { + resp, err := testutil.GetRequest(tc.url) + ets.NoError(err) + require := ets.Require() + if tc.expectErr { + require.Contains(string(resp), tc.errorMsg) + } else { + var response registrytypes.QueryParamsResponse + err := val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + sr.NoError(err) + + params := registrytypes.DefaultParams() + ets.updateParams(¶ms) + sr.Equal(params.String(), response.GetParams().String()) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCQueryWhoIs() { + val := ets.network.Validators[0] + sr := ets.Require() + reqUrl := val.APIAddress + "/cerc/registry/v1/whois/%s" + authorityName := "QueryWhoIS" + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + preRun func(authorityName string) + }{ + { + "invalid url", + reqUrl + badPath, + true, + "", + func(authorityName string) { + }, + }, + { + "valid request", + reqUrl, + false, + "", + func(authorityName string) { ets.reserveName(authorityName) }, + }, + } + + for _, tc := range testCases { + ets.Run(tc.name, func() { + tc.preRun(authorityName) + tc.url = fmt.Sprintf(tc.url, authorityName) + + resp, err := testutil.GetRequest(tc.url) + ets.NoError(err) + require := ets.Require() + if tc.expectErr { + require.Contains(string(resp), tc.errorMsg) + } else { + var response registrytypes.QueryWhoisResponse + err := val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + sr.NoError(err) + sr.Equal(registrytypes.AuthorityActive, response.GetNameAuthority().Status) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCQueryLookup() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := val.APIAddress + "/cerc/registry/v1/lookup" + authorityName := "QueryLookUp" + + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + preRun func(authorityName string) + }{ + { + "invalid url", + reqURL + badPath, + true, + "", + func(authorityName string) { + }, + }, + { + "valid request", + fmt.Sprintf(reqURL+"?lrn=lrn://%s/", authorityName), + false, + "", + func(authorityName string) { + // create name record + ets.createNameRecord(authorityName) + }, + }, + } + + for _, tc := range testCases { + ets.Run(tc.name, func() { + tc.preRun(authorityName) + resp, err := testutil.GetRequest(tc.url) + ets.NoError(err) + if tc.expectErr { + sr.Contains(string(resp), tc.errorMsg) + } else { + var response registrytypes.QueryLookupLrnResponse + err := val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + sr.NoError(err) + sr.NotZero(len(response.Name.Latest.Id)) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCQueryListRecords() { + val := ets.network.Validators[0] + sr := ets.Require() + reqUrl := val.APIAddress + "/cerc/registry/v1/records" + + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + preRun func(bondId string) + }{ + { + "invalid url", + reqUrl + badPath, + true, + "", + func(bondId string) { + }, + }, + { + "valid request", + reqUrl, + false, + "", + func(bondId string) { ets.createRecord(bondId) }, + }, + } + + for _, tc := range testCases { + ets.Run(tc.name, func() { + tc.preRun(ets.bondId) + resp, err := testutil.GetRequest(tc.url) + ets.NoError(err) + require := ets.Require() + if tc.expectErr { + require.Contains(string(resp), tc.errorMsg) + } else { + var response registrytypes.QueryRecordsResponse + err := val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + sr.NoError(err) + sr.NotZero(len(response.GetRecords())) + sr.Equal(ets.bondId, response.GetRecords()[0].GetBondId()) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCQueryGetRecordById() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := val.APIAddress + "/cerc/registry/v1/records/%s" + + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + preRun func(bondId string) string + }{ + { + "invalid url", + reqURL + badPath, + true, + "", + func(bondId string) string { + return "" + }, + }, + { + "valid request", + reqURL, + false, + "", + func(bondId string) string { + // creating the record + ets.createRecord(bondId) + + // list the records + clientCtx := val.ClientCtx + cmd := cli.GetCmdList() + args := []string{ + fmt.Sprintf("--%s=json", flags.FlagOutput), + } + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + sr.NoError(err) + var records []registrytypes.ReadableRecord + err = json.Unmarshal(out.Bytes(), &records) + sr.NoError(err) + return records[0].Id + }, + }, + } + + for _, tc := range testCases { + ets.Run(tc.name, func() { + recordId := tc.preRun(ets.bondId) + tc.url = fmt.Sprintf(reqURL, recordId) + + resp, err := testutil.GetRequest(tc.url) + ets.NoError(err) + require := ets.Require() + if tc.expectErr { + require.Contains(string(resp), tc.errorMsg) + } else { + var response registrytypes.QueryRecordByIdResponse + err := val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + sr.NoError(err) + record := response.GetRecord() + sr.NotZero(len(record.GetId())) + sr.Equal(record.GetId(), recordId) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCQueryGetRecordByBondId() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := val.APIAddress + "/cerc/registry/v1/records-by-bond-id/%s" + + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + preRun func(bondId string) + }{ + { + "invalid url", + reqURL + badPath, + true, + "", + func(bondId string) { + }, + }, + { + "valid request", + reqURL, + false, + "", + func(bondId string) { + // creating the record + ets.createRecord(bondId) + }, + }, + } + + for _, tc := range testCases { + ets.Run(tc.name, func() { + tc.preRun(ets.bondId) + tc.url = fmt.Sprintf(reqURL, ets.bondId) + + resp, err := testutil.GetRequest(tc.url) + ets.NoError(err) + require := ets.Require() + if tc.expectErr { + require.Contains(string(resp), tc.errorMsg) + } else { + var response registrytypes.QueryRecordsByBondIdResponse + err := val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + sr.NoError(err) + records := response.GetRecords() + sr.NotZero(len(records)) + sr.Equal(records[0].GetBondId(), ets.bondId) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCQueryGetRegistryModuleBalance() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := val.APIAddress + "/cerc/registry/v1/balance" + + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + preRun func(bondId string) + }{ + { + "invalid url", + reqURL + badPath, + true, + "", + func(bondId string) { + }, + }, + { + "Success", + reqURL, + false, + "", + func(bondId string) { + // creating the record + ets.createRecord(bondId) + }, + }, + } + + for _, tc := range testCases { + ets.Run(tc.name, func() { + tc.preRun(ets.bondId) + resp, err := testutil.GetRequest(tc.url) + ets.NoError(err) + require := ets.Require() + if tc.expectErr { + require.Contains(string(resp), tc.errorMsg) + } else { + var response registrytypes.QueryGetRegistryModuleBalanceResponse + err := val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + sr.NoError(err) + sr.NotZero(len(response.GetBalances())) + } + }) + } +} + +func (ets *E2ETestSuite) TestGRPCQueryNamesList() { + val := ets.network.Validators[0] + sr := ets.Require() + reqURL := val.APIAddress + "/cerc/registry/v1/names" + + testCases := []struct { + name string + url string + expectErr bool + errorMsg string + preRun func(authorityName string) + }{ + { + "invalid url", + reqURL + badPath, + true, + "", + func(authorityName string) { + }, + }, + { + "valid request", + reqURL, + false, + "", + func(authorityName string) { + // create name record + ets.createNameRecord(authorityName) + }, + }, + } + + for _, tc := range testCases { + ets.Run(tc.name, func() { + tc.preRun("ListNameRecords") + resp, err := testutil.GetRequest(tc.url) + ets.NoError(err) + require := ets.Require() + if tc.expectErr { + require.Contains(string(resp), tc.errorMsg) + } else { + var response registrytypes.QueryNameRecordsResponse + err := val.ClientCtx.Codec.UnmarshalJSON(resp, &response) + sr.NoError(err) + sr.NotZero(len(response.GetNames())) + } + }) + } +} diff --git a/tests/e2e/registry/suite.go b/tests/e2e/registry/suite.go new file mode 100644 index 00000000..058a5ff0 --- /dev/null +++ b/tests/e2e/registry/suite.go @@ -0,0 +1,282 @@ +package registry + +import ( + "fmt" + "path/filepath" + "time" + + "cosmossdk.io/math" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client/flags" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + + bondtypes "git.vdb.to/cerc-io/laconic2d/x/bond" + bondcli "git.vdb.to/cerc-io/laconic2d/x/bond/client/cli" + registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry" + "git.vdb.to/cerc-io/laconic2d/x/registry/client/cli" +) + +type E2ETestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + + accountName string + accountAddress string + + bondId string +} + +func NewE2ETestSuite(cfg network.Config) *E2ETestSuite { + return &E2ETestSuite{cfg: cfg} +} + +func (ets *E2ETestSuite) SetupSuite() { + sr := ets.Require() + ets.T().Log("setting up e2e test suite") + + var err error + + genesisState := ets.cfg.GenesisState + var registryGenesis registrytypes.GenesisState + ets.Require().NoError(ets.cfg.Codec.UnmarshalJSON(genesisState[registrytypes.ModuleName], ®istryGenesis)) + + ets.updateParams(®istryGenesis.Params) + + registryGenesisBz, err := ets.cfg.Codec.MarshalJSON(®istryGenesis) + ets.Require().NoError(err) + genesisState[registrytypes.ModuleName] = registryGenesisBz + ets.cfg.GenesisState = genesisState + + ets.network, err = network.New(ets.T(), ets.T().TempDir(), ets.cfg) + sr.NoError(err) + + _, err = ets.network.WaitForHeight(2) + sr.NoError(err) + + // setting up random account + ets.accountName = "accountName" + ets.createAccountWithBalance(ets.accountName, &ets.accountAddress) + + ets.bondId = ets.createBond() +} + +func (ets *E2ETestSuite) TearDownSuite() { + ets.T().Log("tearing down e2e test suite") + ets.network.Cleanup() +} + +func (ets *E2ETestSuite) createAccountWithBalance(accountName string, accountAddress *string) { + val := ets.network.Validators[0] + sr := ets.Require() + + info, _, err := val.ClientCtx.Keyring.NewMnemonic(accountName, keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + sr.NoError(err) + + newAddr, _ := info.GetAddress() + _, err = clitestutil.MsgSendExec( + val.ClientCtx, + val.Address, + newAddr, + sdk.NewCoins(sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(100000000))), + addresscodec.NewBech32Codec("laconic"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(10))).String()), + ) + sr.NoError(err) + *accountAddress = newAddr.String() + + // wait for tx to take effect + err = ets.network.WaitForNextBlock() + sr.NoError(err) +} + +func (ets *E2ETestSuite) createBond() string { + val := ets.network.Validators[0] + sr := ets.Require() + createBondCmd := bondcli.NewCreateBondCmd() + args := []string{ + fmt.Sprintf("1000000%s", ets.cfg.BondDenom), + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + } + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, createBondCmd, args) + sr.NoError(err) + var d sdk.TxResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.NoError(err) + sr.Zero(d.Code) + + // wait for tx to take effect + err = ets.network.WaitForNextBlock() + sr.NoError(err) + + // getting the bonds list and returning the bond-id + clientCtx := val.ClientCtx + cmd := bondcli.GetQueryBondList() + args = []string{ + fmt.Sprintf("--%s=json", flags.FlagOutput), + } + out, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + sr.NoError(err) + var queryResponse bondtypes.QueryGetBondsResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &queryResponse) + sr.NoError(err) + + // extract bond id from bonds list + bond := queryResponse.GetBonds()[0] + return bond.GetId() +} + +func (ets *E2ETestSuite) reserveName(authorityName string) { + val := ets.network.Validators[0] + sr := ets.Require() + + clientCtx := val.ClientCtx + cmd := cli.GetCmdReserveName() + args := []string{ + authorityName, + fmt.Sprintf("--owner=%s", ets.accountAddress), + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + } + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + sr.NoError(err) + + var d sdk.TxResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.NoError(err) + sr.Zero(d.Code) + + err = ets.network.WaitForNextBlock() + sr.NoError(err) +} + +func (ets *E2ETestSuite) createNameRecord(authorityName string) { + val := ets.network.Validators[0] + sr := ets.Require() + + // reserving the name + clientCtx := val.ClientCtx + cmd := cli.GetCmdReserveName() + args := []string{ + authorityName, + fmt.Sprintf("--owner=%s", ets.accountAddress), + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + } + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + sr.NoError(err) + var d sdk.TxResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.NoError(err) + sr.Zero(d.Code) + + err = ets.network.WaitForNextBlock() + sr.NoError(err) + + // Get the bond-id + bondId := ets.bondId + + // adding bond-id to name authority + args = []string{ + authorityName, bondId, + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + } + cmd = cli.GetCmdSetAuthorityBond() + + out, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + sr.NoError(err) + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.NoError(err) + sr.Zero(d.Code) + + err = ets.network.WaitForNextBlock() + sr.NoError(err) + + args = []string{ + fmt.Sprintf("lrn://%s/", authorityName), + "test_hello_cid", + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + } + + cmd = cli.GetCmdSetName() + + out, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + sr.NoError(err) + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.NoError(err) + sr.Zero(d.Code) + + err = ets.network.WaitForNextBlock() + sr.NoError(err) +} + +func (ets *E2ETestSuite) createRecord(bondId string) { + val := ets.network.Validators[0] + sr := ets.Require() + + payloadPath := "../../data/examples/service_provider_example.yml" + payloadFilePath, err := filepath.Abs(payloadPath) + sr.NoError(err) + + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + } + args = append([]string{payloadFilePath, bondId}, args...) + clientCtx := val.ClientCtx + cmd := cli.GetCmdSetRecord() + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + sr.NoError(err) + var d sdk.TxResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.NoError(err) + sr.Zero(d.Code, d.RawLog) + + err = ets.network.WaitForNextBlock() + sr.NoError(err) +} + +func (ets *E2ETestSuite) updateParams(params *registrytypes.Params) { + params.RecordRent = sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(1000)) + params.RecordRentDuration = 10 * time.Second + + params.AuthorityRent = sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(1000)) + params.AuthorityGracePeriod = 10 * time.Second + + params.AuthorityAuctionCommitFee = sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(100)) + params.AuthorityAuctionRevealFee = sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(100)) + params.AuthorityAuctionMinimumBid = sdk.NewCoin(ets.cfg.BondDenom, math.NewInt(500)) +} diff --git a/tests/e2e/registry/tx.go b/tests/e2e/registry/tx.go new file mode 100644 index 00000000..1267728a --- /dev/null +++ b/tests/e2e/registry/tx.go @@ -0,0 +1,70 @@ +package registry + +import ( + "fmt" + "path/filepath" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + + "git.vdb.to/cerc-io/laconic2d/x/registry/client/cli" +) + +func (ets *E2ETestSuite) TestGetCmdSetRecord() { + val := ets.network.Validators[0] + sr := ets.Require() + + bondId := ets.bondId + payloadPath := "../../data/examples/service_provider_example.yml" + payloadFilePath, err := filepath.Abs(payloadPath) + sr.NoError(err) + + testCases := []struct { + name string + args []string + err bool + }{ + { + "invalid request without bond id/without payload", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + }, + true, + }, + { + "success", + []string{ + payloadFilePath, bondId, + fmt.Sprintf("--%s=%s", flags.FlagFrom, ets.accountName), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=json", flags.FlagOutput), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, fmt.Sprintf("3%s", ets.cfg.BondDenom)), + }, + false, + }, + } + + for _, tc := range testCases { + ets.Run(fmt.Sprintf("Case %s", tc.name), func() { + clientCtx := val.ClientCtx + cmd := cli.GetCmdSetRecord() + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.err { + sr.Error(err) + } else { + sr.NoError(err) + var d sdk.TxResponse + err = val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &d) + sr.NoError(err) + sr.Zero(d.Code) + } + }) + } +} diff --git a/tests/integration/registry/keeper/query_server_test.go b/tests/integration/registry/keeper/query_server_test.go index 0c527dd8..ee4294e3 100644 --- a/tests/integration/registry/keeper/query_server_test.go +++ b/tests/integration/registry/keeper/query_server_test.go @@ -37,9 +37,9 @@ func (kts *KeeperTestSuite) TestGrpcGetRecordLists() { var recordId string examples := []string{ - "../../data/examples/service_provider_example.yml", - "../../data/examples/website_registration_example.yml", - "../../data/examples/general_record_example.yml", + "../../../data/examples/service_provider_example.yml", + "../../../data/examples/website_registration_example.yml", + "../../../data/examples/general_record_example.yml", } testCases := []struct { msg string @@ -268,7 +268,7 @@ func (kts *KeeperTestSuite) TestGrpcGetRecordLists() { } // Get the records by record id - testCasesByBondID := []struct { + testCasesByBondId := []struct { msg string req *types.QueryRecordsByBondIdRequest createRecord bool @@ -292,7 +292,7 @@ func (kts *KeeperTestSuite) TestGrpcGetRecordLists() { 1, }, } - for _, test := range testCasesByBondID { + for _, test := range testCasesByBondId { kts.Run(fmt.Sprintf("Case %s ", test.msg), func() { resp, err := queryClient.GetRecordsByBondId(context.Background(), test.req) @@ -314,8 +314,8 @@ func (kts *KeeperTestSuite) TestGrpcQueryRegistryModuleBalance() { queryClient, ctx := kts.queryClient, kts.SdkCtx sr := kts.Require() examples := []string{ - "../../data/examples/service_provider_example.yml", - "../../data/examples/website_registration_example.yml", + "../../../data/examples/service_provider_example.yml", + "../../../data/examples/website_registration_example.yml", } testCases := []struct { msg string diff --git a/x/auction/client/cli/query.go b/x/auction/client/cli/query.go new file mode 100644 index 00000000..6cfa10a4 --- /dev/null +++ b/x/auction/client/cli/query.go @@ -0,0 +1,35 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + + types "git.vdb.to/cerc-io/laconic2d/x/auction" +) + +// GetCmdList queries all auctions. +func GetCmdList() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List auctions.", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.Auctions(cmd.Context(), &types.QueryAuctionsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/auction/client/cli/tx.go b/x/auction/client/cli/tx.go index 90e98030..4c75a403 100644 --- a/x/auction/client/cli/tx.go +++ b/x/auction/client/cli/tx.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "fmt" "os" + "time" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -128,3 +129,60 @@ func GetCmdRevealBid() *cobra.Command { return cmd } + +func GetCmdCreateAuction() *cobra.Command { + cmd := &cobra.Command{ + Use: "create [commits-duration] [reveals-duration] [commit-fee] [reveal-fee] [minimum-bid]", + Short: "Create auction.", + Args: cobra.ExactArgs(5), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + commitsDuration, err := time.ParseDuration(args[0]) + if err != nil { + return err + } + + revealsDuration, err := time.ParseDuration(args[1]) + if err != nil { + return err + } + + commitFee, err := sdk.ParseCoinNormalized(args[2]) + if err != nil { + return err + } + + revealFee, err := sdk.ParseCoinNormalized(args[3]) + if err != nil { + return err + } + + minimumBid, err := sdk.ParseCoinNormalized(args[4]) + if err != nil { + return err + } + + params := auctiontypes.Params{ + CommitsDuration: commitsDuration, + RevealsDuration: revealsDuration, + CommitFee: commitFee, + RevealFee: revealFee, + MinimumBid: minimumBid, + } + msg := auctiontypes.NewMsgCreateAuction(params, clientCtx.GetFromAddress()) + err = msg.ValidateBasic() + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/auction/msgs.go b/x/auction/msgs.go index 6aa4f798..a21427fc 100644 --- a/x/auction/msgs.go +++ b/x/auction/msgs.go @@ -33,6 +33,27 @@ func NewMsgCommitBid(auctionId string, commitHash string, signer sdk.AccAddress) } } +// ValidateBasic Implements Msg. +func (msg MsgCreateAuction) ValidateBasic() error { + if msg.Signer == "" { + return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer) + } + + if msg.CommitsDuration <= 0 { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "commit phase duration invalid.") + } + + if msg.RevealsDuration <= 0 { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "reveal phase duration invalid.") + } + + if !msg.MinimumBid.IsPositive() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "minimum bid should be greater than zero.") + } + + return nil +} + // ValidateBasic Implements Msg. func (msg MsgCommitBid) ValidateBasic() error { if msg.Signer == "" { diff --git a/x/bond/client/cli/query.go b/x/bond/client/cli/query.go new file mode 100644 index 00000000..019c7d05 --- /dev/null +++ b/x/bond/client/cli/query.go @@ -0,0 +1,48 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + + bondtypes "git.vdb.to/cerc-io/laconic2d/x/bond" +) + +// GetQueryBondList implements the bond lists query command. +func GetQueryBondList() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List bonds.", + Long: strings.TrimSpace( + fmt.Sprintf(`Get bond list . + +Example: +$ %s query %s list +`, + version.AppName, bondtypes.ModuleName, + ), + ), + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := bondtypes.NewQueryClient(clientCtx) + res, err := queryClient.Bonds(cmd.Context(), &bondtypes.QueryGetBondsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/bond/client/cli/tx.go b/x/bond/client/cli/tx.go new file mode 100644 index 00000000..d93f8efb --- /dev/null +++ b/x/bond/client/cli/tx.go @@ -0,0 +1,38 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/cobra" + + bondtypes "git.vdb.to/cerc-io/laconic2d/x/bond" +) + +// NewCreateBondCmd is the CLI command for creating a bond. +// Used in e2e tests +func NewCreateBondCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create [amount]", + Short: "Create bond.", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + coin, err := sdk.ParseCoinNormalized(args[0]) + if err != nil { + return err + } + + msg := bondtypes.NewMsgCreateBond(sdk.NewCoins(coin), clientCtx.GetFromAddress()) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/bond/genesis.pb.go b/x/bond/genesis.pb.go index 3b841b38..8d55cf9e 100644 --- a/x/bond/genesis.pb.go +++ b/x/bond/genesis.pb.go @@ -28,7 +28,6 @@ type GenesisState struct { // params defines all the parameters of the module. Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` // bonds defines all the bonds - // TODO: Add nullable = false ? Bonds []*Bond `protobuf:"bytes,2,rep,name=bonds,proto3" json:"bonds,omitempty" json:"bonds" yaml:"bonds"` } diff --git a/x/bond/keeper/msg_server.go b/x/bond/keeper/msg_server.go index 2a28cb67..719f17b4 100644 --- a/x/bond/keeper/msg_server.go +++ b/x/bond/keeper/msg_server.go @@ -26,7 +26,8 @@ func (ms msgServer) CreateBond(c context.Context, msg *bond.MsgCreateBond) (*bon if err != nil { return nil, err } - _, err = ms.k.CreateBond(ctx, signerAddress, msg.Coins) + + resp, err := ms.k.CreateBond(ctx, signerAddress, msg.Coins) if err != nil { return nil, err } @@ -44,7 +45,7 @@ func (ms msgServer) CreateBond(c context.Context, msg *bond.MsgCreateBond) (*bon ), }) - return &bond.MsgCreateBondResponse{}, nil + return &bond.MsgCreateBondResponse{Id: resp.Id}, nil } // RefillBond implements bond.MsgServer. diff --git a/x/bond/msgs.go b/x/bond/msgs.go new file mode 100644 index 00000000..c473c612 --- /dev/null +++ b/x/bond/msgs.go @@ -0,0 +1,17 @@ +package bond + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + _ sdk.Msg = &MsgCreateBond{} +) + +// NewMsgCreateBond is the constructor function for MsgCreateBond. +func NewMsgCreateBond(coins sdk.Coins, signer sdk.AccAddress) MsgCreateBond { + return MsgCreateBond{ + Coins: coins, + Signer: signer.String(), + } +} diff --git a/x/registry/client/cli/query.go b/x/registry/client/cli/query.go new file mode 100644 index 00000000..c13564a2 --- /dev/null +++ b/x/registry/client/cli/query.go @@ -0,0 +1,58 @@ +package cli + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + + registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry" +) + +// GetCmdList queries all records. +func GetCmdList() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List records.", + Long: strings.TrimSpace( + fmt.Sprintf(`Get the records. +Example: +$ %s query %s list +`, + version.AppName, registrytypes.ModuleName, + ), + ), + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := registrytypes.NewQueryClient(clientCtx) + res, err := queryClient.Records(cmd.Context(), ®istrytypes.QueryRecordsRequest{}) + if err != nil { + return err + } + + recordsList := res.GetRecords() + records := make([]registrytypes.ReadableRecord, len(recordsList)) + for i, record := range res.GetRecords() { + records[i] = record.ToReadableRecord() + } + bytesResult, err := json.Marshal(records) + if err != nil { + return err + } + return clientCtx.PrintBytes(bytesResult) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/registry/client/cli/tx.go b/x/registry/client/cli/tx.go index 168aeab7..2c645d74 100644 --- a/x/registry/client/cli/tx.go +++ b/x/registry/client/cli/tx.go @@ -1,11 +1,15 @@ package cli import ( + "fmt" "os" + "strings" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" "gopkg.in/yaml.v3" "github.com/spf13/cobra" @@ -80,3 +84,111 @@ func GetPayloadFromFile(filePath string) (*registrytypes.ReadablePayload, error) return &payload, nil } + +// GetCmdReserveName is the CLI command for reserving a name. +func GetCmdReserveName() *cobra.Command { + cmd := &cobra.Command{ + Use: "reserve-name [name]", + Short: "Reserve name.", + Long: strings.TrimSpace( + fmt.Sprintf(`Reserver name with owner address . +Example: +$ %s tx %s reserve-name [name] --owner [ownerAddress] +`, + version.AppName, registrytypes.ModuleName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + owner, err := cmd.Flags().GetString("owner") + if err != nil { + return err + } + ownerAddress, err := sdk.AccAddressFromBech32(owner) + if err != nil { + return err + } + + msg := registrytypes.NewMsgReserveAuthority(args[0], clientCtx.GetFromAddress(), ownerAddress) + err = msg.ValidateBasic() + if err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + cmd.Flags().String("owner", "", "Owner address, if creating a sub-authority.") + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetCmdSetAuthorityBond is the CLI command for associating a bond with an authority. +func GetCmdSetAuthorityBond() *cobra.Command { + cmd := &cobra.Command{ + Use: "authority-bond [name] [bond-id]", + Short: "Associate authority with bond.", + Long: strings.TrimSpace( + fmt.Sprintf(`Reserver name with owner address . +Example: +$ %s tx %s authority-bond [name] [bond-id] +`, + version.AppName, registrytypes.ModuleName, + ), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + msg := registrytypes.NewMsgSetAuthorityBond(args[0], args[1], clientCtx.GetFromAddress()) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// GetCmdSetName is the CLI command for mapping a name to a CID. +func GetCmdSetName() *cobra.Command { + cmd := &cobra.Command{ + Use: "set-name [crn] [cid]", + Short: "Set CRN to CID mapping.", + Long: strings.TrimSpace( + fmt.Sprintf(`Set name with crn and cid. +Example: +$ %s tx %s set-name [crn] [cid] +`, + version.AppName, registrytypes.ModuleName, + ), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := registrytypes.NewMsgSetName(args[0], args[1], clientCtx.GetFromAddress()) + err = msg.ValidateBasic() + if err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/registry/msgs.go b/x/registry/msgs.go index 7a68ded3..93f65743 100644 --- a/x/registry/msgs.go +++ b/x/registry/msgs.go @@ -33,3 +33,60 @@ func (msg MsgSetRecord) ValidateBasic() error { return nil } + +// NewMsgReserveAuthority is the constructor function for MsgReserveName. +func NewMsgReserveAuthority(name string, signer sdk.AccAddress, owner sdk.AccAddress) MsgReserveAuthority { + return MsgReserveAuthority{ + Name: name, + Owner: owner.String(), + Signer: signer.String(), + } +} + +// ValidateBasic Implements Msg. +func (msg MsgReserveAuthority) ValidateBasic() error { + if len(msg.Name) == 0 { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "name is required.") + } + + if len(msg.Signer) == 0 { + return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer") + } + + return nil +} + +// NewMsgSetAuthorityBond is the constructor function for MsgSetAuthorityBond. +func NewMsgSetAuthorityBond(name string, bondID string, signer sdk.AccAddress) MsgSetAuthorityBond { + return MsgSetAuthorityBond{ + Name: name, + Signer: signer.String(), + BondId: bondID, + } +} + +// NewMsgSetName is the constructor function for MsgSetName. +func NewMsgSetName(lrn string, cid string, signer sdk.AccAddress) *MsgSetName { + return &MsgSetName{ + Lrn: lrn, + Cid: cid, + Signer: signer.String(), + } +} + +// ValidateBasic Implements Msg. +func (msg MsgSetName) ValidateBasic() error { + if msg.Lrn == "" { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "LRN is required.") + } + + if msg.Cid == "" { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "CID is required.") + } + + if len(msg.Signer) == 0 { + return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid signer") + } + + return nil +}