neo-go/cli/smartcontract/smart_contract_test.go

431 lines
9 KiB
Go
Raw Normal View History

package smartcontract
import (
"flag"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
func TestInitSmartContract(t *testing.T) {
d, err := ioutil.TempDir("./", "")
require.NoError(t, err)
os.Chdir(d)
defer func() {
os.Chdir("..")
os.RemoveAll(d)
}()
contractName := "testContract"
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("name", contractName, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
require.NoError(t, initSmartContract(ctx))
dirInfo, err := os.Stat(contractName)
require.NoError(t, err)
require.True(t, dirInfo.IsDir())
files, err := ioutil.ReadDir(contractName)
require.NoError(t, err)
require.Equal(t, 2, len(files))
require.Equal(t, "main.go", files[0].Name())
require.Equal(t, "neo-go.yml", files[1].Name())
main, err := ioutil.ReadFile(contractName + "/" + files[0].Name())
require.NoError(t, err)
require.Equal(t,
`package `+contractName+`
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
var notificationName string
// init initializes notificationName before calling any other smart-contract method
func init() {
notificationName = "Hello world!"
}
// RuntimeNotify sends runtime notification with "Hello world!" name
func RuntimeNotify(args []interface{}) {
runtime.Notify(notificationName, args)
}`, string(main))
manifest, err := ioutil.ReadFile(contractName + "/" + files[1].Name())
require.NoError(t, err)
require.Equal(t,
`hasstorage: false
ispayable: false
supportedstandards: []
events:
- name: Hello world!
parameters:
- name: args
type: Array
`, string(manifest))
}
func TestGetFeatures(t *testing.T) {
cfg := ProjectConfig{
IsPayable: true,
HasStorage: true,
}
f := cfg.GetFeatures()
require.Equal(t, smartcontract.IsPayable|smartcontract.HasStorage, f)
}
func TestParseCosigner(t *testing.T) {
acc := util.Uint160{1, 3, 5, 7}
testCases := map[string]transaction.Signer{
acc.StringLE(): {
Account: acc,
Scopes: transaction.Global,
},
"0x" + acc.StringLE(): {
Account: acc,
Scopes: transaction.Global,
},
acc.StringLE() + ":Global": {
Account: acc,
Scopes: transaction.Global,
},
acc.StringLE() + ":CalledByEntry": {
Account: acc,
Scopes: transaction.CalledByEntry,
},
acc.StringLE() + ":FeeOnly": {
Account: acc,
Scopes: transaction.FeeOnly,
},
acc.StringLE() + ":CalledByEntry,CustomContracts": {
Account: acc,
Scopes: transaction.CalledByEntry | transaction.CustomContracts,
},
}
for s, expected := range testCases {
actual, err := parseCosigner(s)
require.NoError(t, err)
require.Equal(t, expected, actual)
}
errorCases := []string{
acc.StringLE() + "0",
acc.StringLE() + ":Unknown",
acc.StringLE() + ":Global,CustomContracts",
acc.StringLE() + ":Global,FeeOnly",
}
for _, s := range errorCases {
_, err := parseCosigner(s)
require.Error(t, err)
}
}
func TestParseParams_CalledFromItself(t *testing.T) {
testCases := map[string]struct {
WordsRead int
Value []smartcontract.Parameter
}{
"]": {
WordsRead: 1,
Value: []smartcontract.Parameter{},
},
"[ [ ] ] ]": {
WordsRead: 5,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{},
},
},
},
},
},
"a b c ]": {
WordsRead: 4,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.StringType,
Value: "c",
},
},
},
"a [ b [ [ c d ] e ] ] f ] extra items": {
WordsRead: 13, // the method should return right after the last bracket, as calledFromMain == false
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "c",
},
{
Type: smartcontract.StringType,
Value: "d",
},
},
},
{
Type: smartcontract.StringType,
Value: "e",
},
},
},
},
},
{
Type: smartcontract.StringType,
Value: "f",
},
},
},
}
for str, expected := range testCases {
input := strings.Split(str, " ")
offset, actual, err := parseParams(input, false)
require.NoError(t, err)
require.Equal(t, expected.WordsRead, offset)
require.Equal(t, expected.Value, actual)
}
errorCases := []string{
"[ ]",
"[ a b [ c ] d ]",
"[ ] --",
"--",
"not-int:integer ]",
}
for _, str := range errorCases {
input := strings.Split(str, " ")
_, _, err := parseParams(input, false)
require.Error(t, err)
}
}
func TestParseParams_CalledFromOutside(t *testing.T) {
testCases := map[string]struct {
WordsRead int
Parameters []smartcontract.Parameter
}{
"-- cosigner1": {
WordsRead: 1, // the `--` only
Parameters: []smartcontract.Parameter{},
},
"a b c": {
WordsRead: 3,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.StringType,
Value: "c",
},
},
},
"a b c -- cosigner1": {
WordsRead: 4,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.StringType,
Value: "c",
},
},
},
"a [ b [ [ c d ] e ] ] f": {
WordsRead: 12,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "c",
},
{
Type: smartcontract.StringType,
Value: "d",
},
},
},
{
Type: smartcontract.StringType,
Value: "e",
},
},
},
},
},
{
Type: smartcontract.StringType,
Value: "f",
},
},
},
"a [ b ] -- cosigner1 cosigner2": {
WordsRead: 5,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
},
},
},
},
"a [ b ]": {
WordsRead: 4,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
},
},
},
},
"a [ b ] [ [ c ] ] [ [ [ d ] ] ]": {
WordsRead: 16,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
},
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "c",
},
},
},
},
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "d",
},
},
},
},
},
},
},
},
},
}
for str, expected := range testCases {
input := strings.Split(str, " ")
offset, arr, err := parseParams(input, true)
require.NoError(t, err)
require.Equal(t, expected.WordsRead, offset)
require.Equal(t, expected.Parameters, arr)
}
errorCases := []string{
"[",
"]",
"[ [ ]",
"[ [ ] --",
"[ -- ]",
}
for _, str := range errorCases {
input := strings.Split(str, " ")
_, _, err := parseParams(input, true)
require.Error(t, err)
}
}