Skip to content
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

Implement action test suite helper #1139

Merged
merged 41 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9e53841
implement action test suite helper
iFrostizz Jul 15, 2024
fb5d9a7
add morpheusvm action tests
iFrostizz Jul 15, 2024
18ffdf2
add check for contains error for dynamic errors
iFrostizz Jul 15, 2024
f189b56
remove morpheusvm test, simplify suite
iFrostizz Jul 15, 2024
504df82
add morpheusvm parametrized tests
iFrostizz Jul 15, 2024
c07bca0
Merge branch 'main' into action_testing_suite
iFrostizz Jul 15, 2024
adcf1ae
add licence
iFrostizz Jul 15, 2024
48af784
Merge branch 'main' into action_testing_suite
iFrostizz Jul 15, 2024
566fff4
check err
iFrostizz Jul 15, 2024
4f860f1
Merge branch 'action_testing_suite' of github.com:ava-labs/hypersdk i…
iFrostizz Jul 15, 2024
96a8d0a
cleanup
iFrostizz Jul 15, 2024
900cf09
remove `Actor` usage and implement `InMemoryStore`
iFrostizz Jul 16, 2024
41c7e18
Merge branch 'main' into action_testing_suite
iFrostizz Jul 16, 2024
85b254b
remove unused ctx
iFrostizz Jul 16, 2024
5a11a97
add review suggestions
iFrostizz Jul 16, 2024
5fc6d05
Update chaintesting/action_test_helpers.go
iFrostizz Jul 17, 2024
5dac07b
Update chaintesting/action_test_helpers.go
iFrostizz Jul 17, 2024
25f23e2
add tests, set actor, add assertion
iFrostizz Jul 17, 2024
94c5cf0
Merge branch 'action_testing_suite' of github.com:ava-labs/hypersdk i…
iFrostizz Jul 17, 2024
0fe76f1
Updated Token Example (#1142)
samliok Jul 16, 2024
8e5bc5b
Expose state functions from context (#1153)
richardpringle Jul 17, 2024
5b5ff1c
use `WindowSize` constant (#1155)
iFrostizz Jul 17, 2024
c31613d
update readme to point to an existing tokenvm action (#1049)
najeal Jul 17, 2024
5197db3
Refactor `vm.Config` out of `vm.Controller` (#1146)
joshua-kim Jul 17, 2024
2bb3973
remove unused parallelism key from test configs (#1158)
joshua-kim Jul 17, 2024
b3ed3b5
Remove `key_create` endpoint and use `Address` everywhere (#1121)
iFrostizz Jul 17, 2024
dbc4076
rename to chaintest, slice of `ActionTest`, remove suite
iFrostizz Jul 18, 2024
45383c3
Merge branch 'main' into action_testing_suite
iFrostizz Jul 18, 2024
8cd0738
rename to `InMemoryStore` to export type
iFrostizz Jul 18, 2024
c9c9dea
Merge branch 'main' into action_testing_suite
iFrostizz Jul 18, 2024
160137b
Update examples/morpheusvm/actions/transfer_test.go
iFrostizz Jul 18, 2024
b2be701
add sender post-balance assertion
iFrostizz Jul 18, 2024
3c266e6
Merge branch 'main' into action_testing_suite
iFrostizz Jul 19, 2024
9055d39
pass in `*testing.T` to the `Assertion` func pointer
iFrostizz Jul 19, 2024
7b55c6f
Merge branch 'action_testing_suite' of github.com:ava-labs/hypersdk i…
iFrostizz Jul 19, 2024
86f8f46
pass context from `Run`, handle error signal on done
iFrostizz Jul 19, 2024
ddd5084
Context plumbing in `ActionTest` suite (#1170)
ARR4N Jul 22, 2024
658b810
write helper function
iFrostizz Jul 22, 2024
0cf9190
Update chaintest/action_test_helpers.go
iFrostizz Jul 23, 2024
c97b9b9
Update examples/morpheusvm/actions/transfer_test.go
iFrostizz Jul 23, 2024
0286e57
Merge branch 'main' into action_testing_suite
iFrostizz Jul 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions chaintest/action_test_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package chaintest

import (
"context"
"testing"

"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/ids"
"github.com/stretchr/testify/require"

"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/codec"
"github.com/ava-labs/hypersdk/state"
)

var _ state.Mutable = (*InMemoryStore)(nil)

// InMemoryStore is a storage that acts as a wrapper around a map and implements state.Mutable.
iFrostizz marked this conversation as resolved.
Show resolved Hide resolved
type InMemoryStore struct {
Storage map[string][]byte
}

func NewInMemoryStore() *InMemoryStore {
return &InMemoryStore{
Storage: make(map[string][]byte),
}
}
Comment on lines +26 to +30
Copy link
Contributor Author

@iFrostizz iFrostizz Jul 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could have modified the API such that these methods never return an error because all of them cannot really fail, in the case of GetValue then the check of nil could have just been used for the value but it's a bad idea for an API that tries to mimic other databases so it "simulates" errors.


func (i *InMemoryStore) GetValue(_ context.Context, key []byte) ([]byte, error) {
val, ok := i.Storage[string(key)]
if !ok {
return nil, database.ErrNotFound
}
return val, nil
}

func (i *InMemoryStore) Insert(_ context.Context, key []byte, value []byte) error {
iFrostizz marked this conversation as resolved.
Show resolved Hide resolved
i.Storage[string(key)] = value
return nil
}

func (i *InMemoryStore) Remove(_ context.Context, key []byte) error {
delete(i.Storage, string(key))
return nil
}

// ActionTest is a single parameterized test. It calls Execute on the action with the passed parameters
// and checks that all assertions pass.
type ActionTest struct {
Name string

Action chain.Action

Rules chain.Rules
State state.Mutable
Timestamp int64
Actor codec.Address
ActionID ids.ID

ExpectedOutputs [][]byte
ExpectedErr error

Assertion func(state.Mutable) bool
}

// Run execute all tests from the test suite and make sure all assertions pass.
func Run(t *testing.T, tests []ActionTest) {
for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
require := require.New(t)

output, err := test.Action.Execute(context.TODO(), test.Rules, test.State, test.Timestamp, test.Actor, test.ActionID)
iFrostizz marked this conversation as resolved.
Show resolved Hide resolved

require.ErrorIs(err, test.ExpectedErr)
require.Equal(output, test.ExpectedOutputs)

if test.Assertion != nil {
require.True(test.Assertion(test.State))
}
})
}
}
126 changes: 126 additions & 0 deletions examples/morpheusvm/actions/transfer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package actions

import (
"context"
"math"
"testing"

"github.com/stretchr/testify/require"

"github.com/ava-labs/hypersdk/chaintest"
"github.com/ava-labs/hypersdk/codec"
"github.com/ava-labs/hypersdk/examples/morpheusvm/storage"
"github.com/ava-labs/hypersdk/state"
"github.com/ava-labs/hypersdk/tstate"
)

func TestTransferAction(t *testing.T) {
require := require.New(t)
ts := tstate.New(1)
emptyBalanceKey := storage.BalanceKey(codec.EmptyAddress)
addrSlice := make([]byte, codec.AddressLen)
for i := range addrSlice {
addrSlice[i] = 1
}
oneAddr, err := codec.ToAddress(addrSlice)
require.NoError(err)

tests := []chaintest.ActionTest{
{
Name: "ZeroTransfer",
Actor: codec.EmptyAddress,
Action: &Transfer{
To: codec.EmptyAddress,
Value: 0,
},
ExpectedErr: ErrOutputValueZero,
},
{
Name: "InvalidStateKey",
Actor: codec.EmptyAddress,
Action: &Transfer{
To: codec.EmptyAddress,
Value: 1,
},
State: ts.NewView(make(state.Keys), map[string][]byte{}),
ExpectedErr: tstate.ErrInvalidKeyOrPermission,
},
{
Name: "NotEnoughBalance",
Actor: codec.EmptyAddress,
Action: &Transfer{
To: codec.EmptyAddress,
Value: 1,
},
State: func() state.Mutable {
keys := make(state.Keys)
keys.Add(string(emptyBalanceKey), state.Read)
tsv := ts.NewView(keys, map[string][]byte{})
return tsv
}(),
ExpectedErr: storage.ErrInvalidBalance,
},
{
Name: "SimpleZeroTransfer",
iFrostizz marked this conversation as resolved.
Show resolved Hide resolved
Actor: codec.EmptyAddress,
Action: &Transfer{
To: codec.EmptyAddress,
Value: 1,
},
State: func() state.Mutable {
keys := make(state.Keys)
store := chaintest.NewInMemoryStore()
require.NoError(storage.SetBalance(context.Background(), store, codec.EmptyAddress, 1))
keys.Add(string(emptyBalanceKey), state.All)
return ts.NewView(keys, store.Storage)
}(),
Assertion: func(store state.Mutable) bool {
balance, err := storage.GetBalance(context.Background(), store, codec.EmptyAddress)
require.NoError(err)
return balance == 1
},
},
{
Name: "OverflowBalance",
Actor: codec.EmptyAddress,
Action: &Transfer{
To: codec.EmptyAddress,
Value: math.MaxUint64,
},
State: func() state.Mutable {
keys := make(state.Keys)
store := chaintest.NewInMemoryStore()
require.NoError(storage.SetBalance(context.Background(), store, codec.EmptyAddress, 1))
keys.Add(string(emptyBalanceKey), state.All)
return ts.NewView(keys, store.Storage)
}(),
ExpectedErr: storage.ErrInvalidBalance,
},
{
Name: "SimpleTransfer",
Actor: codec.EmptyAddress,
Action: &Transfer{
To: oneAddr,
Value: 1,
},
State: func() state.Mutable {
keys := make(state.Keys)
store := chaintest.NewInMemoryStore()
require.NoError(storage.SetBalance(context.Background(), store, codec.EmptyAddress, 1))
keys.Add(string(emptyBalanceKey), state.All)
keys.Add(string(storage.BalanceKey(oneAddr)), state.All)
return ts.NewView(keys, store.Storage)
}(),
Assertion: func(store state.Mutable) bool {
balance, err := storage.GetBalance(context.Background(), store, oneAddr)
require.NoError(err)
return balance == 1
},
iFrostizz marked this conversation as resolved.
Show resolved Hide resolved
},
}

chaintest.Run(t, tests)
}
Loading