-
Notifications
You must be signed in to change notification settings - Fork 608
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(e2e): modular test runner (#1999)
* use should_run_go_test to skip e2e * attempt to disable upgrade * remporary remove get data from build cache * go setup * bring back cache * log for upgrade * invert the disable condition * comment * add more logic regarding skipping some e2e components * fix markdown lint * change branch to main * refactor to be able to run local version without building Docker image in CI * remove if on building local image * TestCreatePool runs only post-upgrade * lint * TestCreatePoolPostUpgrade * Document * restore git diff * Update tests/e2e/README.md Co-authored-by: Dev Ojha <[email protected]> * README updates * rename files; move validator configs, factory and constants to separate files * avoid exporting base configurer * all reference to "local" are replaced to" current branch" * fix formatting * move all docker logic to containers package * move hermes resource to container manager * move val resources to container manager * clear docker resources in manager * unexport hermes resource * configurer chain package * pass chain config to RunHermesResource * rename configurer chain to chain config consistently * unexport validator resources * unexport network * define commands and queries on chain config struct * lint * format * remove unused noRestart function * remove more redundant structs * refactor(e2e): modular test runner * restore test.yml * fix e2e_setup_test.go * space * readme * fix sf test * restore initialization * ibc skip * minimize diff and lint * Update tests/e2e/README.md Co-authored-by: Adam Tucker <[email protected]> * factory design pattern * Adam's suggestions Co-authored-by: Dev Ojha <[email protected]> Co-authored-by: Adam Tucker <[email protected]>
- Loading branch information
1 parent
1b5a1d3
commit 05375f8
Showing
16 changed files
with
1,286 additions
and
861 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
package configurer | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/require" | ||
rpchttp "github.com/tendermint/tendermint/rpc/client/http" | ||
|
||
"github.com/osmosis-labs/osmosis/v7/tests/e2e/configurer/chain" | ||
"github.com/osmosis-labs/osmosis/v7/tests/e2e/containers" | ||
"github.com/osmosis-labs/osmosis/v7/tests/e2e/initialization" | ||
"github.com/osmosis-labs/osmosis/v7/tests/e2e/util" | ||
) | ||
|
||
// baseConfigurer is the base implementation for the | ||
// other 2 types of configurers. It is not meant to be used | ||
// on its own. Instead, it is meant to be embedded | ||
// by composition into more concrete configurers. | ||
type baseConfigurer struct { | ||
chainConfigs []*chain.Config | ||
containerManager *containers.Manager | ||
setupTests setupFn | ||
syncUntilHeight int64 // the height until which to wait for validators to sync when first started. | ||
t *testing.T | ||
} | ||
|
||
// defaultSyncUntilHeight arbitrary small height to make sure the chain is making progress. | ||
const defaultSyncUntilHeight = 3 | ||
|
||
func (bc *baseConfigurer) ClearResources() error { | ||
bc.t.Log("tearing down e2e integration test suite...") | ||
|
||
if err := bc.containerManager.ClearResources(); err != nil { | ||
return err | ||
} | ||
|
||
for _, chainConfig := range bc.chainConfigs { | ||
os.RemoveAll(chainConfig.DataDir) | ||
} | ||
return nil | ||
} | ||
|
||
func (bc *baseConfigurer) GetChainConfig(chainIndex int) *chain.Config { | ||
return bc.chainConfigs[chainIndex] | ||
} | ||
|
||
func (bc *baseConfigurer) RunValidators() error { | ||
for _, chainConfig := range bc.chainConfigs { | ||
if err := bc.runValidators(chainConfig); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (bc *baseConfigurer) runValidators(chainConfig *chain.Config) error { | ||
bc.t.Logf("starting %s validator containers...", chainConfig.Id) | ||
|
||
for _, val := range chainConfig.NodeConfigs { | ||
resource, err := bc.containerManager.RunValidatorResource(chainConfig.Id, val.Name, val.ConfigDir) | ||
if err != nil { | ||
return err | ||
} | ||
bc.t.Logf("started %s validator container: %s", resource.Container.Name[1:], resource.Container.ID) | ||
} | ||
|
||
validatorHostPort, err := bc.containerManager.GetValidatorHostPort(chainConfig.Id, 0, "26657/tcp") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
rpcClient, err := rpchttp.New(fmt.Sprintf("tcp://%s", validatorHostPort), "/websocket") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
require.Eventually( | ||
bc.t, | ||
func() bool { | ||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) | ||
defer cancel() | ||
|
||
status, err := rpcClient.Status(ctx) | ||
if err != nil { | ||
return false | ||
} | ||
|
||
// let the node produce a few blocks | ||
if status.SyncInfo.CatchingUp && status.SyncInfo.LatestBlockHeight < bc.syncUntilHeight { | ||
return false | ||
} | ||
|
||
return true | ||
}, | ||
5*time.Minute, | ||
time.Second, | ||
"Osmosis node failed to produce blocks", | ||
) | ||
chainConfig.ExtractValidatorOperatorAddresses() | ||
return nil | ||
} | ||
|
||
func (bc *baseConfigurer) RunIBC() error { | ||
// Run a relayer between every possible pair of chains. | ||
for i := 0; i < len(bc.chainConfigs); i++ { | ||
for j := i + 1; j < len(bc.chainConfigs); j++ { | ||
if err := bc.runIBCRelayer(bc.chainConfigs[i], bc.chainConfigs[j]); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (bc *baseConfigurer) runIBCRelayer(chainConfigA *chain.Config, chainConfigB *chain.Config) error { | ||
bc.t.Log("starting Hermes relayer container...") | ||
|
||
tmpDir, err := os.MkdirTemp("", "osmosis-e2e-testnet-hermes-") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
hermesCfgPath := path.Join(tmpDir, "hermes") | ||
|
||
if err := os.MkdirAll(hermesCfgPath, 0o755); err != nil { | ||
return err | ||
} | ||
|
||
_, err = util.CopyFile( | ||
filepath.Join("./scripts/", "hermes_bootstrap.sh"), | ||
filepath.Join(hermesCfgPath, "hermes_bootstrap.sh"), | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
hermesResource, err := bc.containerManager.RunHermesResource( | ||
chainConfigA.Id, | ||
chainConfigA.NodeConfigs[0].Mnemonic, | ||
chainConfigB.Id, chainConfigB.NodeConfigs[0].Mnemonic, | ||
hermesCfgPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
endpoint := fmt.Sprintf("http://%s/state", hermesResource.GetHostPort("3031/tcp")) | ||
|
||
require.Eventually(bc.t, func() bool { | ||
resp, err := http.Get(endpoint) | ||
if err != nil { | ||
return false | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
bz, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return false | ||
} | ||
|
||
var respBody map[string]interface{} | ||
if err := json.Unmarshal(bz, &respBody); err != nil { | ||
return false | ||
} | ||
|
||
status, ok := respBody["status"].(string) | ||
require.True(bc.t, ok) | ||
result, ok := respBody["result"].(map[string]interface{}) | ||
require.True(bc.t, ok) | ||
|
||
chains, ok := result["chains"].([]interface{}) | ||
require.True(bc.t, ok) | ||
|
||
return status == "success" && len(chains) == 2 | ||
}, | ||
5*time.Minute, | ||
time.Second, | ||
"hermes relayer not healthy") | ||
|
||
bc.t.Logf("started Hermes relayer container: %s", bc.containerManager.GetHermesContainerID()) | ||
|
||
// XXX: Give time to both networks to start, otherwise we might see gRPC | ||
// transport errors. | ||
time.Sleep(10 * time.Second) | ||
|
||
// create the client, connection and channel between the two Osmosis chains | ||
return bc.connectIBCChains(chainConfigA, chainConfigB) | ||
} | ||
|
||
func (bc *baseConfigurer) connectIBCChains(chainA *chain.Config, chainB *chain.Config) error { | ||
bc.t.Logf("connecting %s and %s chains via IBC", chainA.ChainMeta.Id, chainB.ChainMeta.Id) | ||
cmd := []string{"hermes", "create", "channel", chainA.ChainMeta.Id, chainB.ChainMeta.Id, "--port-a=transfer", "--port-b=transfer"} | ||
_, _, err := bc.containerManager.ExecCmd(bc.t, "", 0, cmd, "successfully opened init channel") | ||
if err != nil { | ||
return err | ||
} | ||
bc.t.Logf("connected %s and %s chains via IBC", chainA.ChainMeta.Id, chainB.ChainMeta.Id) | ||
return nil | ||
} | ||
|
||
func (bc *baseConfigurer) initializeChainConfigFromInitChain(initializedChain *initialization.Chain, chainConfig *chain.Config) { | ||
chainConfig.ChainMeta = initializedChain.ChainMeta | ||
chainConfig.NodeConfigs = make([]*chain.ValidatorConfig, 0, len(initializedChain.Nodes)) | ||
for _, validator := range initializedChain.Nodes { | ||
chainConfig.NodeConfigs = append(chainConfig.NodeConfigs, &chain.ValidatorConfig{ | ||
Node: *validator, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package chain | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/osmosis-labs/osmosis/v7/tests/e2e/containers" | ||
"github.com/osmosis-labs/osmosis/v7/tests/e2e/initialization" | ||
) | ||
|
||
type Config struct { | ||
initialization.ChainMeta | ||
|
||
ValidatorInitConfigs []*initialization.NodeConfig | ||
// voting period is number of blocks it takes to deposit, 1.2 seconds per validator to vote on the prop, and a buffer. | ||
VotingPeriod float32 | ||
// upgrade proposal height for chain. | ||
PropHeight int | ||
LatestProposalNumber int | ||
LatestLockNumber int | ||
NodeConfigs []*ValidatorConfig | ||
|
||
t *testing.T | ||
containerManager *containers.Manager | ||
} | ||
|
||
type status struct { | ||
LatestHeight string `json:"latest_block_height"` | ||
} | ||
|
||
type syncInfo struct { | ||
SyncInfo status `json:"SyncInfo"` | ||
} | ||
|
||
func New(t *testing.T, containerManager *containers.Manager, id string, initValidatorConfigs []*initialization.NodeConfig) *Config { | ||
return &Config{ | ||
ChainMeta: initialization.ChainMeta{ | ||
Id: id, | ||
}, | ||
ValidatorInitConfigs: initValidatorConfigs, | ||
t: t, | ||
containerManager: containerManager, | ||
} | ||
} |
Oops, something went wrong.