Skip to content

Commit

Permalink
testutils refactors and cleanup (#472)
Browse files Browse the repository at this point in the history
* changes

* mas

* Update README.md

* Update README.md

* sorry for the friday night emails

* Update instance_test.go

* util naming
  • Loading branch information
shaspitz authored Nov 15, 2022
1 parent 0096317 commit 33137b3
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 158 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ Inspect the [Makefile](./Makefile) if curious.

### Unit Tests

Unit tests are useful for simple standalone functionality, and CRUD operations. Unit tests should use golang's standard testing package, and be defined in files formatted as ```<file being tested>_test.go``` in the same directory as the file being tested, following standard conventions.
Unit tests are useful for simple standalone functionality, and CRUD operations. Unit tests should use golang's standard testing package, and be defined in files formatted as ```<file being tested>_test.go``` in the same directory as the file being tested, following standard conventions.

[Mocked external keepers](./testutil/keeper/mocks.go) (implemented with [gomock](https://github.com/golang/mock)) are available for testing more complex functionality, but still only relevant to execution within a single node. Ie. no internode or interchain communication.
[Mocked external keepers](./testutil/keeper/mocks.go) (implemented with [gomock](https://github.com/golang/mock)) are available for testing code that briefly interacts with external modules, but still only a single function/method relevant to ccv, and a single chain. Ie. do not use mocked external keepers to test the integration of the ccv module with external modules, or integration between consumer and provider.

### End to End (e2e) Tests

[e2e-tests](./tests/e2e/) utilize the [IBC Testing Package](https://github.com/cosmos/ibc-go/tree/main/testing), and test functionality that is wider in scope than a unit test, but still able to be validated in-memory. Ie. code where advancing blocks would be useful, simulated handshakes, simulated packet relays, etc.

To run e2e tests against your own consumer/provider implementations, use [instance_test.go](./tests/e2e/instance_test.go) as an example. All you'll need to do is make sure your applications implement the necessary interfaces defined in [interfaces.go](./testutil/e2e/interfaces.go), then pass in an appropriate callback to the testing suites.
To run e2e tests against your own consumer/provider implementations, use [instance_test.go](./tests/e2e/instance_test.go) as an example. All you'll need to do is make sure your applications implement the necessary interfaces defined in [interfaces.go](./testutil/e2e/interfaces.go), pattern match [specific_setup.go](./testutil/ibc_testing/specific_setup.go), then pass in the appropriate types and parameters to the suite, as is done in `instance_test.go` for the dummy provider/consumer implementations.

### Differential Tests (WIP)

Expand Down
5 changes: 3 additions & 2 deletions app/consumer-democracy/proposals_whitelisting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import (

ibctesting "github.com/cosmos/ibc-go/v3/testing"
appConsumer "github.com/cosmos/interchain-security/app/consumer-democracy"
simapp "github.com/cosmos/interchain-security/testutil/simapp"
icstestingutils "github.com/cosmos/interchain-security/testutil/ibc_testing"
"github.com/stretchr/testify/require"
)

func TestDemocracyGovernanceWhitelistingKeys(t *testing.T) {
chain := ibctesting.NewTestChain(t, simapp.NewBasicCoordinator(t), simapp.SetupTestingAppConsumerDemocracy, "test")
chain := ibctesting.NewTestChain(t, ibctesting.NewCoordinator(t, 0),
icstestingutils.DemocracyConsumerAppIniter, "test")
paramKeeper := chain.App.(*appConsumer.App).ParamsKeeper
for paramKey := range appConsumer.WhitelistedParams {
ss, ok := paramKeeper.GetSubspace(paramKey.Subspace)
Expand Down
11 changes: 5 additions & 6 deletions tests/difference/core/driver/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/interchain-security/x/ccv/types"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
Expand All @@ -23,7 +22,6 @@ import (

cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
simapp "github.com/cosmos/interchain-security/testutil/simapp"

cryptoEd25519 "crypto/ed25519"

Expand All @@ -39,6 +37,7 @@ import (
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
appConsumer "github.com/cosmos/interchain-security/app/consumer"
appProvider "github.com/cosmos/interchain-security/app/provider"
icstestingutils "github.com/cosmos/interchain-security/testutil/ibc_testing"
simibc "github.com/cosmos/interchain-security/testutil/simibc"
consumerkeeper "github.com/cosmos/interchain-security/x/ccv/consumer/keeper"
consumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types"
Expand Down Expand Up @@ -337,14 +336,14 @@ func (b *Builder) createValidators() (*tmtypes.ValidatorSet, map[string]tmtypes.

func (b *Builder) createChains() {

coordinator := simapp.NewBasicCoordinator(b.suite.T())
coordinator := ibctesting.NewCoordinator(b.suite.T(), 0)

// Create validators
validators, signers, addresses := b.createValidators()
// Create provider
coordinator.Chains[ibctesting.GetChainID(0)] = b.newChain(coordinator, simapp.SetupTestingappProvider, ibctesting.GetChainID(0), validators, signers)
coordinator.Chains[ibctesting.GetChainID(0)] = b.newChain(coordinator, icstestingutils.ProviderAppIniter, ibctesting.GetChainID(0), validators, signers)
// Create consumer, using the same validators.
coordinator.Chains[ibctesting.GetChainID(1)] = b.newChain(coordinator, simapp.SetupTestingAppConsumer, ibctesting.GetChainID(1), validators, signers)
coordinator.Chains[ibctesting.GetChainID(1)] = b.newChain(coordinator, icstestingutils.ConsumerAppIniter, ibctesting.GetChainID(1), validators, signers)

b.coordinator = coordinator
b.valAddresses = addresses
Expand Down Expand Up @@ -539,7 +538,7 @@ func (b *Builder) sendEmptyVSCPacketToFinishHandshake() {

timeout := uint64(b.chain(P).CurrentHeader.Time.Add(ccv.DefaultCCVTimeoutPeriod).UnixNano())

pd := types.NewValidatorSetChangePacketData(
pd := ccv.NewValidatorSetChangePacketData(
[]abci.ValidatorUpdate{},
vscID,
nil,
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ E2e tests are categorized into files as follows:
- `normal_operations.go` - e2e tests for _normal operations_ of ICS enabled chains
- `instance_test.go` - ties the e2e test structure into golang's standard test mechanism, with appropriate definitions for concrete app types and setup callback

To run the e2e tests defined in this repo on any arbitrary consumer and provider implementation, copy the pattern exemplified in `instance_test.go`
To run the e2e tests defined in this repo on any arbitrary consumer and provider implementation, copy the pattern exemplified in `instance_test.go` and `specific_setup.go`
38 changes: 27 additions & 11 deletions tests/e2e/democracy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import (
"time"

sdk "github.com/cosmos/cosmos-sdk/types"

ibctesting "github.com/cosmos/ibc-go/v3/testing"
icstestingutils "github.com/cosmos/interchain-security/testutil/ibc_testing"

authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/cosmos/cosmos-sdk/x/params/types/proposal"
proposaltypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal"
"github.com/cosmos/interchain-security/testutil/e2e"
e2eutil "github.com/cosmos/interchain-security/testutil/e2e"
consumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types"
"github.com/stretchr/testify/suite"
)
Expand All @@ -24,15 +23,32 @@ type ConsumerDemocracyTestSuite struct {
suite.Suite
coordinator *ibctesting.Coordinator
consumerChain *ibctesting.TestChain
consumerApp e2e.DemocConsumerApp
consumerApp e2eutil.DemocConsumerApp
setupCallback DemocSetupCallback
}

// NewCCVTestSuite returns a new instance of ConsumerDemocracyTestSuite,
// ready to be tested against using suite.Run().
func NewConsumerDemocracyTestSuite(setupCallback DemocSetupCallback) *ConsumerDemocracyTestSuite {
func NewConsumerDemocracyTestSuite[T e2eutil.DemocConsumerApp](
democConsumerAppIniter ibctesting.AppIniter) *ConsumerDemocracyTestSuite {

democSuite := new(ConsumerDemocracyTestSuite)
democSuite.setupCallback = setupCallback

democSuite.setupCallback = func(t *testing.T) (
*ibctesting.Coordinator,
*ibctesting.TestChain,
e2eutil.DemocConsumerApp,
) {
// Instantiate the test coordinator
coordinator := ibctesting.NewCoordinator(t, 0)

// Add single democracy consumer to coordinator, store returned test chain and app.
democConsumer, democConsumerApp := icstestingutils.AddDemocracyConsumer[T](
coordinator, t, democConsumerAppIniter)

// Pass variables to suite.
return coordinator, democConsumer, democConsumerApp
}
return democSuite
}

Expand All @@ -41,7 +57,7 @@ func NewConsumerDemocracyTestSuite(setupCallback DemocSetupCallback) *ConsumerDe
type DemocSetupCallback func(t *testing.T) (
coord *ibctesting.Coordinator,
consumerChain *ibctesting.TestChain,
consumerApp e2e.DemocConsumerApp,
consumerApp e2eutil.DemocConsumerApp,
)

// SetupTest sets up in-mem state before every test relevant to ccv with a democracy consumer
Expand Down Expand Up @@ -138,8 +154,8 @@ func (s *ConsumerDemocracyTestSuite) TestDemocracyGovernanceWhitelisting() {
mintKeeper := s.consumerApp.GetE2eMintKeeper()
newAuthParamValue := uint64(128)
newMintParamValue := sdk.NewDecWithPrec(1, 1) // "0.100000000000000000"
allowedChange := proposal.ParamChange{Subspace: minttypes.ModuleName, Key: "InflationMax", Value: fmt.Sprintf("\"%s\"", newMintParamValue)}
forbiddenChange := proposal.ParamChange{Subspace: authtypes.ModuleName, Key: "MaxMemoCharacters", Value: fmt.Sprintf("\"%s\"", strconv.FormatUint(newAuthParamValue, 10))}
allowedChange := proposaltypes.ParamChange{Subspace: minttypes.ModuleName, Key: "InflationMax", Value: fmt.Sprintf("\"%s\"", newMintParamValue)}
forbiddenChange := proposaltypes.ParamChange{Subspace: authtypes.ModuleName, Key: "MaxMemoCharacters", Value: fmt.Sprintf("\"%s\"", strconv.FormatUint(newAuthParamValue, 10))}
votingAccounts := s.consumerChain.SenderAccounts
bondDenom := stakingKeeper.BondDenom(s.consumerCtx())
depositAmount := govKeeper.GetDepositParams(s.consumerCtx()).MinDeposit
Expand Down Expand Up @@ -203,7 +219,7 @@ func (s *ConsumerDemocracyTestSuite) TestDemocracyGovernanceWhitelisting() {
s.Assert().Equal(votersOldBalances, getAccountsBalances(s.consumerCtx(), bankKeeper, bondDenom, votingAccounts))
}

func submitProposalWithDepositAndVote(govKeeper e2e.E2eGovKeeper, ctx sdk.Context, paramChange proposaltypes.ParameterChangeProposal,
func submitProposalWithDepositAndVote(govKeeper e2eutil.E2eGovKeeper, ctx sdk.Context, paramChange proposaltypes.ParameterChangeProposal,
accounts []ibctesting.SenderAccount, depositAmount sdk.Coins) error {
proposal, err := govKeeper.SubmitProposal(ctx, &paramChange)
if err != nil {
Expand All @@ -223,7 +239,7 @@ func submitProposalWithDepositAndVote(govKeeper e2e.E2eGovKeeper, ctx sdk.Contex
return nil
}

func getAccountsBalances(ctx sdk.Context, bankKeeper e2e.E2eBankKeeper, bondDenom string, accounts []ibctesting.SenderAccount) map[string]sdk.Int {
func getAccountsBalances(ctx sdk.Context, bankKeeper e2eutil.E2eBankKeeper, bondDenom string, accounts []ibctesting.SenderAccount) map[string]sdk.Int {
accountsBalances := map[string]sdk.Int{}
for _, acc := range accounts {
accountsBalances[string(acc.SenderAccount.GetAddress())] =
Expand Down
52 changes: 19 additions & 33 deletions tests/e2e/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,43 @@ package e2e_test
import (
"testing"

ibctesting "github.com/cosmos/ibc-go/v3/testing"
appConsumer "github.com/cosmos/interchain-security/app/consumer"
appConsumerDemocracy "github.com/cosmos/interchain-security/app/consumer-democracy"
appProvider "github.com/cosmos/interchain-security/app/provider"
"github.com/cosmos/interchain-security/tests/e2e"
e2etestutil "github.com/cosmos/interchain-security/testutil/e2e"
"github.com/cosmos/interchain-security/testutil/simapp"
icstestingutils "github.com/cosmos/interchain-security/testutil/ibc_testing"
"github.com/stretchr/testify/suite"
)

// This file can be used as an example e2e testing instance for any provider/consumer applications.
// In the case of this repo, we're testing the dummy provider/consumer applications,
// but to test any arbitrary app, you only need to replicate this file, and pass in the
// appropriate callback to the testing suites. Note that any provider/consumer applications
// must implement the interfaces defined in /testutil/e2e/interfaces.go
// but to test any arbitrary app, one only needs to replicate this file and "specific_setup.go",
// then pass in the appropriate types and parameters to the suite. Note that provider and consumer
// applications types must implement the interfaces defined in /testutil/e2e/interfaces.go to compile.

// Executes the standard group of ccv tests against a consumer and provider app.go implementation.
func TestCCVTestSuite(t *testing.T) {

ccvSuite := e2e.NewCCVTestSuite(
func(t *testing.T) (
*ibctesting.Coordinator,
*ibctesting.TestChain,
*ibctesting.TestChain,
e2etestutil.ProviderApp,
e2etestutil.ConsumerApp,
) {
// Here we pass the concrete types that must implement the necessary interfaces
// to be ran with e2e tests.
coord, prov, cons := simapp.NewProviderConsumerCoordinator(t)
return coord, prov, cons, prov.App.(*appProvider.App), cons.App.(*appConsumer.App)
},
)
// Pass in concrete app types that implement the interfaces defined in /testutil/e2e/interfaces.go
ccvSuite := e2e.NewCCVTestSuite[*appProvider.App, *appConsumer.App](
// Pass in ibctesting.AppIniters for provider and consumer.
icstestingutils.ProviderAppIniter, icstestingutils.ConsumerAppIniter)

// Run tests
suite.Run(t, ccvSuite)
}

// TODO: Run the gov enabled consumer against the standard suite of tests: https://github.com/cosmos/interchain-security/issues/397

// Executes a specialized group of tests specific to a democracy consumer, against a democracy consumer app.go implementation.
// Executes a specialized group of tests specific to a democracy consumer,
// against a democracy consumer app.go implementation.
func TestConsumerDemocracyTestSuite(t *testing.T) {
democSuite := e2e.NewConsumerDemocracyTestSuite(
func(t *testing.T) (
*ibctesting.Coordinator,
*ibctesting.TestChain,
e2etestutil.DemocConsumerApp,
) {
// Here we pass the concrete types that must implement the necessary interfaces
// to be ran with e2e tests.
coord, _, cons := simapp.NewProviderConsumerDemocracyCoordinator(t)
return coord, cons, cons.App.(*appConsumerDemocracy.App)
},
)

// Pass in concrete app type that implement the interface defined in /testutil/e2e/interfaces.go
democSuite := e2e.NewConsumerDemocracyTestSuite[*appConsumerDemocracy.App](
// Pass in ibctesting.AppIniter for democracy consumer.
icstestingutils.DemocracyConsumerAppIniter)

// Run tests
suite.Run(t, democSuite)
}
46 changes: 36 additions & 10 deletions tests/e2e/setup.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package e2e

import (
ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types"

"bytes"
"testing"

e2e "github.com/cosmos/interchain-security/testutil/e2e"
ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types"
e2eutil "github.com/cosmos/interchain-security/testutil/e2e"
icstestingutils "github.com/cosmos/interchain-security/testutil/ibc_testing"

ccv "github.com/cosmos/interchain-security/x/ccv/types"
"github.com/cosmos/interchain-security/x/ccv/utils"
Expand All @@ -29,28 +29,54 @@ type CCVTestSuite struct {
coordinator *ibctesting.Coordinator
providerChain *ibctesting.TestChain
consumerChain *ibctesting.TestChain
providerApp e2e.ProviderApp
consumerApp e2e.ConsumerApp
providerApp e2eutil.ProviderApp
consumerApp e2eutil.ConsumerApp
path *ibctesting.Path
transferPath *ibctesting.Path
setupCallback SetupCallback
}

// NewCCVTestSuite returns a new instance of CCVTestSuite, ready to be tested against using suite.Run().
func NewCCVTestSuite(setupCallback SetupCallback) *CCVTestSuite {
func NewCCVTestSuite[Tp e2eutil.ProviderApp, Tc e2eutil.ConsumerApp](
providerAppIniter ibctesting.AppIniter, consumerAppIniter ibctesting.AppIniter) *CCVTestSuite {

ccvSuite := new(CCVTestSuite)
ccvSuite.setupCallback = setupCallback

ccvSuite.setupCallback = func(t *testing.T) (
*ibctesting.Coordinator,
*ibctesting.TestChain,
*ibctesting.TestChain,
e2eutil.ProviderApp,
e2eutil.ConsumerApp,
) {
// Instantiate the test coordinator.
coordinator := ibctesting.NewCoordinator(t, 0)

// Add provider to coordinator, store returned test chain and app.
// Concrete provider app type is passed to the generic function here.
provider, providerApp := icstestingutils.AddProvider[Tp](
coordinator, t, providerAppIniter)

// Add specified number of consumers to coordinator, store returned test chains and apps.
// Concrete consumer app type is passed to the generic function here.
consumers, consumerApps := icstestingutils.AddConsumers[Tc](
coordinator, t, 1, consumerAppIniter)

// Pass variables to suite.
// TODO: accept multiple consumers here
return coordinator, provider, consumers[0], providerApp, consumerApps[0]
}
return ccvSuite
}

// Callback for instantiating a new coordinator, provider/consumer test chains, and provider/consumer app
// Callback for instantiating a new coordinator, provider/consumer test chains, and provider/consumer apps
// before every test defined on the suite.
type SetupCallback func(t *testing.T) (
coord *ibctesting.Coordinator,
providerChain *ibctesting.TestChain,
consumerChain *ibctesting.TestChain,
providerApp e2e.ProviderApp,
consumerApp e2e.ConsumerApp,
providerApp e2eutil.ProviderApp,
consumerApp e2eutil.ConsumerApp,
)

// SetupTest sets up in-mem state before every test
Expand Down
Loading

0 comments on commit 33137b3

Please sign in to comment.