forked from cerc-io/laconicd
Integrate go-nitro
- Add nitro server and x/nitro module
- wire go-nitro p2p through cometbft
- Add distsig server, currently WIP
- integrate DKG and DSS schemes into ABCI methods
- Remove deprecated features
- crisis module
- module invariants
- Update to use newer SDK patterns
- upgrade sdk to v0.53.x
- custom address codec
- expand use of depinject
- migrate e2e tests to system tests
- use depinject to set up integration tests
- change reserved protobuf field name `cerc.registry.v1.Record.type`
- Revise & add documentation
- TransferCoinsToModuleAccount: clarify function
- Update init.sh script
This commit is contained in:
1 parent
a8ebc0aceb
commit
5aa2594073
179 files changed
+15631
-3058
No files matched your search
@@ -3,7 +3,6 @@ package keeper_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
integrationTest "git.vdb.to/cerc-io/laconicd/tests/integration"
|
||||
@@ -19,7 +18,7 @@ type KeeperTestSuite struct {
|
||||
|
||||
func (kts *KeeperTestSuite) SetupTest() {
|
||||
err := kts.TestFixture.Setup()
|
||||
assert.Nil(kts.T(), err)
|
||||
kts.Require().NoError(err)
|
||||
|
||||
qr := kts.App.QueryHelper()
|
||||
kts.queryClient = types.NewQueryClient(qr)
|
||||
|
||||
@@ -30,7 +30,7 @@ func (kts *KeeperTestSuite) TestGrpcQueryParams() {
|
||||
for _, test := range testCases {
|
||||
kts.Run(fmt.Sprintf("Case %s", test.msg), func() {
|
||||
resp, err := kts.queryClient.Params(context.Background(), test.req)
|
||||
kts.Require().Nil(err)
|
||||
kts.Require().NoError(err)
|
||||
kts.Require().Equal(*(resp.Params), types.DefaultParams())
|
||||
})
|
||||
}
|
||||
@@ -59,14 +59,14 @@ func (kts *KeeperTestSuite) TestGrpcGetAuction() {
|
||||
var expectedAuction types.Auction
|
||||
if test.createAuction {
|
||||
auction, _, err := kts.createAuctionAndCommitBid(false)
|
||||
kts.Require().Nil(err)
|
||||
kts.Require().NoError(err)
|
||||
test.req.Id = auction.Id
|
||||
expectedAuction = *auction
|
||||
}
|
||||
|
||||
resp, err := kts.queryClient.GetAuction(context.Background(), test.req)
|
||||
if test.createAuction {
|
||||
kts.Require().Nil(err)
|
||||
kts.Require().NoError(err)
|
||||
kts.Require().NotNil(resp.GetAuction())
|
||||
kts.Require().EqualExportedValues(expectedAuction, *(resp.GetAuction()))
|
||||
} else {
|
||||
@@ -103,7 +103,7 @@ func (kts *KeeperTestSuite) TestGrpcGetAllAuctions() {
|
||||
kts.Run(fmt.Sprintf("Case %s", test.msg), func() {
|
||||
if test.createAuctions {
|
||||
_, _, err := kts.createAuctionAndCommitBid(false)
|
||||
kts.Require().Nil(err)
|
||||
kts.Require().NoError(err)
|
||||
}
|
||||
|
||||
resp, _ := kts.queryClient.Auctions(context.Background(), test.req)
|
||||
@@ -153,7 +153,7 @@ func (kts *KeeperTestSuite) TestGrpcGetBids() {
|
||||
|
||||
resp, err := kts.queryClient.GetBids(context.Background(), test.req)
|
||||
if test.createAuction {
|
||||
kts.Require().Nil(err)
|
||||
kts.Require().NoError(err)
|
||||
kts.Require().Equal(test.bidCount, len(resp.GetBids()))
|
||||
} else {
|
||||
kts.Require().NotNil(err)
|
||||
|
||||
@@ -3,7 +3,6 @@ package keeper_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
integrationTest "git.vdb.to/cerc-io/laconicd/tests/integration"
|
||||
@@ -19,7 +18,7 @@ type KeeperTestSuite struct {
|
||||
|
||||
func (kts *KeeperTestSuite) SetupTest() {
|
||||
err := kts.TestFixture.Setup()
|
||||
assert.Nil(kts.T(), err)
|
||||
kts.Require().NoError(err)
|
||||
|
||||
qr := kts.App.QueryHelper()
|
||||
kts.queryClient = types.NewQueryClient(qr)
|
||||
|
||||
@@ -26,7 +26,7 @@ func (kts *KeeperTestSuite) TestGrpcQueryParams() {
|
||||
for _, test := range testCases {
|
||||
kts.Run(fmt.Sprintf("Case %s", test.msg), func() {
|
||||
resp, err := kts.queryClient.Params(context.Background(), test.req)
|
||||
kts.Require().Nil(err)
|
||||
kts.Require().NoError(err)
|
||||
kts.Require().Equal(*(resp.Params), types.DefaultParams())
|
||||
})
|
||||
}
|
||||
@@ -101,7 +101,7 @@ func (kts *KeeperTestSuite) TestGrpcQueryBondByBondId() {
|
||||
}
|
||||
resp, err := kts.queryClient.GetBondById(context.Background(), test.req)
|
||||
if !test.errResponse {
|
||||
kts.Require().Nil(err)
|
||||
kts.Require().NoError(err)
|
||||
kts.Require().NotNil(resp.GetBond())
|
||||
kts.Require().Equal(test.req.Id, resp.GetBond().GetId())
|
||||
} else {
|
||||
@@ -148,7 +148,7 @@ func (kts *KeeperTestSuite) TestGrpcGetBondsByOwner() {
|
||||
}
|
||||
resp, err := kts.queryClient.GetBondsByOwner(context.Background(), test.req)
|
||||
if !test.errResponse {
|
||||
kts.Require().Nil(err)
|
||||
kts.Require().NoError(err)
|
||||
kts.Require().NotNil(resp.GetBonds())
|
||||
kts.Require().Equal(test.noOfBonds, len(resp.GetBonds()))
|
||||
} else {
|
||||
@@ -184,7 +184,7 @@ func (kts *KeeperTestSuite) TestGrpcGetModuleBalance() {
|
||||
}
|
||||
resp, err := kts.queryClient.GetBondModuleBalance(context.Background(), test.req)
|
||||
if !test.errResponse {
|
||||
kts.Require().Nil(err)
|
||||
kts.Require().NoError(err)
|
||||
kts.Require().NotNil(resp.GetBalance())
|
||||
kts.Require().Equal(resp.GetBalance(), sdk.NewCoins(sdk.NewCoin(params.CoinUnit, math.NewInt(10))))
|
||||
} else {
|
||||
|
||||
+152
-98
@@ -3,17 +3,20 @@ package integration_test
|
||||
import (
|
||||
"context"
|
||||
|
||||
runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1"
|
||||
appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
|
||||
authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1"
|
||||
bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1"
|
||||
"cosmossdk.io/core/appconfig"
|
||||
"cosmossdk.io/core/appmodule"
|
||||
"cosmossdk.io/depinject"
|
||||
"cosmossdk.io/log"
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
cmtprototypes "github.com/cometbft/cometbft/proto/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
|
||||
"github.com/cosmos/cosmos-sdk/runtime"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/integration"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
||||
authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation"
|
||||
@@ -21,16 +24,22 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
||||
|
||||
auctionmodulev1 "git.vdb.to/cerc-io/laconicd/api/cerc/auction/module/v1"
|
||||
bondmodulev1 "git.vdb.to/cerc-io/laconicd/api/cerc/bond/module/v1"
|
||||
registrymodulev1 "git.vdb.to/cerc-io/laconicd/api/cerc/registry/module/v1"
|
||||
"git.vdb.to/cerc-io/laconicd/app/params"
|
||||
auctionTypes "git.vdb.to/cerc-io/laconicd/x/auction"
|
||||
"git.vdb.to/cerc-io/laconicd/server"
|
||||
"git.vdb.to/cerc-io/laconicd/utils"
|
||||
auctiontypes "git.vdb.to/cerc-io/laconicd/x/auction"
|
||||
auctionkeeper "git.vdb.to/cerc-io/laconicd/x/auction/keeper"
|
||||
auctionmodule "git.vdb.to/cerc-io/laconicd/x/auction/module"
|
||||
bondTypes "git.vdb.to/cerc-io/laconicd/x/bond"
|
||||
bondtypes "git.vdb.to/cerc-io/laconicd/x/bond"
|
||||
bondkeeper "git.vdb.to/cerc-io/laconicd/x/bond/keeper"
|
||||
bondmodule "git.vdb.to/cerc-io/laconicd/x/bond/module"
|
||||
registryTypes "git.vdb.to/cerc-io/laconicd/x/registry"
|
||||
registrytypes "git.vdb.to/cerc-io/laconicd/x/registry"
|
||||
registrykeeper "git.vdb.to/cerc-io/laconicd/x/registry/keeper"
|
||||
registrymodule "git.vdb.to/cerc-io/laconicd/x/registry/module"
|
||||
)
|
||||
@@ -51,108 +60,136 @@ type TestFixture struct {
|
||||
}
|
||||
|
||||
func (tf *TestFixture) Setup() error {
|
||||
keys := storetypes.NewKVStoreKeys(
|
||||
authtypes.StoreKey, banktypes.StoreKey, auctionTypes.StoreKey, bondTypes.StoreKey, registryTypes.StoreKey,
|
||||
logger := log.NewNopLogger()
|
||||
authority := authtypes.NewModuleAddress(govtypes.ModuleName)
|
||||
|
||||
moduleAccPerms := []*authmodulev1.ModuleAccountPermission{
|
||||
{Account: minttypes.ModuleName, Permissions: []string{authtypes.Minter}},
|
||||
{Account: auctiontypes.ModuleName},
|
||||
{Account: auctiontypes.AuctionBurnModuleAccountName},
|
||||
{Account: bondtypes.ModuleName},
|
||||
{Account: registrytypes.ModuleName},
|
||||
{Account: registrytypes.RecordRentModuleAccountName},
|
||||
{Account: registrytypes.AuthorityRentModuleAccountName},
|
||||
}
|
||||
|
||||
moduleConfigs := []*appv1alpha1.ModuleConfig{
|
||||
{
|
||||
Name: runtime.ModuleName,
|
||||
Config: appconfig.WrapAny(&runtimev1alpha1.Module{
|
||||
AppName: "TestApp",
|
||||
BeginBlockers: []string{
|
||||
authtypes.ModuleName,
|
||||
banktypes.ModuleName,
|
||||
auctiontypes.ModuleName,
|
||||
bondtypes.ModuleName,
|
||||
registrytypes.ModuleName,
|
||||
},
|
||||
EndBlockers: []string{
|
||||
auctiontypes.ModuleName,
|
||||
bondtypes.ModuleName,
|
||||
registrytypes.ModuleName,
|
||||
banktypes.ModuleName,
|
||||
authtypes.ModuleName,
|
||||
},
|
||||
InitGenesis: []string{
|
||||
authtypes.ModuleName,
|
||||
banktypes.ModuleName,
|
||||
auctiontypes.ModuleName,
|
||||
bondtypes.ModuleName,
|
||||
registrytypes.ModuleName,
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: authtypes.ModuleName,
|
||||
Config: appconfig.WrapAny(&authmodulev1.Module{
|
||||
Bech32Prefix: params.Bech32PrefixAccAddr,
|
||||
ModuleAccountPermissions: moduleAccPerms,
|
||||
EnableUnorderedTransactions: true,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: banktypes.ModuleName,
|
||||
Config: appconfig.WrapAny(&bankmodulev1.Module{}),
|
||||
},
|
||||
{
|
||||
Name: auctiontypes.ModuleName,
|
||||
Config: appconfig.WrapAny(&auctionmodulev1.Module{}),
|
||||
},
|
||||
{
|
||||
Name: bondtypes.ModuleName,
|
||||
Config: appconfig.WrapAny(&bondmodulev1.Module{}),
|
||||
},
|
||||
{
|
||||
Name: registrytypes.ModuleName,
|
||||
Config: appconfig.WrapAny(®istrymodulev1.Module{}),
|
||||
},
|
||||
}
|
||||
|
||||
storeKeys := StoreKeyExtractor{}
|
||||
appConfig := depinject.Configs(
|
||||
appconfig.Compose(&appv1alpha1.Config{
|
||||
Modules: moduleConfigs,
|
||||
}),
|
||||
depinject.Supply(
|
||||
logger,
|
||||
authority,
|
||||
utils.NewAddressCodec(),
|
||||
),
|
||||
depinject.Provide(
|
||||
server.NewGasService,
|
||||
),
|
||||
storeKeys.config(),
|
||||
)
|
||||
cdc := moduletestutil.MakeTestEncodingConfig(
|
||||
auth.AppModuleBasic{},
|
||||
auctionmodule.AppModule{},
|
||||
bondmodule.AppModule{},
|
||||
registrymodule.AppModule{},
|
||||
).Codec
|
||||
|
||||
logger := log.NewNopLogger() // Use log.NewTestLogger(kts.T()) for help with debugging
|
||||
cms := integration.CreateMultiStore(keys, logger)
|
||||
var (
|
||||
appBuilder *runtime.AppBuilder
|
||||
cdc codec.Codec
|
||||
accountKeeper authkeeper.AccountKeeper
|
||||
bankKeeper bankkeeper.Keeper
|
||||
auctionKeeper *auctionkeeper.Keeper
|
||||
bondKeeper *bondkeeper.Keeper
|
||||
registryKeeper registrykeeper.Keeper
|
||||
)
|
||||
if err := depinject.Inject(
|
||||
appConfig,
|
||||
&appBuilder,
|
||||
&cdc,
|
||||
&accountKeeper,
|
||||
&bankKeeper,
|
||||
&auctionKeeper,
|
||||
&bondKeeper,
|
||||
®istryKeeper,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cms := integration.CreateMultiStore(storeKeys, logger)
|
||||
newCtx := sdk.NewContext(cms, cmtprototypes.Header{}, true, logger)
|
||||
|
||||
authority := authtypes.NewModuleAddress("gov")
|
||||
|
||||
maccPerms := map[string][]string{
|
||||
minttypes.ModuleName: {authtypes.Minter},
|
||||
auctionTypes.ModuleName: {},
|
||||
auctionTypes.AuctionBurnModuleAccountName: {},
|
||||
bondTypes.ModuleName: {},
|
||||
registryTypes.ModuleName: {},
|
||||
registryTypes.RecordRentModuleAccountName: {},
|
||||
registryTypes.AuthorityRentModuleAccountName: {},
|
||||
modules := map[string]appmodule.AppModule{
|
||||
authtypes.ModuleName: auth.NewAppModule(cdc, accountKeeper, authsims.RandomGenesisAccounts, nil),
|
||||
banktypes.ModuleName: bank.NewAppModule(cdc, bankKeeper, accountKeeper, nil),
|
||||
auctiontypes.ModuleName: auctionmodule.NewAppModule(cdc, auctionKeeper),
|
||||
bondtypes.ModuleName: bondmodule.NewAppModule(cdc, bondKeeper),
|
||||
registrytypes.ModuleName: registrymodule.NewAppModule(cdc, registryKeeper),
|
||||
}
|
||||
|
||||
accountKeeper := authkeeper.NewAccountKeeper(
|
||||
cdc,
|
||||
runtime.NewKVStoreService(keys[authtypes.StoreKey]),
|
||||
authtypes.ProtoBaseAccount,
|
||||
maccPerms,
|
||||
addresscodec.NewBech32Codec(params.Bech32PrefixAccAddr),
|
||||
params.Bech32PrefixAccAddr,
|
||||
authority.String(),
|
||||
)
|
||||
|
||||
blockedAddresses := map[string]bool{
|
||||
accountKeeper.GetAuthority(): false,
|
||||
}
|
||||
bankKeeper := bankkeeper.NewBaseKeeper(
|
||||
cdc,
|
||||
runtime.NewKVStoreService(keys[banktypes.StoreKey]),
|
||||
accountKeeper,
|
||||
blockedAddresses,
|
||||
authority.String(),
|
||||
log.NewNopLogger(),
|
||||
)
|
||||
|
||||
auctionKeeper := auctionkeeper.NewKeeper(cdc, runtime.NewKVStoreService(keys[auctionTypes.StoreKey]), accountKeeper, bankKeeper, authority.String())
|
||||
|
||||
bondKeeper := bondkeeper.NewKeeper(cdc, runtime.NewKVStoreService(keys[bondTypes.StoreKey]), accountKeeper, bankKeeper, authority.String())
|
||||
|
||||
registryKeeper := registrykeeper.NewKeeper(
|
||||
cdc,
|
||||
runtime.NewKVStoreService(keys[registryTypes.StoreKey]),
|
||||
accountKeeper,
|
||||
bankKeeper,
|
||||
bondKeeper,
|
||||
auctionKeeper,
|
||||
authority.String(),
|
||||
)
|
||||
|
||||
authModule := auth.NewAppModule(cdc, accountKeeper, authsims.RandomGenesisAccounts, nil)
|
||||
bankModule := bank.NewAppModule(cdc, bankKeeper, accountKeeper, nil)
|
||||
auctionModule := auctionmodule.NewAppModule(cdc, auctionKeeper)
|
||||
bondModule := bondmodule.NewAppModule(cdc, bondKeeper)
|
||||
registryModule := registrymodule.NewAppModule(cdc, registryKeeper)
|
||||
|
||||
integrationApp := integration.NewIntegrationApp(newCtx, logger, keys, cdc, map[string]appmodule.AppModule{
|
||||
authtypes.ModuleName: authModule,
|
||||
banktypes.ModuleName: bankModule,
|
||||
auctionTypes.ModuleName: auctionModule,
|
||||
bondTypes.ModuleName: bondModule,
|
||||
registryTypes.ModuleName: registryModule,
|
||||
})
|
||||
|
||||
integrationApp := integration.NewIntegrationApp(newCtx, logger, storeKeys, cdc, modules)
|
||||
sdkCtx := sdk.UnwrapSDKContext(integrationApp.Context())
|
||||
|
||||
// Register MsgServer and QueryServer
|
||||
auctionTypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), auctionkeeper.NewMsgServerImpl(auctionKeeper))
|
||||
auctionTypes.RegisterQueryServer(integrationApp.QueryHelper(), auctionkeeper.NewQueryServerImpl(auctionKeeper))
|
||||
auctiontypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), auctionkeeper.NewMsgServerImpl(auctionKeeper))
|
||||
auctiontypes.RegisterQueryServer(integrationApp.QueryHelper(), auctionkeeper.NewQueryServerImpl(auctionKeeper))
|
||||
bondtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), bondkeeper.NewMsgServerImpl(bondKeeper))
|
||||
bondtypes.RegisterQueryServer(integrationApp.QueryHelper(), bondkeeper.NewQueryServerImpl(bondKeeper))
|
||||
registrytypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), registrykeeper.NewMsgServerImpl(registryKeeper))
|
||||
registrytypes.RegisterQueryServer(integrationApp.QueryHelper(), registrykeeper.NewQueryServerImpl(registryKeeper))
|
||||
|
||||
bondTypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), bondkeeper.NewMsgServerImpl(bondKeeper))
|
||||
bondTypes.RegisterQueryServer(integrationApp.QueryHelper(), bondkeeper.NewQueryServerImpl(bondKeeper))
|
||||
auctionKeeper.Params.Set(sdkCtx, auctiontypes.DefaultParams())
|
||||
bondKeeper.Params.Set(sdkCtx, bondtypes.DefaultParams())
|
||||
registryKeeper.Params.Set(sdkCtx, registrytypes.DefaultParams())
|
||||
|
||||
registryTypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), registrykeeper.NewMsgServerImpl(registryKeeper))
|
||||
registryTypes.RegisterQueryServer(integrationApp.QueryHelper(), registrykeeper.NewQueryServerImpl(registryKeeper))
|
||||
|
||||
// set default params
|
||||
if err := auctionKeeper.Params.Set(sdkCtx, auctionTypes.DefaultParams()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bondKeeper.Params.Set(sdkCtx, bondTypes.DefaultParams()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := registryKeeper.Params.Set(sdkCtx, registryTypes.DefaultParams()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tf.App = integrationApp
|
||||
tf.SdkCtx, tf.cdc, tf.keys = sdkCtx, cdc, keys
|
||||
tf.App, tf.SdkCtx, tf.cdc, tf.keys = integrationApp, sdkCtx, cdc, storeKeys
|
||||
tf.AccountKeeper, tf.BankKeeper = accountKeeper, bankKeeper
|
||||
tf.AuctionKeeper, tf.BondKeeper, tf.RegistryKeeper = auctionKeeper, bondKeeper, registryKeeper
|
||||
|
||||
@@ -164,3 +201,20 @@ type BondDenomProvider struct{}
|
||||
func (bdp BondDenomProvider) BondDenom(ctx context.Context) (string, error) {
|
||||
return params.CoinUnit, nil
|
||||
}
|
||||
|
||||
type StoreKeyExtractor map[string]*storetypes.KVStoreKey
|
||||
|
||||
func (ske *StoreKeyExtractor) ExtractKey(mkey depinject.ModuleKey, storeKey *storetypes.KVStoreKey) {
|
||||
(*ske)[mkey.Name()] = storeKey
|
||||
}
|
||||
|
||||
func (ske *StoreKeyExtractor) config() depinject.Config {
|
||||
return depinject.Configs(
|
||||
depinject.Supply(ske),
|
||||
depinject.InvokeInModule(banktypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
|
||||
depinject.InvokeInModule(authtypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
|
||||
depinject.InvokeInModule(bondtypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
|
||||
depinject.InvokeInModule(auctiontypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
|
||||
depinject.InvokeInModule(registrytypes.ModuleName, (*StoreKeyExtractor).ExtractKey),
|
||||
)
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"cosmossdk.io/math"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"git.vdb.to/cerc-io/laconicd/app/params"
|
||||
@@ -27,18 +26,18 @@ type KeeperTestSuite struct {
|
||||
|
||||
func (kts *KeeperTestSuite) SetupTest() {
|
||||
err := kts.TestFixture.Setup()
|
||||
assert.Nil(kts.T(), err)
|
||||
kts.Require().NoError(err)
|
||||
|
||||
// set default params
|
||||
err = kts.RegistryKeeper.Params.Set(kts.SdkCtx, types.DefaultParams())
|
||||
assert.Nil(kts.T(), err)
|
||||
kts.Require().NoError(err)
|
||||
|
||||
qr := kts.App.QueryHelper()
|
||||
kts.queryClient = types.NewQueryClient(qr)
|
||||
|
||||
// Create a bond
|
||||
bond, err := kts.createBond()
|
||||
assert.Nil(kts.T(), err)
|
||||
kts.Require().NoError(err)
|
||||
kts.bond = *bond
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"git.vdb.to/cerc-io/laconicd/x/auction"
|
||||
)
|
||||
|
||||
const (
|
||||
randomAuctionId = "randomAuctionId"
|
||||
randomBidderAddress = "randomBidderAddress"
|
||||
randomOwnerAddress = "randomOwnerAddress"
|
||||
)
|
||||
|
||||
func (s *auctionSuite) TestGRPCQueryParams() {
|
||||
reqURL := s.endpointURL("auction/v1/params")
|
||||
|
||||
s.Run("valid request to get auction params", func() {
|
||||
require := s.Require()
|
||||
|
||||
resp, err := testutil.GetRequest(reqURL)
|
||||
require.NoError(err)
|
||||
|
||||
var params auction.QueryParamsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, ¶ms), string(resp))
|
||||
require.Equal(auction.DefaultParams(), *params.GetParams())
|
||||
})
|
||||
}
|
||||
|
||||
func (s *auctionSuite) TestGRPCGetAllAuctions() {
|
||||
reqURL := s.endpointURL("auction/v1/auctions")
|
||||
|
||||
s.createAuction()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
}{
|
||||
{
|
||||
"invalid request",
|
||||
reqURL + "-asdasd",
|
||||
"Not Implemented",
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, tc.url)
|
||||
} else {
|
||||
var auctions auction.QueryAuctionsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &auctions), string(resp))
|
||||
require.NotEmpty(auctions.Auctions.Auctions)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *auctionSuite) TestGRPCGetAuction() {
|
||||
reqURL := s.endpointURL("auction/v1/auctions/%s")
|
||||
|
||||
auctionId := s.createAuction()
|
||||
s.createBid(auctionId)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
exists bool
|
||||
}{
|
||||
{
|
||||
"nonexistent auction",
|
||||
fmt.Sprintf(reqURL, randomAuctionId),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
fmt.Sprintf(reqURL, auctionId),
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.exists {
|
||||
var auction auction.QueryGetAuctionResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &auction), string(resp))
|
||||
require.Equal(auctionId, auction.Auction.Id)
|
||||
} else {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, "not found", tc.url)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *auctionSuite) TestGRPCGetBids() {
|
||||
reqURL := s.endpointURL("auction/v1/bids/%s")
|
||||
|
||||
auctionId := s.createAuction()
|
||||
s.createBid(auctionId)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
exists bool
|
||||
}{
|
||||
{
|
||||
"nonexistent auction",
|
||||
fmt.Sprintf(reqURL, randomAuctionId),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
fmt.Sprintf(reqURL, auctionId),
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
var bids auction.QueryGetBidsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &bids), string(resp))
|
||||
if tc.exists {
|
||||
require.Equal(auctionId, bids.Bids[0].AuctionId)
|
||||
} else {
|
||||
require.Empty(bids.Bids)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *auctionSuite) TestGRPCGetBid() {
|
||||
reqURL := s.endpointURL("auction/v1/bids/%s/%s")
|
||||
bidderAddress := s.cli().GetKeyAddr(s.bidderAccount)
|
||||
|
||||
auctionId := s.createAuction()
|
||||
s.createBid(auctionId)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
exists bool
|
||||
}{
|
||||
{
|
||||
"nonexistent auction",
|
||||
fmt.Sprintf(reqURL, randomAuctionId, bidderAddress),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nonexistent bidder",
|
||||
fmt.Sprintf(reqURL, auctionId, randomBidderAddress),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
fmt.Sprintf(reqURL, auctionId, bidderAddress),
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.exists {
|
||||
var bid auction.QueryGetBidResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &bid), string(resp))
|
||||
require.Equal(auctionId, bid.Bid.AuctionId)
|
||||
} else {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, "not found", tc.url)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *auctionSuite) TestGRPCGetAuctionsByOwner() {
|
||||
reqURL := s.endpointURL("auction/v1/by-owner/%s")
|
||||
ownerAddress := s.cli().GetKeyAddr(s.ownerAccount)
|
||||
|
||||
auctionId := s.createAuction()
|
||||
s.createBid(auctionId)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
exists bool
|
||||
}{
|
||||
{
|
||||
"nonexistent owner",
|
||||
fmt.Sprintf(reqURL, randomOwnerAddress),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
fmt.Sprintf(reqURL, ownerAddress),
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
var auctions auction.QueryAuctionsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &auctions), string(resp))
|
||||
if tc.exists {
|
||||
require.Equal(auctionId, auctions.Auctions.Auctions[0].Id)
|
||||
} else {
|
||||
require.Empty(auctions.Auctions.Auctions)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *auctionSuite) TestGRPCQueryBalance() {
|
||||
reqURL := s.endpointURL("auction/v1/balance")
|
||||
|
||||
s.Run("valid request", func() {
|
||||
require := s.Require()
|
||||
|
||||
auctionId := s.createAuction()
|
||||
s.createBid(auctionId)
|
||||
|
||||
resp, err := testutil.GetRequest(reqURL)
|
||||
require.NoError(err)
|
||||
|
||||
var response auction.QueryGetAuctionModuleBalanceResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
|
||||
require.NotEmpty(response.GetBalance())
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.vdb.to/cerc-io/laconicd/x/auction"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
const (
|
||||
sampleCommitTime = "90s"
|
||||
sampleRevealTime = "5s"
|
||||
placeholderAuctionId = "placeholder_auction_id"
|
||||
)
|
||||
|
||||
type auctionSuite struct {
|
||||
testSuite
|
||||
|
||||
ownerAccount string
|
||||
bidderAccount string
|
||||
defaultAuctionId string
|
||||
}
|
||||
|
||||
func TestAuction(t *testing.T) {
|
||||
suite.Run(t, new(auctionSuite))
|
||||
}
|
||||
|
||||
func (s *auctionSuite) SetupTest() {
|
||||
s.resetChain()
|
||||
|
||||
s.ownerAccount = s.account(0)
|
||||
s.bidderAccount = s.account(1)
|
||||
|
||||
Sut.StartChain(s.T())
|
||||
}
|
||||
|
||||
func (s *auctionSuite) TearDownTest() {
|
||||
s.cleanupBidFiles()
|
||||
}
|
||||
|
||||
func (s *auctionSuite) TestQueryList() {
|
||||
sr := s.Require()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
createAuction bool
|
||||
}{
|
||||
{
|
||||
"when no auctions exist",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"after creating an auction",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
s.Run(test.name, func() {
|
||||
if test.createAuction {
|
||||
s.createAuction()
|
||||
}
|
||||
|
||||
out := s.cli().CustomQuery("q", "auction", "list")
|
||||
var response auction.QueryAuctionsResponse
|
||||
sr.NoError(s.codec.UnmarshalJSON([]byte(out), &response), out)
|
||||
if test.createAuction {
|
||||
sr.NotEmpty(response.Auctions.Auctions)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *auctionSuite) TestTxCommitBid() {
|
||||
auctionId := s.createAuction()
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
"with missing args",
|
||||
[]string{auctionId},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"with zero bid",
|
||||
[]string{auctionId, ("0" + s.bondDenom)},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"with invalid auction",
|
||||
[]string{"fake", ("200" + s.bondDenom)},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"with valid args",
|
||||
[]string{auctionId, ("200" + s.bondDenom)},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
s.Run(test.name, func() {
|
||||
cmd := []string{
|
||||
"tx", "auction", "commit-bid",
|
||||
}
|
||||
cmd = append(cmd, test.args...)
|
||||
s.runTx(cmd, test.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *auctionSuite) createAuction() string {
|
||||
require := s.Require()
|
||||
|
||||
cmd := []string{
|
||||
"tx", "auction", "create",
|
||||
sampleCommitTime,
|
||||
sampleRevealTime,
|
||||
fmt.Sprintf("10%s", s.bondDenom),
|
||||
fmt.Sprintf("10%s", s.bondDenom),
|
||||
"--kind", auction.AuctionKindVickrey,
|
||||
"--minimum-bid", fmt.Sprintf("100%s", s.bondDenom),
|
||||
"--max-price", fmt.Sprintf("0%s", s.bondDenom),
|
||||
"--num-providers", "0",
|
||||
}
|
||||
s.runTx(cmd, false)
|
||||
|
||||
out := s.cli().CustomQuery("q", "auction", "list")
|
||||
var response auction.QueryAuctionsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON([]byte(out), &response), out)
|
||||
return response.Auctions.Auctions[0].Id
|
||||
}
|
||||
|
||||
func (s *auctionSuite) createBid(auctionId string) {
|
||||
cmd := []string{
|
||||
"tx", "auction", "commit-bid",
|
||||
auctionId, fmt.Sprintf("200%s", s.bondDenom),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, s.bidderAccount),
|
||||
}
|
||||
s.runTx(cmd, false)
|
||||
}
|
||||
|
||||
func (s *auctionSuite) cleanupBidFiles() {
|
||||
matches, err := filepath.Glob(fmt.Sprintf("%s-*.json", s.bidderAccount))
|
||||
if err != nil {
|
||||
s.T().Errorf("Error matching bidder files: %v\n", err)
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
err := os.Remove(match)
|
||||
if err != nil {
|
||||
s.T().Errorf("Error removing bidder file: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
|
||||
"git.vdb.to/cerc-io/laconicd/x/bond"
|
||||
)
|
||||
|
||||
func (s *bondSuite) TestGRPCGetParams() {
|
||||
reqURL := s.endpointURL("bond/v1/params")
|
||||
|
||||
var params bond.QueryParamsResponse
|
||||
resp, err := testutil.GetRequest(reqURL)
|
||||
require.NoError(s.T(), err)
|
||||
require.NoError(s.T(), s.codec.UnmarshalJSON(resp, ¶ms))
|
||||
|
||||
require.Equal(s.T(), bond.DefaultParams().MaxBondAmount, params.GetParams().MaxBondAmount)
|
||||
}
|
||||
|
||||
func (s *bondSuite) TestGRPCGetBond() {
|
||||
reqURL := s.endpointURL("bond/v1/bonds")
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
prerun func()
|
||||
}{
|
||||
{
|
||||
"invalid request with headers",
|
||||
reqURL + "asdasdas",
|
||||
"Not Implemented",
|
||||
func() {},
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
"",
|
||||
func() { s.createBond() },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
tc.prerun()
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, string(resp))
|
||||
} else {
|
||||
var bonds bond.QueryBondsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &bonds), string(resp))
|
||||
require.NotEmpty(bonds.GetBonds())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *bondSuite) TestGRPCGetBondsByOwner() {
|
||||
reqURL := s.endpointURL("bond/v1/by-owner/%s")
|
||||
accountAddress := s.cli().GetKeyAddr(s.account(0))
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
url string
|
||||
err bool
|
||||
prerun func()
|
||||
}{
|
||||
{
|
||||
"empty list",
|
||||
fmt.Sprintf(reqURL, "asdasd"),
|
||||
true,
|
||||
func() {},
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
fmt.Sprintf(reqURL, accountAddress),
|
||||
false,
|
||||
func() { s.createBond() },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
tc.prerun()
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
var bonds bond.QueryGetBondsByOwnerResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &bonds))
|
||||
if tc.err {
|
||||
require.Empty(bonds.GetBonds())
|
||||
} else {
|
||||
bondsList := bonds.GetBonds()
|
||||
require.NotEmpty(bondsList)
|
||||
require.Equal(accountAddress, bondsList[0].GetOwner())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *bondSuite) TestGRPCGetBondById() {
|
||||
reqURL := s.endpointURL("bond/v1/bonds/%s")
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
url string
|
||||
err bool
|
||||
prerun func() string
|
||||
}{
|
||||
{
|
||||
"invalid request",
|
||||
reqURL,
|
||||
true,
|
||||
func() string { return "asdadad" },
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
false,
|
||||
func() string { return s.createBond() },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
bondId := tc.prerun()
|
||||
tc.url = fmt.Sprintf(reqURL, bondId)
|
||||
|
||||
var bond bond.QueryGetBondByIdResponse
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
if tc.err {
|
||||
require.Error(s.codec.UnmarshalJSON(resp, &bond))
|
||||
} else {
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &bond))
|
||||
require.Equal(bondId, bond.GetBond().GetId())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *bondSuite) TestGRPCGetBondModuleBalance() {
|
||||
reqURL := s.endpointURL("bond/v1/balance")
|
||||
|
||||
s.createBond()
|
||||
|
||||
require := s.Require()
|
||||
|
||||
resp, err := testutil.GetRequest(reqURL)
|
||||
require.NoError(err)
|
||||
|
||||
var response bond.QueryGetBondModuleBalanceResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response))
|
||||
require.False(response.GetBalance().IsZero())
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.vdb.to/cerc-io/laconicd/x/bond"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
type bondSuite struct {
|
||||
testSuite
|
||||
}
|
||||
|
||||
func TestBonds(t *testing.T) {
|
||||
suite.Run(t, new(bondSuite))
|
||||
}
|
||||
|
||||
func (s *bondSuite) SetupTest() {
|
||||
s.resetChain()
|
||||
Sut.StartChain(s.T())
|
||||
}
|
||||
|
||||
func (s *bondSuite) TestQueryList() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
prerun func()
|
||||
}{
|
||||
{
|
||||
"create and get bond lists",
|
||||
nil,
|
||||
func() { s.createBond() },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
tc.prerun()
|
||||
out := s.cli().CustomQuery("q", "bond", "list")
|
||||
s.Require().NotEmpty(gjson.Get(out, "bonds").Array(), out)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *bondSuite) TestTxCreateBond() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
"without deposit",
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"create bond",
|
||||
[]string{
|
||||
fmt.Sprintf("10%s", s.bondDenom),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
cmd := append([]string{"tx", "bond", "create"}, tc.args...)
|
||||
s.runTx(cmd, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *bondSuite) createBond() string {
|
||||
return createBond(&s.testSuite)
|
||||
}
|
||||
|
||||
// free function for use in other suites
|
||||
func createBond(s *testSuite) string {
|
||||
require := s.Require()
|
||||
|
||||
cli := s.cli()
|
||||
cmd := []string{
|
||||
"tx", "bond", "create", ("1000000" + s.bondDenom),
|
||||
}
|
||||
s.runTx(cmd, false)
|
||||
|
||||
raw := cli.CustomQuery("q", "bond", "list")
|
||||
var queryResponse bond.QueryBondsResponse
|
||||
require.NoError(json.Unmarshal([]byte(raw), &queryResponse))
|
||||
bonds := queryResponse.GetBonds()
|
||||
require.NotEmpty(bonds)
|
||||
|
||||
return bonds[0].GetId()
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/systemtests"
|
||||
)
|
||||
|
||||
var (
|
||||
Sut *systemtests.SystemUnderTest
|
||||
|
||||
verbose bool
|
||||
nodesCount = flag.Int("nodes", 4, "number of nodes in the cluster")
|
||||
blockTime = 3 * time.Second // expected time between blocks
|
||||
// txTimeout = 10 * time.Second // timeout for transactions to be included in a block
|
||||
txTimeout = 10 * time.Minute // timeout for transactions to be included in a block
|
||||
|
||||
workDir string
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// sdk systemtests expects this to be set for v2 server/runtime
|
||||
os.Setenv("COSMOS_BUILD_OPTIONS", "v2")
|
||||
|
||||
flag.BoolVar(&verbose, "verbose", false, "verbose output")
|
||||
flag.Parse()
|
||||
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
workDir = dir
|
||||
if verbose {
|
||||
println("Work dir: ", workDir)
|
||||
}
|
||||
|
||||
Sut = systemtests.NewSystemUnderTest("laconicd", verbose, *nodesCount, blockTime)
|
||||
// Sut.SetTestnetInitializer(initer)
|
||||
|
||||
Sut.SetupChain() // setup chain and keyring
|
||||
|
||||
// run tests
|
||||
exitCode := m.Run()
|
||||
|
||||
// postprocess
|
||||
Sut.StopChain()
|
||||
if verbose || exitCode != 0 {
|
||||
Sut.PrintBuffer()
|
||||
}
|
||||
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
nitrocrypto "github.com/statechannels/go-nitro/crypto"
|
||||
chainutils "github.com/statechannels/go-nitro/node/chain"
|
||||
"github.com/statechannels/go-nitro/node/query"
|
||||
"github.com/statechannels/go-nitro/node_test"
|
||||
nitrotypes "github.com/statechannels/go-nitro/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
systest "cosmossdk.io/systemtests"
|
||||
)
|
||||
|
||||
var (
|
||||
ethChainPort = "8546"
|
||||
ethChainUrl = "ws://127.0.0.1:8546"
|
||||
)
|
||||
|
||||
type nitroSuite struct {
|
||||
testSuite
|
||||
|
||||
tokenDenom string
|
||||
ethChainOpts *node_test.LocalChainOptions
|
||||
|
||||
alice, bob nitrocrypto.Credential
|
||||
intermediaries []nitrocrypto.Credential
|
||||
}
|
||||
|
||||
func TestNitro(t *testing.T) {
|
||||
suite.Run(t, new(nitroSuite))
|
||||
}
|
||||
|
||||
func (s *nitroSuite) SetupSuite() {
|
||||
s.testSuite.SetupSuite()
|
||||
|
||||
s.tokenDenom = "eth"
|
||||
|
||||
// Set up Ethereum test chain
|
||||
// TODO: use anvil for better self-containment
|
||||
ethChainOpts := node_test.ExternalHardhatChainOptions(s.T())
|
||||
ethChainOpts.ChainUrl = ethChainUrl
|
||||
aliceKey := ethChainOpts.ChainPks[0]
|
||||
bobKey := ethChainOpts.ChainPks[1]
|
||||
s.ethChainOpts = ethChainOpts
|
||||
|
||||
ethChain, err := node_test.ConnectLocalChain(ethChainOpts, nitrotypes.BytesFromHex(aliceKey))
|
||||
s.Require().NoError(err)
|
||||
s.T().Cleanup(func() { s.NoError(ethChain.Close()) })
|
||||
|
||||
s.alice = nitrocrypto.NewSimpleCredential(nitrotypes.BytesFromHex(aliceKey))
|
||||
s.bob = nitrocrypto.NewSimpleCredential(nitrotypes.BytesFromHex(bobKey))
|
||||
for _, pk := range ethChainOpts.ChainPks {
|
||||
creds := nitrocrypto.NewSimpleCredential(nitrotypes.BytesFromHex(pk))
|
||||
s.intermediaries = append(s.intermediaries, creds)
|
||||
}
|
||||
|
||||
s.configureEthChain()
|
||||
// TODO when using intermediaries, set more validator node pks here
|
||||
s.setValidatorNitroKeys(s.ethChainOpts.ChainPks[1])
|
||||
}
|
||||
|
||||
func (s *nitroSuite) SetupTest() {
|
||||
s.resetChain()
|
||||
|
||||
s.setClientKeys(s.ethChainOpts.ChainPks[0])
|
||||
|
||||
Sut.StartChain(s.T())
|
||||
}
|
||||
|
||||
func (s *nitroSuite) SetupSubTest() { s.SetupTest() }
|
||||
|
||||
func (s *nitroSuite) configureEthChain() {
|
||||
cas, err := chainutils.LoadEnvContractAddresses()
|
||||
s.Require().NoError(err)
|
||||
|
||||
cmds := [][]string{
|
||||
{"config", "set", "app", "nitro.eth-na-address", "'" + cas.NaAddress.String() + "'"},
|
||||
{"config", "set", "app", "nitro.eth-vpa-address", "'" + cas.VpaAddress.String() + "'"},
|
||||
{"config", "set", "app", "nitro.eth-ca-address", "'" + cas.CaAddress.String() + "'"},
|
||||
{"config", "set", "app", "nitro.eth-url", ethChainUrl},
|
||||
}
|
||||
_ = Sut.ForEachNodeExecAndWait(s.T(), cmds...)
|
||||
for _, cmd := range cmds {
|
||||
cmd = append(cmd, "--home", s.clientHome())
|
||||
_ = systest.MustRunShellCmd(s.T(), Sut.ExecBinary(), cmd...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *nitroSuite) TestTxOpenChannel() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
"missing args",
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"missing amount",
|
||||
[]string{s.bob.AsParticipant().String()},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
[]string{s.bob.AsParticipant().String(), "10" + s.tokenDenom},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
cmd := append([]string{"tx", "nitro", "open-channel"}, tc.args...)
|
||||
s.runTx(cmd, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *nitroSuite) TestTxCloseChannel() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
err bool
|
||||
setup func() []string
|
||||
}{
|
||||
{
|
||||
"missing args",
|
||||
[]string{},
|
||||
true,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"invalid channel",
|
||||
[]string{"invalid-channel-id"},
|
||||
true,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"valid channel",
|
||||
nil,
|
||||
false,
|
||||
func() []string { return s.setupLedgerChannel("10") },
|
||||
},
|
||||
// {
|
||||
// "valid channel with challenge",
|
||||
// []string{"--challenge"},
|
||||
// false,
|
||||
// func() []string { return s.setupLedgerChannel("10") },
|
||||
// },
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
cmd := []string{"tx", "nitro", "close-channel"}
|
||||
if tc.setup != nil {
|
||||
cmd = append(cmd, tc.setup()...)
|
||||
}
|
||||
cmd = append(cmd, tc.args...)
|
||||
s.runTx(cmd, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *nitroSuite) TestTxOpenPaymentChannel() {
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
err bool
|
||||
setup func() []string
|
||||
}{
|
||||
{
|
||||
"missing args",
|
||||
[]string{},
|
||||
true,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"missing amount",
|
||||
[]string{s.bob.AsParticipant().String()},
|
||||
true,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"zero hops",
|
||||
[]string{s.bob.AsParticipant().String(), "10" + s.tokenDenom},
|
||||
false,
|
||||
func() []string { s.setupLedgerChannel("20"); return nil },
|
||||
},
|
||||
// { // TODO open n-hop ledger channels
|
||||
// "one hop",
|
||||
// []string{"10" + s.tokenDenom, s.bob.AsParticipant().String(), s.intermediaries[0].String()},
|
||||
// false,
|
||||
// setupLedgerChannelForPaymentChannel,
|
||||
// },
|
||||
// { // TODO use a different counterparty, cannot have duplicate channels
|
||||
// "direct channel with challenge duration",
|
||||
// []string{"10" + s.tokenDenom, s.bob.AsParticipant().String(), "--challenge-duration", "100"},
|
||||
// false,
|
||||
// setupLedgerChannelForPaymentChannel,
|
||||
// },
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
cmd := []string{"tx", "nitro", "open-payment-channel"}
|
||||
if tc.setup != nil {
|
||||
tc.setup()
|
||||
}
|
||||
cmd = append(cmd, tc.args...)
|
||||
s.runTx(cmd, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *nitroSuite) TestTxClosePaymentChannel() {
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
err bool
|
||||
setup func() []string
|
||||
}{
|
||||
{
|
||||
"missing args",
|
||||
[]string{},
|
||||
true,
|
||||
nil,
|
||||
},
|
||||
// { // TODO use different counterparty
|
||||
// "invalid channel ID",
|
||||
// []string{"invalid-channel-id"},
|
||||
// true,
|
||||
// setupLedgerChannel,
|
||||
// },
|
||||
{
|
||||
"zero hops",
|
||||
nil,
|
||||
false,
|
||||
func() []string {
|
||||
s.setupLedgerChannel("20")
|
||||
return s.setupPaymentChannel("10")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
cmd := []string{"tx", "nitro", "close-payment-channel"}
|
||||
if tc.setup != nil {
|
||||
cmd = append(cmd, tc.setup()...)
|
||||
}
|
||||
cmd = append(cmd, tc.args...)
|
||||
s.runTx(cmd, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *nitroSuite) TestTxPay() {
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
error bool
|
||||
setup func() []string
|
||||
paid *big.Int
|
||||
}{
|
||||
{
|
||||
name: "missing args",
|
||||
error: true,
|
||||
},
|
||||
{
|
||||
name: "invalid channel ID",
|
||||
args: []string{"invalid-channel-id", "10" + s.tokenDenom},
|
||||
error: true,
|
||||
},
|
||||
{
|
||||
name: "invalid amount format",
|
||||
args: []string{"0x0102030000000000000000000000000000000000000000000000000000000000", "invalid-amount"},
|
||||
error: true,
|
||||
},
|
||||
{
|
||||
name: "nonexistent channel",
|
||||
args: []string{"0x0102030000000000000000000000000000000000000000000000000000000000", "5" + s.tokenDenom},
|
||||
error: true,
|
||||
},
|
||||
{
|
||||
name: "valid payment",
|
||||
args: []string{"10" + s.tokenDenom},
|
||||
setup: func() []string {
|
||||
s.setupLedgerChannel("100")
|
||||
return s.setupPaymentChannel("50")
|
||||
},
|
||||
paid: big.NewInt(10),
|
||||
},
|
||||
// {
|
||||
// "insufficient funds",
|
||||
// []string{"10" + s.tokenDenom},
|
||||
// false,
|
||||
// func() []string {
|
||||
// s.setupLedgerChannel("100")
|
||||
// return s.setupPaymentChannel("50")
|
||||
// },
|
||||
// },
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
var channelId string
|
||||
|
||||
cmd := []string{"tx", "nitro", "pay"}
|
||||
if tc.setup != nil {
|
||||
setupArgs := tc.setup()
|
||||
cmd = append(cmd, setupArgs...)
|
||||
|
||||
if !tc.error && len(setupArgs) > 0 {
|
||||
channelId = setupArgs[0]
|
||||
}
|
||||
}
|
||||
cmd = append(cmd, tc.args...)
|
||||
s.runTx(cmd, tc.error)
|
||||
|
||||
// Check balance change for successful payments
|
||||
if channelId != "" {
|
||||
balance := s.getChannelBalance(channelId)
|
||||
s.Require().Equal(tc.paid, balance.PaidSoFar.ToInt())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *nitroSuite) TestFullPaymentFlow() {
|
||||
s.Run("complete payment lifecycle", func() {
|
||||
// Open ledger and payment channels
|
||||
ledgerChannel := s.setupLedgerChannel("100")[0]
|
||||
paymentChannel := s.setupPaymentChannel("50")[0]
|
||||
|
||||
// Verify initial balance
|
||||
s.Require().Equal("0", s.getChannelBalance(paymentChannel).PaidSoFar.ToInt().String())
|
||||
|
||||
// Make payments: 5 + 10 + 15 = 30
|
||||
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "5" + s.tokenDenom}, false)
|
||||
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "10" + s.tokenDenom}, false)
|
||||
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "15" + s.tokenDenom}, false)
|
||||
|
||||
// Verify final balance
|
||||
finalBalance := s.getChannelBalance(paymentChannel)
|
||||
s.Require().Equal("30", finalBalance.PaidSoFar.ToInt().String())
|
||||
s.Require().Equal("20", finalBalance.RemainingFunds.ToInt().String())
|
||||
|
||||
// Close channels in correct order
|
||||
s.runTx([]string{"tx", "nitro", "close-payment-channel", paymentChannel}, false)
|
||||
s.runTx([]string{"tx", "nitro", "close-channel", ledgerChannel}, false)
|
||||
})
|
||||
|
||||
s.Run("insufficient funds scenario", func() {
|
||||
s.setupLedgerChannel("20")
|
||||
paymentChannel := s.setupPaymentChannel("10")[0]
|
||||
|
||||
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "5" + s.tokenDenom}, false)
|
||||
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "10" + s.tokenDenom}, true) // insufficient funds
|
||||
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "5" + s.tokenDenom}, false)
|
||||
|
||||
// Verify all funds used
|
||||
balance := s.getChannelBalance(paymentChannel)
|
||||
s.Require().Equal("10", balance.PaidSoFar.ToInt().String())
|
||||
s.Require().Equal("0", balance.RemainingFunds.ToInt().String())
|
||||
})
|
||||
|
||||
s.Run("invalid closure order", func() {
|
||||
// Open both channels
|
||||
ledgerChannel := s.setupLedgerChannel("50")[0]
|
||||
paymentChannel := s.setupPaymentChannel("30")[0]
|
||||
|
||||
// Make a payment
|
||||
s.runTx([]string{"tx", "nitro", "pay", paymentChannel, "10" + s.tokenDenom}, false)
|
||||
|
||||
// Try to close ledger channel before payment channel - should fail
|
||||
s.runTx([]string{"tx", "nitro", "close-channel", ledgerChannel}, true)
|
||||
|
||||
// Correct order should work
|
||||
s.runTx([]string{"tx", "nitro", "close-payment-channel", paymentChannel}, false)
|
||||
s.runTx([]string{"tx", "nitro", "close-channel", ledgerChannel}, false)
|
||||
})
|
||||
}
|
||||
|
||||
// setupLedgerChannel creates a ledger channel and returns the channel ID as args
|
||||
func (s *nitroSuite) setupLedgerChannel(amount string) []string {
|
||||
openCmd := []string{"tx", "nitro", "open-channel", s.bob.AsParticipant().String(), amount + s.tokenDenom}
|
||||
txhash := s.runTx(openCmd, false)
|
||||
return []string{s.getChannelIdFromTx(txhash)}
|
||||
}
|
||||
|
||||
// setupPaymentChannel creates a ledger channel and payment channel, returns payment channel ID as args
|
||||
func (s *nitroSuite) setupPaymentChannel(amount string) []string {
|
||||
openPcCmd := []string{"tx", "nitro", "open-payment-channel", s.bob.AsParticipant().String(), amount + s.tokenDenom}
|
||||
txhash := s.runTx(openPcCmd, false)
|
||||
return []string{s.getChannelIdFromTx(txhash)}
|
||||
}
|
||||
|
||||
// getChannelIdFromTx opens a channel and extracts the channel ID from the transaction result
|
||||
func (s *nitroSuite) getChannelIdFromTx(txHash string) string {
|
||||
txDetails := s.cli().CustomQuery("query", "tx", txHash)
|
||||
|
||||
// find channel_id in any event's attributes
|
||||
channelId := gjson.Get(txDetails, "events.#.attributes.#(key==\"channel_id\").value|0")
|
||||
s.Require().True(channelId.Exists(), "channel_id not found in transaction events")
|
||||
|
||||
return channelId.String()
|
||||
}
|
||||
|
||||
func (s *nitroSuite) getChannelBalance(channelId string) query.PaymentChannelBalance {
|
||||
channelInfo := s.cli().CustomQuery(
|
||||
s.withNitroQueryFlags("q", "nitro", "get-channel", "--payment", channelId)...,
|
||||
)
|
||||
|
||||
var info query.PaymentChannelInfo
|
||||
err := json.Unmarshal([]byte(channelInfo), &info)
|
||||
s.Require().NoError(err, "could not parse PaymentChannelInfo: %s", channelInfo)
|
||||
return info.Balance
|
||||
}
|
||||
|
||||
func (s *nitroSuite) withNitroQueryFlags(cmd ...string) []string {
|
||||
return append(cmd, "--home", s.clientHome())
|
||||
}
|
||||
|
||||
func (s *nitroSuite) Test_Hang() {
|
||||
hang := os.Getenv("HANG")
|
||||
if hang == "" {
|
||||
s.T().Skip("Placeholder to run the validator nodes and hang for manual testing")
|
||||
}
|
||||
|
||||
if hang == "ledger" || hang == "payment" {
|
||||
s.setupLedgerChannel("20")
|
||||
}
|
||||
if hang == "payment" {
|
||||
s.setupPaymentChannel("10")
|
||||
}
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
<-c
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"git.vdb.to/cerc-io/laconicd/x/registry"
|
||||
)
|
||||
|
||||
const badPath = "/asdasd"
|
||||
|
||||
func (s *registrySuite) TestGRPCQueryParams() {
|
||||
reqURL := s.endpointURL("registry/v1/params")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
}{
|
||||
{
|
||||
"invalid request",
|
||||
reqURL + badPath,
|
||||
"Not Implemented",
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, tc.url)
|
||||
} else {
|
||||
var response registry.QueryParamsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
|
||||
|
||||
params := registry.DefaultParams()
|
||||
s.updateParams(¶ms)
|
||||
require.Equal(params.String(), response.GetParams().String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *registrySuite) TestGRPCQueryWhoIs() {
|
||||
reqURL := s.endpointURL("registry/v1/whois/%s")
|
||||
authorityName := "QueryWhoIS"
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
prerun func(string)
|
||||
}{
|
||||
{
|
||||
"invalid url",
|
||||
reqURL + badPath,
|
||||
"Not Implemented",
|
||||
func(string) {},
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
"",
|
||||
func(name string) { s.reserveName(name) },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
tc.prerun(authorityName)
|
||||
url := fmt.Sprintf(tc.url, authorityName)
|
||||
|
||||
resp, err := testutil.GetRequest(url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, tc.url)
|
||||
} else {
|
||||
var response registry.QueryWhoisResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
|
||||
require.Equal(registry.AuthorityActive, response.GetNameAuthority().Status)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *registrySuite) TestGRPCQueryLookup() {
|
||||
reqURL := s.endpointURL("registry/v1/lookup")
|
||||
authorityName := "QueryLookUp"
|
||||
|
||||
s.createNameRecord(authorityName)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
}{
|
||||
{
|
||||
"invalid url",
|
||||
reqURL + badPath,
|
||||
"Not Implemented",
|
||||
},
|
||||
{
|
||||
"nonexistent LRN",
|
||||
fmt.Sprintf(reqURL+"?lrn=lrn://%s/", "nonexistent"),
|
||||
"not found",
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
fmt.Sprintf(reqURL+"?lrn=lrn://%s/", authorityName),
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, tc.url)
|
||||
} else {
|
||||
var response registry.QueryLookupLrnResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
|
||||
require.NotEmpty(response.Name.Latest.Id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *registrySuite) TestGRPCQueryListRecords() {
|
||||
reqURL := s.endpointURL("registry/v1/records")
|
||||
bondId := s.bondId
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
prerun func(string)
|
||||
}{
|
||||
{
|
||||
"invalid url",
|
||||
reqURL + badPath,
|
||||
"not found",
|
||||
func(string) {},
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
"",
|
||||
func(id string) { s.createRecord(id) },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
tc.prerun(bondId)
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, tc.url)
|
||||
} else {
|
||||
var response registry.QueryRecordsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
|
||||
require.NotEmpty(response.GetRecords())
|
||||
require.Equal(bondId, response.GetRecords()[0].GetBondId())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *registrySuite) TestGRPCQueryGetRecordById() {
|
||||
reqURL := s.endpointURL("registry/v1/records/%s")
|
||||
bondId := s.bondId
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
prerun func(string) string
|
||||
}{
|
||||
{
|
||||
"invalid url",
|
||||
reqURL + badPath,
|
||||
"not found",
|
||||
func(string) string { return "" },
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
"",
|
||||
func(id string) string {
|
||||
require := s.Require()
|
||||
|
||||
// create a record and get the id
|
||||
s.createRecord(id)
|
||||
out := s.cli().CustomQuery("q", "registry", "list")
|
||||
var response registry.QueryRecordsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON([]byte(out), &response), out)
|
||||
require.NotEmpty(response.GetRecords())
|
||||
|
||||
return response.GetRecords()[0].Id
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
recordId := tc.prerun(bondId)
|
||||
url := fmt.Sprintf(tc.url, recordId)
|
||||
|
||||
resp, err := testutil.GetRequest(url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, tc.url)
|
||||
} else {
|
||||
var response registry.QueryGetRecordResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
|
||||
record := response.GetRecord()
|
||||
require.NotEmpty(record.GetId())
|
||||
require.Equal(recordId, record.GetId())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *registrySuite) TestGRPCQueryGetRecordByBondId() {
|
||||
reqURL := s.endpointURL("registry/v1/records-by-bond-id/%s")
|
||||
bondId := s.bondId
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
prerun func(string)
|
||||
}{
|
||||
{
|
||||
"invalid url",
|
||||
reqURL + badPath,
|
||||
"Not Implemented",
|
||||
func(string) {},
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
"",
|
||||
func(id string) { s.createRecord(id) },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
tc.prerun(bondId)
|
||||
url := fmt.Sprintf(tc.url, bondId)
|
||||
|
||||
resp, err := testutil.GetRequest(url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, tc.url)
|
||||
} else {
|
||||
var response registry.QueryGetRecordsByBondIdResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
|
||||
records := response.GetRecords()
|
||||
require.NotEmpty(records)
|
||||
require.Equal(bondId, records[0].GetBondId())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *registrySuite) TestGRPCQueryGetRegistryModuleBalance() {
|
||||
reqURL := s.endpointURL("registry/v1/balance")
|
||||
bondId := s.bondId
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
prerun func(string)
|
||||
}{
|
||||
{
|
||||
"invalid url",
|
||||
reqURL + badPath,
|
||||
"Not Implemented",
|
||||
func(string) {},
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
"",
|
||||
func(id string) { s.createRecord(id) },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
tc.prerun(bondId)
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, tc.url)
|
||||
} else {
|
||||
var response registry.QueryGetRegistryModuleBalanceResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
|
||||
require.NotEmpty(response.GetBalances())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *registrySuite) TestGRPCQueryNamesList() {
|
||||
reqURL := s.endpointURL("registry/v1/names")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
errmsg string
|
||||
prerun func(string)
|
||||
}{
|
||||
{
|
||||
"invalid url",
|
||||
reqURL + badPath,
|
||||
"Not Implemented",
|
||||
func(string) {},
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
reqURL,
|
||||
"",
|
||||
func(name string) { s.createNameRecord(name) },
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
require := s.Require()
|
||||
|
||||
tc.prerun("ListNameRecords")
|
||||
|
||||
resp, err := testutil.GetRequest(tc.url)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.errmsg != "" {
|
||||
message := gjson.Get(string(resp), "message").String()
|
||||
require.Contains(message, tc.errmsg, tc.url)
|
||||
} else {
|
||||
var response registry.QueryNameRecordsResponse
|
||||
require.NoError(s.codec.UnmarshalJSON(resp, &response), string(resp))
|
||||
require.NotEmpty(response.GetNames())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
|
||||
// accounts "github.com/cosmos/cosmos-sdk/x/accounts/v1"
|
||||
"cosmossdk.io/math"
|
||||
systest "cosmossdk.io/systemtests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
// nitro "git.vdb.to/cerc-io/laconicd/x/nitro/v1"
|
||||
"git.vdb.to/cerc-io/laconicd/x/registry"
|
||||
)
|
||||
|
||||
var recordFilePath = "../data/examples/service_provider_example.yml"
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
if recordFilePath, err = filepath.Abs(recordFilePath); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err = os.Stat(recordFilePath); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type registrySuite struct {
|
||||
testSuite
|
||||
|
||||
accountAddress string
|
||||
bondId string
|
||||
}
|
||||
|
||||
func TestRegistry(t *testing.T) {
|
||||
suite.Run(t, new(registrySuite))
|
||||
}
|
||||
|
||||
func (s *registrySuite) SetupTest() {
|
||||
s.resetChain()
|
||||
|
||||
Sut.ModifyGenesisJSON(s.T(), UpdateGenesisRegistry(s))
|
||||
s.accountAddress = s.cli().GetKeyAddr(s.account(0))
|
||||
|
||||
Sut.StartChain(s.T())
|
||||
s.bondId = createBond(&s.testSuite)
|
||||
}
|
||||
|
||||
func (s *registrySuite) TestTxSetRecord() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
"request with invalid payload file arg",
|
||||
[]string{"bad-file", s.bondId},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"success",
|
||||
[]string{recordFilePath, s.bondId},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
cmd := []string{"tx", "registry", "set"}
|
||||
cmd = append(cmd, tc.args...)
|
||||
s.runTx(cmd, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testSuite) updateParams(params *registry.Params) {
|
||||
params.RecordRent = sdk.NewCoin(s.bondDenom, math.NewInt(1000))
|
||||
params.RecordRentDuration = 10 * time.Second
|
||||
|
||||
params.AuthorityRent = sdk.NewCoin(s.bondDenom, math.NewInt(1000))
|
||||
params.AuthorityGracePeriod = 10 * time.Second
|
||||
|
||||
params.AuthorityAuctionCommitFee = sdk.NewCoin(s.bondDenom, math.NewInt(100))
|
||||
params.AuthorityAuctionRevealFee = sdk.NewCoin(s.bondDenom, math.NewInt(100))
|
||||
params.AuthorityAuctionMinimumBid = sdk.NewCoin(s.bondDenom, math.NewInt(500))
|
||||
}
|
||||
|
||||
func UpdateGenesisRegistry(s *registrySuite) systest.GenesisMutator {
|
||||
require := s.Require()
|
||||
|
||||
return func(genesis []byte) []byte {
|
||||
var regState registry.GenesisState
|
||||
raw := gjson.Get(string(genesis), "app_state.registry").String()
|
||||
require.NoError(s.codec.UnmarshalJSON([]byte(raw), ®State))
|
||||
|
||||
s.updateParams(®State.Params)
|
||||
regStateBz, err := s.codec.MarshalJSON(®State)
|
||||
require.NoError(err)
|
||||
genesis, err = sjson.SetRawBytes(genesis, "app_state.registry", regStateBz)
|
||||
require.NoError(err)
|
||||
|
||||
// // add consensus-controlled module account(s)
|
||||
// bondinitmsg, err := codectypes.NewAnyWithValue(&nitro.MsgInitAccount{})
|
||||
// require.NoError(s.T(), err)
|
||||
// var accountsState accounts.GenesisState
|
||||
// raw = gjson.Get(string(genesis), "app_state.accounts").String()
|
||||
// require.NoError(s.T(), cdc.UnmarshalJSON([]byte(raw), &accountsState))
|
||||
// accountsState.InitAccountMsgs = []*accounts.MsgInit{
|
||||
// {
|
||||
// Sender: accountAddress,
|
||||
// AccountType: "nitro",
|
||||
// Message: bondinitmsg,
|
||||
// },
|
||||
// }
|
||||
// accountsStateBz, err := cdc.MarshalJSON(&accountsState)
|
||||
// require.NoError(s.T(), err)
|
||||
// genesis, err = sjson.SetRawBytes(genesis, "app_state.accounts", accountsStateBz)
|
||||
// require.NoError(s.T(), err)
|
||||
|
||||
return genesis
|
||||
}
|
||||
}
|
||||
|
||||
func (s *registrySuite) reserveName(authorityName string) {
|
||||
cmd := []string{
|
||||
"tx", "registry", "reserve-authority", authorityName, s.accountAddress,
|
||||
}
|
||||
s.runTx(cmd, false)
|
||||
}
|
||||
|
||||
func (s *registrySuite) createNameRecord(authorityName string) {
|
||||
for _, cmd := range [][]string{
|
||||
// reserve name authority
|
||||
{"tx", "registry", "reserve-authority", authorityName, s.accountAddress},
|
||||
// add bond-id to name authority
|
||||
{"tx", "registry", "authority-bond", authorityName, s.bondId},
|
||||
// create actual name record
|
||||
{"tx", "registry", "set-name", fmt.Sprintf("lrn://%s/", authorityName), "test_hello_cid"},
|
||||
} {
|
||||
s.runTx(cmd, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *registrySuite) createRecord(bondId string) {
|
||||
s.T().Helper()
|
||||
cmd := []string{
|
||||
"tx", "registry", "set", recordFilePath, bondId,
|
||||
}
|
||||
s.runTx(cmd, false)
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
systest "cosmossdk.io/systemtests"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"git.vdb.to/cerc-io/laconicd/app/params"
|
||||
)
|
||||
|
||||
type testSuite struct {
|
||||
suite.Suite
|
||||
|
||||
codec codec.Codec
|
||||
accountNamePrefix string
|
||||
fees string
|
||||
bondDenom string
|
||||
|
||||
clientEthKey string
|
||||
validatorEthKey string
|
||||
}
|
||||
|
||||
func (s *testSuite) SetupSuite() {
|
||||
s.codec = codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
|
||||
s.accountNamePrefix = "node" // from testnet (--node-dir-prefix)
|
||||
s.fees = "1" + params.CoinUnit
|
||||
s.bondDenom = sdk.DefaultBondDenom
|
||||
|
||||
s.validatorEthKey = "validator-eth-key"
|
||||
s.clientEthKey = "client-eth-key"
|
||||
|
||||
// configure the validators and client
|
||||
commonInitCommands := [][]string{
|
||||
{"config", "set", "client", "chain-id", "testing"},
|
||||
{"config", "set", "client", "keyring-backend", "test"},
|
||||
}
|
||||
|
||||
// disable nitro on validators by default
|
||||
Sut.ForEachNodeExecAndWait(s.T(), append(commonInitCommands,
|
||||
[]string{"config", "set", "app", "nitro.enable", "false"},
|
||||
)...)
|
||||
|
||||
clientInitCommands := append(commonInitCommands,
|
||||
[]string{"config", "set", "app", "nitro.eth-key", s.clientEthKey},
|
||||
)
|
||||
for _, cmd := range clientInitCommands {
|
||||
cmd = append(cmd, "--home", s.clientHome())
|
||||
_ = systest.MustRunShellCmd(s.T(), Sut.ExecBinary(), cmd...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testSuite) setValidatorNitroKeys(keys ...string) {
|
||||
Sut.WithEachNodeHome(func(i int, home string) {
|
||||
var cmds [][]string
|
||||
// TODO: complete and enable distsig
|
||||
// []string{"config", "set", "app", "distsig.enable", "true"},
|
||||
// []string{"config", "set", "app", "distsig.longterm-key", s.validatorNitroKey},
|
||||
// []string{"config", "set", "app", "nitro.use-distsig", "true"},
|
||||
|
||||
if i < len(keys) {
|
||||
// configure validator as a payment channel (individual) counterparty
|
||||
cmds = [][]string{
|
||||
{"config", "set", "app", "nitro.enable", "true"},
|
||||
|
||||
{"keys", "import-hex", s.validatorEthKey, keys[i]},
|
||||
{"config", "set", "app", "nitro.eth-key", s.validatorEthKey},
|
||||
}
|
||||
}
|
||||
|
||||
for _, cmd := range cmds {
|
||||
systest.MustRunShellCmd(s.T(), Sut.ExecBinary(), append(cmd, "--home", home)...)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// note: keys have to be created after ResetChain, which clears the keyring
|
||||
func (s *testSuite) setClientKeys(ethKey string) {
|
||||
commands := [][]string{
|
||||
{"keys", "import-hex", s.clientEthKey, ethKey},
|
||||
}
|
||||
for _, cmd := range commands {
|
||||
cmd = append(cmd, "--home", s.clientHome())
|
||||
_ = systest.MustRunShellCmd(s.T(), Sut.ExecBinary(), cmd...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testSuite) resetChain() {
|
||||
Sut.ResetChain(s.T())
|
||||
// clear the nitro data dirs
|
||||
Sut.WithEachNodeHome(func(i int, home string) {
|
||||
_ = os.RemoveAll(filepath.Join(home, "nitro"))
|
||||
|
||||
})
|
||||
_ = os.RemoveAll(filepath.Join(s.clientHome(), "nitro"))
|
||||
}
|
||||
|
||||
func (s *testSuite) account(i int) string {
|
||||
return fmt.Sprintf("%s%d", s.accountNamePrefix, i)
|
||||
}
|
||||
|
||||
// the home dir systemtests uses to run client commands
|
||||
func (s *testSuite) clientHome() string {
|
||||
return filepath.Join(workDir, "testnet")
|
||||
}
|
||||
|
||||
func (s *testSuite) endpointURL(endpoint string) string {
|
||||
return fmt.Sprintf("%s/cerc/%s", Sut.APIAddress(), endpoint)
|
||||
}
|
||||
|
||||
func (s *testSuite) cli() *systest.CLIWrapper {
|
||||
cli := systest.NewCLIWrapper(s.T(), Sut, verbose).
|
||||
WithRunStderr(os.Stderr)
|
||||
return &cli
|
||||
}
|
||||
|
||||
func (s *testSuite) assertWithStderr(asserter systest.RunErrorAssert) systest.RunErrorAssert {
|
||||
return func(t assert.TestingT, err error, msgAndArgs ...any) bool {
|
||||
if asserter(t, err, msgAndArgs...) {
|
||||
return true
|
||||
}
|
||||
var exitError *exec.ExitError
|
||||
if errors.As(err, &exitError) {
|
||||
if stderr := exitError.Stderr; len(stderr) > 0 {
|
||||
defer t.Errorf("stderr: %s", stderr)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testSuite) runTx(cmd []string, err bool) string {
|
||||
s.T().Helper()
|
||||
cli := s.cli()
|
||||
asserter := assert.NoError
|
||||
if err {
|
||||
asserter = assert.Error
|
||||
}
|
||||
// don't use Run(), it will clobber our custom --home
|
||||
out := cli.
|
||||
WithRunErrorMatcher(s.assertWithStderr(asserter)).
|
||||
RunCommandWithArgs(s.withTxFlags(cmd)...)
|
||||
if err {
|
||||
return ""
|
||||
}
|
||||
txResult, committed := cli.AwaitTxCommitted(out, txTimeout)
|
||||
s.Require().True(committed)
|
||||
systest.RequireTxSuccess(s.T(), txResult)
|
||||
txHash := gjson.Get(out, "txhash")
|
||||
s.Require().True(txHash.Exists(), "txhash not found in output: %s", out)
|
||||
return txHash.String()
|
||||
}
|
||||
|
||||
func (s *testSuite) withTxFlags(cmd []string) []string {
|
||||
cmd = s.cli().WithTXFlags(cmd...)
|
||||
return append(cmd, []string{
|
||||
"--from", s.account(0),
|
||||
"--fees", s.fees,
|
||||
}...)
|
||||
}
|
||||
Reference in new issue
Block a user