1package funk 2 3import ( 4 "database/sql" 5 "fmt" 6 "reflect" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11) 12 13func TestMap(t *testing.T) { 14 is := assert.New(t) 15 16 r := Map([]int{1, 2, 3, 4}, func(x int) string { 17 return "Hello" 18 }) 19 20 result, ok := r.([]string) 21 22 is.True(ok) 23 is.Equal(len(result), 4) 24 25 r = Map([]int{1, 2, 3, 4}, func(x int) (int, int) { 26 return x, x 27 }) 28 29 resultType := reflect.TypeOf(r) 30 31 is.True(resultType.Kind() == reflect.Map) 32 is.True(resultType.Key().Kind() == reflect.Int) 33 is.True(resultType.Elem().Kind() == reflect.Int) 34 35 mapping := map[int]string{ 36 1: "Florent", 37 2: "Gilles", 38 } 39 40 r = Map(mapping, func(k int, v string) int { 41 return k 42 }) 43 44 is.True(reflect.TypeOf(r).Kind() == reflect.Slice) 45 is.True(reflect.TypeOf(r).Elem().Kind() == reflect.Int) 46 47 r = Map(mapping, func(k int, v string) (string, string) { 48 return fmt.Sprintf("%d", k), v 49 }) 50 51 resultType = reflect.TypeOf(r) 52 53 is.True(resultType.Kind() == reflect.Map) 54 is.True(resultType.Key().Kind() == reflect.String) 55 is.True(resultType.Elem().Kind() == reflect.String) 56} 57 58func TestFlatMap(t *testing.T) { 59 60 is := assert.New(t) 61 62 x := reflect.Value{}.IsValid() 63 fmt.Println(x) 64 65 r := FlatMap([][]int{{1}, {2}, {3}, {4}}, func(x []int) []int { 66 return x 67 }) 68 69 result, ok := r.([]int) 70 71 is.True(ok) 72 is.ElementsMatch(result, []int{1, 2, 3, 4}) 73 74 mapping := map[string][]int{ 75 "a": {1}, 76 "b": {2}, 77 } 78 79 r = FlatMap(mapping, func(k string, v []int) []int { 80 return v 81 }) 82 83 result, ok = r.([]int) 84 85 is.True(ok) 86 is.ElementsMatch(result, []int{1, 2}) 87} 88 89func TestToMap(t *testing.T) { 90 is := assert.New(t) 91 92 f := &Foo{ 93 ID: 1, 94 FirstName: "Dark", 95 LastName: "Vador", 96 Age: 30, 97 Bar: &Bar{ 98 Name: "Test", 99 }, 100 } 101 102 results := []*Foo{f} 103 104 instanceMap := ToMap(results, "ID") 105 106 is.True(reflect.TypeOf(instanceMap).Kind() == reflect.Map) 107 108 mapping, ok := instanceMap.(map[int]*Foo) 109 110 is.True(ok) 111 112 for _, result := range results { 113 item, ok := mapping[result.ID] 114 115 is.True(ok) 116 is.True(reflect.TypeOf(item).Kind() == reflect.Ptr) 117 is.True(reflect.TypeOf(item).Elem().Kind() == reflect.Struct) 118 119 is.Equal(item.ID, result.ID) 120 } 121} 122 123func TestChunk(t *testing.T) { 124 is := assert.New(t) 125 126 results := Chunk([]int{0, 1, 2, 3, 4}, 2).([][]int) 127 128 is.Len(results, 3) 129 is.Len(results[0], 2) 130 is.Len(results[1], 2) 131 is.Len(results[2], 1) 132 133 is.Len(Chunk([]int{}, 2), 0) 134 is.Len(Chunk([]int{1}, 2), 1) 135 is.Len(Chunk([]int{1, 2, 3}, 0), 3) 136} 137 138func TestFlatten(t *testing.T) { 139 is := assert.New(t) 140 141 is.Equal(Flatten([][][]int{{{1, 2}}, {{3, 4}}}), [][]int{{1, 2}, {3, 4}}) 142} 143 144func TestFlattenDeep(t *testing.T) { 145 is := assert.New(t) 146 147 is.Equal(FlattenDeep([][][]int{{{1, 2}}, {{3, 4}}}), []int{1, 2, 3, 4}) 148} 149 150func TestShuffle(t *testing.T) { 151 initial := []int{0, 1, 2, 3, 4} 152 153 results := Shuffle(initial) 154 155 is := assert.New(t) 156 157 is.Len(results, 5) 158 159 for _, entry := range initial { 160 is.True(Contains(results, entry)) 161 } 162} 163 164func TestReverse(t *testing.T) { 165 results := Reverse([]int{0, 1, 2, 3, 4}) 166 167 is := assert.New(t) 168 169 is.Equal(Reverse("abcdefg"), "gfedcba") 170 is.Len(results, 5) 171 172 is.Equal(results, []int{4, 3, 2, 1, 0}) 173} 174 175func TestUniq(t *testing.T) { 176 is := assert.New(t) 177 178 results := Uniq([]int{0, 1, 1, 2, 3, 0, 0, 12}) 179 is.Len(results, 5) 180 is.Equal(results, []int{0, 1, 2, 3, 12}) 181 182 results = Uniq([]string{"foo", "bar", "foo", "bar", "bar"}) 183 is.Len(results, 2) 184 is.Equal(results, []string{"foo", "bar"}) 185} 186 187func TestConvertSlice(t *testing.T) { 188 instances := []*Foo{foo, foo2} 189 190 var raw []Model 191 192 ConvertSlice(instances, &raw) 193 194 is := assert.New(t) 195 196 is.Len(raw, len(instances)) 197} 198 199func TestDrop(t *testing.T) { 200 results := Drop([]int{0, 1, 1, 2, 3, 0, 0, 12}, 3) 201 202 is := assert.New(t) 203 204 is.Len(results, 5) 205 206 is.Equal([]int{2, 3, 0, 0, 12}, results) 207} 208 209func TestPrune(t *testing.T) { 210 211 var testCases = []struct { 212 OriginalFoo *Foo 213 Paths []string 214 ExpectedFoo *Foo 215 }{ 216 { 217 foo, 218 []string{"FirstName"}, 219 &Foo{ 220 FirstName: foo.FirstName, 221 }, 222 }, 223 { 224 foo, 225 []string{"FirstName", "ID"}, 226 &Foo{ 227 FirstName: foo.FirstName, 228 ID: foo.ID, 229 }, 230 }, 231 { 232 foo, 233 []string{"EmptyValue.Int64"}, 234 &Foo{ 235 EmptyValue: sql.NullInt64{ 236 Int64: foo.EmptyValue.Int64, 237 }, 238 }, 239 }, 240 { 241 foo, 242 []string{"FirstName", "ID", "EmptyValue.Int64"}, 243 &Foo{ 244 FirstName: foo.FirstName, 245 ID: foo.ID, 246 EmptyValue: sql.NullInt64{ 247 Int64: foo.EmptyValue.Int64, 248 }, 249 }, 250 }, 251 { 252 foo, 253 []string{"FirstName", "ID", "EmptyValue.Int64"}, 254 &Foo{ 255 FirstName: foo.FirstName, 256 ID: foo.ID, 257 EmptyValue: sql.NullInt64{ 258 Int64: foo.EmptyValue.Int64, 259 }, 260 }, 261 }, 262 { 263 foo, 264 []string{"FirstName", "ID", "Bar"}, 265 &Foo{ 266 FirstName: foo.FirstName, 267 ID: foo.ID, 268 Bar: foo.Bar, 269 }, 270 }, 271 { 272 foo, 273 []string{"Bar", "Bars"}, 274 &Foo{ 275 Bar: foo.Bar, 276 Bars: foo.Bars, 277 }, 278 }, 279 { 280 foo, 281 []string{"FirstName", "Bars.Name"}, 282 &Foo{ 283 FirstName: foo.FirstName, 284 Bars: []*Bar{ 285 {Name: bar.Name}, 286 {Name: bar.Name}, 287 }, 288 }, 289 }, 290 { 291 foo, 292 []string{"Bars.Name", "Bars.Bars.Name"}, 293 &Foo{ 294 Bars: []*Bar{ 295 {Name: bar.Name, Bars: []*Bar{{Name: "Level1-1"}, {Name: "Level1-2"}}}, 296 {Name: bar.Name, Bars: []*Bar{{Name: "Level1-1"}, {Name: "Level1-2"}}}, 297 }, 298 }, 299 }, 300 { 301 foo, 302 []string{"BarInterface", "BarPointer"}, 303 &Foo{ 304 BarInterface: bar, 305 BarPointer: &bar, 306 }, 307 }, 308 } 309 310 // pass to prune by pointer to struct 311 for idx, tc := range testCases { 312 t.Run(fmt.Sprintf("Prune pointer test case #%v", idx), func(t *testing.T) { 313 is := assert.New(t) 314 res, err := Prune(tc.OriginalFoo, tc.Paths) 315 require.NoError(t, err) 316 317 fooPrune := res.(*Foo) 318 is.Equal(tc.ExpectedFoo, fooPrune) 319 }) 320 } 321 322 // pass to prune by struct directly 323 for idx, tc := range testCases { 324 t.Run(fmt.Sprintf("Prune non pointer test case #%v", idx), func(t *testing.T) { 325 is := assert.New(t) 326 fooNonPtr := *tc.OriginalFoo 327 res, err := Prune(fooNonPtr, tc.Paths) 328 require.NoError(t, err) 329 330 fooPrune := res.(Foo) 331 is.Equal(*tc.ExpectedFoo, fooPrune) 332 }) 333 } 334 335 // test PruneByTag 336 var TagTestCases = []struct { 337 OriginalFoo *Foo 338 Paths []string 339 ExpectedFoo *Foo 340 Tag string 341 }{ 342 { 343 foo, 344 []string{"tag 1", "tag 4.BarName"}, 345 &Foo{ 346 FirstName: foo.FirstName, 347 Bar: &Bar{ 348 Name: bar.Name, 349 }, 350 }, 351 "tag_name", 352 }, 353 } 354 355 for idx, tc := range TagTestCases { 356 t.Run(fmt.Sprintf("PruneByTag test case #%v", idx), func(t *testing.T) { 357 is := assert.New(t) 358 fooNonPtr := *tc.OriginalFoo 359 res, err := PruneByTag(fooNonPtr, tc.Paths, tc.Tag) 360 require.NoError(t, err) 361 362 fooPrune := res.(Foo) 363 is.Equal(*tc.ExpectedFoo, fooPrune) 364 }) 365 } 366 367 t.Run("Bar Slice", func(t *testing.T) { 368 barSlice := []*Bar{bar, bar} 369 barSlicePruned, err := pruneByTag(barSlice, []string{"Name"}, nil /*tag*/) 370 require.NoError(t, err) 371 assert.Equal(t, []*Bar{{Name: bar.Name}, {Name: bar.Name}}, barSlicePruned) 372 }) 373 374 t.Run("Bar Array", func(t *testing.T) { 375 barArr := [2]*Bar{bar, bar} 376 barArrPruned, err := pruneByTag(barArr, []string{"Name"}, nil /*tag*/) 377 require.NoError(t, err) 378 assert.Equal(t, [2]*Bar{{Name: bar.Name}, {Name: bar.Name}}, barArrPruned) 379 }) 380 381 // test values are copied and not referenced in return result 382 // NOTE: pointers at the end of path are referenced. Maybe we need to make a copy 383 t.Run("Copy Value Str", func(t *testing.T) { 384 is := assert.New(t) 385 fooTest := &Foo{ 386 Bar: &Bar{ 387 Name: "bar", 388 }, 389 } 390 res, err := pruneByTag(fooTest, []string{"Bar.Name"}, nil) 391 require.NoError(t, err) 392 fooTestPruned := res.(*Foo) 393 is.Equal(fooTest, fooTestPruned) 394 395 // change pruned 396 fooTestPruned.Bar.Name = "changed bar" 397 // check original is unchanged 398 is.Equal(fooTest.Bar.Name, "bar") 399 }) 400 401 // error cases 402 var errCases = []struct { 403 InputFoo *Foo 404 Paths []string 405 TagName *string 406 }{ 407 { 408 foo, 409 []string{"NotExist"}, 410 nil, 411 }, 412 { 413 foo, 414 []string{"FirstName.NotExist", "LastName"}, 415 nil, 416 }, 417 { 418 foo, 419 []string{"LastName", "FirstName.NotExist"}, 420 nil, 421 }, 422 { 423 foo, 424 []string{"LastName", "Bars.NotExist"}, 425 nil, 426 }, 427 // tags 428 { 429 foo, 430 []string{"tag 999"}, 431 &[]string{"tag_name"}[0], 432 }, 433 { 434 foo, 435 []string{"tag 1.NotExist"}, 436 &[]string{"tag_name"}[0], 437 }, 438 { 439 foo, 440 []string{"tag 4.NotExist"}, 441 &[]string{"tag_name"}[0], 442 }, 443 { 444 foo, 445 []string{"FirstName"}, 446 &[]string{"tag_name_not_exist"}[0], 447 }, 448 } 449 450 for idx, errTC := range errCases { 451 t.Run(fmt.Sprintf("error test case #%v", idx), func(t *testing.T) { 452 _, err := pruneByTag(errTC.InputFoo, errTC.Paths, errTC.TagName) 453 assert.Error(t, err) 454 }) 455 } 456} 457 458func ExamplePrune() { 459 type ExampleFoo struct { 460 ExampleFooPtr *ExampleFoo `json:"example_foo_ptr"` 461 Name string `json:"name"` 462 Number int `json:"number"` 463 } 464 465 exampleFoo := ExampleFoo{ 466 ExampleFooPtr: &ExampleFoo{ 467 Name: "ExampleFooPtr", 468 Number: 2, 469 }, 470 Name: "ExampleFoo", 471 Number: 1, 472 } 473 474 // prune using struct field name 475 res, _ := Prune(exampleFoo, []string{"ExampleFooPtr.Name", "Number"}) 476 prunedFoo := res.(ExampleFoo) 477 fmt.Println(prunedFoo.ExampleFooPtr.Name) 478 fmt.Println(prunedFoo.ExampleFooPtr.Number) 479 fmt.Println(prunedFoo.Name) 480 fmt.Println(prunedFoo.Number) 481 482 // prune using struct json tag 483 res2, _ := PruneByTag(exampleFoo, []string{"example_foo_ptr.name", "number"}, "json") 484 prunedByTagFoo := res2.(ExampleFoo) 485 fmt.Println(prunedByTagFoo.ExampleFooPtr.Name) 486 fmt.Println(prunedByTagFoo.ExampleFooPtr.Number) 487 fmt.Println(prunedByTagFoo.Name) 488 fmt.Println(prunedByTagFoo.Number) 489 // output: 490 // ExampleFooPtr 491 // 0 492 // 493 // 1 494 // ExampleFooPtr 495 // 0 496 // 497 // 1 498} 499