neo-go/cli/smartcontract/generate_test.go
2022-03-17 19:39:18 +03:00

329 lines
11 KiB
Go

package smartcontract
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
func TestGenerate(t *testing.T) {
m := manifest.NewManifest("MyContract")
m.ABI.Methods = append(m.ABI.Methods,
manifest.Method{
Name: manifest.MethodDeploy,
ReturnType: smartcontract.VoidType,
},
manifest.Method{
Name: "sum",
Parameters: []manifest.Parameter{
manifest.NewParameter("first", smartcontract.IntegerType),
manifest.NewParameter("second", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
manifest.Method{
Name: "sum", // overloaded method
Parameters: []manifest.Parameter{
manifest.NewParameter("first", smartcontract.IntegerType),
manifest.NewParameter("second", smartcontract.IntegerType),
manifest.NewParameter("third", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
manifest.Method{
Name: "sum3",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.IntegerType,
Safe: true,
},
manifest.Method{
Name: "justExecute",
Parameters: []manifest.Parameter{
manifest.NewParameter("arr", smartcontract.ArrayType),
},
ReturnType: smartcontract.VoidType,
},
manifest.Method{
Name: "getPublicKey",
Parameters: nil,
ReturnType: smartcontract.PublicKeyType,
},
manifest.Method{
Name: "otherTypes",
Parameters: []manifest.Parameter{
manifest.NewParameter("ctr", smartcontract.Hash160Type),
manifest.NewParameter("tx", smartcontract.Hash256Type),
manifest.NewParameter("sig", smartcontract.SignatureType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.BoolType,
},
manifest.Method{
Name: "emptyName",
Parameters: []manifest.Parameter{
manifest.NewParameter("", smartcontract.MapType),
},
ReturnType: smartcontract.AnyType,
},
manifest.Method{
Name: "searchStorage",
Parameters: []manifest.Parameter{
manifest.NewParameter("ctx", smartcontract.InteropInterfaceType),
},
ReturnType: smartcontract.InteropInterfaceType,
},
manifest.Method{
Name: "getFromMap",
Parameters: []manifest.Parameter{
manifest.NewParameter("intMap", smartcontract.MapType),
manifest.NewParameter("indices", smartcontract.ArrayType),
},
ReturnType: smartcontract.ArrayType,
},
manifest.Method{
Name: "doSomething",
Parameters: []manifest.Parameter{
manifest.NewParameter("bytes", smartcontract.ByteArrayType),
manifest.NewParameter("str", smartcontract.StringType),
},
ReturnType: smartcontract.InteropInterfaceType,
},
manifest.Method{
Name: "getBlockWrapper",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.InteropInterfaceType,
},
manifest.Method{
Name: "myFunc",
Parameters: []manifest.Parameter{
manifest.NewParameter("in", smartcontract.MapType),
},
ReturnType: smartcontract.ArrayType,
})
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
outFile := filepath.Join(t.TempDir(), "out.go")
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
h := util.Uint160{
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
}
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd}
rawCfg := `package: wrapper
hash: ` + h.StringLE() + `
overrides:
searchStorage.ctx: storage.Context
searchStorage: iterator.Iterator
getFromMap.intMap: "map[string]int"
getFromMap.indices: "[]string"
getFromMap: "[]int"
getBlockWrapper: ledger.Block
myFunc.in: "map[int]github.com/heyitsme/mycontract.Input"
myFunc: "[]github.com/heyitsme/mycontract.Output"
callflags:
doSomething: ReadStates
`
cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
require.NoError(t, app.Run([]string{"", "generate-wrapper",
"--manifest", manifestFile,
"--config", cfgPath,
"--out", outFile,
"--hash", h.StringLE(),
}))
const expected = `// Package wrapper contains wrappers for MyContract contract.
package wrapper
import (
"github.com/heyitsme/mycontract"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
// Hash contains contract hash in big-endian form.
const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\xbe\xef\x03\x04"
// Sum invokes ` + "`sum`" + ` method of contract.
func Sum(first int, second int) int {
return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second).(int)
}
// Sum_3 invokes ` + "`sum`" + ` method of contract.
func Sum_3(first int, second int, third int) int {
return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second, third).(int)
}
// Sum3 invokes ` + "`sum3`" + ` method of contract.
func Sum3() int {
return neogointernal.CallWithToken(Hash, "sum3", int(contract.ReadOnly)).(int)
}
// JustExecute invokes ` + "`justExecute`" + ` method of contract.
func JustExecute(arr []interface{}) {
neogointernal.CallWithTokenNoRet(Hash, "justExecute", int(contract.All), arr)
}
// GetPublicKey invokes ` + "`getPublicKey`" + ` method of contract.
func GetPublicKey() interop.PublicKey {
return neogointernal.CallWithToken(Hash, "getPublicKey", int(contract.All)).(interop.PublicKey)
}
// OtherTypes invokes ` + "`otherTypes`" + ` method of contract.
func OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data interface{}) bool {
return neogointernal.CallWithToken(Hash, "otherTypes", int(contract.All), ctr, tx, sig, data).(bool)
}
// EmptyName invokes ` + "`emptyName`" + ` method of contract.
func EmptyName(arg0 map[string]interface{}) interface{} {
return neogointernal.CallWithToken(Hash, "emptyName", int(contract.All), arg0).(interface{})
}
// SearchStorage invokes ` + "`searchStorage`" + ` method of contract.
func SearchStorage(ctx storage.Context) iterator.Iterator {
return neogointernal.CallWithToken(Hash, "searchStorage", int(contract.All), ctx).(iterator.Iterator)
}
// GetFromMap invokes ` + "`getFromMap`" + ` method of contract.
func GetFromMap(intMap map[string]int, indices []string) []int {
return neogointernal.CallWithToken(Hash, "getFromMap", int(contract.All), intMap, indices).([]int)
}
// DoSomething invokes ` + "`doSomething`" + ` method of contract.
func DoSomething(bytes []byte, str string) interface{} {
return neogointernal.CallWithToken(Hash, "doSomething", int(contract.ReadStates), bytes, str).(interface{})
}
// GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract.
func GetBlockWrapper() ledger.Block {
return neogointernal.CallWithToken(Hash, "getBlockWrapper", int(contract.All)).(ledger.Block)
}
// MyFunc invokes ` + "`myFunc`" + ` method of contract.
func MyFunc(in map[int]mycontract.Input) []mycontract.Output {
return neogointernal.CallWithToken(Hash, "myFunc", int(contract.All), in).([]mycontract.Output)
}
`
data, err := os.ReadFile(outFile)
require.NoError(t, err)
require.Equal(t, expected, string(data))
}
func TestGenerateValidPackageName(t *testing.T) {
m := manifest.NewManifest("My space\tcontract")
m.ABI.Methods = append(m.ABI.Methods,
manifest.Method{
Name: "get",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.IntegerType,
},
)
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
outFile := filepath.Join(t.TempDir(), "out.go")
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
h := util.Uint160{
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
}
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd}
require.NoError(t, app.Run([]string{"", "generate-wrapper",
"--manifest", manifestFile,
"--out", outFile,
"--hash", "0x" + h.StringLE(),
}))
data, err := os.ReadFile(outFile)
require.NoError(t, err)
require.Equal(t, `// Package myspacecontract contains wrappers for My space contract contract.
package myspacecontract
import (
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
)
// Hash contains contract hash in big-endian form.
const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\xbe\xef\x03\x04"
// Get invokes `+"`get`"+` method of contract.
func Get() int {
return neogointernal.CallWithToken(Hash, "get", int(contract.All)).(int)
}
`, string(data))
}
func TestGenerate_Errors(t *testing.T) {
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd}
app.ExitErrHandler = func(*cli.Context, error) {}
checkError := func(t *testing.T, msg string, args ...string) {
// cli.ExitError doesn't implement wraping properly, so we check for an error message.
err := app.Run(append([]string{"", "generate-wrapper"}, args...))
require.True(t, strings.Contains(err.Error(), msg), "got: %v", err)
}
t.Run("missing manifest argument", func(t *testing.T) {
checkError(t, errNoManifestFile.Error())
})
t.Run("missing manifest file", func(t *testing.T) {
checkError(t, "can't read contract manifest", "--manifest", "notexists")
})
t.Run("invalid manifest", func(t *testing.T) {
manifestFile := filepath.Join(t.TempDir(), "invalid.json")
require.NoError(t, os.WriteFile(manifestFile, []byte("[]"), os.ModePerm))
checkError(t, "", "--manifest", manifestFile)
})
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
m := manifest.NewManifest("MyContract")
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
t.Run("invalid hash", func(t *testing.T) {
checkError(t, "invalid contract hash", "--manifest", manifestFile, "--hash", "xxx")
})
t.Run("missing config", func(t *testing.T) {
checkError(t, "can't read config file",
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", filepath.Join(t.TempDir(), "not.exists.yml"))
})
t.Run("invalid config", func(t *testing.T) {
rawCfg := `package: wrapper
callflags:
someFunc: ReadSometimes
`
cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
checkError(t, "can't parse config file",
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", cfgPath)
})
}