github.com/lmittmann/w3@v0.20.0/internal/abi/tuple_test.go (about)

     1  package abi
     2  
     3  import (
     4  	"errors"
     5  	"maps"
     6  	"math/big"
     7  	"reflect"
     8  	"slices"
     9  	"strconv"
    10  	"testing"
    11  
    12  	"github.com/ethereum/go-ethereum/accounts/abi"
    13  	"github.com/ethereum/go-ethereum/common"
    14  	"github.com/google/go-cmp/cmp"
    15  	"github.com/google/go-cmp/cmp/cmpopts"
    16  	"github.com/lmittmann/w3/internal"
    17  )
    18  
    19  func TestTupleMap(t *testing.T) {
    20  	tests := []struct {
    21  		Tuples     []any
    22  		WantTuples map[string]reflect.Type
    23  		WantErr    error
    24  	}{
    25  		{
    26  			Tuples: []any{Tuple1{}},
    27  			WantTuples: map[string]reflect.Type{
    28  				"Tuple1": reflect.TypeOf(Tuple1{}),
    29  			},
    30  		},
    31  		{
    32  			Tuples:  []any{Tuple1{}, Tuple1{}},
    33  			WantErr: errors.New("duplicate tuple definition: Tuple1"),
    34  		},
    35  		{
    36  			Tuples:  []any{123}, // int instead of struct
    37  			WantErr: errors.New("expected struct, got int"),
    38  		},
    39  		{
    40  			Tuples:  []any{"hello"}, // string instead of struct
    41  			WantErr: errors.New("expected struct, got string"),
    42  		},
    43  		{
    44  			Tuples: []any{Tuple1{}, Tuple2{}},
    45  			WantTuples: map[string]reflect.Type{
    46  				"Tuple1": reflect.TypeOf(Tuple1{}),
    47  				"Tuple2": reflect.TypeOf(Tuple2{}),
    48  			},
    49  		},
    50  	}
    51  
    52  	for i, test := range tests {
    53  		t.Run(strconv.Itoa(i), func(t *testing.T) {
    54  			gotTuples, gotErr := tupleMap(test.Tuples...)
    55  			if diff := cmp.Diff(test.WantErr, gotErr,
    56  				internal.EquateErrors(),
    57  			); diff != "" {
    58  				t.Fatalf("Err (-want, +got):\n%s", diff)
    59  			}
    60  
    61  			want := slices.Sorted(maps.Keys(test.WantTuples))
    62  			got := slices.Sorted(maps.Keys(gotTuples))
    63  			if diff := cmp.Diff(want, got); diff != "" {
    64  				t.Fatalf("Tuples (-want, +got):\n%s", diff)
    65  			}
    66  		})
    67  	}
    68  }
    69  
    70  type Tuple1 struct {
    71  	Arg0 *big.Int
    72  }
    73  
    74  type Tuple2 struct {
    75  	Arg0 *big.Int
    76  	Arg1 *big.Int `abitype:"int256"`
    77  }
    78  
    79  func TestTypeOfField(t *testing.T) {
    80  	tests := []struct {
    81  		Field   reflect.StructField
    82  		Want    *abi.Type
    83  		WantErr error
    84  	}{
    85  		// default types
    86  		{
    87  			Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[bool]()},
    88  			Want:  &abi.Type{T: abi.BoolTy},
    89  		},
    90  		{
    91  			Field: reflect.StructField{Name: "TestUint8", Type: reflect.TypeFor[uint8]()},
    92  			Want:  &abi.Type{T: abi.UintTy, Size: 8},
    93  		},
    94  		{
    95  			Field: reflect.StructField{Name: "TestUint16", Type: reflect.TypeFor[uint16]()},
    96  			Want:  &abi.Type{T: abi.UintTy, Size: 16},
    97  		},
    98  		{
    99  			Field: reflect.StructField{Name: "TestUint32", Type: reflect.TypeFor[uint32]()},
   100  			Want:  &abi.Type{T: abi.UintTy, Size: 32},
   101  		},
   102  		{
   103  			Field: reflect.StructField{Name: "TestUint64", Type: reflect.TypeFor[uint64]()},
   104  			Want:  &abi.Type{T: abi.UintTy, Size: 64},
   105  		},
   106  		{
   107  			Field: reflect.StructField{Name: "TestInt8", Type: reflect.TypeFor[int8]()},
   108  			Want:  &abi.Type{T: abi.IntTy, Size: 8},
   109  		},
   110  		{
   111  			Field: reflect.StructField{Name: "TestInt16", Type: reflect.TypeFor[int16]()},
   112  			Want:  &abi.Type{T: abi.IntTy, Size: 16},
   113  		},
   114  		{
   115  			Field: reflect.StructField{Name: "TestInt32", Type: reflect.TypeFor[int32]()},
   116  			Want:  &abi.Type{T: abi.IntTy, Size: 32},
   117  		},
   118  		{
   119  			Field: reflect.StructField{Name: "TestInt64", Type: reflect.TypeFor[int64]()},
   120  			Want:  &abi.Type{T: abi.IntTy, Size: 64},
   121  		},
   122  		{
   123  			Field: reflect.StructField{Name: "TestBigInt", Type: reflect.TypeFor[*big.Int]()},
   124  			Want:  &abi.Type{T: abi.UintTy, Size: 256},
   125  		},
   126  		{
   127  			Field: reflect.StructField{Name: "TestAddress", Type: reflect.TypeFor[common.Address]()},
   128  			Want:  &abi.Type{T: abi.AddressTy, Size: 20},
   129  		},
   130  		{
   131  			Field: reflect.StructField{Name: "TestHash", Type: reflect.TypeFor[common.Hash]()},
   132  			Want:  &abi.Type{T: abi.FixedBytesTy, Size: 32},
   133  		},
   134  		{
   135  			Field: reflect.StructField{Name: "TestBytes", Type: reflect.TypeFor[[]byte]()},
   136  			Want:  &abi.Type{T: abi.BytesTy},
   137  		},
   138  		{
   139  			Field: reflect.StructField{Name: "TestString", Type: reflect.TypeFor[string]()},
   140  			Want:  &abi.Type{T: abi.StringTy},
   141  		},
   142  
   143  		// slice types
   144  		{
   145  			Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[]bool]()},
   146  			Want:  &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.BoolTy}},
   147  		},
   148  		{
   149  			Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[1]bool]()},
   150  			Want:  &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.BoolTy}, Size: 1},
   151  		},
   152  		{
   153  			Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[][]bool]()},
   154  			Want:  &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.BoolTy}}},
   155  		},
   156  		{
   157  			Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[1][]bool]()},
   158  			Want:  &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.BoolTy}}, Size: 1},
   159  		},
   160  		{
   161  			Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[][1]bool]()},
   162  			Want:  &abi.Type{T: abi.SliceTy, Elem: &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.BoolTy}, Size: 1}},
   163  		},
   164  		{
   165  			Field: reflect.StructField{Name: "TestBool", Type: reflect.TypeFor[[1][1]bool]()},
   166  			Want:  &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.ArrayTy, Elem: &abi.Type{T: abi.BoolTy}, Size: 1}, Size: 1},
   167  		},
   168  
   169  		// tagged types
   170  		{
   171  			Field: reflect.StructField{Name: "TestTagUint256", Type: reflect.TypeFor[*big.Int](), Tag: `abitype:"uint256"`},
   172  			Want:  &abi.Type{T: abi.UintTy, Size: 256},
   173  		},
   174  		{
   175  			Field: reflect.StructField{Name: "TestTagUint256", Type: reflect.TypeFor[*big.Int](), Tag: `abitype:"uint16"`},
   176  			Want:  &abi.Type{T: abi.UintTy, Size: 16},
   177  		},
   178  		{
   179  			Field: reflect.StructField{Name: "TestTagInt256", Type: reflect.TypeFor[*big.Int](), Tag: `abitype:"int256"`},
   180  			Want:  &abi.Type{T: abi.IntTy, Size: 256},
   181  		},
   182  		{
   183  			Field: reflect.StructField{Name: "TestTagAddress", Type: reflect.TypeFor[common.Address](), Tag: `abitype:"address"`},
   184  			Want:  &abi.Type{T: abi.AddressTy, Size: 20},
   185  		},
   186  		{
   187  			Field: reflect.StructField{Name: "TestTagBytes32", Type: reflect.TypeFor[[32]byte](), Tag: `abitype:"bytes32"`},
   188  			Want:  &abi.Type{T: abi.FixedBytesTy, Size: 32},
   189  		},
   190  		{
   191  			Field:   reflect.StructField{Name: "TestTagBytes32Hash", Type: reflect.TypeFor[common.Hash](), Tag: `abitype:"bytes32"`},
   192  			WantErr: errors.New(`tagged type "bytes32" does not match type common.Hash`),
   193  		},
   194  		{
   195  			Field:   reflect.StructField{Name: "TestUnknownTag", Type: reflect.TypeFor[*big.Int](), Tag: `abitype:"unknown"`},
   196  			WantErr: errors.New(`unknown abi type "unknown"`),
   197  		},
   198  		{
   199  			Field:   reflect.StructField{Name: "TestIncompatible", Type: reflect.TypeFor[string](), Tag: `abitype:"uint256"`},
   200  			WantErr: errors.New(`tagged type "uint256" does not match type string`),
   201  		},
   202  		{
   203  			Field:   reflect.StructField{Name: "TestUnsupported", Type: reflect.TypeFor[float64]()},
   204  			WantErr: errors.New(`unknown type "float64"`),
   205  		},
   206  	}
   207  
   208  	for i, test := range tests {
   209  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   210  			got, err := typeOfField(test.Field)
   211  			if diff := cmp.Diff(test.WantErr, err,
   212  				internal.EquateErrors(),
   213  			); diff != "" {
   214  				t.Fatalf("Err: (-want +got):\n%s", diff)
   215  			}
   216  			if diff := cmp.Diff(test.Want, got,
   217  				cmpopts.IgnoreUnexported(abi.Type{}),
   218  			); diff != "" {
   219  				t.Errorf("(-want +got):\n%s", diff)
   220  			}
   221  		})
   222  	}
   223  }
   224  
   225  func TestToCamelCase(t *testing.T) {
   226  	tests := []struct {
   227  		Input string
   228  		Want  string
   229  	}{
   230  		{"", ""},
   231  		{"test", "test"},
   232  		{"TEST", "test"},
   233  		{"Test", "test"},
   234  		{"testCase", "testCase"},
   235  		{"TestCase", "testCase"},
   236  		{"testID", "testId"},
   237  		{"TestID", "testId"},
   238  	}
   239  
   240  	for i, test := range tests {
   241  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   242  			got := toCamelCase(test.Input)
   243  			if test.Want != got {
   244  				t.Fatalf("want %q, got %q", test.Want, got)
   245  			}
   246  		})
   247  	}
   248  }