-
Notifications
You must be signed in to change notification settings - Fork 124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support procedural test style for VM-defined workloads #1667
Changes from 36 commits
6f2fc50
1833c3f
a4f51a1
8e22542
fe528f5
baf90fd
ee7974c
0b935bb
83afa8d
2523292
1716ecf
b681a95
9fcc4dc
62048d7
3f7f35c
bb6ca7b
b27c3e7
c55e886
53d05e2
848a8b9
32b6cff
69625eb
f06bcf8
834bf53
128d5fc
93541c0
c4621d6
bb12aa7
473a08d
66d5493
4c55dc3
83231c7
4bc7424
f087929
23f5b79
782f4b2
920c45a
e5bb823
5471cac
2e7261f
4021021
a346111
3b5d436
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 is global tests registry instance where all the tests are to be registered. Each VM would typically have only | ||
// a single instance of such a registry. | ||
var TestsRegistry = ®istry.Registry{} | ||
|
||
var _ = registry.Register(TestsRegistry, "Transfer Transaction", func(t ginkgo.FullGinkgoTInterface, tn tworkload.TestNetwork) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we are using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
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})) | ||
return nil | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 { | ||
|
@@ -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() | ||
aaronbuchwald marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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) | ||
|
@@ -60,3 +71,28 @@ func NewDefaultKeys() []ed25519.PrivateKey { | |
|
||
return testKeys | ||
} | ||
|
||
type NetworkConfiguration struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: we should add a type check for NetworkConfiguration
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
|
@@ -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 | ||
|
@@ -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) | ||
|
@@ -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) | ||
|
@@ -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 | ||
|
||
|
@@ -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() { | ||
|
@@ -244,6 +242,20 @@ var _ = ginkgo.Describe("[HyperSDK Syncing]", func() { | |
}) | ||
}) | ||
|
||
var _ = ginkgo.Describe("[Custom VM Tests]", ginkgo.Serial, func() { | ||
tc := e2e.NewTestContext() | ||
require := require.New(tc) | ||
|
||
for testRegistry := range registry.GetTestsRegistries() { | ||
for _, test := range testRegistry.List() { | ||
ginkgo.It(test.Name, func() { | ||
testNetwork := NewNetwork(tc) | ||
require.NoError(test.Fnc(ginkgo.GinkgoT(), testNetwork), "Test %s failed with an error", test.Name) | ||
}) | ||
} | ||
} | ||
}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm so I think the only requirement is that we populate it via a global/init function rather than in Could we get rid of the nested for loop here in favor of a single registry of tests and remove the |
||
|
||
func getE2EURIs(tc tests.TestContext, blockchainID ids.ID) []string { | ||
nodeURIs := e2e.GetEnv(tc).GetNetwork().GetNodeURIs() | ||
uris := make([]string, 0, len(nodeURIs)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done