From 91da53858bfcc533b148476ca4270295d91d89f6 Mon Sep 17 00:00:00 2001 From: marun Date: Fri, 2 Aug 2024 11:39:53 -0700 Subject: [PATCH] [e2e] Abstract usage of ginkgo with a new test context (#3254) --- tests/colors.go | 30 ----- tests/context_helpers.go | 32 +++++ tests/e2e/banff/suites.go | 39 +++--- tests/e2e/c/dynamic_fees.go | 56 ++++---- tests/e2e/c/interchain_workflow.go | 63 ++++----- tests/e2e/e2e_test.go | 3 +- tests/e2e/faultinjection/duplicate_node_id.go | 44 +++--- tests/e2e/p/interchain_workflow.go | 71 +++++----- tests/e2e/p/permissionless_subnets.go | 43 +++--- tests/e2e/p/staking_rewards.go | 110 +++++++-------- tests/e2e/p/validator_sets.go | 40 +++--- tests/e2e/p/workflow.go | 56 ++++---- tests/e2e/vms/xsvm.go | 46 +++---- tests/e2e/x/interchain_workflow.go | 53 ++++---- tests/e2e/x/transfer/virtuous.go | 38 +++--- tests/fixture/e2e/env.go | 74 +++++----- tests/fixture/e2e/ginkgo_test_context.go | 97 +++++++++++++ tests/fixture/e2e/helpers.go | 127 ++++++------------ tests/test_context.go | 42 ++++++ tests/upgrade/upgrade_test.go | 19 +-- 20 files changed, 611 insertions(+), 472 deletions(-) delete mode 100644 tests/colors.go create mode 100644 tests/context_helpers.go create mode 100644 tests/fixture/e2e/ginkgo_test_context.go create mode 100644 tests/test_context.go diff --git a/tests/colors.go b/tests/colors.go deleted file mode 100644 index 84b406560c4..00000000000 --- a/tests/colors.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package tests - -import ( - "github.com/onsi/ginkgo/v2/formatter" - - ginkgo "github.com/onsi/ginkgo/v2" -) - -// Outputs to stdout. -// -// Examples: -// -// - Out("{{green}}{{bold}}hi there %q{{/}}", "aa") -// - Out("{{magenta}}{{bold}}hi therea{{/}} {{cyan}}{{underline}}b{{/}}") -// -// See https://github.com/onsi/ginkgo/blob/v2.0.0/formatter/formatter.go#L52-L73 -// for an exhaustive list of color options. -func Outf(format string, args ...interface{}) { - s := formatter.F(format, args...) - // Use GinkgoWriter to ensure that output from this function is - // printed sequentially within other test output produced with - // GinkgoWriter (e.g. `STEP:...`) when tests are run in - // parallel. ginkgo collects and writes stdout separately from - // GinkgoWriter during parallel execution and the resulting output - // can be confusing. - ginkgo.GinkgoWriter.Print(s) -} diff --git a/tests/context_helpers.go b/tests/context_helpers.go new file mode 100644 index 00000000000..cea6d46b893 --- /dev/null +++ b/tests/context_helpers.go @@ -0,0 +1,32 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package tests + +import ( + "context" + "time" + + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" +) + +// A long default timeout used to timeout failed operations but unlikely to induce +// flaking due to unexpected resource contention. +const DefaultTimeout = 2 * time.Minute + +// Helper simplifying use of a timed context by canceling the context with the test context. +func ContextWithTimeout(tc TestContext, duration time.Duration) context.Context { + ctx, cancel := context.WithTimeout(context.Background(), duration) + tc.DeferCleanup(cancel) + return ctx +} + +// Helper simplifying use of a timed context configured with the default timeout. +func DefaultContext(tc TestContext) context.Context { + return ContextWithTimeout(tc, DefaultTimeout) +} + +// Helper simplifying use via an option of a timed context configured with the default timeout. +func WithDefaultContext(tc TestContext) common.Option { + return common.WithContext(DefaultContext(tc)) +} diff --git a/tests/e2e/banff/suites.go b/tests/e2e/banff/suites.go index b6da324c98e..b117080453a 100644 --- a/tests/e2e/banff/suites.go +++ b/tests/e2e/banff/suites.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/tests" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/units" @@ -20,12 +19,14 @@ import ( ) var _ = ginkgo.Describe("[Banff]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) ginkgo.It("can send custom assets X->P and P->X", func() { - keychain := e2e.Env.NewKeychain(1) - wallet := e2e.NewWallet(keychain, e2e.Env.GetRandomNodeURI()) + env := e2e.GetEnv(tc) + keychain := env.NewKeychain(1) + wallet := e2e.NewWallet(tc, keychain, env.GetRandomNodeURI()) // Get the P-chain and the X-chain wallets pWallet := wallet.P() @@ -43,7 +44,7 @@ var _ = ginkgo.Describe("[Banff]", func() { } var assetID ids.ID - ginkgo.By("create new X-chain asset", func() { + tc.By("create new X-chain asset", func() { assetTx, err := xWallet.IssueCreateAssetTx( "RnM", "RNM", @@ -56,15 +57,15 @@ var _ = ginkgo.Describe("[Banff]", func() { }, }, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) assetID = assetTx.ID() - tests.Outf("{{green}}created new X-chain asset{{/}}: %s\n", assetID) + tc.Outf("{{green}}created new X-chain asset{{/}}: %s\n", assetID) }) - ginkgo.By("export new X-chain asset to P-chain", func() { + tc.By("export new X-chain asset to P-chain", func() { tx, err := xWallet.IssueExportTx( constants.PlatformChainID, []*avax.TransferableOutput{ @@ -78,25 +79,25 @@ var _ = ginkgo.Describe("[Banff]", func() { }, }, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) - tests.Outf("{{green}}issued X-chain export{{/}}: %s\n", tx.ID()) + tc.Outf("{{green}}issued X-chain export{{/}}: %s\n", tx.ID()) }) - ginkgo.By("import new asset from X-chain on the P-chain", func() { + tc.By("import new asset from X-chain on the P-chain", func() { tx, err := pWallet.IssueImportTx( xChainID, owner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) - tests.Outf("{{green}}issued P-chain import{{/}}: %s\n", tx.ID()) + tc.Outf("{{green}}issued P-chain import{{/}}: %s\n", tx.ID()) }) - ginkgo.By("export asset from P-chain to the X-chain", func() { + tc.By("export asset from P-chain to the X-chain", func() { tx, err := pWallet.IssueExportTx( xChainID, []*avax.TransferableOutput{ @@ -110,22 +111,22 @@ var _ = ginkgo.Describe("[Banff]", func() { }, }, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) - tests.Outf("{{green}}issued P-chain export{{/}}: %s\n", tx.ID()) + tc.Outf("{{green}}issued P-chain export{{/}}: %s\n", tx.ID()) }) - ginkgo.By("import asset from P-chain on the X-chain", func() { + tc.By("import asset from P-chain on the X-chain", func() { tx, err := xWallet.IssueImportTx( constants.PlatformChainID, owner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) - tests.Outf("{{green}}issued X-chain import{{/}}: %s\n", tx.ID()) + tc.Outf("{{green}}issued X-chain import{{/}}: %s\n", tx.ID()) }) }) }) diff --git a/tests/e2e/c/dynamic_fees.go b/tests/e2e/c/dynamic_fees.go index 78087070c1d..ea3921bcf5f 100644 --- a/tests/e2e/c/dynamic_fees.go +++ b/tests/e2e/c/dynamic_fees.go @@ -14,7 +14,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "github.com/ava-labs/avalanchego/tests" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" @@ -26,7 +25,8 @@ import ( // well as its ABI contained in `hashing_contract.go`. var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) // Need a gas limit much larger than the standard 21_000 to enable // the contract to induce a gas price increase @@ -36,24 +36,24 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { gasTip := big.NewInt(1000 * params.GWei) ginkgo.It("should ensure that the gas price is affected by load", func() { - ginkgo.By("creating a new private network to ensure isolation from other tests") + tc.By("creating a new private network to ensure isolation from other tests") privateNetwork := tmpnet.NewDefaultNetwork("avalanchego-e2e-dynamic-fees") - e2e.Env.StartPrivateNetwork(privateNetwork) + e2e.GetEnv(tc).StartPrivateNetwork(privateNetwork) - ginkgo.By("allocating a pre-funded key") + tc.By("allocating a pre-funded key") key := privateNetwork.PreFundedKeys[0] ethAddress := evm.GetEthAddress(key) - ginkgo.By("initializing a coreth client") + tc.By("initializing a coreth client") node := privateNetwork.Nodes[0] nodeURI := tmpnet.NodeURI{ NodeID: node.NodeID, URI: node.URI, } - ethClient := e2e.NewEthClient(nodeURI) + ethClient := e2e.NewEthClient(tc, nodeURI) - ginkgo.By("initializing a transaction signer") - cChainID, err := ethClient.ChainID(e2e.DefaultContext()) + tc.By("initializing a transaction signer") + cChainID, err := ethClient.ChainID(tc.DefaultContext()) require.NoError(err) signer := types.NewEIP155Signer(cChainID) ecdsaKey := key.ToECDSA() @@ -64,9 +64,9 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { } var contractAddress common.Address - ginkgo.By("deploying an expensive contract", func() { + tc.By("deploying an expensive contract", func() { // Create transaction - nonce, err := ethClient.AcceptedNonceAt(e2e.DefaultContext(), ethAddress) + nonce, err := ethClient.AcceptedNonceAt(tc.DefaultContext(), ethAddress) require.NoError(err) compiledContract := common.Hex2Bytes(hashingCompiledContract) tx := types.NewTx(&types.LegacyTx{ @@ -79,13 +79,13 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { // Send the transaction and wait for acceptance signedTx := sign(tx) - receipt := e2e.SendEthTransaction(ethClient, signedTx) + receipt := e2e.SendEthTransaction(tc, ethClient, signedTx) contractAddress = receipt.ContractAddress }) var gasPrice *big.Int - ginkgo.By("calling the expensive contract repeatedly until a gas price increase is detected", func() { + tc.By("calling the expensive contract repeatedly until a gas price increase is detected", func() { // Evaluate the bytes representation of the contract hashingABI, err := abi.JSON(strings.NewReader(hashingABIJson)) require.NoError(err) @@ -93,22 +93,22 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { require.NoError(err) var initialGasPrice *big.Int - e2e.Eventually(func() bool { + tc.Eventually(func() bool { // Check the gas price var err error - gasPrice, err = ethClient.SuggestGasPrice(e2e.DefaultContext()) + gasPrice, err = ethClient.SuggestGasPrice(tc.DefaultContext()) require.NoError(err) if initialGasPrice == nil { initialGasPrice = gasPrice - tests.Outf("{{blue}}initial gas price is %v{{/}}\n", initialGasPrice) + tc.Outf("{{blue}}initial gas price is %v{{/}}\n", initialGasPrice) } else if gasPrice.Cmp(initialGasPrice) > 0 { // Gas price has increased - tests.Outf("{{blue}}gas price has increased to %v{{/}}\n", gasPrice) + tc.Outf("{{blue}}gas price has increased to %v{{/}}\n", gasPrice) return true } // Create the transaction - nonce, err := ethClient.AcceptedNonceAt(e2e.DefaultContext(), ethAddress) + nonce, err := ethClient.AcceptedNonceAt(tc.DefaultContext(), ethAddress) require.NoError(err) tx := types.NewTx(&types.LegacyTx{ Nonce: nonce, @@ -121,33 +121,33 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { // Send the transaction and wait for acceptance signedTx := sign(tx) - _ = e2e.SendEthTransaction(ethClient, signedTx) + _ = e2e.SendEthTransaction(tc, ethClient, signedTx) // The gas price will be checked at the start of the next iteration return false }, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see gas price increase before timeout") }) - ginkgo.By("waiting for the gas price to decrease...", func() { + tc.By("waiting for the gas price to decrease...", func() { initialGasPrice := gasPrice - e2e.Eventually(func() bool { + tc.Eventually(func() bool { var err error - gasPrice, err = ethClient.SuggestGasPrice(e2e.DefaultContext()) + gasPrice, err = ethClient.SuggestGasPrice(tc.DefaultContext()) require.NoError(err) - tests.Outf("{{blue}}.{{/}}") + tc.Outf("{{blue}}.{{/}}") return initialGasPrice.Cmp(gasPrice) > 0 }, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see gas price decrease before timeout") - tests.Outf("\n{{blue}}gas price has decreased to %v{{/}}\n", gasPrice) + tc.Outf("\n{{blue}}gas price has decreased to %v{{/}}\n", gasPrice) }) - ginkgo.By("sending funds at the current gas price", func() { + tc.By("sending funds at the current gas price", func() { // Create a recipient address recipientKey, err := secp256k1.NewPrivateKey() require.NoError(err) recipientEthAddress := evm.GetEthAddress(recipientKey) // Create transaction - nonce, err := ethClient.AcceptedNonceAt(e2e.DefaultContext(), ethAddress) + nonce, err := ethClient.AcceptedNonceAt(tc.DefaultContext(), ethAddress) require.NoError(err) tx := types.NewTx(&types.LegacyTx{ Nonce: nonce, @@ -159,9 +159,9 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() { // Send the transaction and wait for acceptance signedTx := sign(tx) - _ = e2e.SendEthTransaction(ethClient, signedTx) + _ = e2e.SendEthTransaction(tc, ethClient, signedTx) }) - _ = e2e.CheckBootstrapIsPossible(privateNetwork) + _ = e2e.CheckBootstrapIsPossible(tc, privateNetwork) }) }) diff --git a/tests/e2e/c/interchain_workflow.go b/tests/e2e/c/interchain_workflow.go index 4d4af4e6756..3ec8b0b6e47 100644 --- a/tests/e2e/c/interchain_workflow.go +++ b/tests/e2e/c/interchain_workflow.go @@ -23,30 +23,33 @@ import ( ) var _ = e2e.DescribeCChain("[Interchain Workflow]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) const txAmount = 10 * units.Avax // Arbitrary amount to send and transfer ginkgo.It("should ensure that funds can be transferred from the C-Chain to the X-Chain and the P-Chain", func() { - ginkgo.By("initializing a new eth client") + env := e2e.GetEnv(tc) + + tc.By("initializing a new eth client") // Select a random node URI to use for both the eth client and // the wallet to avoid having to verify that all nodes are at // the same height before initializing the wallet. - nodeURI := e2e.Env.GetRandomNodeURI() - ethClient := e2e.NewEthClient(nodeURI) + nodeURI := env.GetRandomNodeURI() + ethClient := e2e.NewEthClient(tc, nodeURI) - ginkgo.By("allocating a pre-funded key to send from and a recipient key to deliver to") - senderKey := e2e.Env.AllocatePreFundedKey() + tc.By("allocating a pre-funded key to send from and a recipient key to deliver to") + senderKey := env.AllocatePreFundedKey() senderEthAddress := evm.GetEthAddress(senderKey) recipientKey, err := secp256k1.NewPrivateKey() require.NoError(err) recipientEthAddress := evm.GetEthAddress(recipientKey) - ginkgo.By("sending funds from one address to another on the C-Chain", func() { + tc.By("sending funds from one address to another on the C-Chain", func() { // Create transaction - acceptedNonce, err := ethClient.AcceptedNonceAt(e2e.DefaultContext(), senderEthAddress) + acceptedNonce, err := ethClient.AcceptedNonceAt(tc.DefaultContext(), senderEthAddress) require.NoError(err) - gasPrice := e2e.SuggestGasPrice(ethClient) + gasPrice := e2e.SuggestGasPrice(tc, ethClient) tx := types.NewTransaction( acceptedNonce, recipientEthAddress, @@ -57,17 +60,17 @@ var _ = e2e.DescribeCChain("[Interchain Workflow]", func() { ) // Sign transaction - cChainID, err := ethClient.ChainID(e2e.DefaultContext()) + cChainID, err := ethClient.ChainID(tc.DefaultContext()) require.NoError(err) signer := types.NewEIP155Signer(cChainID) signedTx, err := types.SignTx(tx, signer, senderKey.ToECDSA()) require.NoError(err) - _ = e2e.SendEthTransaction(ethClient, signedTx) + _ = e2e.SendEthTransaction(tc, ethClient, signedTx) - ginkgo.By("waiting for the C-Chain recipient address to have received the sent funds") - e2e.Eventually(func() bool { - balance, err := ethClient.BalanceAt(e2e.DefaultContext(), recipientEthAddress, nil) + tc.By("waiting for the C-Chain recipient address to have received the sent funds") + tc.Eventually(func() bool { + balance, err := ethClient.BalanceAt(tc.DefaultContext(), recipientEthAddress, nil) require.NoError(err) return balance.Cmp(big.NewInt(0)) > 0 }, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see funds delivered before timeout") @@ -76,14 +79,14 @@ var _ = e2e.DescribeCChain("[Interchain Workflow]", func() { // Wallet must be initialized after sending funds on the // C-Chain with the same node URI to ensure wallet state // matches on-chain state. - ginkgo.By("initializing a keychain and associated wallet") + tc.By("initializing a keychain and associated wallet") keychain := secp256k1fx.NewKeychain(senderKey, recipientKey) - baseWallet := e2e.NewWallet(keychain, nodeURI) + baseWallet := e2e.NewWallet(tc, keychain, nodeURI) xWallet := baseWallet.X() cWallet := baseWallet.C() pWallet := baseWallet.P() - ginkgo.By("defining common configuration") + tc.By("defining common configuration") xBuilder := xWallet.Builder() xContext := xBuilder.Context() cBuilder := cWallet.Builder() @@ -109,26 +112,26 @@ var _ = e2e.DescribeCChain("[Interchain Workflow]", func() { }, } - ginkgo.By("exporting AVAX from the C-Chain to the X-Chain", func() { + tc.By("exporting AVAX from the C-Chain to the X-Chain", func() { _, err := cWallet.IssueExportTx( xContext.BlockchainID, exportOutputs, - e2e.WithDefaultContext(), - e2e.WithSuggestedGasPrice(ethClient), + tc.WithDefaultContext(), + e2e.WithSuggestedGasPrice(tc, ethClient), ) require.NoError(err) }) - ginkgo.By("importing AVAX from the C-Chain to the X-Chain", func() { + tc.By("importing AVAX from the C-Chain to the X-Chain", func() { _, err := xWallet.IssueImportTx( cContext.BlockchainID, &recipientOwner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("checking that the recipient address has received imported funds on the X-Chain", func() { + tc.By("checking that the recipient address has received imported funds on the X-Chain", func() { balances, err := xWallet.Builder().GetFTBalance(common.WithCustomAddresses(set.Of( recipientKey.Address(), ))) @@ -136,26 +139,26 @@ var _ = e2e.DescribeCChain("[Interchain Workflow]", func() { require.Positive(balances[avaxAssetID]) }) - ginkgo.By("exporting AVAX from the C-Chain to the P-Chain", func() { + tc.By("exporting AVAX from the C-Chain to the P-Chain", func() { _, err := cWallet.IssueExportTx( constants.PlatformChainID, exportOutputs, - e2e.WithDefaultContext(), - e2e.WithSuggestedGasPrice(ethClient), + tc.WithDefaultContext(), + e2e.WithSuggestedGasPrice(tc, ethClient), ) require.NoError(err) }) - ginkgo.By("importing AVAX from the C-Chain to the P-Chain", func() { + tc.By("importing AVAX from the C-Chain to the P-Chain", func() { _, err = pWallet.IssueImportTx( cContext.BlockchainID, &recipientOwner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("checking that the recipient address has received imported funds on the P-Chain", func() { + tc.By("checking that the recipient address has received imported funds on the P-Chain", func() { balances, err := pWallet.Builder().GetBalance(common.WithCustomAddresses(set.Of( recipientKey.Address(), ))) @@ -163,6 +166,6 @@ var _ = e2e.DescribeCChain("[Interchain Workflow]", func() { require.Positive(balances[avaxAssetID]) }) - _ = e2e.CheckBootstrapIsPossible(e2e.Env.GetNetwork()) + _ = e2e.CheckBootstrapIsPossible(tc, env.GetNetwork()) }) }) diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index f33a3524d2a..cd43e9a80b3 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -37,6 +37,7 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { nodes := tmpnet.NewNodesOrPanic(flagVars.NodeCount()) subnets := vms.XSVMSubnetsOrPanic(nodes...) return e2e.NewTestEnvironment( + e2e.NewTestContext(), flagVars, &tmpnet.Network{ Owner: "avalanchego-e2e", @@ -48,5 +49,5 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { // Run in every ginkgo process // Initialize the local test environment from the global state - e2e.InitSharedTestEnvironment(envBytes) + e2e.InitSharedTestEnvironment(ginkgo.GinkgoT(), envBytes) }) diff --git a/tests/e2e/faultinjection/duplicate_node_id.go b/tests/e2e/faultinjection/duplicate_node_id.go index 288583c7e09..5032a0e306b 100644 --- a/tests/e2e/faultinjection/duplicate_node_id.go +++ b/tests/e2e/faultinjection/duplicate_node_id.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/config" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/tests" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/utils/set" @@ -20,19 +21,20 @@ import ( ) var _ = ginkgo.Describe("Duplicate node handling", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) ginkgo.It("should ensure that a given Node ID (i.e. staking keypair) can be used at most once on a network", func() { - network := e2e.Env.GetNetwork() + network := e2e.GetEnv(tc).GetNetwork() - ginkgo.By("creating new node") - node1 := e2e.AddEphemeralNode(network, tmpnet.FlagsMap{}) - e2e.WaitForHealthy(node1) + tc.By("creating new node") + node1 := e2e.AddEphemeralNode(tc, network, tmpnet.FlagsMap{}) + e2e.WaitForHealthy(tc, node1) - ginkgo.By("checking that the new node is connected to its peers") - checkConnectedPeers(network.Nodes, node1) + tc.By("checking that the new node is connected to its peers") + checkConnectedPeers(tc, network.Nodes, node1) - ginkgo.By("creating a second new node with the same staking keypair as the first new node") + tc.By("creating a second new node with the same staking keypair as the first new node") node1Flags := node1.Flags node2Flags := tmpnet.FlagsMap{ config.StakingTLSKeyContentKey: node1Flags[config.StakingTLSKeyContentKey], @@ -42,32 +44,32 @@ var _ = ginkgo.Describe("Duplicate node handling", func() { // the same node ID. config.DataDirKey: fmt.Sprintf("%s-second", node1Flags[config.DataDirKey]), } - node2 := e2e.AddEphemeralNode(network, node2Flags) + node2 := e2e.AddEphemeralNode(tc, network, node2Flags) - ginkgo.By("checking that the second new node fails to become healthy before timeout") - err := tmpnet.WaitForHealthy(e2e.DefaultContext(), node2) + tc.By("checking that the second new node fails to become healthy before timeout") + err := tmpnet.WaitForHealthy(tc.DefaultContext(), node2) require.ErrorIs(err, context.DeadlineExceeded) - ginkgo.By("stopping the first new node") - require.NoError(node1.Stop(e2e.DefaultContext())) + tc.By("stopping the first new node") + require.NoError(node1.Stop(tc.DefaultContext())) - ginkgo.By("checking that the second new node becomes healthy within timeout") - e2e.WaitForHealthy(node2) + tc.By("checking that the second new node becomes healthy within timeout") + e2e.WaitForHealthy(tc, node2) - ginkgo.By("checking that the second new node is connected to its peers") - checkConnectedPeers(network.Nodes, node2) + tc.By("checking that the second new node is connected to its peers") + checkConnectedPeers(tc, network.Nodes, node2) // A bootstrap check was already performed by the second node. }) }) // Check that a new node is connected to existing nodes and vice versa -func checkConnectedPeers(existingNodes []*tmpnet.Node, newNode *tmpnet.Node) { - require := require.New(ginkgo.GinkgoT()) +func checkConnectedPeers(tc tests.TestContext, existingNodes []*tmpnet.Node, newNode *tmpnet.Node) { + require := require.New(tc) // Collect the node ids of the new node's peers infoClient := info.NewClient(newNode.URI) - peers, err := infoClient.Peers(e2e.DefaultContext()) + peers, err := infoClient.Peers(tc.DefaultContext()) require.NoError(err) peerIDs := set.NewSet[ids.NodeID](len(existingNodes)) for _, peer := range peers { @@ -80,7 +82,7 @@ func checkConnectedPeers(existingNodes []*tmpnet.Node, newNode *tmpnet.Node) { // Check that the new node is a peer infoClient := info.NewClient(existingNode.URI) - peers, err := infoClient.Peers(e2e.DefaultContext()) + peers, err := infoClient.Peers(tc.DefaultContext()) require.NoError(err) isPeer := false for _, peer := range peers { diff --git a/tests/e2e/p/interchain_workflow.go b/tests/e2e/p/interchain_workflow.go index 88376a3d8c5..e0be6c45be4 100644 --- a/tests/e2e/p/interchain_workflow.go +++ b/tests/e2e/p/interchain_workflow.go @@ -30,7 +30,8 @@ import ( ) var _ = e2e.DescribePChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainLabel), func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) const ( transferAmount = 10 * units.Avax @@ -38,20 +39,22 @@ var _ = e2e.DescribePChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL ) ginkgo.It("should ensure that funds can be transferred from the P-Chain to the X-Chain and the C-Chain", func() { - network := e2e.Env.GetNetwork() + env := e2e.GetEnv(tc) - ginkgo.By("checking that the network has a compatible minimum stake duration", func() { + network := env.GetNetwork() + + tc.By("checking that the network has a compatible minimum stake duration", func() { minStakeDuration := cast.ToDuration(network.DefaultFlags[config.MinStakeDurationKey]) require.Equal(tmpnet.DefaultMinStakeDuration, minStakeDuration) }) - ginkgo.By("creating wallet with a funded key to send from and recipient key to deliver to") + tc.By("creating wallet with a funded key to send from and recipient key to deliver to") recipientKey, err := secp256k1.NewPrivateKey() require.NoError(err) - keychain := e2e.Env.NewKeychain(1) + keychain := env.NewKeychain(1) keychain.Add(recipientKey) - nodeURI := e2e.Env.GetRandomNodeURI() - baseWallet := e2e.NewWallet(keychain, nodeURI) + nodeURI := env.GetRandomNodeURI() + baseWallet := e2e.NewWallet(tc, keychain, nodeURI) xWallet := baseWallet.X() cWallet := baseWallet.C() pWallet := baseWallet.P() @@ -63,7 +66,7 @@ var _ = e2e.DescribePChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL cBuilder := cWallet.Builder() cContext := cBuilder.Context() - ginkgo.By("defining common configuration") + tc.By("defining common configuration") recipientEthAddress := evm.GetEthAddress(recipientKey) avaxAssetID := xContext.AVAXAssetID // Use the same owner for sending to X-Chain and importing funds to P-Chain @@ -91,18 +94,18 @@ var _ = e2e.DescribePChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL }, } - ginkgo.By("adding new node and waiting for it to report healthy") - node := e2e.AddEphemeralNode(network, tmpnet.FlagsMap{}) - e2e.WaitForHealthy(node) + tc.By("adding new node and waiting for it to report healthy") + node := e2e.AddEphemeralNode(tc, network, tmpnet.FlagsMap{}) + e2e.WaitForHealthy(tc, node) - ginkgo.By("retrieving new node's id and pop") + tc.By("retrieving new node's id and pop") infoClient := info.NewClient(node.URI) - nodeID, nodePOP, err := infoClient.GetNodeID(e2e.DefaultContext()) + nodeID, nodePOP, err := infoClient.GetNodeID(tc.DefaultContext()) require.NoError(err) // Adding a validator should not break interchain transfer. endTime := time.Now().Add(30 * time.Second) - ginkgo.By("adding the new node as a validator", func() { + tc.By("adding the new node as a validator", func() { rewardKey, err := secp256k1.NewPrivateKey() require.NoError(err) @@ -131,13 +134,13 @@ var _ = e2e.DescribePChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL Addrs: []ids.ShortID{rewardKey.Address()}, }, delegationShare, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) // Adding a delegator should not break interchain transfer. - ginkgo.By("adding a delegator to the new node", func() { + tc.By("adding a delegator to the new node", func() { rewardKey, err := secp256k1.NewPrivateKey() require.NoError(err) @@ -155,30 +158,30 @@ var _ = e2e.DescribePChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL Threshold: 1, Addrs: []ids.ShortID{rewardKey.Address()}, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("exporting AVAX from the P-Chain to the X-Chain", func() { + tc.By("exporting AVAX from the P-Chain to the X-Chain", func() { _, err := pWallet.IssueExportTx( xContext.BlockchainID, exportOutputs, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("importing AVAX from the P-Chain to the X-Chain", func() { + tc.By("importing AVAX from the P-Chain to the X-Chain", func() { _, err := xWallet.IssueImportTx( constants.PlatformChainID, &recipientOwner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("checking that the recipient address has received imported funds on the X-Chain", func() { + tc.By("checking that the recipient address has received imported funds on the X-Chain", func() { balances, err := xWallet.Builder().GetFTBalance(common.WithCustomAddresses(set.Of( recipientKey.Address(), ))) @@ -186,36 +189,36 @@ var _ = e2e.DescribePChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL require.Positive(balances[avaxAssetID]) }) - ginkgo.By("exporting AVAX from the P-Chain to the C-Chain", func() { + tc.By("exporting AVAX from the P-Chain to the C-Chain", func() { _, err := pWallet.IssueExportTx( cContext.BlockchainID, exportOutputs, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("initializing a new eth client") - ethClient := e2e.NewEthClient(nodeURI) + tc.By("initializing a new eth client") + ethClient := e2e.NewEthClient(tc, nodeURI) - ginkgo.By("importing AVAX from the P-Chain to the C-Chain", func() { + tc.By("importing AVAX from the P-Chain to the C-Chain", func() { _, err := cWallet.IssueImportTx( constants.PlatformChainID, recipientEthAddress, - e2e.WithDefaultContext(), - e2e.WithSuggestedGasPrice(ethClient), + tc.WithDefaultContext(), + e2e.WithSuggestedGasPrice(tc, ethClient), ) require.NoError(err) }) - ginkgo.By("checking that the recipient address has received imported funds on the C-Chain") - balance, err := ethClient.BalanceAt(e2e.DefaultContext(), recipientEthAddress, nil) + tc.By("checking that the recipient address has received imported funds on the C-Chain") + balance, err := ethClient.BalanceAt(tc.DefaultContext(), recipientEthAddress, nil) require.NoError(err) require.Positive(balance.Cmp(big.NewInt(0))) - ginkgo.By("stopping validator node to free up resources for a bootstrap check") - require.NoError(node.Stop(e2e.DefaultContext())) + tc.By("stopping validator node to free up resources for a bootstrap check") + require.NoError(node.Stop(tc.DefaultContext())) - _ = e2e.CheckBootstrapIsPossible(network) + _ = e2e.CheckBootstrapIsPossible(tc, network) }) }) diff --git a/tests/e2e/p/permissionless_subnets.go b/tests/e2e/p/permissionless_subnets.go index dc92bdd60d5..21934ff6c61 100644 --- a/tests/e2e/p/permissionless_subnets.go +++ b/tests/e2e/p/permissionless_subnets.go @@ -25,14 +25,17 @@ import ( ) var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) ginkgo.It("subnets operations", func() { - nodeURI := e2e.Env.GetRandomNodeURI() + env := e2e.GetEnv(tc) - keychain := e2e.Env.NewKeychain(1) - baseWallet := e2e.NewWallet(keychain, nodeURI) + nodeURI := env.GetRandomNodeURI() + + keychain := env.NewKeychain(1) + baseWallet := e2e.NewWallet(tc, keychain, nodeURI) pWallet := baseWallet.P() xWallet := baseWallet.X() @@ -41,9 +44,9 @@ var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { xChainID := xContext.BlockchainID var validatorID ids.NodeID - ginkgo.By("retrieving the node ID of a primary network validator", func() { + tc.By("retrieving the node ID of a primary network validator", func() { pChainClient := platformvm.NewClient(nodeURI.URI) - validatorIDs, err := pChainClient.SampleValidators(e2e.DefaultContext(), constants.PrimaryNetworkID, 1) + validatorIDs, err := pChainClient.SampleValidators(tc.DefaultContext(), constants.PrimaryNetworkID, 1) require.NoError(err) validatorID = validatorIDs[0] }) @@ -56,10 +59,10 @@ var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { } var subnetID ids.ID - ginkgo.By("create a permissioned subnet", func() { + tc.By("create a permissioned subnet", func() { subnetTx, err := pWallet.IssueCreateSubnetTx( owner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) subnetID = subnetTx.ID() @@ -68,7 +71,7 @@ var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { }) var subnetAssetID ids.ID - ginkgo.By("create a custom asset for the permissionless subnet", func() { + tc.By("create a custom asset for the permissionless subnet", func() { subnetAssetTx, err := xWallet.IssueCreateAssetTx( "RnM", "RNM", @@ -81,13 +84,13 @@ var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { }, }, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) subnetAssetID = subnetAssetTx.ID() }) - ginkgo.By(fmt.Sprintf("Send 100 MegaAvax of asset %s to the P-chain", subnetAssetID), func() { + tc.By(fmt.Sprintf("Send 100 MegaAvax of asset %s to the P-chain", subnetAssetID), func() { _, err := xWallet.IssueExportTx( constants.PlatformChainID, []*avax.TransferableOutput{ @@ -101,21 +104,21 @@ var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { }, }, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By(fmt.Sprintf("Import the 100 MegaAvax of asset %s from the X-chain into the P-chain", subnetAssetID), func() { + tc.By(fmt.Sprintf("Import the 100 MegaAvax of asset %s from the X-chain into the P-chain", subnetAssetID), func() { _, err := pWallet.IssueImportTx( xChainID, owner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("make subnet permissionless", func() { + tc.By("make subnet permissionless", func() { _, err := pWallet.IssueTransformSubnetTx( subnetID, subnetAssetID, @@ -131,13 +134,13 @@ var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { 1, 5, .80*reward.PercentDenominator, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) endTime := time.Now().Add(time.Minute) - ginkgo.By("add permissionless validator", func() { + tc.By("add permissionless validator", func() { _, err := pWallet.IssueAddPermissionlessValidatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ @@ -152,12 +155,12 @@ var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { &secp256k1fx.OutputOwners{}, &secp256k1fx.OutputOwners{}, reward.PercentDenominator, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("add permissionless delegator", func() { + tc.By("add permissionless delegator", func() { _, err := pWallet.IssueAddPermissionlessDelegatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ @@ -169,7 +172,7 @@ var _ = e2e.DescribePChain("[Permissionless Subnets]", func() { }, subnetAssetID, &secp256k1fx.OutputOwners{}, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) diff --git a/tests/e2e/p/staking_rewards.go b/tests/e2e/p/staking_rewards.go index 64231381499..2eff3f212a7 100644 --- a/tests/e2e/p/staking_rewards.go +++ b/tests/e2e/p/staking_rewards.go @@ -14,7 +14,6 @@ import ( "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/config" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/tests" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/utils/constants" @@ -34,39 +33,42 @@ const ( ) var _ = ginkgo.Describe("[Staking Rewards]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) ginkgo.It("should ensure that validator node uptime determines whether a staking reward is issued", func() { - network := e2e.Env.GetNetwork() + env := e2e.GetEnv(tc) - ginkgo.By("checking that the network has a compatible minimum stake duration", func() { + network := env.GetNetwork() + + tc.By("checking that the network has a compatible minimum stake duration", func() { minStakeDuration := cast.ToDuration(network.DefaultFlags[config.MinStakeDurationKey]) require.Equal(tmpnet.DefaultMinStakeDuration, minStakeDuration) }) - ginkgo.By("adding alpha node, whose uptime should result in a staking reward") - alphaNode := e2e.AddEphemeralNode(network, tmpnet.FlagsMap{}) - ginkgo.By("adding beta node, whose uptime should not result in a staking reward") - betaNode := e2e.AddEphemeralNode(network, tmpnet.FlagsMap{}) + tc.By("adding alpha node, whose uptime should result in a staking reward") + alphaNode := e2e.AddEphemeralNode(tc, network, tmpnet.FlagsMap{}) + tc.By("adding beta node, whose uptime should not result in a staking reward") + betaNode := e2e.AddEphemeralNode(tc, network, tmpnet.FlagsMap{}) // Wait to check health until both nodes have started to minimize the duration // required for both nodes to report healthy. - ginkgo.By("waiting until alpha node is healthy") - e2e.WaitForHealthy(alphaNode) - ginkgo.By("waiting until beta node is healthy") - e2e.WaitForHealthy(betaNode) + tc.By("waiting until alpha node is healthy") + e2e.WaitForHealthy(tc, alphaNode) + tc.By("waiting until beta node is healthy") + e2e.WaitForHealthy(tc, betaNode) - ginkgo.By("retrieving alpha node id and pop") + tc.By("retrieving alpha node id and pop") alphaInfoClient := info.NewClient(alphaNode.URI) - alphaNodeID, alphaPOP, err := alphaInfoClient.GetNodeID(e2e.DefaultContext()) + alphaNodeID, alphaPOP, err := alphaInfoClient.GetNodeID(tc.DefaultContext()) require.NoError(err) - ginkgo.By("retrieving beta node id and pop") + tc.By("retrieving beta node id and pop") betaInfoClient := info.NewClient(betaNode.URI) - betaNodeID, betaPOP, err := betaInfoClient.GetNodeID(e2e.DefaultContext()) + betaNodeID, betaPOP, err := betaInfoClient.GetNodeID(tc.DefaultContext()) require.NoError(err) - ginkgo.By("generating reward keys") + tc.By("generating reward keys") alphaValidationRewardKey, err := secp256k1.NewPrivateKey() require.NoError(err) @@ -93,15 +95,15 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { deltaDelegationRewardKey, } - ginkgo.By("creating keychain and P-Chain wallet") + tc.By("creating keychain and P-Chain wallet") keychain := secp256k1fx.NewKeychain(rewardKeys...) - fundedKey := e2e.Env.AllocatePreFundedKey() + fundedKey := env.AllocatePreFundedKey() keychain.Add(fundedKey) nodeURI := tmpnet.NodeURI{ NodeID: alphaNodeID, URI: alphaNode.URI, } - baseWallet := e2e.NewWallet(keychain, nodeURI) + baseWallet := e2e.NewWallet(tc, keychain, nodeURI) pWallet := baseWallet.P() pBuilder := pWallet.Builder() @@ -115,14 +117,14 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { pvmClient := platformvm.NewClient(alphaNode.URI) - ginkgo.By("retrieving supply before inserting validators") - supplyAtValidatorsStart, _, err := pvmClient.GetCurrentSupply(e2e.DefaultContext(), constants.PrimaryNetworkID) + tc.By("retrieving supply before inserting validators") + supplyAtValidatorsStart, _, err := pvmClient.GetCurrentSupply(tc.DefaultContext(), constants.PrimaryNetworkID) require.NoError(err) alphaValidatorsEndTime := time.Now().Add(targetValidationPeriod) - tests.Outf("alpha node validation period ending at: %v\n", alphaValidatorsEndTime) + tc.Outf("alpha node validation period ending at: %v\n", alphaValidatorsEndTime) - ginkgo.By("adding alpha node as a validator", func() { + tc.By("adding alpha node as a validator", func() { _, err := pWallet.IssueAddPermissionlessValidatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ @@ -143,15 +145,15 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { Addrs: []ids.ShortID{alphaDelegationRewardKey.Address()}, }, delegationShare, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) betaValidatorEndTime := time.Now().Add(targetValidationPeriod) - tests.Outf("beta node validation period ending at: %v\n", betaValidatorEndTime) + tc.Outf("beta node validation period ending at: %v\n", betaValidatorEndTime) - ginkgo.By("adding beta node as a validator", func() { + tc.By("adding beta node as a validator", func() { _, err := pWallet.IssueAddPermissionlessValidatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ @@ -172,19 +174,19 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { Addrs: []ids.ShortID{betaDelegationRewardKey.Address()}, }, delegationShare, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("retrieving supply before inserting delegators") - supplyAtDelegatorsStart, _, err := pvmClient.GetCurrentSupply(e2e.DefaultContext(), constants.PrimaryNetworkID) + tc.By("retrieving supply before inserting delegators") + supplyAtDelegatorsStart, _, err := pvmClient.GetCurrentSupply(tc.DefaultContext(), constants.PrimaryNetworkID) require.NoError(err) gammaDelegatorEndTime := time.Now().Add(targetDelegationPeriod) - tests.Outf("gamma delegation period ending at: %v\n", gammaDelegatorEndTime) + tc.Outf("gamma delegation period ending at: %v\n", gammaDelegatorEndTime) - ginkgo.By("adding gamma as delegator to the alpha node", func() { + tc.By("adding gamma as delegator to the alpha node", func() { _, err := pWallet.IssueAddPermissionlessDelegatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ @@ -199,15 +201,15 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { Threshold: 1, Addrs: []ids.ShortID{gammaDelegationRewardKey.Address()}, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) deltaDelegatorEndTime := time.Now().Add(targetDelegationPeriod) - tests.Outf("delta delegation period ending at: %v\n", deltaDelegatorEndTime) + tc.Outf("delta delegation period ending at: %v\n", deltaDelegatorEndTime) - ginkgo.By("adding delta as delegator to the beta node", func() { + tc.By("adding delta as delegator to the beta node", func() { _, err := pWallet.IssueAddPermissionlessDelegatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ @@ -222,30 +224,30 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { Threshold: 1, Addrs: []ids.ShortID{deltaDelegationRewardKey.Address()}, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("stopping beta node to prevent it and its delegator from receiving a validation reward") - require.NoError(betaNode.Stop(e2e.DefaultContext())) + tc.By("stopping beta node to prevent it and its delegator from receiving a validation reward") + require.NoError(betaNode.Stop(tc.DefaultContext())) - ginkgo.By("retrieving staking periods from the chain") - data, err := pvmClient.GetCurrentValidators(e2e.DefaultContext(), constants.PlatformChainID, []ids.NodeID{alphaNodeID}) + tc.By("retrieving staking periods from the chain") + data, err := pvmClient.GetCurrentValidators(tc.DefaultContext(), constants.PlatformChainID, []ids.NodeID{alphaNodeID}) require.NoError(err) require.Len(data, 1) actualAlphaValidationPeriod := time.Duration(data[0].EndTime-data[0].StartTime) * time.Second delegatorData := data[0].Delegators[0] actualGammaDelegationPeriod := time.Duration(delegatorData.EndTime-delegatorData.StartTime) * time.Second - ginkgo.By("waiting until all validation periods are over") + tc.By("waiting until all validation periods are over") // The beta validator was the last added and so has the latest end time. The // delegation periods are shorter than the validation periods. time.Sleep(time.Until(betaValidatorEndTime)) - ginkgo.By("waiting until the alpha and beta nodes are no longer validators") - e2e.Eventually(func() bool { - validators, err := pvmClient.GetCurrentValidators(e2e.DefaultContext(), constants.PrimaryNetworkID, nil) + tc.By("waiting until the alpha and beta nodes are no longer validators") + tc.Eventually(func() bool { + validators, err := pvmClient.GetCurrentValidators(tc.DefaultContext(), constants.PrimaryNetworkID, nil) require.NoError(err) for _, validator := range validators { if validator.NodeID == alphaNodeID || validator.NodeID == betaNodeID { @@ -255,13 +257,13 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { return true }, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "nodes failed to stop validating before timeout ") - ginkgo.By("retrieving reward configuration for the network") + tc.By("retrieving reward configuration for the network") // TODO(marun) Enable GetConfig to return *node.Config // directly. Currently, due to a circular dependency issue, a // map-based equivalent is used for which manual unmarshaling // is required. - adminClient := admin.NewClient(e2e.Env.GetRandomNodeURI().URI) - rawNodeConfigMap, err := adminClient.GetConfig(e2e.DefaultContext()) + adminClient := admin.NewClient(env.GetRandomNodeURI().URI) + rawNodeConfigMap, err := adminClient.GetConfig(tc.DefaultContext()) require.NoError(err) nodeConfigMap, ok := rawNodeConfigMap.(map[string]interface{}) require.True(ok) @@ -271,11 +273,11 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { rewardConfig := reward.Config{} require.NoError(mapstructure.Decode(rawRewardConfig, &rewardConfig)) - ginkgo.By("retrieving reward address balances") + tc.By("retrieving reward address balances") rewardBalances := make(map[ids.ShortID]uint64, len(rewardKeys)) for _, rewardKey := range rewardKeys { keychain := secp256k1fx.NewKeychain(rewardKey) - baseWallet := e2e.NewWallet(keychain, nodeURI) + baseWallet := e2e.NewWallet(tc, keychain, nodeURI) pWallet := baseWallet.P() balances, err := pWallet.Builder().GetBalance() require.NoError(err) @@ -283,13 +285,13 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { } require.Len(rewardBalances, len(rewardKeys)) - ginkgo.By("determining expected validation and delegation rewards") + tc.By("determining expected validation and delegation rewards") calculator := reward.NewCalculator(rewardConfig) expectedValidationReward := calculator.Calculate(actualAlphaValidationPeriod, weight, supplyAtValidatorsStart) potentialDelegationReward := calculator.Calculate(actualGammaDelegationPeriod, weight, supplyAtDelegatorsStart) expectedDelegationFee, expectedDelegatorReward := reward.Split(potentialDelegationReward, delegationShare) - ginkgo.By("checking expected rewards against actual rewards") + tc.By("checking expected rewards against actual rewards") expectedRewardBalances := map[ids.ShortID]uint64{ alphaValidationRewardKey.Address(): expectedValidationReward, alphaDelegationRewardKey.Address(): expectedDelegationFee, @@ -302,9 +304,9 @@ var _ = ginkgo.Describe("[Staking Rewards]", func() { require.Equal(expectedRewardBalances[address], rewardBalances[address]) } - ginkgo.By("stopping alpha to free up resources for a bootstrap check") - require.NoError(alphaNode.Stop(e2e.DefaultContext())) + tc.By("stopping alpha to free up resources for a bootstrap check") + require.NoError(alphaNode.Stop(tc.DefaultContext())) - _ = e2e.CheckBootstrapIsPossible(network) + _ = e2e.CheckBootstrapIsPossible(tc, network) }) }) diff --git a/tests/e2e/p/validator_sets.go b/tests/e2e/p/validator_sets.go index 5e116535c9b..d13e056929c 100644 --- a/tests/e2e/p/validator_sets.go +++ b/tests/e2e/p/validator_sets.go @@ -12,7 +12,6 @@ import ( "github.com/ava-labs/avalanchego/genesis" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/validators" - "github.com/ava-labs/avalanchego/tests" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/utils/constants" @@ -25,22 +24,25 @@ import ( ) var _ = e2e.DescribePChain("[Validator Sets]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) ginkgo.It("should be identical for every height for all nodes in the network", func() { - network := e2e.Env.GetNetwork() + env := e2e.GetEnv(tc) - ginkgo.By("creating wallet with a funded key to source delegated funds from") - keychain := e2e.Env.NewKeychain(1) - nodeURI := e2e.Env.GetRandomNodeURI() - baseWallet := e2e.NewWallet(keychain, nodeURI) + network := env.GetNetwork() + + tc.By("creating wallet with a funded key to source delegated funds from") + keychain := env.NewKeychain(1) + nodeURI := env.GetRandomNodeURI() + baseWallet := e2e.NewWallet(tc, keychain, nodeURI) pWallet := baseWallet.P() pBuilder := pWallet.Builder() pContext := pBuilder.Context() const delegatorCount = 15 - ginkgo.By(fmt.Sprintf("adding %d delegators", delegatorCount), func() { + tc.By(fmt.Sprintf("adding %d delegators", delegatorCount), func() { rewardKey, err := secp256k1.NewPrivateKey() require.NoError(err) avaxAssetID := pContext.AVAXAssetID @@ -65,24 +67,24 @@ var _ = e2e.DescribePChain("[Validator Sets]", func() { Threshold: 1, Addrs: []ids.ShortID{rewardKey.Address()}, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) } }) - ginkgo.By("getting the current P-Chain height from the wallet") - currentPChainHeight, err := platformvm.NewClient(nodeURI.URI).GetHeight(e2e.DefaultContext()) + tc.By("getting the current P-Chain height from the wallet") + currentPChainHeight, err := platformvm.NewClient(nodeURI.URI).GetHeight(tc.DefaultContext()) require.NoError(err) - ginkgo.By("checking that validator sets are equal across all heights for all nodes", func() { - pvmClients := make([]platformvm.Client, len(e2e.Env.URIs)) - for i, nodeURI := range e2e.Env.URIs { + tc.By("checking that validator sets are equal across all heights for all nodes", func() { + pvmClients := make([]platformvm.Client, len(env.URIs)) + for i, nodeURI := range env.URIs { pvmClients[i] = platformvm.NewClient(nodeURI.URI) // Ensure that the height of the target node is at least the expected height - e2e.Eventually( + tc.Eventually( func() bool { - pChainHeight, err := pvmClients[i].GetHeight(e2e.DefaultContext()) + pChainHeight, err := pvmClients[i].GetHeight(tc.DefaultContext()) require.NoError(err) return pChainHeight >= currentPChainHeight }, @@ -93,11 +95,11 @@ var _ = e2e.DescribePChain("[Validator Sets]", func() { } for height := uint64(0); height <= currentPChainHeight; height++ { - tests.Outf(" checked validator sets for height %d\n", height) + tc.Outf(" checked validator sets for height %d\n", height) var observedValidatorSet map[ids.NodeID]*validators.GetValidatorOutput for _, pvmClient := range pvmClients { validatorSet, err := pvmClient.GetValidatorsAt( - e2e.DefaultContext(), + tc.DefaultContext(), constants.PrimaryNetworkID, height, ) @@ -111,6 +113,6 @@ var _ = e2e.DescribePChain("[Validator Sets]", func() { } }) - _ = e2e.CheckBootstrapIsPossible(network) + _ = e2e.CheckBootstrapIsPossible(tc, network) }) }) diff --git a/tests/e2e/p/workflow.go b/tests/e2e/p/workflow.go index 3708c6b82c0..fc539010ed5 100644 --- a/tests/e2e/p/workflow.go +++ b/tests/e2e/p/workflow.go @@ -10,7 +10,6 @@ import ( "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/tests" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" @@ -32,13 +31,16 @@ import ( // - Checks the expected value of the funding address var _ = e2e.DescribePChain("[Workflow]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) ginkgo.It("P-chain main operations", func() { - nodeURI := e2e.Env.GetRandomNodeURI() - keychain := e2e.Env.NewKeychain(2) - baseWallet := e2e.NewWallet(keychain, nodeURI) + env := e2e.GetEnv(tc) + + nodeURI := env.GetRandomNodeURI() + keychain := env.NewKeychain(2) + baseWallet := e2e.NewWallet(tc, keychain, nodeURI) pWallet := baseWallet.P() pBuilder := pWallet.Builder() @@ -49,25 +51,25 @@ var _ = e2e.DescribePChain("[Workflow]", func() { xContext := xBuilder.Context() pChainClient := platformvm.NewClient(nodeURI.URI) - tests.Outf("{{blue}} fetching minimal stake amounts {{/}}\n") - minValStake, minDelStake, err := pChainClient.GetMinStake(e2e.DefaultContext(), constants.PlatformChainID) + tc.Outf("{{blue}} fetching minimal stake amounts {{/}}\n") + minValStake, minDelStake, err := pChainClient.GetMinStake(tc.DefaultContext(), constants.PlatformChainID) require.NoError(err) - tests.Outf("{{green}} minimal validator stake: %d {{/}}\n", minValStake) - tests.Outf("{{green}} minimal delegator stake: %d {{/}}\n", minDelStake) + tc.Outf("{{green}} minimal validator stake: %d {{/}}\n", minValStake) + tc.Outf("{{green}} minimal delegator stake: %d {{/}}\n", minDelStake) - tests.Outf("{{blue}} fetching tx fee {{/}}\n") + tc.Outf("{{blue}} fetching tx fee {{/}}\n") infoClient := info.NewClient(nodeURI.URI) - fees, err := infoClient.GetTxFee(e2e.DefaultContext()) + fees, err := infoClient.GetTxFee(tc.DefaultContext()) require.NoError(err) txFees := uint64(fees.TxFee) - tests.Outf("{{green}} txFee: %d {{/}}\n", txFees) + tc.Outf("{{green}} txFee: %d {{/}}\n", txFees) // amount to transfer from P to X chain toTransfer := 1 * units.Avax pShortAddr := keychain.Keys[0].Address() xTargetAddr := keychain.Keys[1].Address() - ginkgo.By("check selected keys have sufficient funds", func() { + tc.By("check selected keys have sufficient funds", func() { pBalances, err := pWallet.Builder().GetBalance() pBalance := pBalances[avaxAssetID] minBalance := minValStake + txFees + minDelStake + txFees + toTransfer + txFees @@ -98,7 +100,7 @@ var _ = e2e.DescribePChain("[Workflow]", func() { require.NoError(err) pop := signer.NewProofOfPossession(sk) - ginkgo.By("issue add validator tx", func() { + tc.By("issue add validator tx", func() { _, err := pWallet.IssueAddPermissionlessValidatorTx( vdr, pop, @@ -106,17 +108,17 @@ var _ = e2e.DescribePChain("[Workflow]", func() { rewardOwner, rewardOwner, shares, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("issue add delegator tx", func() { + tc.By("issue add delegator tx", func() { _, err := pWallet.IssueAddPermissionlessDelegatorTx( vdr, avaxAssetID, rewardOwner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) @@ -125,12 +127,12 @@ var _ = e2e.DescribePChain("[Workflow]", func() { pBalances, err := pWallet.Builder().GetBalance() require.NoError(err) pStartBalance := pBalances[avaxAssetID] - tests.Outf("{{blue}} P-chain balance before P->X export: %d {{/}}\n", pStartBalance) + tc.Outf("{{blue}} P-chain balance before P->X export: %d {{/}}\n", pStartBalance) xBalances, err := xWallet.Builder().GetFTBalance() require.NoError(err) xStartBalance := xBalances[avaxAssetID] - tests.Outf("{{blue}} X-chain balance before P->X export: %d {{/}}\n", xStartBalance) + tc.Outf("{{blue}} X-chain balance before P->X export: %d {{/}}\n", xStartBalance) outputOwner := secp256k1fx.OutputOwners{ Threshold: 1, @@ -143,7 +145,7 @@ var _ = e2e.DescribePChain("[Workflow]", func() { OutputOwners: outputOwner, } - ginkgo.By("export avax from P to X chain", func() { + tc.By("export avax from P to X chain", func() { _, err := pWallet.IssueExportTx( xContext.BlockchainID, []*avax.TransferableOutput{ @@ -154,7 +156,7 @@ var _ = e2e.DescribePChain("[Workflow]", func() { Out: output, }, }, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) @@ -163,21 +165,21 @@ var _ = e2e.DescribePChain("[Workflow]", func() { pBalances, err = pWallet.Builder().GetBalance() require.NoError(err) pPreImportBalance := pBalances[avaxAssetID] - tests.Outf("{{blue}} P-chain balance after P->X export: %d {{/}}\n", pPreImportBalance) + tc.Outf("{{blue}} P-chain balance after P->X export: %d {{/}}\n", pPreImportBalance) xBalances, err = xWallet.Builder().GetFTBalance() require.NoError(err) xPreImportBalance := xBalances[avaxAssetID] - tests.Outf("{{blue}} X-chain balance after P->X export: %d {{/}}\n", xPreImportBalance) + tc.Outf("{{blue}} X-chain balance after P->X export: %d {{/}}\n", xPreImportBalance) require.Equal(xPreImportBalance, xStartBalance) // import not performed yet require.Equal(pPreImportBalance, pStartBalance-toTransfer-txFees) - ginkgo.By("import avax from P into X chain", func() { + tc.By("import avax from P into X chain", func() { _, err := xWallet.IssueImportTx( constants.PlatformChainID, &outputOwner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) @@ -186,12 +188,12 @@ var _ = e2e.DescribePChain("[Workflow]", func() { pBalances, err = pWallet.Builder().GetBalance() require.NoError(err) pFinalBalance := pBalances[avaxAssetID] - tests.Outf("{{blue}} P-chain balance after P->X import: %d {{/}}\n", pFinalBalance) + tc.Outf("{{blue}} P-chain balance after P->X import: %d {{/}}\n", pFinalBalance) xBalances, err = xWallet.Builder().GetFTBalance() require.NoError(err) xFinalBalance := xBalances[avaxAssetID] - tests.Outf("{{blue}} X-chain balance after P->X import: %d {{/}}\n", xFinalBalance) + tc.Outf("{{blue}} X-chain balance after P->X import: %d {{/}}\n", xFinalBalance) require.Equal(xFinalBalance, xPreImportBalance+toTransfer-txFees) // import not performed yet require.Equal(pFinalBalance, pPreImportBalance) diff --git a/tests/e2e/vms/xsvm.go b/tests/e2e/vms/xsvm.go index 3610ac6dd7f..562df5edb01 100644 --- a/tests/e2e/vms/xsvm.go +++ b/tests/e2e/vms/xsvm.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/tests" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/subnet" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" @@ -51,10 +50,11 @@ func XSVMSubnetsOrPanic(nodes ...*tmpnet.Node) []*tmpnet.Subnet { } var _ = ginkgo.Describe("[XSVM]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) ginkgo.It("should support transfers between subnets", func() { - network := e2e.Env.GetNetwork() + network := e2e.GetEnv(tc).GetNetwork() sourceSubnet := network.GetSubnet(subnetAName) require.NotNil(sourceSubnet) @@ -67,29 +67,29 @@ var _ = ginkgo.Describe("[XSVM]", func() { sourceValidators := getNodesForIDs(network.Nodes, sourceSubnet.ValidatorIDs) require.NotEmpty(sourceValidators) sourceAPINode := sourceValidators[0] - tests.Outf(" issuing transactions for source subnet on %s (%s)\n", sourceAPINode.NodeID, sourceAPINode.URI) + tc.Outf(" issuing transactions for source subnet on %s (%s)\n", sourceAPINode.NodeID, sourceAPINode.URI) destinationValidators := getNodesForIDs(network.Nodes, destinationSubnet.ValidatorIDs) require.NotEmpty(destinationValidators) destinationAPINode := destinationValidators[0] - tests.Outf(" issuing transactions for destination subnet on %s (%s)\n", destinationAPINode.NodeID, destinationAPINode.URI) + tc.Outf(" issuing transactions for destination subnet on %s (%s)\n", destinationAPINode.NodeID, destinationAPINode.URI) destinationKey, err := secp256k1.NewPrivateKey() require.NoError(err) - ginkgo.By("checking that the funded key has sufficient funds for the export") + tc.By("checking that the funded key has sufficient funds for the export") sourceClient := api.NewClient(sourceAPINode.URI, sourceChain.ChainID.String()) initialSourcedBalance, err := sourceClient.Balance( - e2e.DefaultContext(), + tc.DefaultContext(), sourceChain.PreFundedKey.Address(), sourceChain.ChainID, ) require.NoError(err) require.GreaterOrEqual(initialSourcedBalance, units.Schmeckle) - ginkgo.By(fmt.Sprintf("exporting from chain %s on subnet %s", sourceChain.ChainID, sourceSubnet.SubnetID)) + tc.By(fmt.Sprintf("exporting from chain %s on subnet %s", sourceChain.ChainID, sourceSubnet.SubnetID)) exportTxStatus, err := export.Export( - e2e.DefaultContext(), + tc.DefaultContext(), &export.Config{ URI: sourceAPINode.URI, SourceChainID: sourceChain.ChainID, @@ -100,12 +100,12 @@ var _ = ginkgo.Describe("[XSVM]", func() { }, ) require.NoError(err) - tests.Outf(" issued transaction with ID: %s\n", exportTxStatus.TxID) + tc.Outf(" issued transaction with ID: %s\n", exportTxStatus.TxID) - ginkgo.By("checking that the export transaction has been accepted on all nodes") + tc.By("checking that the export transaction has been accepted on all nodes") for _, node := range sourceValidators[1:] { require.NoError(api.AwaitTxAccepted( - e2e.DefaultContext(), + tc.DefaultContext(), api.NewClient(node.URI, sourceChain.ChainID.String()), sourceChain.PreFundedKey.Address(), exportTxStatus.Nonce, @@ -113,12 +113,12 @@ var _ = ginkgo.Describe("[XSVM]", func() { )) } - ginkgo.By(fmt.Sprintf("issuing transaction on chain %s on subnet %s to activate snowman++ consensus", + tc.By(fmt.Sprintf("issuing transaction on chain %s on subnet %s to activate snowman++ consensus", destinationChain.ChainID, destinationSubnet.SubnetID)) recipientKey, err := secp256k1.NewPrivateKey() require.NoError(err) transferTxStatus, err := transfer.Transfer( - e2e.DefaultContext(), + tc.DefaultContext(), &transfer.Config{ URI: destinationAPINode.URI, ChainID: destinationChain.ChainID, @@ -129,15 +129,15 @@ var _ = ginkgo.Describe("[XSVM]", func() { }, ) require.NoError(err) - tests.Outf(" issued transaction with ID: %s\n", transferTxStatus.TxID) + tc.Outf(" issued transaction with ID: %s\n", transferTxStatus.TxID) - ginkgo.By(fmt.Sprintf("importing to blockchain %s on subnet %s", destinationChain.ChainID, destinationSubnet.SubnetID)) + tc.By(fmt.Sprintf("importing to blockchain %s on subnet %s", destinationChain.ChainID, destinationSubnet.SubnetID)) sourceURIs := make([]string, len(sourceValidators)) for i, node := range sourceValidators { sourceURIs[i] = node.URI } importTxStatus, err := importtx.Import( - e2e.DefaultContext(), + tc.DefaultContext(), &importtx.Config{ URI: destinationAPINode.URI, SourceURIs: sourceURIs, @@ -148,20 +148,20 @@ var _ = ginkgo.Describe("[XSVM]", func() { }, ) require.NoError(err) - tests.Outf(" issued transaction with ID: %s\n", importTxStatus.TxID) + tc.Outf(" issued transaction with ID: %s\n", importTxStatus.TxID) - ginkgo.By("checking that the balance of the source key has decreased") - sourceBalance, err := sourceClient.Balance(e2e.DefaultContext(), sourceChain.PreFundedKey.Address(), sourceChain.ChainID) + tc.By("checking that the balance of the source key has decreased") + sourceBalance, err := sourceClient.Balance(tc.DefaultContext(), sourceChain.PreFundedKey.Address(), sourceChain.ChainID) require.NoError(err) require.GreaterOrEqual(initialSourcedBalance-units.Schmeckle, sourceBalance) - ginkgo.By("checking that the balance of the destination key is non-zero") + tc.By("checking that the balance of the destination key is non-zero") destinationClient := api.NewClient(destinationAPINode.URI, destinationChain.ChainID.String()) - destinationBalance, err := destinationClient.Balance(e2e.DefaultContext(), destinationKey.Address(), sourceChain.ChainID) + destinationBalance, err := destinationClient.Balance(tc.DefaultContext(), destinationKey.Address(), sourceChain.ChainID) require.NoError(err) require.Equal(units.Schmeckle, destinationBalance) - _ = e2e.CheckBootstrapIsPossible(network) + _ = e2e.CheckBootstrapIsPossible(tc, network) }) }) diff --git a/tests/e2e/x/interchain_workflow.go b/tests/e2e/x/interchain_workflow.go index 2aa836ce7bb..3d5933ab54e 100644 --- a/tests/e2e/x/interchain_workflow.go +++ b/tests/e2e/x/interchain_workflow.go @@ -23,24 +23,27 @@ import ( ) var _ = e2e.DescribeXChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainLabel), func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) const transferAmount = 10 * units.Avax ginkgo.It("should ensure that funds can be transferred from the X-Chain to the C-Chain and the P-Chain", func() { - nodeURI := e2e.Env.GetRandomNodeURI() + env := e2e.GetEnv(tc) - ginkgo.By("creating wallet with a funded key to send from and recipient key to deliver to") + nodeURI := env.GetRandomNodeURI() + + tc.By("creating wallet with a funded key to send from and recipient key to deliver to") recipientKey, err := secp256k1.NewPrivateKey() require.NoError(err) - keychain := e2e.Env.NewKeychain(1) + keychain := env.NewKeychain(1) keychain.Add(recipientKey) - baseWallet := e2e.NewWallet(keychain, nodeURI) + baseWallet := e2e.NewWallet(tc, keychain, nodeURI) xWallet := baseWallet.X() cWallet := baseWallet.C() pWallet := baseWallet.P() - ginkgo.By("defining common configuration") + tc.By("defining common configuration") recipientEthAddress := evm.GetEthAddress(recipientKey) xBuilder := xWallet.Builder() xContext := xBuilder.Context() @@ -72,7 +75,7 @@ var _ = e2e.DescribeXChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL }, } - ginkgo.By("sending funds from one address to another on the X-Chain", func() { + tc.By("sending funds from one address to another on the X-Chain", func() { _, err = xWallet.IssueBaseTx( []*avax.TransferableOutput{{ Asset: avax.Asset{ @@ -83,12 +86,12 @@ var _ = e2e.DescribeXChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL OutputOwners: recipientOwner, }, }}, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("checking that the X-Chain recipient address has received the sent funds", func() { + tc.By("checking that the X-Chain recipient address has received the sent funds", func() { balances, err := xWallet.Builder().GetFTBalance(common.WithCustomAddresses(set.Of( recipientKey.Address(), ))) @@ -96,54 +99,54 @@ var _ = e2e.DescribeXChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL require.Positive(balances[avaxAssetID]) }) - ginkgo.By("exporting AVAX from the X-Chain to the C-Chain", func() { + tc.By("exporting AVAX from the X-Chain to the C-Chain", func() { _, err := xWallet.IssueExportTx( cContext.BlockchainID, exportOutputs, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("initializing a new eth client") - ethClient := e2e.NewEthClient(nodeURI) + tc.By("initializing a new eth client") + ethClient := e2e.NewEthClient(tc, nodeURI) - ginkgo.By("importing AVAX from the X-Chain to the C-Chain", func() { + tc.By("importing AVAX from the X-Chain to the C-Chain", func() { _, err := cWallet.IssueImportTx( xContext.BlockchainID, recipientEthAddress, - e2e.WithDefaultContext(), - e2e.WithSuggestedGasPrice(ethClient), + tc.WithDefaultContext(), + e2e.WithSuggestedGasPrice(tc, ethClient), ) require.NoError(err) }) - ginkgo.By("checking that the recipient address has received imported funds on the C-Chain") - e2e.Eventually(func() bool { - balance, err := ethClient.BalanceAt(e2e.DefaultContext(), recipientEthAddress, nil) + tc.By("checking that the recipient address has received imported funds on the C-Chain") + tc.Eventually(func() bool { + balance, err := ethClient.BalanceAt(tc.DefaultContext(), recipientEthAddress, nil) require.NoError(err) return balance.Cmp(big.NewInt(0)) > 0 }, e2e.DefaultTimeout, e2e.DefaultPollingInterval, "failed to see recipient address funded before timeout") - ginkgo.By("exporting AVAX from the X-Chain to the P-Chain", func() { + tc.By("exporting AVAX from the X-Chain to the P-Chain", func() { _, err := xWallet.IssueExportTx( constants.PlatformChainID, exportOutputs, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("importing AVAX from the X-Chain to the P-Chain", func() { + tc.By("importing AVAX from the X-Chain to the P-Chain", func() { _, err := pWallet.IssueImportTx( xContext.BlockchainID, &recipientOwner, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) }) - ginkgo.By("checking that the recipient address has received imported funds on the P-Chain", func() { + tc.By("checking that the recipient address has received imported funds on the P-Chain", func() { balances, err := pWallet.Builder().GetBalance(common.WithCustomAddresses(set.Of( recipientKey.Address(), ))) @@ -151,6 +154,6 @@ var _ = e2e.DescribeXChain("[Interchain Workflow]", ginkgo.Label(e2e.UsesCChainL require.Positive(balances[avaxAssetID]) }) - _ = e2e.CheckBootstrapIsPossible(e2e.Env.GetNetwork()) + _ = e2e.CheckBootstrapIsPossible(tc, env.GetNetwork()) }) }) diff --git a/tests/e2e/x/transfer/virtuous.go b/tests/e2e/x/transfer/virtuous.go index 58a0351ba12..db9c1508c1d 100644 --- a/tests/e2e/x/transfer/virtuous.go +++ b/tests/e2e/x/transfer/virtuous.go @@ -40,21 +40,23 @@ var xChainMetricLabels = prometheus.Labels{ // This test requires that the network not have ongoing blocks and // cannot reliably be run in parallel. var _ = e2e.DescribeXChainSerial("[Virtuous Transfer Tx AVAX]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) ginkgo.It("can issue a virtuous transfer tx for AVAX asset", func() { - rpcEps := make([]string, len(e2e.Env.URIs)) - for i, nodeURI := range e2e.Env.URIs { + env := e2e.GetEnv(tc) + rpcEps := make([]string, len(env.URIs)) + for i, nodeURI := range env.URIs { rpcEps[i] = nodeURI.URI } // Waiting for ongoing blocks to have completed before starting this // test avoids the case of a previous test having initiated block // processing but not having completed it. - e2e.Eventually(func() bool { + tc.Eventually(func() bool { allNodeMetrics, err := tests.GetNodesMetrics( - e2e.DefaultContext(), + tc.DefaultContext(), rpcEps, ) require.NoError(err) @@ -74,10 +76,10 @@ var _ = e2e.DescribeXChainSerial("[Virtuous Transfer Tx AVAX]", func() { // Ensure the same set of 10 keys is used for all tests // by retrieving them outside of runFunc. - testKeys := e2e.Env.AllocatePreFundedKeys(10) + testKeys := env.AllocatePreFundedKeys(10) runFunc := func(round int) { - tests.Outf("{{green}}\n\n\n\n\n\n---\n[ROUND #%02d]:{{/}}\n", round) + tc.Outf("{{green}}\n\n\n\n\n\n---\n[ROUND #%02d]:{{/}}\n", round) needPermute := round > 3 if needPermute { @@ -88,7 +90,7 @@ var _ = e2e.DescribeXChainSerial("[Virtuous Transfer Tx AVAX]", func() { } keychain := secp256k1fx.NewKeychain(testKeys...) - baseWallet := e2e.NewWallet(keychain, e2e.Env.GetRandomNodeURI()) + baseWallet := e2e.NewWallet(tc, keychain, env.GetRandomNodeURI()) xWallet := baseWallet.X() xBuilder := xWallet.Builder() xContext := xBuilder.Context() @@ -108,13 +110,13 @@ var _ = e2e.DescribeXChainSerial("[Virtuous Transfer Tx AVAX]", func() { } metricsBeforeTx, err := tests.GetNodesMetrics( - e2e.DefaultContext(), + tc.DefaultContext(), rpcEps, ) require.NoError(err) for _, uri := range rpcEps { for _, metric := range []string{blksProcessingMetric, blksAcceptedMetric} { - tests.Outf("{{green}}%s at %q:{{/}} %v\n", metric, uri, metricsBeforeTx[uri][metric]) + tc.Outf("{{green}}%s at %q:{{/}} %v\n", metric, uri, metricsBeforeTx[uri][metric]) } } @@ -162,7 +164,7 @@ var _ = e2e.DescribeXChainSerial("[Virtuous Transfer Tx AVAX]", func() { senderNewBal := senderOrigBal - amountToTransfer - xContext.BaseTxFee receiverNewBal := receiverOrigBal + amountToTransfer - ginkgo.By("X-Chain transfer with wrong amount must fail", func() { + tc.By("X-Chain transfer with wrong amount must fail", func() { _, err := wallets[fromIdx].X().IssueBaseTx( []*avax.TransferableOutput{{ Asset: avax.Asset{ @@ -176,7 +178,7 @@ var _ = e2e.DescribeXChainSerial("[Virtuous Transfer Tx AVAX]", func() { }, }, }}, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.Contains(err.Error(), "insufficient funds") }) @@ -217,19 +219,19 @@ RECEIVER NEW BALANCE (AFTER) : %21d AVAX }, }, }}, - e2e.WithDefaultContext(), + tc.WithDefaultContext(), ) require.NoError(err) balances, err := wallets[fromIdx].X().Builder().GetFTBalance() require.NoError(err) senderCurBalX := balances[avaxAssetID] - tests.Outf("{{green}}first wallet balance:{{/}} %d\n", senderCurBalX) + tc.Outf("{{green}}first wallet balance:{{/}} %d\n", senderCurBalX) balances, err = wallets[toIdx].X().Builder().GetFTBalance() require.NoError(err) receiverCurBalX := balances[avaxAssetID] - tests.Outf("{{green}}second wallet balance:{{/}} %d\n", receiverCurBalX) + tc.Outf("{{green}}second wallet balance:{{/}} %d\n", receiverCurBalX) require.Equal(senderCurBalX, senderNewBal) require.Equal(receiverCurBalX, receiverNewBal) @@ -237,14 +239,14 @@ RECEIVER NEW BALANCE (AFTER) : %21d AVAX txID := tx.ID() for _, u := range rpcEps { xc := avm.NewClient(u, "X") - require.NoError(avm.AwaitTxAccepted(xc, e2e.DefaultContext(), txID, 2*time.Second)) + require.NoError(avm.AwaitTxAccepted(xc, tc.DefaultContext(), txID, 2*time.Second)) } for _, u := range rpcEps { xc := avm.NewClient(u, "X") - require.NoError(avm.AwaitTxAccepted(xc, e2e.DefaultContext(), txID, 2*time.Second)) + require.NoError(avm.AwaitTxAccepted(xc, tc.DefaultContext(), txID, 2*time.Second)) - mm, err := tests.GetNodeMetrics(e2e.DefaultContext(), u) + mm, err := tests.GetNodeMetrics(tc.DefaultContext(), u) require.NoError(err) prev := metricsBeforeTx[u] diff --git a/tests/fixture/e2e/env.go b/tests/fixture/e2e/env.go index 05fbbd97ac8..f6bf98ddbad 100644 --- a/tests/fixture/e2e/env.go +++ b/tests/fixture/e2e/env.go @@ -19,20 +19,18 @@ import ( "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/vms/secp256k1fx" - - ginkgo "github.com/onsi/ginkgo/v2" ) // Env is used to access shared test fixture. Intended to be -// initialized from SynchronizedBeforeSuite. -var Env *TestEnvironment - -func InitSharedTestEnvironment(envBytes []byte) { - require := require.New(ginkgo.GinkgoT()) - require.Nil(Env, "env already initialized") - Env = &TestEnvironment{} - require.NoError(json.Unmarshal(envBytes, Env)) - Env.require = require +// initialized from SynchronizedBeforeSuite. Not exported to limit +// access to the shared env to GetEnv which adds a test context. +var env *TestEnvironment + +func InitSharedTestEnvironment(t require.TestingT, envBytes []byte) { + require := require.New(t) + require.Nil(env, "env already initialized") + env = &TestEnvironment{} + require.NoError(json.Unmarshal(envBytes, env)) } type TestEnvironment struct { @@ -47,18 +45,29 @@ type TestEnvironment struct { // scraped before shutdown. PrivateNetworkShutdownDelay time.Duration - require *require.Assertions + testContext tests.TestContext +} + +// Retrieve the test environment configured with the provided test context. +func GetEnv(tc tests.TestContext) *TestEnvironment { + return &TestEnvironment{ + NetworkDir: env.NetworkDir, + URIs: env.URIs, + TestDataServerURI: env.TestDataServerURI, + PrivateNetworkShutdownDelay: env.PrivateNetworkShutdownDelay, + testContext: tc, + } } func (te *TestEnvironment) Marshal() []byte { bytes, err := json.Marshal(te) - require.NoError(ginkgo.GinkgoT(), err) + require.NoError(te.testContext, err) return bytes } // Initialize a new test environment with a shared network (either pre-existing or newly created). -func NewTestEnvironment(flagVars *FlagVars, desiredNetwork *tmpnet.Network) *TestEnvironment { - require := require.New(ginkgo.GinkgoT()) +func NewTestEnvironment(tc tests.TestContext, flagVars *FlagVars, desiredNetwork *tmpnet.Network) *TestEnvironment { + require := require.New(tc) var network *tmpnet.Network // Need to load the network if it is being stopped or reused @@ -83,22 +92,22 @@ func NewTestEnvironment(flagVars *FlagVars, desiredNetwork *tmpnet.Network) *Tes var err error network, err = tmpnet.ReadNetwork(networkDir) require.NoError(err) - tests.Outf("{{yellow}}Loaded a network configured at %s{{/}}\n", network.Dir) + tc.Outf("{{yellow}}Loaded a network configured at %s{{/}}\n", network.Dir) } if flagVars.StopNetwork() { if len(networkSymlink) > 0 { // Remove the symlink to avoid attempts to reuse the stopped network - tests.Outf("Removing symlink %s\n", networkSymlink) + tc.Outf("Removing symlink %s\n", networkSymlink) if err := os.Remove(networkSymlink); !errors.Is(err, os.ErrNotExist) { require.NoError(err) } } if network != nil { - tests.Outf("Stopping network\n") - require.NoError(network.Stop(DefaultContext())) + tc.Outf("Stopping network\n") + require.NoError(network.Stop(tc.DefaultContext())) } else { - tests.Outf("No network to stop\n") + tc.Outf("No network to stop\n") } os.Exit(0) } @@ -108,6 +117,7 @@ func NewTestEnvironment(flagVars *FlagVars, desiredNetwork *tmpnet.Network) *Tes if network == nil { network = desiredNetwork StartNetwork( + tc, network, flagVars.AvalancheGoExecPath(), flagVars.PluginDir(), @@ -116,14 +126,14 @@ func NewTestEnvironment(flagVars *FlagVars, desiredNetwork *tmpnet.Network) *Tes ) // Wait for chains to have bootstrapped on all nodes - Eventually(func() bool { + tc.Eventually(func() bool { for _, subnet := range network.Subnets { for _, validatorID := range subnet.ValidatorIDs { uri, err := network.GetURIForNodeID(validatorID) require.NoError(err) infoClient := info.NewClient(uri) for _, chain := range subnet.Chains { - isBootstrapped, err := infoClient.IsBootstrapped(DefaultContext(), chain.ChainID.String()) + isBootstrapped, err := infoClient.IsBootstrapped(tc.DefaultContext(), chain.ChainID.String()) // Ignore errors since a chain id that is not yet known will result in a recoverable error. if err != nil || !isBootstrapped { return false @@ -137,12 +147,12 @@ func NewTestEnvironment(flagVars *FlagVars, desiredNetwork *tmpnet.Network) *Tes uris := network.GetNodeURIs() require.NotEmpty(uris, "network contains no nodes") - tests.Outf("{{green}}network URIs: {{/}} %+v\n", uris) + tc.Outf("{{green}}network URIs: {{/}} %+v\n", uris) testDataServerURI, err := fixture.ServeTestData(fixture.TestData{ PreFundedKeys: network.PreFundedKeys, }) - tests.Outf("{{green}}test data server URI: {{/}} %+v\n", testDataServerURI) + tc.Outf("{{green}}test data server URI: {{/}} %+v\n", testDataServerURI) require.NoError(err) return &TestEnvironment{ @@ -150,7 +160,7 @@ func NewTestEnvironment(flagVars *FlagVars, desiredNetwork *tmpnet.Network) *Tes URIs: uris, TestDataServerURI: testDataServerURI, PrivateNetworkShutdownDelay: flagVars.NetworkShutdownDelay(), - require: require, + testContext: tc, } } @@ -159,22 +169,22 @@ func NewTestEnvironment(flagVars *FlagVars, desiredNetwork *tmpnet.Network) *Tes func (te *TestEnvironment) GetRandomNodeURI() tmpnet.NodeURI { r := rand.New(rand.NewSource(time.Now().Unix())) //#nosec G404 nodeURI := te.URIs[r.Intn(len(te.URIs))] - tests.Outf("{{blue}} targeting node %s with URI: %s{{/}}\n", nodeURI.NodeID, nodeURI.URI) + te.testContext.Outf("{{blue}} targeting node %s with URI: %s{{/}}\n", nodeURI.NodeID, nodeURI.URI) return nodeURI } // Retrieve the network to target for testing. func (te *TestEnvironment) GetNetwork() *tmpnet.Network { network, err := tmpnet.ReadNetwork(te.NetworkDir) - te.require.NoError(err) + require.NoError(te.testContext, err) return network } // Retrieve the specified number of funded keys allocated for the caller's exclusive use. func (te *TestEnvironment) AllocatePreFundedKeys(count int) []*secp256k1.PrivateKey { keys, err := fixture.AllocatePreFundedKeys(te.TestDataServerURI, count) - te.require.NoError(err) - tests.Outf("{{blue}} allocated pre-funded key(s): %+v{{/}}\n", keys) + require.NoError(te.testContext, err) + te.testContext.Outf("{{blue}} allocated pre-funded key(s): %+v{{/}}\n", keys) return keys } @@ -191,14 +201,16 @@ func (te *TestEnvironment) NewKeychain(count int) *secp256k1fx.Keychain { // Create a new private network that is not shared with other tests. func (te *TestEnvironment) StartPrivateNetwork(network *tmpnet.Network) { + require := require.New(te.testContext) // Use the same configuration as the shared network sharedNetwork, err := tmpnet.ReadNetwork(te.NetworkDir) - te.require.NoError(err) + require.NoError(err) pluginDir, err := sharedNetwork.DefaultFlags.GetStringVal(config.PluginDirKey) - te.require.NoError(err) + require.NoError(err) StartNetwork( + te.testContext, network, sharedNetwork.DefaultRuntimeConfig.AvalancheGoPath, pluginDir, diff --git a/tests/fixture/e2e/ginkgo_test_context.go b/tests/fixture/e2e/ginkgo_test_context.go new file mode 100644 index 00000000000..ff3fa7b468a --- /dev/null +++ b/tests/fixture/e2e/ginkgo_test_context.go @@ -0,0 +1,97 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package e2e + +import ( + "context" + "io" + "time" + + "github.com/onsi/ginkgo/v2/formatter" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/tests" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" + + ginkgo "github.com/onsi/ginkgo/v2" +) + +type GinkgoTestContext struct{} + +func NewTestContext() *GinkgoTestContext { + return &GinkgoTestContext{} +} + +func (*GinkgoTestContext) Errorf(format string, args ...interface{}) { + ginkgo.GinkgoT().Errorf(format, args...) +} + +func (*GinkgoTestContext) FailNow() { + ginkgo.GinkgoT().FailNow() +} + +func (*GinkgoTestContext) GetWriter() io.Writer { + return ginkgo.GinkgoWriter +} + +func (*GinkgoTestContext) DeferCleanup(args ...interface{}) { + ginkgo.DeferCleanup(args...) +} + +func (*GinkgoTestContext) By(text string, callback ...func()) { + ginkgo.By(text, callback...) +} + +// Outputs to stdout. +// +// Examples: +// +// - Out("{{green}}{{bold}}hi there %q{{/}}", "aa") +// - Out("{{magenta}}{{bold}}hi therea{{/}} {{cyan}}{{underline}}b{{/}}") +// +// See https://github.com/onsi/ginkgo/blob/v2.0.0/formatter/formatter.go#L52-L73 +// for an exhaustive list of color options. +func (*GinkgoTestContext) Outf(format string, args ...interface{}) { + s := formatter.F(format, args...) + // Use GinkgoWriter to ensure that output from this function is + // printed sequentially within other test output produced with + // GinkgoWriter (e.g. `STEP:...`) when tests are run in + // parallel. ginkgo collects and writes stdout separately from + // GinkgoWriter during parallel execution and the resulting output + // can be confusing. + ginkgo.GinkgoWriter.Print(s) +} + +// Helper simplifying use of a timed context by canceling the context on ginkgo teardown. +func (tc *GinkgoTestContext) ContextWithTimeout(duration time.Duration) context.Context { + return tests.ContextWithTimeout(tc, duration) +} + +// Helper simplifying use of a timed context configured with the default timeout. +func (tc *GinkgoTestContext) DefaultContext() context.Context { + return tests.DefaultContext(tc) +} + +// Helper simplifying use via an option of a timed context configured with the default timeout. +func (tc *GinkgoTestContext) WithDefaultContext() common.Option { + return tests.WithDefaultContext(tc) +} + +// Re-implementation of testify/require.Eventually that is compatible with ginkgo. testify's +// version calls the condition function with a goroutine and ginkgo assertions don't work +// properly in goroutines. +func (*GinkgoTestContext) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msg string) { + ticker := time.NewTicker(tick) + defer ticker.Stop() + + ctx, cancel := context.WithTimeout(context.Background(), waitFor) + defer cancel() + for !condition() { + select { + case <-ctx.Done(): + require.Fail(ginkgo.GinkgoT(), msg) + case <-ticker.C: + } + } +} diff --git a/tests/fixture/e2e/helpers.go b/tests/fixture/e2e/helpers.go index e2d8644fc02..18de668fcbf 100644 --- a/tests/fixture/e2e/helpers.go +++ b/tests/fixture/e2e/helpers.go @@ -24,15 +24,10 @@ import ( "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/subnet/primary" "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" - - ginkgo "github.com/onsi/ginkgo/v2" ) const ( - // A long default timeout used to timeout failed operations but - // unlikely to induce flaking due to unexpected resource - // contention. - DefaultTimeout = 2 * time.Minute + DefaultTimeout = tests.DefaultTimeout DefaultPollingInterval = tmpnet.DefaultPollingInterval @@ -55,78 +50,43 @@ const ( ) // Create a new wallet for the provided keychain against the specified node URI. -func NewWallet(keychain *secp256k1fx.Keychain, nodeURI tmpnet.NodeURI) primary.Wallet { - tests.Outf("{{blue}} initializing a new wallet for node %s with URI: %s {{/}}\n", nodeURI.NodeID, nodeURI.URI) - baseWallet, err := primary.MakeWallet(DefaultContext(), &primary.WalletConfig{ +func NewWallet(tc tests.TestContext, keychain *secp256k1fx.Keychain, nodeURI tmpnet.NodeURI) primary.Wallet { + tc.Outf("{{blue}} initializing a new wallet for node %s with URI: %s {{/}}\n", nodeURI.NodeID, nodeURI.URI) + baseWallet, err := primary.MakeWallet(tc.DefaultContext(), &primary.WalletConfig{ URI: nodeURI.URI, AVAXKeychain: keychain, EthKeychain: keychain, }) - require.NoError(ginkgo.GinkgoT(), err) + require.NoError(tc, err) return primary.NewWalletWithOptions( baseWallet, common.WithPostIssuanceFunc( func(id ids.ID) { - tests.Outf(" issued transaction with ID: %s\n", id) + tc.Outf(" issued transaction with ID: %s\n", id) }, ), ) } // Create a new eth client targeting the specified node URI. -func NewEthClient(nodeURI tmpnet.NodeURI) ethclient.Client { - tests.Outf("{{blue}} initializing a new eth client for node %s with URI: %s {{/}}\n", nodeURI.NodeID, nodeURI.URI) +func NewEthClient(tc tests.TestContext, nodeURI tmpnet.NodeURI) ethclient.Client { + tc.Outf("{{blue}} initializing a new eth client for node %s with URI: %s {{/}}\n", nodeURI.NodeID, nodeURI.URI) nodeAddress := strings.Split(nodeURI.URI, "//")[1] uri := fmt.Sprintf("ws://%s/ext/bc/C/ws", nodeAddress) client, err := ethclient.Dial(uri) - require.NoError(ginkgo.GinkgoT(), err) + require.NoError(tc, err) return client } -// Helper simplifying use of a timed context by canceling the context on ginkgo teardown. -func ContextWithTimeout(duration time.Duration) context.Context { - ctx, cancel := context.WithTimeout(context.Background(), duration) - ginkgo.DeferCleanup(cancel) - return ctx -} - -// Helper simplifying use of a timed context configured with the default timeout. -func DefaultContext() context.Context { - return ContextWithTimeout(DefaultTimeout) -} - -// Helper simplifying use via an option of a timed context configured with the default timeout. -func WithDefaultContext() common.Option { - return common.WithContext(DefaultContext()) -} - -// Re-implementation of testify/require.Eventually that is compatible with ginkgo. testify's -// version calls the condition function with a goroutine and ginkgo assertions don't work -// properly in goroutines. -func Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msg string) { - ticker := time.NewTicker(tick) - defer ticker.Stop() - - ctx, cancel := context.WithTimeout(context.Background(), waitFor) - defer cancel() - for !condition() { - select { - case <-ctx.Done(): - require.Fail(ginkgo.GinkgoT(), msg) - case <-ticker.C: - } - } -} - // Adds an ephemeral node intended to be used by a single test. -func AddEphemeralNode(network *tmpnet.Network, flags tmpnet.FlagsMap) *tmpnet.Node { - require := require.New(ginkgo.GinkgoT()) +func AddEphemeralNode(tc tests.TestContext, network *tmpnet.Network, flags tmpnet.FlagsMap) *tmpnet.Node { + require := require.New(tc) node := tmpnet.NewEphemeralNode(flags) - require.NoError(network.StartNode(DefaultContext(), ginkgo.GinkgoWriter, node)) + require.NoError(network.StartNode(tc.DefaultContext(), tc.GetWriter(), node)) - ginkgo.DeferCleanup(func() { - tests.Outf("shutting down ephemeral node %q\n", node.NodeID) + tc.DeferCleanup(func() { + tc.Outf("shutting down ephemeral node %q\n", node.NodeID) ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout) defer cancel() require.NoError(node.Stop(ctx)) @@ -135,28 +95,28 @@ func AddEphemeralNode(network *tmpnet.Network, flags tmpnet.FlagsMap) *tmpnet.No } // Wait for the given node to report healthy. -func WaitForHealthy(node *tmpnet.Node) { +func WaitForHealthy(t require.TestingT, node *tmpnet.Node) { // Need to use explicit context (vs DefaultContext()) to support use with DeferCleanup ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout) defer cancel() - require.NoError(ginkgo.GinkgoT(), tmpnet.WaitForHealthy(ctx, node)) + require.NoError(t, tmpnet.WaitForHealthy(ctx, node)) } // Sends an eth transaction, waits for the transaction receipt to be issued // and checks that the receipt indicates success. -func SendEthTransaction(ethClient ethclient.Client, signedTx *types.Transaction) *types.Receipt { - require := require.New(ginkgo.GinkgoT()) +func SendEthTransaction(tc tests.TestContext, ethClient ethclient.Client, signedTx *types.Transaction) *types.Receipt { + require := require.New(tc) txID := signedTx.Hash() - tests.Outf(" sending eth transaction with ID: %s\n", txID) + tc.Outf(" sending eth transaction with ID: %s\n", txID) - require.NoError(ethClient.SendTransaction(DefaultContext(), signedTx)) + require.NoError(ethClient.SendTransaction(tc.DefaultContext(), signedTx)) // Wait for the receipt var receipt *types.Receipt - Eventually(func() bool { + tc.Eventually(func() bool { var err error - receipt, err = ethClient.TransactionReceipt(DefaultContext(), txID) + receipt, err = ethClient.TransactionReceipt(tc.DefaultContext(), txID) if errors.Is(err, interfaces.NotFound) { return false // Transaction is still pending } @@ -170,9 +130,9 @@ func SendEthTransaction(ethClient ethclient.Client, signedTx *types.Transaction) // Determines the suggested gas price for the configured client that will // maximize the chances of transaction acceptance. -func SuggestGasPrice(ethClient ethclient.Client) *big.Int { - gasPrice, err := ethClient.SuggestGasPrice(DefaultContext()) - require.NoError(ginkgo.GinkgoT(), err) +func SuggestGasPrice(tc tests.TestContext, ethClient ethclient.Client) *big.Int { + gasPrice, err := ethClient.SuggestGasPrice(tc.DefaultContext()) + require.NoError(tc, err) // Double the suggested gas price to maximize the chances of // acceptance. Maybe this can be revisited pending resolution of // https://github.com/ava-labs/coreth/issues/314. @@ -181,21 +141,21 @@ func SuggestGasPrice(ethClient ethclient.Client) *big.Int { } // Helper simplifying use via an option of a gas price appropriate for testing. -func WithSuggestedGasPrice(ethClient ethclient.Client) common.Option { - baseFee := SuggestGasPrice(ethClient) +func WithSuggestedGasPrice(tc tests.TestContext, ethClient ethclient.Client) common.Option { + baseFee := SuggestGasPrice(tc, ethClient) return common.WithBaseFee(baseFee) } // Verify that a new node can bootstrap into the network. If the check wasn't skipped, // the node will be returned to the caller. -func CheckBootstrapIsPossible(network *tmpnet.Network) *tmpnet.Node { - require := require.New(ginkgo.GinkgoT()) +func CheckBootstrapIsPossible(tc tests.TestContext, network *tmpnet.Network) *tmpnet.Node { + require := require.New(tc) if len(os.Getenv(SkipBootstrapChecksEnvName)) > 0 { - tests.Outf("{{yellow}}Skipping bootstrap check due to the %s env var being set", SkipBootstrapChecksEnvName) + tc.Outf("{{yellow}}Skipping bootstrap check due to the %s env var being set", SkipBootstrapChecksEnvName) return nil } - ginkgo.By("checking if bootstrap is possible with the current network state") + tc.By("checking if bootstrap is possible with the current network state") // Ensure all subnets are bootstrapped subnetIDs := make([]string, len(network.Subnets)) @@ -207,36 +167,37 @@ func CheckBootstrapIsPossible(network *tmpnet.Network) *tmpnet.Node { } node := tmpnet.NewEphemeralNode(flags) - require.NoError(network.StartNode(DefaultContext(), ginkgo.GinkgoWriter, node)) + require.NoError(network.StartNode(tc.DefaultContext(), tc.GetWriter(), node)) // StartNode will initiate node stop if an error is encountered during start, // so no further cleanup effort is required if an error is seen here. // Register a cleanup to ensure the node is stopped at the end of the test - ginkgo.DeferCleanup(func() { + tc.DeferCleanup(func() { ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout) defer cancel() require.NoError(node.Stop(ctx)) }) // Check that the node becomes healthy within timeout - require.NoError(tmpnet.WaitForHealthy(DefaultContext(), node)) + require.NoError(tmpnet.WaitForHealthy(tc.DefaultContext(), node)) return node } // Start a temporary network with the provided avalanchego binary. func StartNetwork( + tc tests.TestContext, network *tmpnet.Network, avalancheGoExecPath string, pluginDir string, shutdownDelay time.Duration, reuseNetwork bool, ) { - require := require.New(ginkgo.GinkgoT()) + require := require.New(tc) require.NoError( tmpnet.BootstrapNewNetwork( - DefaultContext(), - ginkgo.GinkgoWriter, + tc.DefaultContext(), + tc.GetWriter(), network, DefaultNetworkDir, avalancheGoExecPath, @@ -244,7 +205,7 @@ func StartNetwork( ), ) - tests.Outf("{{green}}Successfully started network{{/}}\n") + tc.Outf("{{green}}Successfully started network{{/}}\n") symlinkPath, err := tmpnet.GetReusableNetworkPathForOwner(network.Owner) require.NoError(err) @@ -253,21 +214,21 @@ func StartNetwork( // Symlink the path of the created network to the default owner path (e.g. latest_avalanchego-e2e) // to enable easy discovery for reuse. require.NoError(os.Symlink(network.Dir, symlinkPath)) - tests.Outf("{{green}}Symlinked %s to %s to enable reuse{{/}}\n", network.Dir, symlinkPath) + tc.Outf("{{green}}Symlinked %s to %s to enable reuse{{/}}\n", network.Dir, symlinkPath) } - ginkgo.DeferCleanup(func() { + tc.DeferCleanup(func() { if reuseNetwork { - tests.Outf("{{yellow}}Skipping shutdown for network %s (symlinked to %s) to enable reuse{{/}}\n", network.Dir, symlinkPath) + tc.Outf("{{yellow}}Skipping shutdown for network %s (symlinked to %s) to enable reuse{{/}}\n", network.Dir, symlinkPath) return } if shutdownDelay > 0 { - tests.Outf("Waiting %s before network shutdown to ensure final metrics scrape\n", shutdownDelay) + tc.Outf("Waiting %s before network shutdown to ensure final metrics scrape\n", shutdownDelay) time.Sleep(shutdownDelay) } - tests.Outf("Shutting down network\n") + tc.Outf("Shutting down network\n") ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout) defer cancel() require.NoError(network.Stop(ctx)) diff --git a/tests/test_context.go b/tests/test_context.go new file mode 100644 index 00000000000..2ddacf555a4 --- /dev/null +++ b/tests/test_context.go @@ -0,0 +1,42 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package tests + +import ( + "context" + "io" + "time" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" +) + +type TestContext interface { + // Ensures the context can be used to instantiate a require instance + require.TestingT + + // Ensures compatibility with ginkgo.By + By(text string, callback ...func()) + + // Ensures compatibility with ginkgo.DeferCleanup + // + // TODO(marun) Ensure registered cleanup functions are called at + // the end of the registering test. + DeferCleanup(args ...interface{}) + + // Enables color output to stdout + Outf(format string, args ...interface{}) + + // Ensures compatibility with ginkgo.GinkgoWriter + GetWriter() io.Writer + + // Context helpers requiring cleanup with DeferCleanup + ContextWithTimeout(duration time.Duration) context.Context + DefaultContext() context.Context + WithDefaultContext() common.Option + + // Ensures compatibility with require.Eventually + Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msg string) +} diff --git a/tests/upgrade/upgrade_test.go b/tests/upgrade/upgrade_test.go index c27a88b6ce6..32c76fd6dd0 100644 --- a/tests/upgrade/upgrade_test.go +++ b/tests/upgrade/upgrade_test.go @@ -40,25 +40,26 @@ func init() { } var _ = ginkgo.Describe("[Upgrade]", func() { - require := require.New(ginkgo.GinkgoT()) + tc := e2e.NewTestContext() + require := require.New(tc) ginkgo.It("can upgrade versions", func() { network := tmpnet.NewDefaultNetwork("avalanchego-upgrade") - e2e.StartNetwork(network, avalancheGoExecPath, "" /* pluginDir */, 0 /* shutdownDelay */, false /* reuseNetwork */) + e2e.StartNetwork(tc, network, avalancheGoExecPath, "" /* pluginDir */, 0 /* shutdownDelay */, false /* reuseNetwork */) - ginkgo.By(fmt.Sprintf("restarting all nodes with %q binary", avalancheGoExecPathToUpgradeTo)) + tc.By(fmt.Sprintf("restarting all nodes with %q binary", avalancheGoExecPathToUpgradeTo)) for _, node := range network.Nodes { - ginkgo.By(fmt.Sprintf("restarting node %q with %q binary", node.NodeID, avalancheGoExecPathToUpgradeTo)) - require.NoError(node.Stop(e2e.DefaultContext())) + tc.By(fmt.Sprintf("restarting node %q with %q binary", node.NodeID, avalancheGoExecPathToUpgradeTo)) + require.NoError(node.Stop(tc.DefaultContext())) node.RuntimeConfig.AvalancheGoPath = avalancheGoExecPathToUpgradeTo - require.NoError(network.StartNode(e2e.DefaultContext(), ginkgo.GinkgoWriter, node)) + require.NoError(network.StartNode(tc.DefaultContext(), tc.GetWriter(), node)) - ginkgo.By(fmt.Sprintf("waiting for node %q to report healthy after restart", node.NodeID)) - e2e.WaitForHealthy(node) + tc.By(fmt.Sprintf("waiting for node %q to report healthy after restart", node.NodeID)) + e2e.WaitForHealthy(tc, node) } - _ = e2e.CheckBootstrapIsPossible(network) + _ = e2e.CheckBootstrapIsPossible(tc, network) }) })