github.com/vipernet-xyz/tm@v0.34.24/libs/json/decoder_test.go (about)

     1  package json_test
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/vipernet-xyz/tm/libs/json"
    12  )
    13  
    14  func TestUnmarshal(t *testing.T) {
    15  	i64Nil := (*int64)(nil)
    16  	str := "string"
    17  	strPtr := &str
    18  	structNil := (*Struct)(nil)
    19  	i32 := int32(32)
    20  	i64 := int64(64)
    21  
    22  	testcases := map[string]struct {
    23  		json  string
    24  		value interface{}
    25  		err   bool
    26  	}{
    27  		"bool true":           {"true", true, false},
    28  		"bool false":          {"false", false, false},
    29  		"float32":             {"3.14", float32(3.14), false},
    30  		"float64":             {"3.14", float64(3.14), false},
    31  		"int32":               {`32`, int32(32), false},
    32  		"int32 string":        {`"32"`, int32(32), true},
    33  		"int32 ptr":           {`32`, &i32, false},
    34  		"int64":               {`"64"`, int64(64), false},
    35  		"int64 noend":         {`"64`, int64(64), true},
    36  		"int64 number":        {`64`, int64(64), true},
    37  		"int64 ptr":           {`"64"`, &i64, false},
    38  		"int64 ptr nil":       {`null`, i64Nil, false},
    39  		"string":              {`"foo"`, "foo", false},
    40  		"string noend":        {`"foo`, "foo", true},
    41  		"string ptr":          {`"string"`, &str, false},
    42  		"slice byte":          {`"AQID"`, []byte{1, 2, 3}, false},
    43  		"slice bytes":         {`["AQID"]`, [][]byte{{1, 2, 3}}, false},
    44  		"slice int32":         {`[1,2,3]`, []int32{1, 2, 3}, false},
    45  		"slice int64":         {`["1","2","3"]`, []int64{1, 2, 3}, false},
    46  		"slice int64 number":  {`[1,2,3]`, []int64{1, 2, 3}, true},
    47  		"slice int64 ptr":     {`["64"]`, []*int64{&i64}, false},
    48  		"slice int64 empty":   {`[]`, []int64(nil), false},
    49  		"slice int64 null":    {`null`, []int64(nil), false},
    50  		"array byte":          {`"AQID"`, [3]byte{1, 2, 3}, false},
    51  		"array byte large":    {`"AQID"`, [4]byte{1, 2, 3, 4}, true},
    52  		"array byte small":    {`"AQID"`, [2]byte{1, 2}, true},
    53  		"array int32":         {`[1,2,3]`, [3]int32{1, 2, 3}, false},
    54  		"array int64":         {`["1","2","3"]`, [3]int64{1, 2, 3}, false},
    55  		"array int64 number":  {`[1,2,3]`, [3]int64{1, 2, 3}, true},
    56  		"array int64 large":   {`["1","2","3"]`, [4]int64{1, 2, 3, 4}, true},
    57  		"array int64 small":   {`["1","2","3"]`, [2]int64{1, 2}, true},
    58  		"map bytes":           {`{"b":"AQID"}`, map[string][]byte{"b": {1, 2, 3}}, false},
    59  		"map int32":           {`{"a":1,"b":2}`, map[string]int32{"a": 1, "b": 2}, false},
    60  		"map int64":           {`{"a":"1","b":"2"}`, map[string]int64{"a": 1, "b": 2}, false},
    61  		"map int64 empty":     {`{}`, map[string]int64{}, false},
    62  		"map int64 null":      {`null`, map[string]int64(nil), false},
    63  		"map int key":         {`{}`, map[int]int{}, true},
    64  		"time":                {`"2020-06-03T17:35:30Z"`, time.Date(2020, 6, 3, 17, 35, 30, 0, time.UTC), false},
    65  		"time non-utc":        {`"2020-06-03T17:35:30+02:00"`, time.Time{}, true},
    66  		"time nozone":         {`"2020-06-03T17:35:30"`, time.Time{}, true},
    67  		"car":                 {`{"type":"vehicle/car","value":{"Wheels":4}}`, Car{Wheels: 4}, false},
    68  		"car ptr":             {`{"type":"vehicle/car","value":{"Wheels":4}}`, &Car{Wheels: 4}, false},
    69  		"car iface":           {`{"type":"vehicle/car","value":{"Wheels":4}}`, Vehicle(&Car{Wheels: 4}), false},
    70  		"boat":                {`{"type":"vehicle/boat","value":{"Sail":true}}`, Boat{Sail: true}, false},
    71  		"boat ptr":            {`{"type":"vehicle/boat","value":{"Sail":true}}`, &Boat{Sail: true}, false},
    72  		"boat iface":          {`{"type":"vehicle/boat","value":{"Sail":true}}`, Vehicle(Boat{Sail: true}), false},
    73  		"boat into car":       {`{"type":"vehicle/boat","value":{"Sail":true}}`, Car{}, true},
    74  		"boat into car iface": {`{"type":"vehicle/boat","value":{"Sail":true}}`, Vehicle(&Car{}), true},
    75  		"shoes":               {`{"type":"vehicle/shoes","value":{"Soles":"rubber"}}`, Car{}, true},
    76  		"shoes ptr":           {`{"type":"vehicle/shoes","value":{"Soles":"rubber"}}`, &Car{}, true},
    77  		"shoes iface":         {`{"type":"vehicle/shoes","value":{"Soles":"rubbes"}}`, Vehicle(&Car{}), true},
    78  		"key public":          {`{"type":"key/public","value":"AQIDBAUGBwg="}`, PublicKey{1, 2, 3, 4, 5, 6, 7, 8}, false},
    79  		"key wrong":           {`{"type":"key/public","value":"AQIDBAUGBwg="}`, PrivateKey{1, 2, 3, 4, 5, 6, 7, 8}, true},
    80  		"key into car":        {`{"type":"key/public","value":"AQIDBAUGBwg="}`, Vehicle(&Car{}), true},
    81  		"tags": {
    82  			`{"name":"name","OmitEmpty":"foo","Hidden":"bar","tags":{"name":"child"}}`,
    83  			Tags{JSONName: "name", OmitEmpty: "foo", Tags: &Tags{JSONName: "child"}},
    84  			false,
    85  		},
    86  		"tags ptr": {
    87  			`{"name":"name","OmitEmpty":"foo","tags":null}`,
    88  			&Tags{JSONName: "name", OmitEmpty: "foo"},
    89  			false,
    90  		},
    91  		"tags real name": {`{"JSONName":"name"}`, Tags{}, false},
    92  		"struct": {
    93  			`{
    94  				"Bool":true, "Float64":3.14, "Int32":32, "Int64":"64", "Int64Ptr":"64",
    95  				"String":"foo", "StringPtrPtr": "string", "Bytes":"AQID",
    96  				"Time":"2020-06-02T16:05:13.004346374Z",
    97  				"Car":{"Wheels":4},
    98  				"Boat":{"Sail":true},
    99  				"Vehicles":[
   100  					{"type":"vehicle/car","value":{"Wheels":4}},
   101  					{"type":"vehicle/boat","value":{"Sail":true}}
   102  				],
   103  				"Child":{
   104  					"Bool":false, "Float64":0, "Int32":0, "Int64":"0", "Int64Ptr":null,
   105  					"String":"child", "StringPtrPtr":null, "Bytes":null,
   106  					"Time":"0001-01-01T00:00:00Z",
   107  					"Car":null, "Boat":{"Sail":false}, "Vehicles":null, "Child":null
   108  				},
   109  				"private": "foo", "unknown": "bar"
   110  			}`,
   111  			Struct{
   112  				Bool: true, Float64: 3.14, Int32: 32, Int64: 64, Int64Ptr: &i64,
   113  				String: "foo", StringPtrPtr: &strPtr, Bytes: []byte{1, 2, 3},
   114  				Time: time.Date(2020, 6, 2, 16, 5, 13, 4346374, time.UTC),
   115  				Car:  &Car{Wheels: 4}, Boat: Boat{Sail: true}, Vehicles: []Vehicle{
   116  					Vehicle(&Car{Wheels: 4}),
   117  					Vehicle(Boat{Sail: true}),
   118  				},
   119  				Child: &Struct{Bool: false, String: "child"},
   120  			},
   121  			false,
   122  		},
   123  		"struct key into vehicle": {`{"Vehicles":[
   124  			{"type":"vehicle/car","value":{"Wheels":4}},
   125  			{"type":"key/public","value":"MTIzNDU2Nzg="}
   126  		]}`, Struct{}, true},
   127  		"struct ptr null":  {`null`, structNil, false},
   128  		"custom value":     {`{"Value":"foo"}`, CustomValue{}, false},
   129  		"custom ptr":       {`"foo"`, &CustomPtr{Value: "custom"}, false},
   130  		"custom ptr value": {`"foo"`, CustomPtr{Value: "custom"}, false},
   131  		"invalid type":     {`"foo"`, Struct{}, true},
   132  	}
   133  	for name, tc := range testcases {
   134  		tc := tc
   135  		t.Run(name, func(t *testing.T) {
   136  			// Create a target variable as a pointer to the zero value of the tc.value type,
   137  			// and wrap it in an empty interface. Decode into that interface.
   138  			target := reflect.New(reflect.TypeOf(tc.value)).Interface()
   139  			err := json.Unmarshal([]byte(tc.json), target)
   140  			if tc.err {
   141  				require.Error(t, err)
   142  				return
   143  			}
   144  			require.NoError(t, err)
   145  
   146  			// Unwrap the target pointer and get the value behind the interface.
   147  			actual := reflect.ValueOf(target).Elem().Interface()
   148  			assert.Equal(t, tc.value, actual)
   149  		})
   150  	}
   151  }