github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/mapstruct/hooks_test.go (about)

     1  package mapstruct
     2  
     3  import (
     4  	"errors"
     5  	"math/big"
     6  	"net"
     7  	"reflect"
     8  	"testing"
     9  	"time"
    10  )
    11  
    12  func TestComposeDecodeHookFunc(t *testing.T) {
    13  	f1 := func(
    14  		f reflect.Kind,
    15  		t reflect.Kind,
    16  		data interface{},
    17  	) (interface{}, error) {
    18  		return data.(string) + "foo", nil
    19  	}
    20  
    21  	f2 := func(
    22  		f reflect.Kind,
    23  		t reflect.Kind,
    24  		data interface{},
    25  	) (interface{}, error) {
    26  		return data.(string) + "bar", nil
    27  	}
    28  
    29  	f := ComposeDecodeHookFunc(f1, f2)
    30  
    31  	result, err := DecodeHookExec(
    32  		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
    33  	if err != nil {
    34  		t.Fatalf("bad: %s", err)
    35  	}
    36  	if result.(string) != "foobar" {
    37  		t.Fatalf("bad: %#v", result)
    38  	}
    39  }
    40  
    41  func TestComposeDecodeHookFunc_err(t *testing.T) {
    42  	f1 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
    43  		return nil, errors.New("foo")
    44  	}
    45  
    46  	f2 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
    47  		panic("NOPE")
    48  	}
    49  
    50  	f := ComposeDecodeHookFunc(f1, f2)
    51  
    52  	_, err := DecodeHookExec(
    53  		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
    54  	if err.Error() != "foo" {
    55  		t.Fatalf("bad: %s", err)
    56  	}
    57  }
    58  
    59  func TestComposeDecodeHookFunc_kinds(t *testing.T) {
    60  	var f2From reflect.Kind
    61  
    62  	f1 := func(
    63  		f reflect.Kind,
    64  		t reflect.Kind,
    65  		data interface{},
    66  	) (interface{}, error) {
    67  		return int(42), nil
    68  	}
    69  
    70  	f2 := func(
    71  		f reflect.Kind,
    72  		t reflect.Kind,
    73  		data interface{},
    74  	) (interface{}, error) {
    75  		f2From = f
    76  		return data, nil
    77  	}
    78  
    79  	f := ComposeDecodeHookFunc(f1, f2)
    80  
    81  	_, err := DecodeHookExec(
    82  		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
    83  	if err != nil {
    84  		t.Fatalf("bad: %s", err)
    85  	}
    86  	if f2From != reflect.Int {
    87  		t.Fatalf("bad: %#v", f2From)
    88  	}
    89  }
    90  
    91  func TestStringToSliceHookFunc(t *testing.T) {
    92  	f := StringToSliceHookFunc(",")
    93  
    94  	strValue := reflect.ValueOf("42")
    95  	sliceValue := reflect.ValueOf([]byte("42"))
    96  	cases := []struct {
    97  		f, t   reflect.Value
    98  		result interface{}
    99  		err    bool
   100  	}{
   101  		{sliceValue, sliceValue, []byte("42"), false},
   102  		{strValue, strValue, "42", false},
   103  		{
   104  			reflect.ValueOf("foo,bar,baz"),
   105  			sliceValue,
   106  			[]string{"foo", "bar", "baz"},
   107  			false,
   108  		},
   109  		{
   110  			reflect.ValueOf(""),
   111  			sliceValue,
   112  			[]string{},
   113  			false,
   114  		},
   115  	}
   116  
   117  	for i, tc := range cases {
   118  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   119  		if tc.err != (err != nil) {
   120  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   121  		}
   122  		if !reflect.DeepEqual(actual, tc.result) {
   123  			t.Fatalf(
   124  				"case %d: expected %#v, got %#v",
   125  				i, tc.result, actual)
   126  		}
   127  	}
   128  }
   129  
   130  func TestStringToTimeDurationHookFunc(t *testing.T) {
   131  	f := StringToTimeDurationHookFunc()
   132  
   133  	timeValue := reflect.ValueOf(time.Duration(5))
   134  	strValue := reflect.ValueOf("")
   135  	cases := []struct {
   136  		f, t   reflect.Value
   137  		result interface{}
   138  		err    bool
   139  	}{
   140  		{reflect.ValueOf("5s"), timeValue, 5 * time.Second, false},
   141  		{reflect.ValueOf("5"), timeValue, time.Duration(0), true},
   142  		{reflect.ValueOf("5"), strValue, "5", false},
   143  	}
   144  
   145  	for i, tc := range cases {
   146  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   147  		if tc.err != (err != nil) {
   148  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   149  		}
   150  		if !reflect.DeepEqual(actual, tc.result) {
   151  			t.Fatalf(
   152  				"case %d: expected %#v, got %#v",
   153  				i, tc.result, actual)
   154  		}
   155  	}
   156  }
   157  
   158  func TestStringToTimeHookFunc(t *testing.T) {
   159  	strValue := reflect.ValueOf("5")
   160  	timeValue := reflect.ValueOf(time.Time{})
   161  	cases := []struct {
   162  		f, t   reflect.Value
   163  		layout string
   164  		result interface{}
   165  		err    bool
   166  	}{
   167  		{
   168  			reflect.ValueOf("2006-01-02T15:04:05Z"), timeValue, time.RFC3339,
   169  			time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false,
   170  		},
   171  		{strValue, timeValue, time.RFC3339, time.Time{}, true},
   172  		{strValue, strValue, time.RFC3339, "5", false},
   173  	}
   174  
   175  	for i, tc := range cases {
   176  		f := StringToTimeHookFunc(tc.layout)
   177  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   178  		if tc.err != (err != nil) {
   179  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   180  		}
   181  		if !reflect.DeepEqual(actual, tc.result) {
   182  			t.Fatalf(
   183  				"case %d: expected %#v, got %#v",
   184  				i, tc.result, actual)
   185  		}
   186  	}
   187  }
   188  
   189  func TestStringToIPHookFunc(t *testing.T) {
   190  	strValue := reflect.ValueOf("5")
   191  	ipValue := reflect.ValueOf(net.IP{})
   192  	cases := []struct {
   193  		f, t   reflect.Value
   194  		result interface{}
   195  		err    bool
   196  	}{
   197  		{
   198  			reflect.ValueOf("1.2.3.4"), ipValue,
   199  			net.IPv4(0x01, 0x02, 0x03, 0x04), false,
   200  		},
   201  		{strValue, ipValue, net.IP{}, true},
   202  		{strValue, strValue, "5", false},
   203  	}
   204  
   205  	for i, tc := range cases {
   206  		f := StringToIPHookFunc()
   207  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   208  		if tc.err != (err != nil) {
   209  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   210  		}
   211  		if !reflect.DeepEqual(actual, tc.result) {
   212  			t.Fatalf(
   213  				"case %d: expected %#v, got %#v",
   214  				i, tc.result, actual)
   215  		}
   216  	}
   217  }
   218  
   219  func TestStringToIPNetHookFunc(t *testing.T) {
   220  	strValue := reflect.ValueOf("5")
   221  	ipNetValue := reflect.ValueOf(net.IPNet{})
   222  	var nilNet *net.IPNet = nil
   223  
   224  	cases := []struct {
   225  		f, t   reflect.Value
   226  		result interface{}
   227  		err    bool
   228  	}{
   229  		{
   230  			reflect.ValueOf("1.2.3.4/24"), ipNetValue,
   231  			&net.IPNet{
   232  				IP:   net.IP{0x01, 0x02, 0x03, 0x00},
   233  				Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00),
   234  			}, false,
   235  		},
   236  		{strValue, ipNetValue, nilNet, true},
   237  		{strValue, strValue, "5", false},
   238  	}
   239  
   240  	for i, tc := range cases {
   241  		f := StringToIPNetHookFunc()
   242  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   243  		if tc.err != (err != nil) {
   244  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   245  		}
   246  		if !reflect.DeepEqual(actual, tc.result) {
   247  			t.Fatalf(
   248  				"case %d: expected %#v, got %#v",
   249  				i, tc.result, actual)
   250  		}
   251  	}
   252  }
   253  
   254  func TestWeaklyTypedHook(t *testing.T) {
   255  	var f HookFunc = WeaklyTypedHook
   256  
   257  	strValue := reflect.ValueOf("")
   258  	cases := []struct {
   259  		f, t   reflect.Value
   260  		result interface{}
   261  		err    bool
   262  	}{
   263  		// TO STRING
   264  		{
   265  			reflect.ValueOf(false),
   266  			strValue,
   267  			"0",
   268  			false,
   269  		},
   270  
   271  		{
   272  			reflect.ValueOf(true),
   273  			strValue,
   274  			"1",
   275  			false,
   276  		},
   277  
   278  		{
   279  			reflect.ValueOf(float32(7)),
   280  			strValue,
   281  			"7",
   282  			false,
   283  		},
   284  
   285  		{
   286  			reflect.ValueOf(int(7)),
   287  			strValue,
   288  			"7",
   289  			false,
   290  		},
   291  
   292  		{
   293  			reflect.ValueOf([]uint8("foo")),
   294  			strValue,
   295  			"foo",
   296  			false,
   297  		},
   298  
   299  		{
   300  			reflect.ValueOf(uint(7)),
   301  			strValue,
   302  			"7",
   303  			false,
   304  		},
   305  	}
   306  
   307  	for i, tc := range cases {
   308  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   309  		if tc.err != (err != nil) {
   310  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   311  		}
   312  		if !reflect.DeepEqual(actual, tc.result) {
   313  			t.Fatalf(
   314  				"case %d: expected %#v, got %#v",
   315  				i, tc.result, actual)
   316  		}
   317  	}
   318  }
   319  
   320  func TestStructToMapHookFuncTabled(t *testing.T) {
   321  	var f HookFunc = RecursiveStructToMapHookFunc()
   322  
   323  	type b struct {
   324  		TestKey string
   325  	}
   326  
   327  	type a struct {
   328  		Sub b
   329  	}
   330  
   331  	testStruct := a{
   332  		Sub: b{
   333  			TestKey: "testval",
   334  		},
   335  	}
   336  
   337  	testMap := map[string]interface{}{
   338  		"Sub": map[string]interface{}{
   339  			"TestKey": "testval",
   340  		},
   341  	}
   342  
   343  	cases := []struct {
   344  		name     string
   345  		receiver interface{}
   346  		input    interface{}
   347  		expected interface{}
   348  		err      bool
   349  	}{
   350  		{
   351  			"map receiver",
   352  			func() interface{} {
   353  				var res map[string]interface{}
   354  				return &res
   355  			}(),
   356  			testStruct,
   357  			&testMap,
   358  			false,
   359  		},
   360  		{
   361  			"interface receiver",
   362  			func() interface{} {
   363  				var res interface{}
   364  				return &res
   365  			}(),
   366  			testStruct,
   367  			func() interface{} {
   368  				var exp interface{} = testMap
   369  				return &exp
   370  			}(),
   371  			false,
   372  		},
   373  		{
   374  			"slice receiver errors",
   375  			func() interface{} {
   376  				var res []string
   377  				return &res
   378  			}(),
   379  			testStruct,
   380  			new([]string),
   381  			true,
   382  		},
   383  		{
   384  			"slice to slice - no change",
   385  			func() interface{} {
   386  				var res []string
   387  				return &res
   388  			}(),
   389  			[]string{"a", "b"},
   390  			&[]string{"a", "b"},
   391  			false,
   392  		},
   393  		{
   394  			"string to string - no change",
   395  			func() interface{} {
   396  				var res string
   397  				return &res
   398  			}(),
   399  			"test",
   400  			func() *string {
   401  				s := "test"
   402  				return &s
   403  			}(),
   404  			false,
   405  		},
   406  	}
   407  
   408  	for _, tc := range cases {
   409  		t.Run(tc.name, func(t *testing.T) {
   410  			cfg := &Config{
   411  				Hook:   f,
   412  				Result: tc.receiver,
   413  			}
   414  
   415  			d, err := NewDecoder(cfg)
   416  			if err != nil {
   417  				t.Fatalf("unexpected err %#v", err)
   418  			}
   419  
   420  			err = d.Decode(tc.input)
   421  			if tc.err != (err != nil) {
   422  				t.Fatalf("expected err %#v", err)
   423  			}
   424  
   425  			if !reflect.DeepEqual(tc.expected, tc.receiver) {
   426  				t.Fatalf("expected %#v, got %#v",
   427  					tc.expected, tc.receiver)
   428  			}
   429  		})
   430  	}
   431  }
   432  
   433  func TestTextUnmarshallerHookFunc(t *testing.T) {
   434  	cases := []struct {
   435  		f, t   reflect.Value
   436  		result interface{}
   437  		err    bool
   438  	}{
   439  		{reflect.ValueOf("42"), reflect.ValueOf(big.Int{}), big.NewInt(42), false},
   440  		{reflect.ValueOf("invalid"), reflect.ValueOf(big.Int{}), nil, true},
   441  		{reflect.ValueOf("5"), reflect.ValueOf("5"), "5", false},
   442  	}
   443  
   444  	for i, tc := range cases {
   445  		f := TextUnmarshallerHookFunc()
   446  		actual, err := DecodeHookExec(f, tc.f, tc.t)
   447  		if tc.err != (err != nil) {
   448  			t.Fatalf("case %d: expected err %#v", i, tc.err)
   449  		}
   450  		if !reflect.DeepEqual(actual, tc.result) {
   451  			t.Fatalf(
   452  				"case %d: expected %#v, got %#v",
   453  				i, tc.result, actual)
   454  		}
   455  	}
   456  }