...

Source file src/github.com/thoas/go-funk/assign_test.go

Documentation: github.com/thoas/go-funk

     1  package funk
     2  
     3  import (
     4  	"database/sql"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  )
    10  
    11  func TestSet_EmptyPath(t *testing.T) {
    12  	// it is supposed to change the var passed in
    13  	var testCases = []struct {
    14  		// will use path = ""
    15  		Original interface{}
    16  		SetVal   interface{}
    17  	}{
    18  		// int
    19  		{
    20  			Original: 100,
    21  			SetVal:   1,
    22  		},
    23  		// string
    24  		{
    25  			Original: "",
    26  			SetVal:   "val",
    27  		},
    28  		// struct
    29  		{
    30  			Original: Bar{Name: "bar"},
    31  			SetVal:   Bar{Name: "val"},
    32  		},
    33  		// slice
    34  		{
    35  			Original: []Bar{{Name: "bar"}},
    36  			SetVal:   []Bar{{Name: "val1"}, {Name: "val2"}},
    37  		},
    38  	}
    39  
    40  	for idx, tc := range testCases {
    41  		t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) {
    42  			is := assert.New(t)
    43  			// use empty path
    44  			// must take the addr of the variable to be set
    45  			err := Set(&tc.Original, tc.SetVal, "")
    46  			is.NoError(err)
    47  			is.Equal(tc.Original, tc.SetVal) // original should be set to SetVal
    48  		})
    49  	}
    50  }
    51  
    52  func TestSet_StructBasicOneLevel(t *testing.T) {
    53  	is := assert.New(t)
    54  	// we set field one by one of baz with expected value
    55  	baz := Foo{
    56  		ID:        100,
    57  		FirstName: "firstname",
    58  		LastName:  "lastname",
    59  		Age:       23,
    60  		Bar:       &Bar{Name: "bar"},
    61  		Bars:      []*Bar{{Name: "1"}},
    62  		EmptyValue: sql.NullInt64{
    63  			Int64: 64,
    64  			Valid: false,
    65  		},
    66  	}
    67  	expected := Foo{
    68  		ID:        1,
    69  		FirstName: "firstname1",
    70  		LastName:  "lastname1",
    71  		Age:       24,
    72  		Bar:       &Bar{Name: "b1", Bar: &Bar{Name: "b2"}},
    73  		Bars:      []*Bar{{Name: "1"}, {Name: "2"}},
    74  		EmptyValue: sql.NullInt64{
    75  			Int64: 11,
    76  			Valid: true,
    77  		},
    78  	}
    79  	err := Set(&baz, 1, "ID")
    80  	is.NoError(err)
    81  	err = Set(&baz, expected.FirstName, "FirstName")
    82  	is.NoError(err)
    83  	err = Set(&baz, expected.LastName, "LastName")
    84  	is.NoError(err)
    85  	err = Set(&baz, expected.Age, "Age")
    86  	is.NoError(err)
    87  	err = Set(&baz, expected.Bar, "Bar")
    88  	is.NoError(err)
    89  	err = Set(&baz, expected.Bars, "Bars")
    90  	is.NoError(err)
    91  	err = Set(&baz, expected.EmptyValue, "EmptyValue")
    92  	is.NoError(err)
    93  	is.Equal(baz, expected)
    94  }
    95  
    96  func TestSetStruct_MultiLevels(t *testing.T) {
    97  
    98  	var testCases = []struct {
    99  		Original Bar
   100  		Path     string
   101  		SetVal   interface{}
   102  		Expected Bar
   103  	}{
   104  		// Set slice in 4th level
   105  		{
   106  			Original: Bar{
   107  				Name: "1", // name indicates level
   108  				Bar: &Bar{
   109  					Name: "2",
   110  					Bars: []*Bar{
   111  						{Name: "3-1", Bars: []*Bar{{Name: "4-1"}, {Name: "4-2"}, {Name: "4-3"}}},
   112  						{Name: "3-2", Bars: []*Bar{{Name: "4-1"}, {Name: "4-2"}}},
   113  					},
   114  				},
   115  			},
   116  			Path:   "Bar.Bars.Bars.Name",
   117  			SetVal: "val",
   118  			Expected: Bar{
   119  				Name: "1",
   120  				Bar: &Bar{
   121  					Name: "2",
   122  					Bars: []*Bar{
   123  						{Name: "3-1", Bars: []*Bar{{Name: "val"}, {Name: "val"}, {Name: "val"}}},
   124  						{Name: "3-2", Bars: []*Bar{{Name: "val"}, {Name: "val"}}},
   125  					},
   126  				},
   127  			},
   128  		},
   129  		// Set multilevel uninitialized ptr
   130  		{
   131  			Original: Bar{
   132  				Name: "1", // name indicates level
   133  				Bar:  nil,
   134  			},
   135  			Path:   "Bar.Bar.Bar.Name",
   136  			SetVal: "val",
   137  			Expected: Bar{
   138  				Name: "1",
   139  				Bar: &Bar{
   140  					Name: "", // level 2
   141  					Bar: &Bar{
   142  						Bar: &Bar{
   143  							Name: "val", //level 3
   144  						},
   145  					},
   146  				},
   147  			},
   148  		},
   149  		// mix of uninitialized ptr and slices
   150  		{
   151  			Original: Bar{
   152  				Name: "1", // name indicates level
   153  				Bar: &Bar{
   154  					Name: "2",
   155  					Bars: []*Bar{
   156  						{Name: "3-1", Bars: []*Bar{{Name: "4-1"}, {Name: "4-2"}, {Name: "4-3"}}},
   157  						{Name: "3-2", Bars: []*Bar{{Name: "4-1"}, {Name: "4-2"}}},
   158  					},
   159  				},
   160  			},
   161  			Path:   "Bar.Bars.Bars.Bar.Name",
   162  			SetVal: "val",
   163  			Expected: Bar{
   164  				Name: "1", // name indicates level
   165  				Bar: &Bar{
   166  					Name: "2",
   167  					Bars: []*Bar{
   168  						{Name: "3-1", Bars: []*Bar{{Name: "4-1", Bar: &Bar{Name: "val"}},
   169  							{Name: "4-2", Bar: &Bar{Name: "val"}}, {Name: "4-3", Bar: &Bar{Name: "val"}}}},
   170  						{Name: "3-2", Bars: []*Bar{{Name: "4-1", Bar: &Bar{Name: "val"}}, {Name: "4-2", Bar: &Bar{Name: "val"}}}},
   171  					},
   172  				},
   173  			},
   174  		},
   175  	}
   176  
   177  	for idx, tc := range testCases {
   178  		t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) {
   179  			is := assert.New(t)
   180  			// take the addr and then pass it in
   181  			err := Set(&tc.Original, tc.SetVal, tc.Path)
   182  			is.NoError(err)
   183  			is.Equal(tc.Expected, tc.Original)
   184  		})
   185  	}
   186  }
   187  
   188  func TestSet_StructWithCyclicStruct(t *testing.T) {
   189  	is := assert.New(t)
   190  
   191  	testBar := Bar{
   192  		Name: "testBar",
   193  		Bar:  nil,
   194  	}
   195  	testBar.Bar = &testBar
   196  
   197  	err := Set(&testBar, "val", "Bar.Bar.Name")
   198  	is.NoError(err)
   199  	is.Equal("val", testBar.Name)
   200  }
   201  
   202  func TestSet_StructWithFieldNotInitialized(t *testing.T) {
   203  	is := assert.New(t)
   204  	myFoo := &Foo{
   205  		Bar: nil, // we will try to set bar's field
   206  	}
   207  	err := Set(myFoo, "name", "Bar.Name")
   208  	is.NoError(err)
   209  	is.Equal("name", myFoo.Bar.Name)
   210  }
   211  
   212  func TestSet_SlicePassByPtr(t *testing.T) {
   213  
   214  	var testCases = []struct {
   215  		Original interface{} // slice or array
   216  		Path     string
   217  		SetVal   interface{}
   218  		Expected interface{}
   219  	}{
   220  		// Set Slice itself
   221  		{
   222  			Original: []*Bar{},
   223  			Path:     "", // empty path means set the passed in ptr itself
   224  			SetVal:   []*Bar{{Name: "bar"}},
   225  			Expected: []*Bar{{Name: "bar"}},
   226  		},
   227  		// empty slice
   228  		{
   229  			Original: []*Bar{},
   230  			Path:     "Name",
   231  			SetVal:   "val",
   232  			Expected: []*Bar{},
   233  		},
   234  		// slice of ptr
   235  		{
   236  			Original: []*Bar{{Name: "a"}, {Name: "b"}},
   237  			Path:     "Name",
   238  			SetVal:   "val",
   239  			Expected: []*Bar{{Name: "val"}, {Name: "val"}},
   240  		},
   241  		// slice of struct
   242  		{
   243  			Original: []Bar{{Name: "a"}, {Name: "b"}},
   244  			Path:     "Name",
   245  			SetVal:   "val",
   246  			Expected: []Bar{{Name: "val"}, {Name: "val"}},
   247  		},
   248  		// slice of empty ptr
   249  		{
   250  			Original: []*Bar{nil, nil},
   251  			Path:     "Name",
   252  			SetVal:   "val",
   253  			Expected: []*Bar{{Name: "val"}, {Name: "val"}},
   254  		},
   255  		// mix of init ptr and nil ptr
   256  		{
   257  			Original: []*Bar{{Name: "bar"}, nil},
   258  			Path:     "Name",
   259  			SetVal:   "val",
   260  			Expected: []*Bar{{Name: "val"}, {Name: "val"}},
   261  		},
   262  	}
   263  
   264  	for idx, tc := range testCases {
   265  		t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) {
   266  			is := assert.New(t)
   267  			// take the addr and then pass it in
   268  			err := Set(&tc.Original, tc.SetVal, tc.Path)
   269  			is.NoError(err)
   270  			is.Equal(tc.Expected, tc.Original)
   271  		})
   272  	}
   273  }
   274  
   275  func TestSet_SlicePassDirectly(t *testing.T) {
   276  	var testCases = []struct {
   277  		Original interface{} // slice or array
   278  		Path     string
   279  		SetVal   interface{}
   280  		Expected interface{}
   281  	}{
   282  		// Set Slice itself does not work here since not passing by ptr
   283  
   284  		// empty slice
   285  		{
   286  			Original: []*Bar{},
   287  			Path:     "Name",
   288  			SetVal:   "val",
   289  			Expected: []*Bar{},
   290  		},
   291  		// slice of ptr
   292  		{
   293  			Original: []*Bar{{Name: "a"}, {Name: "b"}},
   294  			Path:     "Name",
   295  			SetVal:   "val",
   296  			Expected: []*Bar{{Name: "val"}, {Name: "val"}},
   297  		},
   298  		// slice of struct
   299  		{
   300  			Original: []Bar{{Name: "a"}, {Name: "b"}},
   301  			Path:     "Name",
   302  			SetVal:   "val",
   303  			Expected: []Bar{{Name: "val"}, {Name: "val"}},
   304  		},
   305  		// slice of empty ptr
   306  		{
   307  			Original: []*Bar{nil, nil},
   308  			Path:     "Name",
   309  			SetVal:   "val",
   310  			Expected: []*Bar{{Name: "val"}, {Name: "val"}},
   311  		},
   312  		// mix of init ptr and nil ptr
   313  		{
   314  			Original: []*Bar{{Name: "bar"}, nil},
   315  			Path:     "Name",
   316  			SetVal:   "val",
   317  			Expected: []*Bar{{Name: "val"}, {Name: "val"}},
   318  		},
   319  	}
   320  
   321  	for idx, tc := range testCases {
   322  		t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) {
   323  			is := assert.New(t)
   324  			// Not take ptr, pass directly
   325  			err := Set(tc.Original, tc.SetVal, tc.Path)
   326  			is.NoError(err)
   327  			is.Equal(tc.Expected, tc.Original)
   328  		})
   329  	}
   330  }
   331  
   332  func TestInterface(t *testing.T) {
   333  
   334  	var testCases = []struct {
   335  		OriginalFoo Foo
   336  		Path        string
   337  		SetVal      interface{}
   338  		ExpectedFoo Foo
   339  	}{
   340  		// set string field
   341  		{
   342  			Foo{FirstName: ""},
   343  			"FirstName",
   344  			"hi",
   345  			Foo{FirstName: "hi"},
   346  		},
   347  		// set interface{} field
   348  		{
   349  			Foo{FirstName: "", GeneralInterface: nil},
   350  			"GeneralInterface",
   351  			"str",
   352  			Foo{FirstName: "", GeneralInterface: "str"},
   353  		},
   354  		// set field of the interface{} field
   355  		// Note: set uninitialized interface{} should fail
   356  		// Note: interface of struct (not ptr to struct) should fail
   357  		{
   358  			Foo{FirstName: "", GeneralInterface: &Foo{FirstName: ""}}, // if Foo is not ptr this will fail
   359  			"GeneralInterface.FirstName",
   360  			"foo",
   361  			Foo{FirstName: "", GeneralInterface: &Foo{FirstName: "foo"}},
   362  		},
   363  		// interface two level
   364  		{
   365  			Foo{FirstName: "", GeneralInterface: &Foo{GeneralInterface: nil}},
   366  			"GeneralInterface.GeneralInterface",
   367  			"val",
   368  			Foo{FirstName: "", GeneralInterface: &Foo{GeneralInterface: "val"}},
   369  		},
   370  	}
   371  
   372  	for idx, tc := range testCases {
   373  		t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) {
   374  			is := assert.New(t)
   375  
   376  			err := Set(&tc.OriginalFoo, tc.SetVal, tc.Path)
   377  			is.NoError(err)
   378  			is.Equal(tc.ExpectedFoo, tc.OriginalFoo)
   379  		})
   380  	}
   381  
   382  }
   383  
   384  func TestSet_ErrorCaces(t *testing.T) {
   385  
   386  	var testCases = []struct {
   387  		OriginalFoo Foo
   388  		Path        string
   389  		SetVal      interface{}
   390  	}{
   391  		// uninit interface
   392  		// Itf is not initialized so Set cannot properly allocate type
   393  		{
   394  			Foo{BarInterface: nil},
   395  			"BarInterface.Name",
   396  			"val",
   397  		},
   398  		{
   399  			Foo{GeneralInterface: &Foo{BarInterface: nil}},
   400  			"GeneralInterface.BarInterface.Name",
   401  			"val",
   402  		},
   403  		// type mismatch
   404  		{
   405  			Foo{FirstName: ""},
   406  			"FirstName",
   407  			20,
   408  		},
   409  	}
   410  
   411  	for idx, tc := range testCases {
   412  		t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) {
   413  			is := assert.New(t)
   414  
   415  			err := Set(&tc.OriginalFoo, tc.SetVal, tc.Path)
   416  			is.Error(err)
   417  		})
   418  	}
   419  
   420  	t.Run("not pointer", func(t *testing.T) {
   421  		is := assert.New(t)
   422  		baz := Bar{Name: "dummy"}
   423  		err := Set(baz, Bar{Name: "dummy2"}, "Name")
   424  		is.Error(err)
   425  	})
   426  
   427  	t.Run("Unexported field", func(t *testing.T) {
   428  		is := assert.New(t)
   429  		s := struct {
   430  			name string
   431  		}{name: "dummy"}
   432  		err := Set(&s, s, "name")
   433  		is.Error(err)
   434  	})
   435  }
   436  
   437  func TestMustSet_Basic(t *testing.T) {
   438  	t.Run("Variable", func(t *testing.T) {
   439  		is := assert.New(t)
   440  		s := 1
   441  		MustSet(&s, 2, "")
   442  		is.Equal(2, s)
   443  	})
   444  
   445  	t.Run("Struct", func(t *testing.T) {
   446  		is := assert.New(t)
   447  		s := Bar{Name: "a"}
   448  		MustSet(&s, "b", "Name")
   449  		is.Equal("b", s.Name)
   450  	})
   451  }
   452  
   453  // Examples
   454  
   455  func ExampleSet() {
   456  
   457  	var bar Bar = Bar{
   458  		Name: "level-0",
   459  		Bar: &Bar{
   460  			Name: "level-1",
   461  			Bars: []*Bar{
   462  				{Name: "level2-1"},
   463  				{Name: "level2-2"},
   464  			},
   465  		},
   466  	}
   467  
   468  	_ = Set(&bar, "level-0-new", "Name")
   469  	fmt.Println(bar.Name)
   470  
   471  	// discard error use MustSet
   472  	MustSet(&bar, "level-1-new", "Bar.Name")
   473  	fmt.Println(bar.Bar.Name)
   474  
   475  	_ = Set(&bar, "level-2-new", "Bar.Bars.Name")
   476  	fmt.Println(bar.Bar.Bars[0].Name)
   477  	fmt.Println(bar.Bar.Bars[1].Name)
   478  
   479  	// Output:
   480  	// level-0-new
   481  	// level-1-new
   482  	// level-2-new
   483  	// level-2-new
   484  }
   485  

View as plain text