smartcontract: add user-facing testinvokefunction command
With a very special syntax.
This commit is contained in:
parent
05f3329ec0
commit
e63b25d5ad
3 changed files with 670 additions and 0 deletions
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc"
|
||||
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/compiler"
|
||||
|
@ -27,6 +28,7 @@ var (
|
|||
errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag")
|
||||
errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag")
|
||||
errNoWIF = errors.New("no WIF parameter found, specify it with the '--wif or -w' flag")
|
||||
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument")
|
||||
errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag")
|
||||
errFileExist = errors.New("A file with given smart-contract name already exists")
|
||||
)
|
||||
|
@ -95,6 +97,80 @@ func NewCommands() []cli.Command {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testinvokefunction",
|
||||
Usage: "invoke deployed contract on the blockchain (test mode)",
|
||||
UsageText: "neo-go contract testinvokefunction -e endpoint scripthash [method] [arguments...]",
|
||||
Description: `Executes given (as a script hash) deployed script with the given method and
|
||||
arguments. If no method is given "" is passed to the script, if no arguments
|
||||
are given, an empty array is passed. All of the given arguments are
|
||||
encapsulated into array before invoking the script. The script thus should
|
||||
follow the regular convention of smart contract arguments (method string and
|
||||
an array of other arguments).
|
||||
|
||||
Arguments always do have regular Neo smart contract parameter types, either
|
||||
specified explicitly or being inferred from the value. To specify the type
|
||||
manually use "type:value" syntax where the type is one of the following:
|
||||
'signature', 'bool', 'int', 'hash160', 'hash256', 'bytes', 'key' or 'string'.
|
||||
Array types are not currently supported.
|
||||
|
||||
Given values are type-checked against given types with the following
|
||||
restrictions applied:
|
||||
* 'signature' type values should be hex-encoded and have a (decoded)
|
||||
length of 64 bytes.
|
||||
* 'bool' type values are 'true' and 'false'.
|
||||
* 'int' values are decimal integers that can be successfully converted
|
||||
from the string.
|
||||
* 'hash160' values are Neo addresses and hex-encoded 20-bytes long (after
|
||||
decoding) strings.
|
||||
* 'hash256' type values should be hex-encoded and have a (decoded)
|
||||
length of 32 bytes.
|
||||
* 'bytes' type values are any hex-encoded things.
|
||||
* 'key' type values are hex-encoded marshalled public keys.
|
||||
* 'string' type values are any valid UTF-8 strings. In the value's part of
|
||||
the string the colon looses it's special meaning as a separator between
|
||||
type and value and is taken literally.
|
||||
|
||||
If no type is explicitly specified, it is inferred from the value using the
|
||||
following logic:
|
||||
- anything that can be interpreted as a decimal integer gets
|
||||
an 'int' type
|
||||
- 'true' and 'false' strings get 'bool' type
|
||||
- valid Neo addresses and 20 bytes long hex-encoded strings get 'hash160'
|
||||
type
|
||||
- valid hex-encoded public keys get 'key' type
|
||||
- 32 bytes long hex-encoded values get 'hash256' type
|
||||
- 64 bytes long hex-encoded values get 'signature' type
|
||||
- any other valid hex-encoded values get 'bytes' type
|
||||
- anything else is a 'string'
|
||||
|
||||
Backslash character is used as an escape character and allows to use colon in
|
||||
an implicitly typed string. For any other characters it has no special
|
||||
meaning, to get a literal backslash in the string use the '\\' sequence.
|
||||
|
||||
Examples:
|
||||
* 'int:42' is an integer with a value of 42
|
||||
* '42' is an integer with a value of 42
|
||||
* 'bad' is a string with a value of 'bad'
|
||||
* 'dead' is a byte array with a value of 'dead'
|
||||
* 'string:dead' is a string with a value of 'dead'
|
||||
* 'AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y' is a hash160 with a value
|
||||
of '23ba2703c53263e8d6e522dc32203339dcd8eee9'
|
||||
* '\4\2' is an integer with a value of 42
|
||||
* '\\4\2' is a string with a value of '\42'
|
||||
* 'string:string' is a string with a value of 'string'
|
||||
* 'string\:string' is a string with a value of 'string:string'
|
||||
* '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
|
||||
key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
|
||||
`,
|
||||
Action: testInvokeFunction,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "endpoint, e",
|
||||
Usage: "RPC endpoint address (like 'http://seed4.ngd.network:20332')",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "testinvokescript",
|
||||
Usage: "Invoke compiled AVM code on the blockchain (test mode, not creating a transaction for it)",
|
||||
|
@ -211,6 +287,52 @@ func contractCompile(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func testInvokeFunction(ctx *cli.Context) error {
|
||||
endpoint := ctx.String("endpoint")
|
||||
if len(endpoint) == 0 {
|
||||
return cli.NewExitError(errNoEndpoint, 1)
|
||||
}
|
||||
|
||||
args := ctx.Args()
|
||||
if !args.Present() {
|
||||
return cli.NewExitError(errNoScriptHash, 1)
|
||||
}
|
||||
script := args[0]
|
||||
operation := ""
|
||||
if len(args) > 1 {
|
||||
operation = args[1]
|
||||
}
|
||||
params := make([]smartcontract.Parameter, 0)
|
||||
if len(args) > 2 {
|
||||
for k, s := range args[2:] {
|
||||
param, err := smartcontract.NewParameterFromString(s)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to parse argument #%d: %v", k+2+1, err), 1)
|
||||
}
|
||||
params = append(params, *param)
|
||||
}
|
||||
}
|
||||
|
||||
client, err := rpc.NewClient(context.TODO(), endpoint, rpc.ClientOptions{})
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
resp, err := client.InvokeFunction(script, operation, params)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(resp.Result, "", " ")
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
fmt.Println(string(b))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testInvokeScript(ctx *cli.Context) error {
|
||||
src := ctx.String("in")
|
||||
if len(src) == 0 {
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
package smartcontract
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/io"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
@ -89,6 +97,203 @@ func NewParameter(t ParamType) Parameter {
|
|||
}
|
||||
}
|
||||
|
||||
// parseParamType is a user-friendly string to ParamType converter, it's
|
||||
// case-insensitive and makes the following conversions:
|
||||
// signature -> SignatureType
|
||||
// bool -> BoolType
|
||||
// int -> IntegerType
|
||||
// hash160 -> Hash160Type
|
||||
// hash256 -> Hash256Type
|
||||
// bytes -> ByteArrayType
|
||||
// key -> PublicKeyType
|
||||
// string -> StringType
|
||||
// anything else generates an error.
|
||||
func parseParamType(typ string) (ParamType, error) {
|
||||
switch strings.ToLower(typ) {
|
||||
case "signature":
|
||||
return SignatureType, nil
|
||||
case "bool":
|
||||
return BoolType, nil
|
||||
case "int":
|
||||
return IntegerType, nil
|
||||
case "hash160":
|
||||
return Hash160Type, nil
|
||||
case "hash256":
|
||||
return Hash256Type, nil
|
||||
case "bytes":
|
||||
return ByteArrayType, nil
|
||||
case "key":
|
||||
return PublicKeyType, nil
|
||||
case "string":
|
||||
return StringType, nil
|
||||
default:
|
||||
// We deliberately don't support array here.
|
||||
return 0, errors.New("wrong or unsupported parameter type")
|
||||
}
|
||||
}
|
||||
|
||||
// adjustValToType is a value type-checker and converter.
|
||||
func adjustValToType(typ ParamType, val string) (interface{}, error) {
|
||||
switch typ {
|
||||
case SignatureType:
|
||||
b, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(b) != 64 {
|
||||
return nil, errors.New("not a signature")
|
||||
}
|
||||
return val, nil
|
||||
case BoolType:
|
||||
switch val {
|
||||
case "true":
|
||||
return true, nil
|
||||
case "false":
|
||||
return false, nil
|
||||
default:
|
||||
return nil, errors.New("invalid boolean value")
|
||||
}
|
||||
case IntegerType:
|
||||
return strconv.Atoi(val)
|
||||
case Hash160Type:
|
||||
u, err := crypto.Uint160DecodeAddress(val)
|
||||
if err == nil {
|
||||
return hex.EncodeToString(u.Bytes()), nil
|
||||
}
|
||||
b, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(b) != 20 {
|
||||
return nil, errors.New("not a hash160")
|
||||
}
|
||||
return val, nil
|
||||
case Hash256Type:
|
||||
b, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(b) != 32 {
|
||||
return nil, errors.New("not a hash256")
|
||||
}
|
||||
return val, nil
|
||||
case ByteArrayType:
|
||||
_, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return val, nil
|
||||
case PublicKeyType:
|
||||
_, err := keys.NewPublicKeyFromString(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return val, nil
|
||||
case StringType:
|
||||
return val, nil
|
||||
default:
|
||||
return nil, errors.New("unsupported parameter type")
|
||||
}
|
||||
}
|
||||
|
||||
// inferParamType tries to infer the value type from its contents. It returns
|
||||
// IntegerType for anything that looks like decimal integer (can be converted
|
||||
// with strconv.Atoi), BoolType for true and false values, Hash160Type for
|
||||
// addresses and hex strings encoding 20 bytes long values, PublicKeyType for
|
||||
// valid hex-encoded public keys, Hash256Type for hex-encoded 32 bytes values,
|
||||
// SignatureType for hex-encoded 64 bytes values, ByteArrayType for any other
|
||||
// valid hex-encoded values and StringType for anything else.
|
||||
func inferParamType(val string) ParamType {
|
||||
var err error
|
||||
|
||||
_, err = strconv.Atoi(val)
|
||||
if err == nil {
|
||||
return IntegerType
|
||||
}
|
||||
|
||||
if val == "true" || val == "false" {
|
||||
return BoolType
|
||||
}
|
||||
|
||||
_, err = crypto.Uint160DecodeAddress(val)
|
||||
if err == nil {
|
||||
return Hash160Type
|
||||
}
|
||||
|
||||
_, err = keys.NewPublicKeyFromString(val)
|
||||
if err == nil {
|
||||
return PublicKeyType
|
||||
}
|
||||
|
||||
unhexed, err := hex.DecodeString(val)
|
||||
if err == nil {
|
||||
switch len(unhexed) {
|
||||
case 20:
|
||||
return Hash160Type
|
||||
case 32:
|
||||
return Hash256Type
|
||||
case 64:
|
||||
return SignatureType
|
||||
default:
|
||||
return ByteArrayType
|
||||
}
|
||||
}
|
||||
// Anything can be a string.
|
||||
return StringType
|
||||
}
|
||||
|
||||
// NewParameterFromString returns a new Parameter initialized from the given
|
||||
// string in neo-go-specific format. It is intended to be used in user-facing
|
||||
// interfaces and has some heuristics in it to simplify parameter passing. Exact
|
||||
// syntax is documented in the cli documentation.
|
||||
func NewParameterFromString(in string) (*Parameter, error) {
|
||||
var (
|
||||
char rune
|
||||
val string
|
||||
err error
|
||||
r *strings.Reader
|
||||
buf strings.Builder
|
||||
escaped bool
|
||||
hadType bool
|
||||
res = &Parameter{}
|
||||
)
|
||||
r = strings.NewReader(in)
|
||||
for char, _, err = r.ReadRune(); err == nil && char != utf8.RuneError; char, _, err = r.ReadRune() {
|
||||
if char == '\\' && !escaped {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
if char == ':' && !escaped && !hadType {
|
||||
typStr := buf.String()
|
||||
res.Type, err = parseParamType(typStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Reset()
|
||||
hadType = true
|
||||
continue
|
||||
}
|
||||
escaped = false
|
||||
// We don't care about length and it never fails.
|
||||
_, _ = buf.WriteRune(char)
|
||||
}
|
||||
if char == utf8.RuneError {
|
||||
return nil, errors.New("bad UTF-8 string")
|
||||
}
|
||||
// The only other error `ReadRune` returns is io.EOF, which is fine and
|
||||
// expected, so we don't check err here.
|
||||
|
||||
val = buf.String()
|
||||
if !hadType {
|
||||
res.Type = inferParamType(val)
|
||||
}
|
||||
res.Value, err = adjustValToType(res.Type, val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ContextItem represents a transaction context item.
|
||||
type ContextItem struct {
|
||||
Script util.Uint160
|
||||
|
|
343
pkg/smartcontract/param_context_test.go
Normal file
343
pkg/smartcontract/param_context_test.go
Normal file
|
@ -0,0 +1,343 @@
|
|||
package smartcontract
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseParamType(t *testing.T) {
|
||||
var inouts = []struct {
|
||||
in string
|
||||
out ParamType
|
||||
err bool
|
||||
}{{
|
||||
in: "signature",
|
||||
out: SignatureType,
|
||||
}, {
|
||||
in: "Signature",
|
||||
out: SignatureType,
|
||||
}, {
|
||||
in: "SiGnAtUrE",
|
||||
out: SignatureType,
|
||||
}, {
|
||||
in: "bool",
|
||||
out: BoolType,
|
||||
}, {
|
||||
in: "int",
|
||||
out: IntegerType,
|
||||
}, {
|
||||
in: "hash160",
|
||||
out: Hash160Type,
|
||||
}, {
|
||||
in: "hash256",
|
||||
out: Hash256Type,
|
||||
}, {
|
||||
in: "bytes",
|
||||
out: ByteArrayType,
|
||||
}, {
|
||||
in: "key",
|
||||
out: PublicKeyType,
|
||||
}, {
|
||||
in: "string",
|
||||
out: StringType,
|
||||
}, {
|
||||
in: "array",
|
||||
err: true,
|
||||
}, {
|
||||
in: "qwerty",
|
||||
err: true,
|
||||
}}
|
||||
for _, inout := range inouts {
|
||||
out, err := parseParamType(inout.in)
|
||||
if inout.err {
|
||||
assert.NotNil(t, err, "should error on '%s' input", inout.in)
|
||||
} else {
|
||||
assert.Nil(t, err, "shouldn't error on '%s' input", inout.in)
|
||||
assert.Equal(t, inout.out, out, "bad output for '%s' input", inout.in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInferParamType(t *testing.T) {
|
||||
var inouts = []struct {
|
||||
in string
|
||||
out ParamType
|
||||
}{{
|
||||
in: "42",
|
||||
out: IntegerType,
|
||||
}, {
|
||||
in: "-42",
|
||||
out: IntegerType,
|
||||
}, {
|
||||
in: "0",
|
||||
out: IntegerType,
|
||||
}, {
|
||||
in: "2e10",
|
||||
out: ByteArrayType,
|
||||
}, {
|
||||
in: "true",
|
||||
out: BoolType,
|
||||
}, {
|
||||
in: "false",
|
||||
out: BoolType,
|
||||
}, {
|
||||
in: "truee",
|
||||
out: StringType,
|
||||
}, {
|
||||
in: "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y",
|
||||
out: Hash160Type,
|
||||
}, {
|
||||
in: "ZK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y",
|
||||
out: StringType,
|
||||
}, {
|
||||
in: "50befd26fdf6e4d957c11e078b24ebce6291456f",
|
||||
out: Hash160Type,
|
||||
}, {
|
||||
in: "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
|
||||
out: PublicKeyType,
|
||||
}, {
|
||||
in: "30b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
|
||||
out: ByteArrayType,
|
||||
}, {
|
||||
in: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
|
||||
out: Hash256Type,
|
||||
}, {
|
||||
in: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7da",
|
||||
out: ByteArrayType,
|
||||
}, {
|
||||
in: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
|
||||
out: SignatureType,
|
||||
}, {
|
||||
in: "qwerty",
|
||||
out: StringType,
|
||||
}, {
|
||||
in: "ab",
|
||||
out: ByteArrayType,
|
||||
}, {
|
||||
in: "az",
|
||||
out: StringType,
|
||||
}, {
|
||||
in: "bad",
|
||||
out: StringType,
|
||||
}, {
|
||||
in: "фыва",
|
||||
out: StringType,
|
||||
}, {
|
||||
in: "dead",
|
||||
out: ByteArrayType,
|
||||
}}
|
||||
for _, inout := range inouts {
|
||||
out := inferParamType(inout.in)
|
||||
assert.Equal(t, inout.out, out, "bad output for '%s' input", inout.in)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdjustValToType(t *testing.T) {
|
||||
var inouts = []struct {
|
||||
typ ParamType
|
||||
val string
|
||||
out interface{}
|
||||
err bool
|
||||
}{{
|
||||
typ: SignatureType,
|
||||
val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
|
||||
out: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
|
||||
}, {
|
||||
typ: SignatureType,
|
||||
val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c",
|
||||
err: true,
|
||||
}, {
|
||||
typ: SignatureType,
|
||||
val: "qwerty",
|
||||
err: true,
|
||||
}, {
|
||||
typ: BoolType,
|
||||
val: "false",
|
||||
out: false,
|
||||
}, {
|
||||
typ: BoolType,
|
||||
val: "true",
|
||||
out: true,
|
||||
}, {
|
||||
typ: BoolType,
|
||||
val: "qwerty",
|
||||
err: true,
|
||||
}, {
|
||||
typ: BoolType,
|
||||
val: "42",
|
||||
err: true,
|
||||
}, {
|
||||
typ: BoolType,
|
||||
val: "0",
|
||||
err: true,
|
||||
}, {
|
||||
typ: IntegerType,
|
||||
val: "0",
|
||||
out: 0,
|
||||
}, {
|
||||
typ: IntegerType,
|
||||
val: "42",
|
||||
out: 42,
|
||||
}, {
|
||||
typ: IntegerType,
|
||||
val: "-42",
|
||||
out: -42,
|
||||
}, {
|
||||
typ: IntegerType,
|
||||
val: "q",
|
||||
err: true,
|
||||
}, {
|
||||
typ: Hash160Type,
|
||||
val: "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y",
|
||||
out: "23ba2703c53263e8d6e522dc32203339dcd8eee9",
|
||||
}, {
|
||||
typ: Hash160Type,
|
||||
val: "50befd26fdf6e4d957c11e078b24ebce6291456f",
|
||||
out: "50befd26fdf6e4d957c11e078b24ebce6291456f",
|
||||
}, {
|
||||
typ: Hash160Type,
|
||||
val: "befd26fdf6e4d957c11e078b24ebce6291456f",
|
||||
err: true,
|
||||
}, {
|
||||
typ: Hash160Type,
|
||||
val: "q",
|
||||
err: true,
|
||||
}, {
|
||||
typ: Hash256Type,
|
||||
val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
|
||||
out: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
|
||||
}, {
|
||||
typ: Hash256Type,
|
||||
val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282d",
|
||||
err: true,
|
||||
}, {
|
||||
typ: Hash256Type,
|
||||
val: "q",
|
||||
err: true,
|
||||
}, {
|
||||
typ: ByteArrayType,
|
||||
val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282d",
|
||||
out: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282d",
|
||||
}, {
|
||||
typ: ByteArrayType,
|
||||
val: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
|
||||
out: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
|
||||
}, {
|
||||
typ: ByteArrayType,
|
||||
val: "50befd26fdf6e4d957c11e078b24ebce6291456f",
|
||||
out: "50befd26fdf6e4d957c11e078b24ebce6291456f",
|
||||
}, {
|
||||
typ: ByteArrayType,
|
||||
val: "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y",
|
||||
err: true,
|
||||
}, {
|
||||
typ: ByteArrayType,
|
||||
val: "q",
|
||||
err: true,
|
||||
}, {
|
||||
typ: ByteArrayType,
|
||||
val: "ab",
|
||||
out: "ab",
|
||||
}, {
|
||||
typ: PublicKeyType,
|
||||
val: "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
|
||||
out: "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
|
||||
}, {
|
||||
typ: PublicKeyType,
|
||||
val: "01b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
|
||||
err: true,
|
||||
}, {
|
||||
typ: PublicKeyType,
|
||||
val: "q",
|
||||
err: true,
|
||||
}, {
|
||||
typ: StringType,
|
||||
val: "q",
|
||||
out: "q",
|
||||
}, {
|
||||
typ: StringType,
|
||||
val: "dead",
|
||||
out: "dead",
|
||||
}, {
|
||||
typ: StringType,
|
||||
val: "йцукен",
|
||||
out: "йцукен",
|
||||
}, {
|
||||
typ: ArrayType,
|
||||
val: "",
|
||||
err: true,
|
||||
}}
|
||||
|
||||
for _, inout := range inouts {
|
||||
out, err := adjustValToType(inout.typ, inout.val)
|
||||
if inout.err {
|
||||
assert.NotNil(t, err, "should error on '%s/%s' input", inout.typ, inout.val)
|
||||
} else {
|
||||
assert.Nil(t, err, "shouldn't error on '%s/%s' input", inout.typ, inout.val)
|
||||
assert.Equal(t, inout.out, out, "bad output for '%s/%s' input", inout.typ, inout.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewParameterFromString(t *testing.T) {
|
||||
var inouts = []struct {
|
||||
in string
|
||||
out Parameter
|
||||
err bool
|
||||
}{{
|
||||
in: "qwerty",
|
||||
out: Parameter{StringType, "qwerty"},
|
||||
}, {
|
||||
in: "42",
|
||||
out: Parameter{IntegerType, 42},
|
||||
}, {
|
||||
in: "Hello, 世界",
|
||||
out: Parameter{StringType, "Hello, 世界"},
|
||||
}, {
|
||||
in: `\4\2`,
|
||||
out: Parameter{IntegerType, 42},
|
||||
}, {
|
||||
in: `\\4\2`,
|
||||
out: Parameter{StringType, `\42`},
|
||||
}, {
|
||||
in: `\\\4\2`,
|
||||
out: Parameter{StringType, `\42`},
|
||||
}, {
|
||||
in: "int:42",
|
||||
out: Parameter{IntegerType, 42},
|
||||
}, {
|
||||
in: "true",
|
||||
out: Parameter{BoolType, true},
|
||||
}, {
|
||||
in: "string:true",
|
||||
out: Parameter{StringType, "true"},
|
||||
}, {
|
||||
in: "\xfe\xff",
|
||||
err: true,
|
||||
}, {
|
||||
in: `string\:true`,
|
||||
out: Parameter{StringType, "string:true"},
|
||||
}, {
|
||||
in: "string:true:true",
|
||||
out: Parameter{StringType, "true:true"},
|
||||
}, {
|
||||
in: `string\\:true`,
|
||||
err: true,
|
||||
}, {
|
||||
in: `qwerty:asdf`,
|
||||
err: true,
|
||||
}, {
|
||||
in: `bool:asdf`,
|
||||
err: true,
|
||||
}}
|
||||
for _, inout := range inouts {
|
||||
out, err := NewParameterFromString(inout.in)
|
||||
if inout.err {
|
||||
assert.NotNil(t, err, "should error on '%s' input", inout.in)
|
||||
} else {
|
||||
assert.Nil(t, err, "shouldn't error on '%s' input", inout.in)
|
||||
assert.Equal(t, inout.out, *out, "bad output for '%s' input", inout.in)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue