From 943f0518e60ad07bf459afa0d49129b03b765794 Mon Sep 17 00:00:00 2001 From: stana-ethernal Date: Fri, 2 Feb 2024 15:09:27 +0100 Subject: [PATCH] integration tests --- app/monitored/app.go | 28 +++++ app/registry/app.go | 28 +++++ tests/integration/healthcheck_test.go | 116 +++++++++++++++++++++ tests/integration/setup.go | 141 ++++++++++++++++++++++++++ x/healthcheck/module.go | 2 +- x/monitored/module_ibc.go | 5 - 6 files changed, 314 insertions(+), 6 deletions(-) create mode 100644 tests/integration/healthcheck_test.go create mode 100644 tests/integration/setup.go diff --git a/app/monitored/app.go b/app/monitored/app.go index ee4a4eb..a2fc175 100644 --- a/app/monitored/app.go +++ b/app/monitored/app.go @@ -96,6 +96,7 @@ import ( ibcporttypes "github.com/cosmos/ibc-go/v6/modules/core/05-port/types" ibchost "github.com/cosmos/ibc-go/v6/modules/core/24-host" ibckeeper "github.com/cosmos/ibc-go/v6/modules/core/keeper" + ibctestingtypes "github.com/cosmos/ibc-go/v6/testing/types" "github.com/spf13/cast" abci "github.com/tendermint/tendermint/abci/types" tmjson "github.com/tendermint/tendermint/libs/json" @@ -907,3 +908,30 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino func (app *App) SimulationManager() *module.SimulationManager { return app.sm } + +// TestingApp functions + +// GetBaseApp implements the TestingApp interface. +func (app *App) GetBaseApp() *baseapp.BaseApp { + return app.BaseApp +} + +// GetStakingKeeper implements the TestingApp interface. +func (app *App) GetStakingKeeper() ibctestingtypes.StakingKeeper { + return app.StakingKeeper +} + +// GetIBCKeeper implements the TestingApp interface. +func (app *App) GetIBCKeeper() *ibckeeper.Keeper { + return app.IBCKeeper +} + +// GetScopedIBCKeeper implements the TestingApp interface. +func (app *App) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { + return app.ScopedIBCKeeper +} + +// GetTxConfig implements the TestingApp interface. +func (app *App) GetTxConfig() client.TxConfig { + return MakeEncodingConfig().TxConfig +} diff --git a/app/registry/app.go b/app/registry/app.go index af5d612..e14febc 100644 --- a/app/registry/app.go +++ b/app/registry/app.go @@ -96,6 +96,7 @@ import ( ibcporttypes "github.com/cosmos/ibc-go/v6/modules/core/05-port/types" ibchost "github.com/cosmos/ibc-go/v6/modules/core/24-host" ibckeeper "github.com/cosmos/ibc-go/v6/modules/core/keeper" + ibctestingtypes "github.com/cosmos/ibc-go/v6/testing/types" "github.com/spf13/cast" abci "github.com/tendermint/tendermint/abci/types" tmjson "github.com/tendermint/tendermint/libs/json" @@ -911,3 +912,30 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino func (app *App) SimulationManager() *module.SimulationManager { return app.sm } + +// TestingApp functions + +// GetBaseApp implements the TestingApp interface. +func (app *App) GetBaseApp() *baseapp.BaseApp { + return app.BaseApp +} + +// GetStakingKeeper implements the TestingApp interface. +func (app *App) GetStakingKeeper() ibctestingtypes.StakingKeeper { + return app.StakingKeeper +} + +// GetIBCKeeper implements the TestingApp interface. +func (app *App) GetIBCKeeper() *ibckeeper.Keeper { + return app.IBCKeeper +} + +// GetScopedIBCKeeper implements the TestingApp interface. +func (app *App) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { + return app.ScopedIBCKeeper +} + +// GetTxConfig implements the TestingApp interface. +func (app *App) GetTxConfig() client.TxConfig { + return MakeEncodingConfig().TxConfig +} diff --git a/tests/integration/healthcheck_test.go b/tests/integration/healthcheck_test.go new file mode 100644 index 0000000..00126e7 --- /dev/null +++ b/tests/integration/healthcheck_test.go @@ -0,0 +1,116 @@ +package integration + +import ( + registryTypes "healthcheck/x/healthcheck/types" + "healthcheck/x/monitored/types" + commonTypes "healthcheck/x/types" + "testing" + "time" + + clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v6/testing" + "github.com/stretchr/testify/suite" +) + +func TestHealthcheckTestSuite(t *testing.T) { + // Run tests + suite.Run(t, NewHealthcheckTestSuite()) +} + +func (s *HealthcheckTestSuite) TestHealthcheck() { + + //Send few healthcheck packets + for i := 0; i < 5; i++ { + ctx := s.monitoredChain.GetContext() + monitoredBlockTime := ctx.BlockTime() + monitoredBlockHeight := uint64(ctx.BlockHeight()) + packet := commonTypes.HealthcheckUpdateData{ + Block: monitoredBlockHeight, + Timestamp: uint64(monitoredBlockTime.UnixNano()), + } + packetData, err := types.ModuleCdc.MarshalJSON(&packet) + s.Require().Nil(err) + + timeout := uint64(monitoredBlockTime.Add(time.Hour).UnixNano()) //packet timeout + sendHealthcheckPacket(s, s.path, timeout, packetData) + + monitoredChainEntry, found := s.registryApp.HealthcheckKeeper.GetMonitoredChain(s.registryChain.GetContext(), MonitoredChainID) + s.Require().True(found) + s.Require().True(monitoredBlockHeight == monitoredChainEntry.Status.Block) + s.Require().True(uint64(monitoredBlockTime.UnixNano()) == monitoredChainEntry.Status.Timestamp) + } +} + +func (s *HealthcheckTestSuite) TestHealthcheckTimeout() { + + //Send one healthcheck packet + ctx := s.monitoredChain.GetContext() + monitoredBlockTime := ctx.BlockTime() + monitoredBlockHeight := uint64(ctx.BlockHeight()) + packet := commonTypes.HealthcheckUpdateData{ + Block: monitoredBlockHeight, + Timestamp: uint64(monitoredBlockTime.UnixNano()), + } + packetData, err := types.ModuleCdc.MarshalJSON(&packet) + s.Require().Nil(err) + + timeout := uint64(monitoredBlockTime.Add(time.Hour).UnixNano()) //packet timeout + sendHealthcheckPacket(s, s.path, timeout, packetData) + + monitoredChainEntry, found := s.registryApp.HealthcheckKeeper.GetMonitoredChain(s.registryChain.GetContext(), MonitoredChainID) + s.Require().True(found) + s.Require().True(monitoredBlockHeight == monitoredChainEntry.Status.Block) + s.Require().True(uint64(monitoredBlockTime.UnixNano()) == monitoredChainEntry.Status.Timestamp) + s.Require().True(monitoredChainEntry.Status.Status == string(registryTypes.Active)) + channelID, found := s.registryApp.HealthcheckKeeper.GetChainToChannelMap(s.registryChain.GetContext(), MonitoredChainID) + s.Require().True(found) + channel, found := s.registryApp.GetIBCKeeper().ChannelKeeper.GetChannel(s.registryChain.GetContext(), commonTypes.HealthcheckPortID, channelID) + s.Require().True(found) + s.Require().True(channel.State == channeltypes.OPEN) + updateInterval := int(monitoredChainEntry.UpdateInterval) + timeoutInterval := int(monitoredChainEntry.TimeoutInterval) + + //Move registry chain to reach update interval which will set monitored chain to inactive + for i := 0; i <= updateInterval; i++ { + s.coordinator.CommitBlock(s.registryChain) + } + + monitoredChainEntry, found = s.registryApp.HealthcheckKeeper.GetMonitoredChain(s.registryChain.GetContext(), MonitoredChainID) + s.Require().True(found) + s.Require().True(monitoredChainEntry.Status.Status == string(registryTypes.Inactive)) + channelID, found = s.registryApp.HealthcheckKeeper.GetChainToChannelMap(s.registryChain.GetContext(), MonitoredChainID) + s.Require().True(found) + channel, found = s.registryApp.GetIBCKeeper().ChannelKeeper.GetChannel(s.registryChain.GetContext(), commonTypes.HealthcheckPortID, channelID) + s.Require().True(found) + s.Require().True(channel.State == channeltypes.OPEN) + + //Move registry chain to reach timeout interval which will close the channel and remove it from chainToChannel map + for i := 0; i <= timeoutInterval; i++ { + s.coordinator.CommitBlock(s.registryChain) + } + channel, found = s.registryApp.GetIBCKeeper().ChannelKeeper.GetChannel(s.registryChain.GetContext(), commonTypes.HealthcheckPortID, channelID) + s.Require().True(found) + s.Require().True(channel.State == channeltypes.CLOSED) + _, found = s.registryApp.HealthcheckKeeper.GetChainToChannelMap(s.registryChain.GetContext(), MonitoredChainID) + s.Require().False(found) + +} + +func sendHealthcheckPacket(s *HealthcheckTestSuite, path *ibctesting.Path, timeoutTimestamp uint64, data []byte) channeltypes.Packet { + sequence, err := path.EndpointA.SendPacket(clienttypes.Height{}, timeoutTimestamp, data) + s.Require().NoError(err) + + packet := s.newHealthcheckPacket(data, sequence, path, clienttypes.Height{}, timeoutTimestamp) + + err = path.EndpointB.RecvPacket(packet) + s.Require().NoError(err) + return packet +} + +func (s *HealthcheckTestSuite) newHealthcheckPacket(data []byte, sequence uint64, path *ibctesting.Path, timeoutHeight clienttypes.Height, timeoutTimestamp uint64) channeltypes.Packet { + return channeltypes.NewPacket(data, sequence, + path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, + path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, + timeoutHeight, timeoutTimestamp) +} diff --git a/tests/integration/setup.go b/tests/integration/setup.go new file mode 100644 index 0000000..2b2c621 --- /dev/null +++ b/tests/integration/setup.go @@ -0,0 +1,141 @@ +package integration + +import ( + "encoding/json" + monitoredApp "healthcheck/app/monitored" + registryApp "healthcheck/app/registry" + registryTypes "healthcheck/x/healthcheck/types" + commonTypes "healthcheck/x/types" + "testing" + + ibctesting "github.com/cosmos/ibc-go/v6/testing" + "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" +) + +const ( + RegistryChainID = "registry" + MonitoredChainID = "monitored" +) + +type SetupRegistryCallback func(t *testing.T, coordinator *ibctesting.Coordinator) (*ibctesting.TestChain, *registryApp.App) +type SetupMonitoredCallback func(t *testing.T, coordinator *ibctesting.Coordinator) (*ibctesting.TestChain, *monitoredApp.App) + +type HealthcheckTestSuite struct { + suite.Suite + + setupRegistryCallback SetupRegistryCallback + setupMonitoredCallback SetupMonitoredCallback + + coordinator *ibctesting.Coordinator + registryChain *ibctesting.TestChain + monitoredChain *ibctesting.TestChain + registryApp *registryApp.App + monitoredApp *monitoredApp.App + + path *ibctesting.Path +} + +func NewHealthcheckTestSuite() *HealthcheckTestSuite { + healthcheckSuite := new(HealthcheckTestSuite) + + healthcheckSuite.setupRegistryCallback = func(t *testing.T, coordinator *ibctesting.Coordinator) ( + *ibctesting.TestChain, + *registryApp.App, + ) { + t.Helper() + ibctesting.DefaultTestingAppInit = SetupRegistryApp + registry := ibctesting.NewTestChain(t, coordinator, RegistryChainID) + coordinator.Chains[RegistryChainID] = registry + + return registry, registry.App.(*registryApp.App) + } + + healthcheckSuite.setupMonitoredCallback = func(t *testing.T, coordinator *ibctesting.Coordinator) ( + *ibctesting.TestChain, + *monitoredApp.App, + ) { + t.Helper() + ibctesting.DefaultTestingAppInit = SetupMonitoredApp + monitored := ibctesting.NewTestChain(t, coordinator, MonitoredChainID) + coordinator.Chains[MonitoredChainID] = monitored + + return monitored, monitored.App.(*monitoredApp.App) + } + + return healthcheckSuite +} + +// SetupTest sets up in-mem state before every test +func (suite *HealthcheckTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 0) + suite.registryChain, suite.registryApp = suite.setupRegistryCallback(suite.T(), suite.coordinator) + suite.monitoredChain, suite.monitoredApp = suite.setupMonitoredCallback(suite.T(), suite.coordinator) + + // create clients and connection + suite.path = ibctesting.NewPath(suite.monitoredChain, suite.registryChain) // clientID, connectionID, channelID empty + suite.coordinator.SetupConnections(suite.path) // clientID, connectionID + suite.Require().Equal("07-tendermint-0", suite.path.EndpointA.ClientID) + suite.Require().Equal("connection-0", suite.path.EndpointA.ConnectionID) + suite.Require().Equal("07-tendermint-0", suite.path.EndpointB.ClientID) + suite.Require().Equal("connection-0", suite.path.EndpointB.ConnectionID) + + //register monitored chain + var newMonitoredChain = registryTypes.MonitoredChain{ + ChainId: MonitoredChainID, + ConnectionId: suite.path.EndpointA.ConnectionID, + } + suite.registryApp.HealthcheckKeeper.SetMonitoredChain(suite.registryChain.GetContext(), newMonitoredChain) + + // - channel config + suite.path.EndpointA.ChannelConfig.PortID = commonTypes.MonitoredPortID + suite.path.EndpointB.ChannelConfig.PortID = commonTypes.HealthcheckPortID + suite.path.EndpointA.ChannelConfig.Version = commonTypes.Version + suite.path.EndpointB.ChannelConfig.Version = commonTypes.Version + suite.coordinator.CreateChannels(suite.path) // setup channel + suite.Require().Equal("channel-0", suite.path.EndpointA.ChannelID) + suite.Require().Equal("channel-0", suite.path.EndpointB.ChannelID) +} + +func SetupRegistryApp() (ibctesting.TestingApp, map[string]json.RawMessage) { + db := dbm.NewMemDB() + encCdc := registryApp.MakeEncodingConfig() + app := registryApp.New( + log.NewNopLogger(), + db, + nil, + true, + map[int64]bool{}, + registryApp.DefaultNodeHome, + 5, + encCdc, + EmptyAppOptions{}, + ) + return app, registryApp.NewDefaultGenesisState(encCdc.Marshaler) +} + +func SetupMonitoredApp() (ibctesting.TestingApp, map[string]json.RawMessage) { + db := dbm.NewMemDB() + encCdc := monitoredApp.MakeEncodingConfig() + app := monitoredApp.New( + log.NewNopLogger(), + db, + nil, + true, + map[int64]bool{}, + monitoredApp.DefaultNodeHome, + 5, + encCdc, + EmptyAppOptions{}, + ) + return app, monitoredApp.NewDefaultGenesisState(encCdc.Marshaler) +} + +// EmptyAppOptions is a stub implementing AppOptions +type EmptyAppOptions struct{} + +// Get implements AppOptions +func (ao EmptyAppOptions) Get(o string) interface{} { + return nil +} diff --git a/x/healthcheck/module.go b/x/healthcheck/module.go index 78a6721..57f2761 100644 --- a/x/healthcheck/module.go +++ b/x/healthcheck/module.go @@ -171,7 +171,7 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Val channelID, ok := am.keeper.GetChainToChannelMap(ctx, chain.ChainId) if ok { am.keeper.CloseChannel(ctx, channelID) - am.keeper.RemoveChanFromChainToChannelMap(ctx, channelID) + am.keeper.RemoveChanFromChainToChannelMap(ctx, chain.ChainId) } } diff --git a/x/monitored/module_ibc.go b/x/monitored/module_ibc.go index 2d486b9..8cc7dc7 100644 --- a/x/monitored/module_ibc.go +++ b/x/monitored/module_ibc.go @@ -207,10 +207,5 @@ func (im IBCModule) OnTimeoutPacket( modulePacket channeltypes.Packet, relayer sdk.AccAddress, ) error { - var packetData commonTypes.HealthcheckPacketData - if err := packetData.Unmarshal(modulePacket.GetData()); err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal packet data: %s", err.Error()) - } - return nil }