goyave.dev/goyave/v4@v4.4.11/util/walk/walk_test.go (about) 1 package walk 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 ) 9 10 func TestPathHasArray(t *testing.T) { 11 path := &Path{ 12 Name: "object", 13 Type: PathTypeObject, 14 Next: &Path{ 15 Name: "array", 16 Type: PathTypeArray, 17 Next: &Path{ 18 Type: PathTypeElement, 19 }, 20 }, 21 } 22 23 assert.True(t, path.HasArray()) 24 assert.False(t, path.Next.Next.HasArray()) 25 } 26 27 func TestPathLastParent(t *testing.T) { 28 path := &Path{ 29 Name: "object", 30 Type: PathTypeObject, 31 Next: &Path{ 32 Name: "array", 33 Type: PathTypeArray, 34 Next: &Path{ 35 Type: PathTypeElement, 36 }, 37 }, 38 } 39 40 assert.Equal(t, path.Next, path.LastParent()) 41 assert.Nil(t, path.Next.Next.LastParent()) 42 } 43 44 func TestPathTail(t *testing.T) { 45 path := &Path{ 46 Name: "object", 47 Type: PathTypeObject, 48 Next: &Path{ 49 Name: "array", 50 Type: PathTypeArray, 51 Next: &Path{ 52 Type: PathTypeElement, 53 }, 54 }, 55 } 56 assert.Equal(t, path.Next.Next, path.Tail()) 57 assert.Equal(t, path.Next.Next, path.Next.Next.Tail()) 58 } 59 60 func TestPathClone(t *testing.T) { 61 i := 1 62 path := &Path{ 63 Name: "object", 64 Type: PathTypeObject, 65 Next: &Path{ 66 Name: "array", 67 Type: PathTypeArray, 68 Index: &i, 69 Next: &Path{ 70 Type: PathTypeElement, 71 }, 72 }, 73 } 74 clone := path.Clone() 75 assert.Equal(t, path, clone) 76 } 77 78 func testPathScanner(t *testing.T, path string, expected []string) { 79 scanner := createPathScanner(path) 80 result := []string{} 81 for scanner.Scan() { 82 result = append(result, scanner.Text()) 83 } 84 85 assert.Equal(t, expected, result) 86 assert.Nil(t, scanner.Err()) 87 } 88 89 func testPathScannerError(t *testing.T, path string) { 90 scanner := createPathScanner(path) 91 result := []string{} 92 for scanner.Scan() { 93 result = append(result, scanner.Text()) 94 } 95 96 err := scanner.Err() 97 if assert.NotNil(t, err) { 98 assert.Contains(t, err.Error(), "Illegal syntax: ") 99 } else { 100 fmt.Printf("%#v\n", result) 101 } 102 } 103 104 func TestPathScanner(t *testing.T) { 105 testPathScanner(t, "object.array[].field", []string{"object", ".", "array", "[]", ".", "field"}) 106 testPathScanner(t, "array[][]", []string{"array", "[]", "[]"}) 107 testPathScanner(t, "object.field", []string{"object", ".", "field"}) 108 109 testPathScannerError(t, "object[].[]") 110 testPathScannerError(t, "object..field") 111 testPathScannerError(t, "object.") 112 testPathScannerError(t, ".object") 113 testPathScannerError(t, "array[") 114 testPathScannerError(t, "array]") 115 testPathScannerError(t, "array[.]") 116 testPathScannerError(t, "array[aa]") 117 testPathScannerError(t, "array[[]]") 118 testPathScannerError(t, "array[a[b]c]") 119 testPathScannerError(t, "[]array") 120 testPathScannerError(t, "array[]field") 121 testPathScannerError(t, "array.[]field") 122 testPathScannerError(t, "array.[]field") 123 testPathScannerError(t, "") 124 } 125 126 func TestParse(t *testing.T) { 127 path, err := Parse("object.array[].field") 128 assert.Nil(t, err) 129 assert.Equal(t, path, &Path{ 130 Name: "object", 131 Type: PathTypeObject, 132 Next: &Path{ 133 Name: "array", 134 Type: PathTypeArray, 135 Next: &Path{ 136 Type: PathTypeObject, 137 Next: &Path{ 138 Name: "field", 139 Type: PathTypeElement, 140 }, 141 }, 142 }, 143 }) 144 145 path, err = Parse("array[][]") 146 assert.Nil(t, err) 147 assert.Equal(t, path, &Path{ 148 Name: "array", 149 Type: PathTypeArray, 150 Next: &Path{ 151 Type: PathTypeArray, 152 Next: &Path{ 153 Type: PathTypeElement, 154 }, 155 }, 156 }) 157 158 path, err = Parse("object.field") 159 assert.Nil(t, err) 160 assert.Equal(t, path, &Path{ 161 Name: "object", 162 Type: PathTypeObject, 163 Next: &Path{ 164 Name: "field", 165 Type: PathTypeElement, 166 }, 167 }) 168 169 path, err = Parse("array[][].field") 170 assert.Nil(t, err) 171 assert.Equal(t, path, &Path{ 172 Name: "array", 173 Type: PathTypeArray, 174 Next: &Path{ 175 Type: PathTypeArray, 176 Next: &Path{ 177 Type: PathTypeObject, 178 Next: &Path{ 179 Name: "field", 180 Type: PathTypeElement, 181 }, 182 }, 183 }, 184 }) 185 186 path, err = Parse("array[][].field[]") 187 assert.Nil(t, err) 188 assert.Equal(t, path, &Path{ 189 Name: "array", 190 Type: PathTypeArray, 191 Next: &Path{ 192 Type: PathTypeArray, 193 Next: &Path{ 194 Type: PathTypeObject, 195 Next: &Path{ 196 Name: "field", 197 Type: PathTypeArray, 198 Next: &Path{ 199 Type: PathTypeElement, 200 }, 201 }, 202 }, 203 }, 204 }) 205 206 path, err = Parse(".invalid[]path") 207 assert.Nil(t, path) 208 assert.NotNil(t, err) 209 } 210 211 func testWalk(t *testing.T, data map[string]interface{}, p string) []Context { 212 matches := make([]Context, 0, 5) 213 path, err := Parse(p) 214 215 if !assert.Nil(t, err) { 216 assert.FailNow(t, err.Error()) 217 } 218 219 path.Walk(data, func(c Context) { 220 matches = append(matches, c) 221 }) 222 223 return matches 224 } 225 226 func TestPathWalk(t *testing.T) { 227 // object.field 228 data := map[string]interface{}{ 229 "object": map[string]interface{}{ 230 "field": 5, 231 }, 232 } 233 expected := []Context{ 234 { 235 Value: 5, 236 Parent: data["object"], 237 Path: &Path{ 238 Name: "object", 239 Type: PathTypeObject, 240 Next: &Path{Name: "field"}, 241 }, 242 Name: "field", 243 Index: -1, 244 Found: Found, 245 }, 246 } 247 matches := testWalk(t, data, "object.field") 248 assert.Equal(t, expected, matches) 249 250 // array[] 251 data = map[string]interface{}{ 252 "array": []string{"a", "b", "c"}, 253 } 254 i := 0 255 j := 1 256 k := 2 257 l := 3 258 m := -1 259 expected = []Context{ 260 { 261 Value: "a", 262 Parent: data["array"], 263 Path: &Path{ 264 Name: "array", 265 Type: PathTypeArray, 266 Index: &i, 267 Next: &Path{}, 268 }, 269 Name: "", 270 Index: 0, 271 Found: Found, 272 }, 273 { 274 Value: "b", 275 Parent: data["array"], 276 Path: &Path{ 277 Name: "array", 278 Type: PathTypeArray, 279 Index: &j, 280 Next: &Path{}, 281 }, 282 Name: "", 283 Index: 1, 284 Found: Found, 285 }, 286 { 287 Value: "c", 288 Parent: data["array"], 289 Path: &Path{ 290 Name: "array", 291 Type: PathTypeArray, 292 Index: &k, 293 Next: &Path{}, 294 }, 295 Name: "", 296 Index: 2, 297 Found: Found, 298 }, 299 } 300 matches = testWalk(t, data, "array[]") 301 assert.Equal(t, expected, matches) 302 303 // array[][] 304 data = map[string]interface{}{ 305 "array": [][]string{ 306 {}, 307 {"a", "b"}, 308 {"c"}, 309 }, 310 } 311 expected = []Context{ 312 { 313 Value: nil, 314 Parent: data["array"].([][]string)[0], 315 Path: &Path{ 316 Name: "array", 317 Type: PathTypeArray, 318 Index: &i, 319 Next: &Path{ 320 Type: PathTypeArray, 321 Next: &Path{}, 322 }, 323 }, 324 Name: "", 325 Index: -1, 326 Found: ElementNotFound, 327 }, 328 { 329 Value: "a", 330 Parent: data["array"].([][]string)[1], 331 Path: &Path{ 332 Name: "array", 333 Type: PathTypeArray, 334 Index: &j, 335 Next: &Path{ 336 Type: PathTypeArray, 337 Index: &i, 338 Next: &Path{}, 339 }, 340 }, 341 Name: "", 342 Index: 0, 343 Found: Found, 344 }, 345 { 346 Value: "b", 347 Parent: data["array"].([][]string)[1], 348 Path: &Path{ 349 Name: "array", 350 Type: PathTypeArray, 351 Index: &j, 352 Next: &Path{ 353 Type: PathTypeArray, 354 Index: &j, 355 Next: &Path{}, 356 }, 357 }, 358 Name: "", 359 Index: 1, 360 Found: Found, 361 }, 362 { 363 Value: "c", 364 Parent: data["array"].([][]string)[2], 365 Path: &Path{ 366 Name: "array", 367 Type: PathTypeArray, 368 Index: &k, 369 Next: &Path{ 370 Type: PathTypeArray, 371 Index: &i, 372 Next: &Path{}, 373 }, 374 }, 375 Name: "", 376 Index: 0, 377 Found: Found, 378 }, 379 } 380 matches = testWalk(t, data, "array[][]") 381 assert.Equal(t, expected, matches) 382 383 // array[].field[] 384 data = map[string]interface{}{ 385 "array": []map[string]interface{}{ 386 {"field": []string{}}, 387 {"field": []string{"a", "b"}}, 388 {}, 389 {"field": []string{"c"}}, 390 }, 391 } 392 expected = []Context{ 393 { 394 Value: nil, 395 Parent: data["array"].([]map[string]interface{})[0]["field"], 396 Path: &Path{ 397 Name: "array", 398 Type: PathTypeArray, 399 Index: &i, 400 Next: &Path{ 401 Type: PathTypeObject, 402 Next: &Path{ 403 Name: "field", 404 Type: PathTypeArray, 405 Next: &Path{}, 406 }, 407 }, 408 }, 409 Name: "", 410 Index: -1, 411 Found: ElementNotFound, 412 }, 413 { 414 Value: "a", 415 Parent: data["array"].([]map[string]interface{})[1]["field"], 416 Path: &Path{ 417 Name: "array", 418 Type: PathTypeArray, 419 Index: &j, 420 Next: &Path{ 421 Type: PathTypeObject, 422 Next: &Path{ 423 Name: "field", 424 Type: PathTypeArray, 425 Index: &i, 426 Next: &Path{}, 427 }, 428 }, 429 }, 430 Name: "", 431 Index: 0, 432 Found: Found, 433 }, 434 { 435 Value: "b", 436 Parent: data["array"].([]map[string]interface{})[1]["field"], 437 Path: &Path{ 438 Name: "array", 439 Type: PathTypeArray, 440 Index: &j, 441 Next: &Path{ 442 Type: PathTypeObject, 443 Next: &Path{ 444 Name: "field", 445 Type: PathTypeArray, 446 Index: &j, 447 Next: &Path{}, 448 }, 449 }, 450 }, 451 Name: "", 452 Index: 1, 453 Found: Found, 454 }, 455 { 456 Value: nil, 457 Parent: data["array"].([]map[string]interface{})[2], 458 Path: &Path{ 459 Name: "array", 460 Type: PathTypeArray, 461 Index: &k, 462 Next: &Path{ 463 Type: PathTypeObject, 464 Next: &Path{ 465 Name: "field", 466 Type: PathTypeArray, 467 Index: &m, 468 Next: &Path{}, 469 }, 470 }, 471 }, 472 Name: "field", 473 Index: -1, 474 Found: ParentNotFound, 475 }, 476 { 477 Value: "c", 478 Parent: data["array"].([]map[string]interface{})[3]["field"], 479 Path: &Path{ 480 Name: "array", 481 Type: PathTypeArray, 482 Index: &l, 483 Next: &Path{ 484 Type: PathTypeObject, 485 Next: &Path{ 486 Name: "field", 487 Type: PathTypeArray, 488 Index: &i, 489 Next: &Path{}, 490 }, 491 }, 492 }, 493 Name: "", 494 Index: 0, 495 Found: Found, 496 }, 497 } 498 matches = testWalk(t, data, "array[].field[]") 499 assert.Equal(t, expected, matches) 500 501 // array[].field index check 502 expected = []Context{ 503 { 504 Value: []string{}, 505 Parent: data["array"].([]map[string]interface{})[0], 506 Path: &Path{ 507 Name: "array", 508 Type: PathTypeArray, 509 Index: &i, 510 Next: &Path{ 511 Type: PathTypeObject, 512 Next: &Path{Name: "field"}, 513 }, 514 }, 515 Name: "field", 516 Index: -1, 517 Found: Found, 518 }, 519 { 520 Value: []string{"a", "b"}, 521 Parent: data["array"].([]map[string]interface{})[1], 522 Path: &Path{ 523 Name: "array", 524 Type: PathTypeArray, 525 Index: &j, 526 Next: &Path{ 527 Type: PathTypeObject, 528 Next: &Path{Name: "field"}, 529 }, 530 }, 531 Name: "field", 532 Index: -1, 533 Found: Found, 534 }, 535 { 536 Value: nil, 537 Parent: data["array"].([]map[string]interface{})[2], 538 Path: &Path{ 539 Name: "array", 540 Type: PathTypeArray, 541 Index: &k, 542 Next: &Path{ 543 Type: PathTypeObject, 544 Next: &Path{Name: "field"}, 545 }, 546 }, 547 Name: "field", 548 Index: -1, 549 Found: ElementNotFound, 550 }, 551 { 552 Value: []string{"c"}, 553 Parent: data["array"].([]map[string]interface{})[3], 554 Path: &Path{ 555 Name: "array", 556 Type: PathTypeArray, 557 Index: &l, 558 Next: &Path{ 559 Type: PathTypeObject, 560 Next: &Path{Name: "field"}, 561 }, 562 }, 563 Name: "field", 564 Index: -1, 565 Found: Found, 566 }, 567 } 568 matches = testWalk(t, data, "array[].field") 569 assert.Equal(t, expected, matches) 570 } 571 572 func TestPathWalkEmptyArray(t *testing.T) { 573 data := map[string]interface{}{ 574 "array": []string{}, 575 "narray": [][][]string{}, 576 } 577 578 expected := []Context{ 579 { 580 Value: nil, 581 Parent: data["array"], 582 Name: "", 583 Path: &Path{ 584 Name: "array", 585 Type: PathTypeArray, 586 Next: &Path{}, 587 }, 588 Index: -1, 589 Found: ElementNotFound, 590 }, 591 } 592 593 matches := testWalk(t, data, "array[]") 594 assert.Equal(t, expected, matches) 595 596 matches = testWalk(t, data, "narray[][][]") 597 expected = []Context{ 598 { 599 Value: nil, 600 Parent: data["narray"], 601 Name: "", 602 Path: &Path{ 603 Name: "narray", 604 Type: PathTypeArray, 605 Next: &Path{}, 606 }, 607 Index: -1, 608 Found: ParentNotFound, 609 }, 610 } 611 assert.Equal(t, expected, matches) 612 } 613 614 func TestPathWalkNotFoundInObject(t *testing.T) { 615 data := map[string]interface{}{ 616 "object": map[string]interface{}{ 617 "field": 5, 618 }, 619 } 620 expected := []Context{ 621 { 622 Value: nil, 623 Parent: data["object"], 624 Path: &Path{ 625 Name: "object", 626 Type: PathTypeObject, 627 Next: &Path{Name: "notafield"}, 628 }, 629 Name: "notafield", 630 Index: -1, 631 Found: ElementNotFound, 632 }, 633 } 634 matches := testWalk(t, data, "object.notafield") 635 assert.Equal(t, expected, matches) 636 } 637 638 func TestPathWalkNotFoundInArray(t *testing.T) { 639 data := map[string]interface{}{ 640 "array": []map[string]interface{}{}, 641 } 642 expected := []Context{ 643 { 644 Value: nil, 645 Parent: data["array"], 646 Path: &Path{ 647 Name: "array", 648 Type: PathTypeArray, 649 Next: &Path{}, 650 }, 651 Name: "", 652 Index: -1, 653 Found: ParentNotFound, 654 }, 655 } 656 matches := testWalk(t, data, "array[].field") 657 assert.Equal(t, expected, matches) 658 } 659 660 func TestPathWalkSliceExpected(t *testing.T) { 661 data := map[string]interface{}{ 662 "object": map[string]interface{}{ 663 "field": []string{"a", "b"}, 664 "array": []interface{}{ 665 5, 666 []string{"a", "b"}, 667 map[string]interface{}{"field": "1"}, 668 }, 669 }, 670 } 671 i := 0 672 j := 1 673 k := 2 674 expected := []Context{ 675 { 676 Value: nil, 677 Parent: data["object"].(map[string]interface{})["field"], 678 Path: &Path{ 679 Name: "object", 680 Type: PathTypeObject, 681 Next: &Path{ 682 Name: "field", 683 Type: PathTypeArray, 684 Index: &i, 685 Next: &Path{}, 686 }, 687 }, 688 Name: "", 689 Index: 0, 690 Found: ParentNotFound, 691 }, 692 { 693 Value: nil, 694 Parent: data["object"].(map[string]interface{})["field"], 695 Path: &Path{ 696 Name: "object", 697 Type: PathTypeObject, 698 Next: &Path{ 699 Name: "field", 700 Type: PathTypeArray, 701 Index: &j, 702 Next: &Path{}, 703 }, 704 }, 705 Name: "", 706 Index: 1, 707 Found: ParentNotFound, 708 }, 709 } 710 matches := testWalk(t, data, "object.field[][]") 711 assert.Equal(t, expected, matches) 712 713 expected = []Context{ 714 { 715 Value: nil, 716 Parent: data["object"].(map[string]interface{})["array"], 717 Path: &Path{ 718 Name: "object", 719 Type: PathTypeObject, 720 Next: &Path{ 721 Name: "array", 722 Type: PathTypeArray, 723 Index: &i, 724 Next: &Path{}, 725 }, 726 }, 727 Name: "", 728 Index: 0, 729 Found: ParentNotFound, 730 }, 731 { 732 Value: "a", 733 Parent: data["object"].(map[string]interface{})["array"].([]interface{})[1], 734 Path: &Path{ 735 Name: "object", 736 Type: PathTypeObject, 737 Next: &Path{ 738 Name: "array", 739 Type: PathTypeArray, 740 Index: &j, 741 Next: &Path{ 742 Type: PathTypeArray, 743 Index: &i, 744 Next: &Path{}, 745 }, 746 }, 747 }, 748 Name: "", 749 Index: 0, 750 Found: Found, 751 }, 752 { 753 Value: "b", 754 Parent: data["object"].(map[string]interface{})["array"].([]interface{})[1], 755 Path: &Path{ 756 Name: "object", 757 Type: PathTypeObject, 758 Next: &Path{ 759 Name: "array", 760 Type: PathTypeArray, 761 Index: &j, 762 Next: &Path{ 763 Type: PathTypeArray, 764 Index: &j, 765 Next: &Path{}, 766 }, 767 }, 768 }, 769 Name: "", 770 Index: 1, 771 Found: Found, 772 }, 773 { 774 Value: nil, 775 Parent: data["object"].(map[string]interface{})["array"], 776 Path: &Path{ 777 Name: "object", 778 Type: PathTypeObject, 779 Next: &Path{ 780 Name: "array", 781 Type: PathTypeArray, 782 Index: &k, 783 Next: &Path{}, 784 }, 785 }, 786 Name: "", 787 Index: 2, 788 Found: ParentNotFound, 789 }, 790 } 791 matches = testWalk(t, data, "object.array[][]") 792 assert.Equal(t, expected, matches) 793 } 794 795 func TestPathWalkWithIndex(t *testing.T) { 796 data := map[string]interface{}{ 797 "array": []map[string]interface{}{ 798 {"field": []string{}}, 799 {"field": []string{"a", "b"}}, 800 {}, 801 {"field": []string{"c"}}, 802 {"field": []string{"d", "e"}}, 803 }, 804 } 805 806 i := 1 807 path := &Path{ 808 Name: "array", 809 Type: PathTypeArray, 810 Index: &i, 811 Next: &Path{ 812 Type: PathTypeObject, 813 Next: &Path{ 814 Name: "field", 815 Type: PathTypeArray, 816 Index: &i, 817 Next: &Path{}, 818 }, 819 }, 820 } 821 822 matches := make([]Context, 0, 1) 823 824 path.Walk(data, func(c Context) { 825 matches = append(matches, c) 826 }) 827 828 expected := []Context{ 829 { 830 Value: "b", 831 Parent: data["array"].([]map[string]interface{})[i]["field"], 832 Path: path, 833 Name: "", 834 Index: i, 835 Found: Found, 836 }, 837 } 838 assert.Equal(t, expected, matches) 839 } 840 841 func TestPathWalkWithIndexOutOfBounds(t *testing.T) { 842 data := map[string]interface{}{ 843 "array": []map[string]interface{}{ 844 {"field": []string{}}, 845 {"field": []string{"a", "b"}}, 846 {}, 847 {"field": []string{"c"}}, 848 {"field": []string{"d", "e"}}, 849 }, 850 } 851 852 i := 1 853 j := 5 854 path := &Path{ 855 Name: "array", 856 Type: PathTypeArray, 857 Index: &i, 858 Next: &Path{ 859 Type: PathTypeObject, 860 Next: &Path{ 861 Name: "field", 862 Type: PathTypeArray, 863 Index: &j, 864 Next: &Path{}, 865 }, 866 }, 867 } 868 869 matches := make([]Context, 0, 1) 870 871 path.Walk(data, func(c Context) { 872 matches = append(matches, c) 873 }) 874 875 expected := []Context{ 876 { 877 Value: nil, 878 Parent: data["array"].([]map[string]interface{})[i]["field"], 879 Path: path, 880 Name: "", 881 Index: j, 882 Found: ElementNotFound, 883 }, 884 } 885 assert.Equal(t, expected, matches) 886 } 887 888 func TestPathWalkMissingObject(t *testing.T) { 889 data := map[string]interface{}{} 890 891 path := &Path{ 892 Name: "object", 893 Type: PathTypeObject, 894 Next: &Path{ 895 Type: PathTypeObject, 896 Name: "subobject", 897 Next: &Path{ 898 Name: "field", 899 Type: PathTypeElement, 900 }, 901 }, 902 } 903 904 matches := make([]Context, 0, 1) 905 906 path.Walk(data, func(c Context) { 907 matches = append(matches, c) 908 }) 909 910 expected := []Context{ 911 { 912 Value: nil, 913 Parent: data, 914 Path: path, 915 Name: "object", 916 Index: -1, 917 Found: ParentNotFound, 918 }, 919 } 920 assert.Equal(t, expected, matches) 921 } 922 923 func TestPathSetAllMissingIndexes(t *testing.T) { 924 path := &Path{ 925 Name: "array", 926 Type: PathTypeArray, 927 Next: &Path{ 928 Type: PathTypeObject, 929 Name: "object", 930 Next: &Path{ 931 Name: "field", 932 Type: PathTypeArray, 933 Next: &Path{}, 934 }, 935 }, 936 } 937 938 path.setAllMissingIndexes() 939 940 assert.Equal(t, -1, *path.Index) 941 assert.Equal(t, -1, *path.Next.Next.Index) 942 }