Skip to content

Commit

Permalink
support procedural test style for VM-defined workloads (#1667)
Browse files Browse the repository at this point in the history
Signed-off-by: Tsachi Herman <[email protected]>
  • Loading branch information
tsachiherman authored Nov 3, 2024
1 parent dbcc6c9 commit b2ad4d3
Show file tree
Hide file tree
Showing 12 changed files with 601 additions and 77 deletions.
22 changes: 10 additions & 12 deletions examples/morpheusvm/tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
package e2e_test

import (
"encoding/json"
"testing"
"time"

"github.com/ava-labs/avalanchego/tests/fixture/e2e"
"github.com/stretchr/testify/require"

_ "github.com/ava-labs/hypersdk/examples/morpheusvm/tests" // include the tests that are shared between the integration and e2e

"github.com/ava-labs/hypersdk/abi"
"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/examples/morpheusvm/consts"
Expand Down Expand Up @@ -39,14 +40,10 @@ func init() {
var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
require := require.New(ginkgo.GinkgoT())

keys := workload.NewDefaultKeys()
genesis := workload.NewGenesis(keys, 100*time.Millisecond)
genesisBytes, err := json.Marshal(genesis)
require.NoError(err)
expectedABI, err := abi.NewABI(vm.ActionParser.GetRegisteredTypes(), vm.OutputParser.GetRegisteredTypes())
testingNetworkConfig, err := workload.NewTestNetworkConfig(100 * time.Millisecond)
require.NoError(err)

parser, err := vm.CreateParser(genesisBytes)
expectedABI, err := abi.NewABI(vm.ActionParser.GetRegisteredTypes(), vm.OutputParser.GetRegisteredTypes())
require.NoError(err)

// Import HyperSDK e2e test coverage and inject MorpheusVM name
Expand All @@ -55,15 +52,16 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
KeyType: auth.ED25519Key,
}

generator := workload.NewTxGenerator(keys[0])
firstKey := testingNetworkConfig.Keys()[0]
generator := workload.NewTxGenerator(firstKey)
spamKey := &auth.PrivateKey{
Address: auth.NewED25519Address(keys[0].PublicKey()),
Bytes: keys[0][:],
Address: auth.NewED25519Address(firstKey.PublicKey()),
Bytes: firstKey[:],
}
tc := e2e.NewTestContext()
he2e.SetWorkload(consts.Name, generator, expectedABI, parser, &spamHelper, spamKey)
he2e.SetWorkload(testingNetworkConfig, generator, expectedABI, &spamHelper, spamKey)

return fixture.NewTestEnvironment(tc, flagVars, owner, consts.Name, consts.ID, genesisBytes).Marshal()
return fixture.NewTestEnvironment(tc, flagVars, owner, testingNetworkConfig, consts.ID).Marshal()
}, func(envBytes []byte) {
// Run in every ginkgo process

Expand Down
12 changes: 5 additions & 7 deletions examples/morpheusvm/tests/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
package integration_test

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/require"

_ "github.com/ava-labs/hypersdk/examples/morpheusvm/tests" // include the tests that are shared between the integration and e2e

"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/crypto/ed25519"
"github.com/ava-labs/hypersdk/examples/morpheusvm/tests/workload"
Expand All @@ -26,23 +27,20 @@ func TestIntegration(t *testing.T) {
var _ = ginkgo.BeforeSuite(func() {
require := require.New(ginkgo.GinkgoT())

keys := workload.NewDefaultKeys()
genesis := workload.NewGenesis(keys, 0)
genesisBytes, err := json.Marshal(genesis)
testingNetworkConfig, err := workload.NewTestNetworkConfig(0)
require.NoError(err)

randomEd25519Priv, err := ed25519.GeneratePrivateKey()
require.NoError(err)

randomEd25519AuthFactory := auth.NewED25519Factory(randomEd25519Priv)

generator := workload.NewTxGenerator(keys[0])
generator := workload.NewTxGenerator(testingNetworkConfig.Keys()[0])
// Setup imports the integration test coverage
integration.Setup(
vm.New,
genesisBytes,
testingNetworkConfig,
lconsts.ID,
vm.CreateParser,
generator,
randomEd25519AuthFactory,
)
Expand Down
49 changes: 49 additions & 0 deletions examples/morpheusvm/tests/transfer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package tests

import (
"context"
"time"

"github.com/stretchr/testify/require"

"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/crypto/ed25519"
"github.com/ava-labs/hypersdk/examples/morpheusvm/actions"
"github.com/ava-labs/hypersdk/examples/morpheusvm/tests/workload"
"github.com/ava-labs/hypersdk/tests/registry"

tworkload "github.com/ava-labs/hypersdk/tests/workload"
ginkgo "github.com/onsi/ginkgo/v2"
)

// TestsRegistry initialized during init to ensure tests are identical during ginkgo
// suite construction and test execution
// ref https://onsi.github.io/ginkgo/#mental-model-how-ginkgo-traverses-the-spec-hierarchy
var TestsRegistry = &registry.Registry{}

var _ = registry.Register(TestsRegistry, "Transfer Transaction", func(t ginkgo.FullGinkgoTInterface, tn tworkload.TestNetwork) {
require := require.New(t)
other, err := ed25519.GeneratePrivateKey()
require.NoError(err)
toAddress := auth.NewED25519Address(other.PublicKey())

networkConfig := tn.Configuration().(*workload.NetworkConfiguration)
spendingKey := networkConfig.Keys()[0]

tx, err := tn.GenerateTx(context.Background(), []chain.Action{&actions.Transfer{
To: toAddress,
Value: 1,
}},
auth.NewED25519Factory(spendingKey),
)
require.NoError(err)

timeoutCtx, timeoutCtxFnc := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second))
defer timeoutCtxFnc()

require.NoError(tn.ConfirmTxs(timeoutCtx, []*chain.Transaction{tx}))
})
40 changes: 38 additions & 2 deletions examples/morpheusvm/tests/workload/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,36 @@
package workload

import (
"encoding/json"
"math"
"time"

"github.com/ava-labs/avalanchego/ids"

"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/codec"
"github.com/ava-labs/hypersdk/crypto/ed25519"
"github.com/ava-labs/hypersdk/examples/morpheusvm/consts"
"github.com/ava-labs/hypersdk/examples/morpheusvm/vm"
"github.com/ava-labs/hypersdk/fees"
"github.com/ava-labs/hypersdk/genesis"
"github.com/ava-labs/hypersdk/tests/workload"
)

const (
// default initial balance for each address
InitialBalance uint64 = 10_000_000_000_000
)

var _ workload.TestNetworkConfiguration = &NetworkConfiguration{}

// hardcoded initial set of ed25519 keys. Each will be initialized with InitialBalance
var ed25519HexKeys = []string{
"323b1d8f4eed5f0da9da93071b034f2dce9d2d22692c172f3cb252a64ddfafd01b057de320297c29ad0c1f589ea216869cf1938d88c9fbd70d6748323dbf2fa7", //nolint:lll
"8a7be2e0c9a2d09ac2861c34326d6fe5a461d920ba9c2b345ae28e603d517df148735063f8d5d8ba79ea4668358943e5c80bc09e9b2b9a15b5b15db6c1862e88", //nolint:lll
}

func NewGenesis(keys []ed25519.PrivateKey, minBlockGap time.Duration) *genesis.DefaultGenesis {
func newGenesis(keys []ed25519.PrivateKey, minBlockGap time.Duration) *genesis.DefaultGenesis {
// allocate the initial balance to the addresses
customAllocs := make([]*genesis.CustomAllocation, 0, len(keys))
for _, key := range keys {
Expand All @@ -45,10 +53,13 @@ func NewGenesis(keys []ed25519.PrivateKey, minBlockGap time.Duration) *genesis.D
genesis.Rules.MaxBlockUnits = fees.Dimensions{1800000, math.MaxUint64, math.MaxUint64, math.MaxUint64, math.MaxUint64}
genesis.Rules.MinBlockGap = minBlockGap.Milliseconds()

genesis.Rules.NetworkID = uint32(1)
genesis.Rules.ChainID = ids.GenerateTestID()

return genesis
}

func NewDefaultKeys() []ed25519.PrivateKey {
func newDefaultKeys() []ed25519.PrivateKey {
testKeys := make([]ed25519.PrivateKey, len(ed25519HexKeys))
for i, keyHex := range ed25519HexKeys {
bytes, err := codec.LoadHex(keyHex, ed25519.PrivateKeyLen)
Expand All @@ -60,3 +71,28 @@ func NewDefaultKeys() []ed25519.PrivateKey {

return testKeys
}

type NetworkConfiguration struct {
workload.DefaultTestNetworkConfiguration
keys []ed25519.PrivateKey
}

func (n *NetworkConfiguration) Keys() []ed25519.PrivateKey {
return n.keys
}

func NewTestNetworkConfig(minBlockGap time.Duration) (*NetworkConfiguration, error) {
keys := newDefaultKeys()
genesis := newGenesis(keys, minBlockGap)
genesisBytes, err := json.Marshal(genesis)
if err != nil {
return nil, err
}
return &NetworkConfiguration{
DefaultTestNetworkConfiguration: workload.NewDefaultTestNetworkConfiguration(
genesisBytes,
consts.Name,
vm.NewParser(genesis)),
keys: keys,
}, nil
}
57 changes: 34 additions & 23 deletions tests/e2e/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"github.com/ava-labs/hypersdk/api/jsonrpc"
"github.com/ava-labs/hypersdk/api/state"
"github.com/ava-labs/hypersdk/auth"
"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/tests/registry"
"github.com/ava-labs/hypersdk/tests/workload"
"github.com/ava-labs/hypersdk/throughput"
"github.com/ava-labs/hypersdk/utils"
Expand All @@ -28,20 +28,18 @@ import (
)

var (
vmName string
txWorkload workload.TxWorkload
parser chain.Parser
expectedABI abi.ABI
spamKey *auth.PrivateKey
spamHelper throughput.SpamHelper
networkConfig workload.TestNetworkConfiguration
txWorkload workload.TxWorkload
expectedABI abi.ABI
spamKey *auth.PrivateKey
spamHelper throughput.SpamHelper
)

func SetWorkload(name string, generator workload.TxGenerator, abi abi.ABI, chainParser chain.Parser, sh throughput.SpamHelper, key *auth.PrivateKey) {
vmName = name
func SetWorkload(networkConfigImpl workload.TestNetworkConfiguration, generator workload.TxGenerator, abi abi.ABI, sh throughput.SpamHelper, key *auth.PrivateKey) {
networkConfig = networkConfigImpl
txWorkload = workload.TxWorkload{
Generator: generator,
}
parser = chainParser
expectedABI = abi
spamHelper = sh
spamKey = key
Expand All @@ -52,23 +50,23 @@ var _ = ginkgo.Describe("[HyperSDK APIs]", func() {
require := require.New(tc)

ginkgo.It("Ping", func() {
expectedBlockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(vmName).Chains[0].ChainID
expectedBlockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(networkConfig.Name()).Chains[0].ChainID
workload.Ping(tc.DefaultContext(), require, getE2EURIs(tc, expectedBlockchainID))
})

ginkgo.It("StableNetworkIdentity", func() {
hardcodedHostPort := "http://localhost:9650"
fixedNodeURL := hardcodedHostPort + "/ext/bc/" + vmName
fixedNodeURL := hardcodedHostPort + "/ext/bc/" + networkConfig.Name()

c := jsonrpc.NewJSONRPCClient(fixedNodeURL)
_, _, chainIDFromRPC, err := c.Network(tc.DefaultContext())
require.NoError(err)
expectedBlockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(vmName).Chains[0].ChainID
expectedBlockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(networkConfig.Name()).Chains[0].ChainID
require.Equal(expectedBlockchainID, chainIDFromRPC)
})

ginkgo.It("GetNetwork", func() {
expectedBlockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(vmName).Chains[0].ChainID
expectedBlockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(networkConfig.Name()).Chains[0].ChainID
baseURIs := getE2EBaseURIs(tc)
baseURI := baseURIs[0]
client := info.NewClient(baseURI)
Expand All @@ -78,12 +76,12 @@ var _ = ginkgo.Describe("[HyperSDK APIs]", func() {
})

ginkgo.It("GetABI", func() {
expectedBlockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(vmName).Chains[0].ChainID
expectedBlockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(networkConfig.Name()).Chains[0].ChainID
workload.GetABI(tc.DefaultContext(), require, getE2EURIs(tc, expectedBlockchainID), expectedABI)
})

ginkgo.It("ReadState", func() {
blockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(vmName).Chains[0].ChainID
blockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(networkConfig.Name()).Chains[0].ChainID
ctx := tc.DefaultContext()
for _, uri := range getE2EURIs(tc, blockchainID) {
client := state.NewJSONRPCStateClient(uri)
Expand All @@ -97,31 +95,31 @@ var _ = ginkgo.Describe("[HyperSDK APIs]", func() {
})
})

var _ = ginkgo.Describe("[HyperSDK Tx Workloads]", func() {
var _ = ginkgo.Describe("[HyperSDK Tx Workloads]", ginkgo.Serial, func() {
ginkgo.It("Basic Tx Workload", func() {
tc := e2e.NewTestContext()
require := require.New(tc)
blockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(vmName).Chains[0].ChainID
blockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(networkConfig.Name()).Chains[0].ChainID

ginkgo.By("Tx workloads", func() {
txWorkload.GenerateBlocks(tc.DefaultContext(), require, getE2EURIs(tc, blockchainID), 1)
})

ginkgo.By("Confirm accepted blocks indexed", func() {
workload.GetBlocks(tc.DefaultContext(), require, parser, getE2EURIs(tc, blockchainID))
workload.GetBlocks(tc.DefaultContext(), require, networkConfig.Parser(), getE2EURIs(tc, blockchainID))
})
})
})

var _ = ginkgo.Describe("[HyperSDK Spam Workloads]", func() {
var _ = ginkgo.Describe("[HyperSDK Spam Workloads]", ginkgo.Serial, func() {
ginkgo.It("Spam Workload", func() {
if spamKey == nil || spamHelper == nil {
return
}

tc := e2e.NewTestContext()
require := require.New(tc)
blockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(vmName).Chains[0].ChainID
blockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(networkConfig.Name()).Chains[0].ChainID
uris := getE2EURIs(tc, blockchainID)
key := spamKey

Expand All @@ -137,11 +135,11 @@ var _ = ginkgo.Describe("[HyperSDK Spam Workloads]", func() {
})
})

var _ = ginkgo.Describe("[HyperSDK Syncing]", func() {
var _ = ginkgo.Describe("[HyperSDK Syncing]", ginkgo.Serial, func() {
ginkgo.It("[Sync]", func() {
tc := e2e.NewTestContext()
require := require.New(tc)
blockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(vmName).Chains[0].ChainID
blockchainID := e2e.GetEnv(tc).GetNetwork().GetSubnet(networkConfig.Name()).Chains[0].ChainID

uris := getE2EURIs(tc, blockchainID)
ginkgo.By("Generate 128 blocks", func() {
Expand Down Expand Up @@ -244,6 +242,19 @@ var _ = ginkgo.Describe("[HyperSDK Syncing]", func() {
})
})

var _ = ginkgo.Describe("[Custom VM Tests]", ginkgo.Serial, func() {
tc := e2e.NewTestContext()

for testRegistry := range registry.GetTestsRegistries() {
for _, test := range testRegistry.List() {
ginkgo.It(test.Name, func() {
testNetwork := NewNetwork(tc)
test.Fnc(ginkgo.GinkgoT(), testNetwork)
})
}
}
})

func getE2EURIs(tc tests.TestContext, blockchainID ids.ID) []string {
nodeURIs := e2e.GetEnv(tc).GetNetwork().GetNodeURIs()
uris := make([]string, 0, len(nodeURIs))
Expand Down
Loading

0 comments on commit b2ad4d3

Please sign in to comment.