package vm import ( "math/big" "testing" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/stretchr/testify/assert" ) var makeStackItemTestCases = []struct { input interface{} result StackItem }{ { input: int64(3), result: &BigIntegerItem{value: big.NewInt(3)}, }, { input: int16(3), result: &BigIntegerItem{value: big.NewInt(3)}, }, { input: 3, result: &BigIntegerItem{value: big.NewInt(3)}, }, { input: uint8(3), result: &BigIntegerItem{value: big.NewInt(3)}, }, { input: uint16(3), result: &BigIntegerItem{value: big.NewInt(3)}, }, { input: uint32(3), result: &BigIntegerItem{value: big.NewInt(3)}, }, { input: uint64(3), result: &BigIntegerItem{value: big.NewInt(3)}, }, { input: big.NewInt(3), result: &BigIntegerItem{value: big.NewInt(3)}, }, { input: []byte{1, 2, 3, 4}, result: &ByteArrayItem{value: []byte{1, 2, 3, 4}}, }, { input: []byte{}, result: &ByteArrayItem{value: []byte{}}, }, { input: "bla", result: &ByteArrayItem{value: []byte("bla")}, }, { input: "", result: &ByteArrayItem{value: []byte{}}, }, { input: true, result: &BoolItem{value: true}, }, { input: false, result: &BoolItem{value: false}, }, { input: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}}, result: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}}}, }, { input: []int{1, 2, 3}, result: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(1)}, &BigIntegerItem{value: big.NewInt(2)}, &BigIntegerItem{value: big.NewInt(3)}}}, }, } var makeStackItemErrorCases = []struct { input interface{} }{ { input: nil, }, } func TestMakeStackItem(t *testing.T) { for _, testCase := range makeStackItemTestCases { assert.Equal(t, testCase.result, makeStackItem(testCase.input)) } for _, errorCase := range makeStackItemErrorCases { assert.Panics(t, func() { makeStackItem(errorCase.input) }) } } var stringerTestCases = []struct { input StackItem result string }{ { input: NewStructItem([]StackItem{}), result: "Struct", }, { input: NewBigIntegerItem(3), result: "BigInteger", }, { input: NewBoolItem(true), result: "Boolean", }, { input: NewByteArrayItem([]byte{}), result: "ByteArray", }, { input: NewArrayItem([]StackItem{}), result: "Array", }, { input: NewMapItem(), result: "Map", }, { input: NewInteropItem(nil), result: "InteropItem", }, } func TestStringer(t *testing.T) { for _, testCase := range stringerTestCases { assert.Equal(t, testCase.result, testCase.input.String()) } } var equalsTestCases = map[string][]struct { item1 StackItem item2 StackItem result bool }{ "struct": { { item1: NewStructItem(nil), item2: nil, result: false, }, { item1: NewStructItem(nil), item2: NewBigIntegerItem(1), result: false, }, { item1: NewStructItem(nil), item2: NewStructItem([]StackItem{NewBigIntegerItem(1)}), result: false, }, { item1: NewStructItem([]StackItem{NewBigIntegerItem(1)}), item2: NewStructItem([]StackItem{NewBigIntegerItem(2)}), result: false, }, { item1: NewStructItem([]StackItem{NewBigIntegerItem(1)}), item2: NewStructItem([]StackItem{NewBigIntegerItem(1)}), result: true, }, }, "bigint": { { item1: NewBigIntegerItem(2), item2: nil, result: false, }, { item1: NewBigIntegerItem(2), item2: NewBigIntegerItem(2), result: true, }, { item1: NewBigIntegerItem(2), item2: NewBoolItem(false), result: false, }, { item1: NewBigIntegerItem(0), item2: NewBoolItem(false), result: false, }, { item1: NewBigIntegerItem(2), item2: makeStackItem(int32(2)), result: true, }, }, "bool": { { item1: NewBoolItem(true), item2: nil, result: false, }, { item1: NewBoolItem(true), item2: NewBoolItem(true), result: true, }, { item1: NewBoolItem(true), item2: NewBigIntegerItem(1), result: true, }, { item1: NewBoolItem(true), item2: NewBoolItem(false), result: false, }, { item1: NewBoolItem(true), item2: makeStackItem(true), result: true, }, }, "bytearray": { { item1: NewByteArrayItem(nil), item2: nil, result: false, }, { item1: NewByteArrayItem([]byte{1, 2, 3}), item2: NewByteArrayItem([]byte{1, 2, 3}), result: true, }, { item1: NewByteArrayItem([]byte{1}), item2: NewBigIntegerItem(1), result: true, }, { item1: NewByteArrayItem([]byte{1, 2, 3}), item2: NewByteArrayItem([]byte{1, 2, 4}), result: false, }, { item1: NewByteArrayItem([]byte{1, 2, 3}), item2: makeStackItem([]byte{1, 2, 3}), result: true, }, }, "array": { { item1: NewArrayItem(nil), item2: nil, result: false, }, { item1: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(3)}}), item2: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(3)}}), result: false, }, { item1: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}}), item2: NewBigIntegerItem(1), result: false, }, { item1: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(3)}}), item2: NewArrayItem([]StackItem{&BigIntegerItem{big.NewInt(1)}, &BigIntegerItem{big.NewInt(2)}, &BigIntegerItem{big.NewInt(4)}}), result: false, }, }, "map": { { item1: NewMapItem(), item2: nil, result: false, }, { item1: NewMapItem(), item2: NewMapItem(), result: false, }, { item1: &MapItem{value: map[interface{}]StackItem{"first": NewBigIntegerItem(1), true: NewByteArrayItem([]byte{2})}}, item2: &MapItem{value: map[interface{}]StackItem{"first": NewBigIntegerItem(1), true: NewByteArrayItem([]byte{2})}}, result: false, }, { item1: &MapItem{value: map[interface{}]StackItem{"first": NewBigIntegerItem(1), true: NewByteArrayItem([]byte{2})}}, item2: &MapItem{value: map[interface{}]StackItem{"first": NewBigIntegerItem(1), true: NewByteArrayItem([]byte{3})}}, result: false, }, }, "interop": { { item1: NewInteropItem(nil), item2: nil, result: false, }, { item1: NewInteropItem(nil), item2: NewInteropItem(nil), result: true, }, { item1: NewInteropItem(2), item2: NewInteropItem(3), result: false, }, { item1: NewInteropItem(3), item2: NewInteropItem(3), result: true, }, }, } func TestEquals(t *testing.T) { for name, testBatch := range equalsTestCases { for _, testCase := range testBatch { t.Run(name, func(t *testing.T) { assert.Equal(t, testCase.result, testCase.item1.Equals(testCase.item2)) // Reference equals assert.Equal(t, true, testCase.item1.Equals(testCase.item1)) }) } } } var marshalJSONTestCases = []struct { input StackItem result []byte }{ { input: NewBigIntegerItem(2), result: []byte(`2`), }, { input: NewBoolItem(true), result: []byte(`true`), }, { input: NewByteArrayItem([]byte{1, 2, 3}), result: []byte(`"010203"`), }, { input: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}}}, result: []byte(`[3,"010203"]`), }, { input: &InteropItem{value: 3}, result: []byte(`3`), }, } func TestMarshalJSON(t *testing.T) { var ( actual []byte err error ) for _, testCase := range marshalJSONTestCases { switch testCase.input.(type) { case *BigIntegerItem: actual, err = testCase.input.(*BigIntegerItem).MarshalJSON() case *BoolItem: actual, err = testCase.input.(*BoolItem).MarshalJSON() case *ByteArrayItem: actual, err = testCase.input.(*ByteArrayItem).MarshalJSON() case *ArrayItem: actual, err = testCase.input.(*ArrayItem).MarshalJSON() case *InteropItem: actual, err = testCase.input.(*InteropItem).MarshalJSON() default: continue } assert.NoError(t, err) assert.Equal(t, testCase.result, actual) } } var toContractParameterTestCases = []struct { input StackItem result smartcontract.Parameter }{ { input: NewStructItem([]StackItem{ NewBigIntegerItem(1), NewBoolItem(true), }), result: smartcontract.Parameter{Type: smartcontract.ArrayType, Value: []smartcontract.Parameter{ {Type: smartcontract.IntegerType, Value: int64(1)}, {Type: smartcontract.BoolType, Value: true}, }}, }, { input: NewBoolItem(false), result: smartcontract.Parameter{Type: smartcontract.BoolType, Value: false}, }, { input: NewByteArrayItem([]byte{0x01, 0x02, 0x03}), result: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte{0x01, 0x02, 0x03}}, }, { input: NewArrayItem([]StackItem{NewBigIntegerItem(2), NewBoolItem(true)}), result: smartcontract.Parameter{Type: smartcontract.ArrayType, Value: []smartcontract.Parameter{ {Type: smartcontract.IntegerType, Value: int64(2)}, {Type: smartcontract.BoolType, Value: true}, }}, }, { input: NewInteropItem(nil), result: smartcontract.Parameter{Type: smartcontract.InteropInterfaceType, Value: nil}, }, { input: &MapItem{value: map[interface{}]StackItem{ toMapKey(NewBigIntegerItem(1)): NewBoolItem(true), toMapKey(NewByteArrayItem([]byte("qwerty"))): NewBigIntegerItem(3), toMapKey(NewBoolItem(true)): NewBoolItem(false), }}, result: smartcontract.Parameter{ Type: smartcontract.MapType, Value: map[smartcontract.Parameter]smartcontract.Parameter{ {Type: smartcontract.IntegerType, Value: int64(1)}: {Type: smartcontract.BoolType, Value: true}, {Type: smartcontract.ByteArrayType, Value: "qwerty"}: {Type: smartcontract.IntegerType, Value: int64(3)}, {Type: smartcontract.BoolType, Value: true}: {Type: smartcontract.BoolType, Value: false}, }, }, }, } func TestToContractParameter(t *testing.T) { for _, tc := range toContractParameterTestCases { seen := make(map[StackItem]bool) res := tc.input.ToContractParameter(seen) assert.Equal(t, res, tc.result) } } var fromMapKeyTestCases = []struct { input interface{} result StackItem }{ { input: true, result: NewBoolItem(true), }, { input: int64(4), result: NewBigIntegerItem(4), }, { input: "qwerty", result: NewByteArrayItem([]byte("qwerty")), }, } func TestFromMapKey(t *testing.T) { for _, tc := range fromMapKeyTestCases { res := fromMapKey(tc.input) assert.Equal(t, res, tc.result) } }