github.com/aavshr/aws-sdk-go@v1.41.3/service/dynamodb/expression/condition_test.go (about) 1 //go:build go1.7 2 // +build go1.7 3 4 package expression 5 6 import ( 7 "reflect" 8 "strings" 9 "testing" 10 11 "github.com/aavshr/aws-sdk-go/aws" 12 "github.com/aavshr/aws-sdk-go/service/dynamodb" 13 ) 14 15 // condErrorMode will help with error cases and checking error types 16 type condErrorMode string 17 18 const ( 19 noConditionError condErrorMode = "" 20 // unsetCondition error will occur when BuildExpression is called on an empty 21 // ConditionBuilder 22 unsetCondition = "unset parameter: ConditionBuilder" 23 // invalidOperand error will occur when an invalid OperandBuilder is used as 24 // an argument 25 invalidConditionOperand = "BuildOperand error" 26 ) 27 28 //Compare 29 func TestCompare(t *testing.T) { 30 cases := []struct { 31 name string 32 input ConditionBuilder 33 expectedNode exprNode 34 err condErrorMode 35 }{ 36 { 37 name: "name equal name", 38 input: Name("foo").Equal(Name("bar")), 39 expectedNode: exprNode{ 40 children: []exprNode{ 41 { 42 names: []string{"foo"}, 43 fmtExpr: "$n", 44 }, 45 { 46 names: []string{"bar"}, 47 fmtExpr: "$n", 48 }, 49 }, 50 fmtExpr: "$c = $c", 51 }, 52 }, 53 { 54 name: "value equal value", 55 input: Value(5).Equal(Value("bar")), 56 expectedNode: exprNode{ 57 children: []exprNode{ 58 { 59 values: []dynamodb.AttributeValue{ 60 { 61 N: aws.String("5"), 62 }, 63 }, 64 fmtExpr: "$v", 65 }, 66 { 67 values: []dynamodb.AttributeValue{ 68 { 69 S: aws.String("bar"), 70 }, 71 }, 72 fmtExpr: "$v", 73 }, 74 }, 75 fmtExpr: "$c = $c", 76 }, 77 }, 78 { 79 name: "name size equal name size", 80 input: Name("foo[1]").Size().Equal(Name("bar").Size()), 81 expectedNode: exprNode{ 82 children: []exprNode{ 83 { 84 names: []string{"foo"}, 85 fmtExpr: "size ($n[1])", 86 }, 87 { 88 names: []string{"bar"}, 89 fmtExpr: "size ($n)", 90 }, 91 }, 92 fmtExpr: "$c = $c", 93 }, 94 }, 95 { 96 name: "name not equal name", 97 input: Name("foo").NotEqual(Name("bar")), 98 expectedNode: exprNode{ 99 children: []exprNode{ 100 { 101 names: []string{"foo"}, 102 fmtExpr: "$n", 103 }, 104 { 105 names: []string{"bar"}, 106 fmtExpr: "$n", 107 }, 108 }, 109 fmtExpr: "$c <> $c", 110 }, 111 }, 112 { 113 name: "value not equal value", 114 input: Value(5).NotEqual(Value("bar")), 115 expectedNode: exprNode{ 116 children: []exprNode{ 117 { 118 values: []dynamodb.AttributeValue{ 119 { 120 N: aws.String("5"), 121 }, 122 }, 123 fmtExpr: "$v", 124 }, 125 { 126 values: []dynamodb.AttributeValue{ 127 { 128 S: aws.String("bar"), 129 }, 130 }, 131 fmtExpr: "$v", 132 }, 133 }, 134 fmtExpr: "$c <> $c", 135 }, 136 }, 137 { 138 name: "name size not equal name size", 139 input: Name("foo[1]").Size().NotEqual(Name("bar").Size()), 140 expectedNode: exprNode{ 141 children: []exprNode{ 142 { 143 names: []string{"foo"}, 144 fmtExpr: "size ($n[1])", 145 }, 146 { 147 names: []string{"bar"}, 148 fmtExpr: "size ($n)", 149 }, 150 }, 151 fmtExpr: "$c <> $c", 152 }, 153 }, 154 { 155 name: "name less than name", 156 input: Name("foo").LessThan(Name("bar")), 157 expectedNode: exprNode{ 158 children: []exprNode{ 159 { 160 names: []string{"foo"}, 161 fmtExpr: "$n", 162 }, 163 { 164 names: []string{"bar"}, 165 fmtExpr: "$n", 166 }, 167 }, 168 fmtExpr: "$c < $c", 169 }, 170 }, 171 { 172 name: "value less than value", 173 input: Value(5).LessThan(Value("bar")), 174 expectedNode: exprNode{ 175 children: []exprNode{ 176 { 177 values: []dynamodb.AttributeValue{ 178 { 179 N: aws.String("5"), 180 }, 181 }, 182 fmtExpr: "$v", 183 }, 184 { 185 values: []dynamodb.AttributeValue{ 186 { 187 S: aws.String("bar"), 188 }, 189 }, 190 fmtExpr: "$v", 191 }, 192 }, 193 fmtExpr: "$c < $c", 194 }, 195 }, 196 { 197 name: "name size less than name size", 198 input: Name("foo[1]").Size().LessThan(Name("bar").Size()), 199 expectedNode: exprNode{ 200 children: []exprNode{ 201 { 202 names: []string{"foo"}, 203 fmtExpr: "size ($n[1])", 204 }, 205 { 206 names: []string{"bar"}, 207 fmtExpr: "size ($n)", 208 }, 209 }, 210 fmtExpr: "$c < $c", 211 }, 212 }, 213 { 214 name: "name less than equal name", 215 input: Name("foo").LessThanEqual(Name("bar")), 216 expectedNode: exprNode{ 217 children: []exprNode{ 218 { 219 names: []string{"foo"}, 220 fmtExpr: "$n", 221 }, 222 { 223 names: []string{"bar"}, 224 fmtExpr: "$n", 225 }, 226 }, 227 fmtExpr: "$c <= $c", 228 }, 229 }, 230 { 231 name: "value less than equal value", 232 input: Value(5).LessThanEqual(Value("bar")), 233 expectedNode: exprNode{ 234 children: []exprNode{ 235 { 236 values: []dynamodb.AttributeValue{ 237 { 238 N: aws.String("5"), 239 }, 240 }, 241 fmtExpr: "$v", 242 }, 243 { 244 values: []dynamodb.AttributeValue{ 245 { 246 S: aws.String("bar"), 247 }, 248 }, 249 fmtExpr: "$v", 250 }, 251 }, 252 fmtExpr: "$c <= $c", 253 }, 254 }, 255 { 256 name: "name size less than equal name size", 257 input: Name("foo[1]").Size().LessThanEqual(Name("bar").Size()), 258 expectedNode: exprNode{ 259 children: []exprNode{ 260 { 261 names: []string{"foo"}, 262 fmtExpr: "size ($n[1])", 263 }, 264 { 265 names: []string{"bar"}, 266 fmtExpr: "size ($n)", 267 }, 268 }, 269 fmtExpr: "$c <= $c", 270 }, 271 }, 272 { 273 name: "name greater than name", 274 input: Name("foo").GreaterThan(Name("bar")), 275 expectedNode: exprNode{ 276 children: []exprNode{ 277 { 278 names: []string{"foo"}, 279 fmtExpr: "$n", 280 }, 281 { 282 names: []string{"bar"}, 283 fmtExpr: "$n", 284 }, 285 }, 286 fmtExpr: "$c > $c", 287 }, 288 }, 289 { 290 name: "value greater than value", 291 input: Value(5).GreaterThan(Value("bar")), 292 expectedNode: exprNode{ 293 children: []exprNode{ 294 { 295 values: []dynamodb.AttributeValue{ 296 { 297 N: aws.String("5"), 298 }, 299 }, 300 fmtExpr: "$v", 301 }, 302 { 303 values: []dynamodb.AttributeValue{ 304 { 305 S: aws.String("bar"), 306 }, 307 }, 308 fmtExpr: "$v", 309 }, 310 }, 311 fmtExpr: "$c > $c", 312 }, 313 }, 314 { 315 name: "name size greater than name size", 316 input: Name("foo[1]").Size().GreaterThan(Name("bar").Size()), 317 expectedNode: exprNode{ 318 children: []exprNode{ 319 { 320 names: []string{"foo"}, 321 fmtExpr: "size ($n[1])", 322 }, 323 { 324 names: []string{"bar"}, 325 fmtExpr: "size ($n)", 326 }, 327 }, 328 fmtExpr: "$c > $c", 329 }, 330 }, 331 { 332 name: "name greater than equal name", 333 input: Name("foo").GreaterThanEqual(Name("bar")), 334 expectedNode: exprNode{ 335 children: []exprNode{ 336 { 337 names: []string{"foo"}, 338 fmtExpr: "$n", 339 }, 340 { 341 names: []string{"bar"}, 342 fmtExpr: "$n", 343 }, 344 }, 345 fmtExpr: "$c >= $c", 346 }, 347 }, 348 { 349 name: "value greater than equal value", 350 input: Value(5).GreaterThanEqual(Value("bar")), 351 expectedNode: exprNode{ 352 children: []exprNode{ 353 { 354 values: []dynamodb.AttributeValue{ 355 { 356 N: aws.String("5"), 357 }, 358 }, 359 fmtExpr: "$v", 360 }, 361 { 362 values: []dynamodb.AttributeValue{ 363 { 364 S: aws.String("bar"), 365 }, 366 }, 367 fmtExpr: "$v", 368 }, 369 }, 370 fmtExpr: "$c >= $c", 371 }, 372 }, 373 { 374 name: "name size greater than equal name size", 375 input: Name("foo[1]").Size().GreaterThanEqual(Name("bar").Size()), 376 expectedNode: exprNode{ 377 children: []exprNode{ 378 { 379 names: []string{"foo"}, 380 fmtExpr: "size ($n[1])", 381 }, 382 { 383 names: []string{"bar"}, 384 fmtExpr: "size ($n)", 385 }, 386 }, 387 fmtExpr: "$c >= $c", 388 }, 389 }, 390 { 391 name: "invalid operand error Equal", 392 input: Name("").Size().Equal(Value(5)), 393 err: invalidConditionOperand, 394 }, 395 { 396 name: "invalid operand error NotEqual", 397 input: Name("").Size().NotEqual(Value(5)), 398 err: invalidConditionOperand, 399 }, 400 { 401 name: "invalid operand error LessThan", 402 input: Name("").Size().LessThan(Value(5)), 403 err: invalidConditionOperand, 404 }, 405 { 406 name: "invalid operand error LessThanEqual", 407 input: Name("").Size().LessThanEqual(Value(5)), 408 err: invalidConditionOperand, 409 }, 410 { 411 name: "invalid operand error GreaterThan", 412 input: Name("").Size().GreaterThan(Value(5)), 413 err: invalidConditionOperand, 414 }, 415 { 416 name: "invalid operand error GreaterThanEqual", 417 input: Name("").Size().GreaterThanEqual(Value(5)), 418 err: invalidConditionOperand, 419 }, 420 } 421 for _, c := range cases { 422 t.Run(c.name, func(t *testing.T) { 423 actual, err := c.input.buildTree() 424 if c.err != noConditionError { 425 if err == nil { 426 t.Errorf("expect error %q, got no error", c.err) 427 } else { 428 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 429 t.Errorf("expect %q error message to be in %q", e, a) 430 } 431 } 432 } else { 433 if err != nil { 434 t.Errorf("expect no error, got unexpected Error %q", err) 435 } 436 437 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 438 t.Errorf("expect %v, got %v", e, a) 439 } 440 } 441 }) 442 } 443 } 444 445 func TestBuildCondition(t *testing.T) { 446 cases := []struct { 447 name string 448 input ConditionBuilder 449 expected exprNode 450 err condErrorMode 451 }{ 452 { 453 name: "no match error", 454 input: ConditionBuilder{}, 455 err: unsetCondition, 456 }, 457 } 458 459 for _, c := range cases { 460 t.Run(c.name, func(t *testing.T) { 461 actual, err := c.input.buildTree() 462 if c.err != noConditionError { 463 if err == nil { 464 t.Errorf("expect error %q, got no error", c.err) 465 } else { 466 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 467 t.Errorf("expect %q error message to be in %q", e, a) 468 } 469 } 470 } else { 471 if err != nil { 472 t.Errorf("expect no error, got unexpected Error %q", err) 473 } 474 if e, a := c.expected, actual; !reflect.DeepEqual(a, e) { 475 t.Errorf("expect %v, got %v", e, a) 476 } 477 } 478 }) 479 } 480 } 481 482 func TestBoolCondition(t *testing.T) { 483 cases := []struct { 484 name string 485 input ConditionBuilder 486 expectedNode exprNode 487 err condErrorMode 488 }{ 489 { 490 name: "basic method and", 491 input: Name("foo").Equal(Value(5)).And(Name("bar").Equal(Value("baz"))), 492 expectedNode: exprNode{ 493 children: []exprNode{ 494 { 495 children: []exprNode{ 496 { 497 names: []string{"foo"}, 498 fmtExpr: "$n", 499 }, 500 { 501 values: []dynamodb.AttributeValue{ 502 { 503 N: aws.String("5"), 504 }, 505 }, 506 fmtExpr: "$v", 507 }, 508 }, 509 fmtExpr: "$c = $c", 510 }, 511 { 512 children: []exprNode{ 513 { 514 names: []string{"bar"}, 515 fmtExpr: "$n", 516 }, 517 { 518 values: []dynamodb.AttributeValue{ 519 { 520 S: aws.String("baz"), 521 }, 522 }, 523 fmtExpr: "$v", 524 }, 525 }, 526 fmtExpr: "$c = $c", 527 }, 528 }, 529 fmtExpr: "($c) AND ($c)", 530 }, 531 }, 532 { 533 name: "basic method or", 534 input: Name("foo").Equal(Value(5)).Or(Name("bar").Equal(Value("baz"))), 535 expectedNode: exprNode{ 536 children: []exprNode{ 537 { 538 children: []exprNode{ 539 { 540 names: []string{"foo"}, 541 fmtExpr: "$n", 542 }, 543 { 544 values: []dynamodb.AttributeValue{ 545 { 546 N: aws.String("5"), 547 }, 548 }, 549 fmtExpr: "$v", 550 }, 551 }, 552 fmtExpr: "$c = $c", 553 }, 554 { 555 children: []exprNode{ 556 { 557 names: []string{"bar"}, 558 fmtExpr: "$n", 559 }, 560 { 561 values: []dynamodb.AttributeValue{ 562 { 563 S: aws.String("baz"), 564 }, 565 }, 566 fmtExpr: "$v", 567 }, 568 }, 569 fmtExpr: "$c = $c", 570 }, 571 }, 572 fmtExpr: "($c) OR ($c)", 573 }, 574 }, 575 { 576 name: "variadic function and", 577 input: And(Name("foo").Equal(Value(5)), Name("bar").Equal(Value("baz")), Name("qux").Equal(Value(true))), 578 expectedNode: exprNode{ 579 children: []exprNode{ 580 { 581 children: []exprNode{ 582 { 583 names: []string{"foo"}, 584 fmtExpr: "$n", 585 }, 586 { 587 values: []dynamodb.AttributeValue{ 588 { 589 N: aws.String("5"), 590 }, 591 }, 592 fmtExpr: "$v", 593 }, 594 }, 595 fmtExpr: "$c = $c", 596 }, 597 { 598 children: []exprNode{ 599 { 600 names: []string{"bar"}, 601 fmtExpr: "$n", 602 }, 603 { 604 values: []dynamodb.AttributeValue{ 605 { 606 S: aws.String("baz"), 607 }, 608 }, 609 fmtExpr: "$v", 610 }, 611 }, 612 fmtExpr: "$c = $c", 613 }, 614 { 615 children: []exprNode{ 616 { 617 names: []string{"qux"}, 618 fmtExpr: "$n", 619 }, 620 { 621 values: []dynamodb.AttributeValue{ 622 { 623 BOOL: aws.Bool(true), 624 }, 625 }, 626 fmtExpr: "$v", 627 }, 628 }, 629 fmtExpr: "$c = $c", 630 }, 631 }, 632 fmtExpr: "($c) AND ($c) AND ($c)", 633 }, 634 }, 635 { 636 name: "variadic function or", 637 input: Or(Name("foo").Equal(Value(5)), Name("bar").Equal(Value("baz")), Name("qux").Equal(Value(true))), 638 expectedNode: exprNode{ 639 children: []exprNode{ 640 { 641 children: []exprNode{ 642 { 643 names: []string{"foo"}, 644 fmtExpr: "$n", 645 }, 646 { 647 values: []dynamodb.AttributeValue{ 648 { 649 N: aws.String("5"), 650 }, 651 }, 652 fmtExpr: "$v", 653 }, 654 }, 655 fmtExpr: "$c = $c", 656 }, 657 { 658 children: []exprNode{ 659 { 660 names: []string{"bar"}, 661 fmtExpr: "$n", 662 }, 663 { 664 values: []dynamodb.AttributeValue{ 665 { 666 S: aws.String("baz"), 667 }, 668 }, 669 fmtExpr: "$v", 670 }, 671 }, 672 fmtExpr: "$c = $c", 673 }, 674 { 675 children: []exprNode{ 676 { 677 names: []string{"qux"}, 678 fmtExpr: "$n", 679 }, 680 { 681 values: []dynamodb.AttributeValue{ 682 { 683 BOOL: aws.Bool(true), 684 }, 685 }, 686 fmtExpr: "$v", 687 }, 688 }, 689 fmtExpr: "$c = $c", 690 }, 691 }, 692 fmtExpr: "($c) OR ($c) OR ($c)", 693 }, 694 }, 695 { 696 name: "invalid operand error And", 697 input: Name("").Size().GreaterThanEqual(Value(5)).And(Name("[5]").Between(Value(3), Value(9))), 698 err: invalidConditionOperand, 699 }, 700 { 701 name: "invalid operand error Or", 702 input: Name("").Size().GreaterThanEqual(Value(5)).Or(Name("[5]").Between(Value(3), Value(9))), 703 err: invalidConditionOperand, 704 }, 705 } 706 707 for _, c := range cases { 708 t.Run(c.name, func(t *testing.T) { 709 actual, err := c.input.buildTree() 710 if c.err != noConditionError { 711 if err == nil { 712 t.Errorf("expect error %q, got no error", c.err) 713 } else { 714 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 715 t.Errorf("expect %q error message to be in %q", e, a) 716 } 717 } 718 } else { 719 if err != nil { 720 t.Errorf("expect no error, got unexpected Error %q", err) 721 } 722 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 723 t.Errorf("expect %v, got %v", e, a) 724 } 725 } 726 }) 727 } 728 } 729 730 func TestNotCondition(t *testing.T) { 731 cases := []struct { 732 name string 733 input ConditionBuilder 734 expectedNode exprNode 735 err condErrorMode 736 }{ 737 { 738 name: "basic method not", 739 input: Name("foo").Equal(Value(5)).Not(), 740 expectedNode: exprNode{ 741 children: []exprNode{ 742 { 743 children: []exprNode{ 744 { 745 names: []string{"foo"}, 746 fmtExpr: "$n", 747 }, 748 { 749 values: []dynamodb.AttributeValue{ 750 { 751 N: aws.String("5"), 752 }, 753 }, 754 fmtExpr: "$v", 755 }, 756 }, 757 fmtExpr: "$c = $c", 758 }, 759 }, 760 fmtExpr: "NOT ($c)", 761 }, 762 }, 763 { 764 name: "basic function not", 765 input: Not(Name("foo").Equal(Value(5))), 766 expectedNode: exprNode{ 767 children: []exprNode{ 768 { 769 children: []exprNode{ 770 { 771 names: []string{"foo"}, 772 fmtExpr: "$n", 773 }, 774 { 775 values: []dynamodb.AttributeValue{ 776 { 777 N: aws.String("5"), 778 }, 779 }, 780 fmtExpr: "$v", 781 }, 782 }, 783 fmtExpr: "$c = $c", 784 }, 785 }, 786 fmtExpr: "NOT ($c)", 787 }, 788 }, 789 { 790 name: "invalid operand error not", 791 input: Name("").Size().GreaterThanEqual(Value(5)).Or(Name("[5]").Between(Value(3), Value(9))).Not(), 792 err: invalidConditionOperand, 793 }, 794 } 795 796 for _, c := range cases { 797 t.Run(c.name, func(t *testing.T) { 798 actual, err := c.input.buildTree() 799 if c.err != noConditionError { 800 if err == nil { 801 t.Errorf("expect error %q, got no error", c.err) 802 } else { 803 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 804 t.Errorf("expect %q error message to be in %q", e, a) 805 } 806 } 807 } else { 808 if err != nil { 809 t.Errorf("expect no error, got unexpected Error %q", err) 810 } 811 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 812 t.Errorf("expect %v, got %v", e, a) 813 } 814 } 815 }) 816 } 817 } 818 819 func TestBetweenCondition(t *testing.T) { 820 cases := []struct { 821 name string 822 input ConditionBuilder 823 expectedNode exprNode 824 err condErrorMode 825 }{ 826 { 827 name: "basic method between for name", 828 input: Name("foo").Between(Value(5), Value(7)), 829 expectedNode: exprNode{ 830 children: []exprNode{ 831 { 832 names: []string{"foo"}, 833 fmtExpr: "$n", 834 }, 835 { 836 values: []dynamodb.AttributeValue{ 837 { 838 N: aws.String("5"), 839 }, 840 }, 841 fmtExpr: "$v", 842 }, 843 { 844 values: []dynamodb.AttributeValue{ 845 { 846 N: aws.String("7"), 847 }, 848 }, 849 fmtExpr: "$v", 850 }, 851 }, 852 fmtExpr: "$c BETWEEN $c AND $c", 853 }, 854 }, 855 { 856 name: "basic method between for value", 857 input: Value(6).Between(Value(5), Value(7)), 858 expectedNode: exprNode{ 859 children: []exprNode{ 860 { 861 values: []dynamodb.AttributeValue{ 862 { 863 N: aws.String("6"), 864 }, 865 }, 866 fmtExpr: "$v", 867 }, 868 { 869 values: []dynamodb.AttributeValue{ 870 { 871 N: aws.String("5"), 872 }, 873 }, 874 fmtExpr: "$v", 875 }, 876 { 877 values: []dynamodb.AttributeValue{ 878 { 879 N: aws.String("7"), 880 }, 881 }, 882 fmtExpr: "$v", 883 }, 884 }, 885 fmtExpr: "$c BETWEEN $c AND $c", 886 }, 887 }, 888 { 889 name: "basic method between for size", 890 input: Name("foo").Size().Between(Value(5), Value(7)), 891 expectedNode: exprNode{ 892 children: []exprNode{ 893 { 894 names: []string{"foo"}, 895 fmtExpr: "size ($n)", 896 }, 897 { 898 values: []dynamodb.AttributeValue{ 899 { 900 N: aws.String("5"), 901 }, 902 }, 903 fmtExpr: "$v", 904 }, 905 { 906 values: []dynamodb.AttributeValue{ 907 { 908 N: aws.String("7"), 909 }, 910 }, 911 fmtExpr: "$v", 912 }, 913 }, 914 fmtExpr: "$c BETWEEN $c AND $c", 915 }, 916 }, 917 { 918 name: "invalid operand error between", 919 input: Name("[5]").Between(Value(3), Name("foo..bar")), 920 err: invalidConditionOperand, 921 }, 922 } 923 924 for _, c := range cases { 925 t.Run(c.name, func(t *testing.T) { 926 actual, err := c.input.buildTree() 927 if c.err != noConditionError { 928 if err == nil { 929 t.Errorf("expect error %q, got no error", c.err) 930 } else { 931 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 932 t.Errorf("expect %q error message to be in %q", e, a) 933 } 934 } 935 } else { 936 if err != nil { 937 t.Errorf("expect no error, got unexpected Error %q", err) 938 } 939 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 940 t.Errorf("expect %v, got %v", e, a) 941 } 942 } 943 }) 944 } 945 } 946 947 func TestInCondition(t *testing.T) { 948 cases := []struct { 949 name string 950 input ConditionBuilder 951 expectedNode exprNode 952 err condErrorMode 953 }{ 954 { 955 name: "basic method in for name", 956 input: Name("foo").In(Value(5), Value(7)), 957 expectedNode: exprNode{ 958 children: []exprNode{ 959 { 960 names: []string{"foo"}, 961 fmtExpr: "$n", 962 }, 963 { 964 values: []dynamodb.AttributeValue{ 965 { 966 N: aws.String("5"), 967 }, 968 }, 969 fmtExpr: "$v", 970 }, 971 { 972 values: []dynamodb.AttributeValue{ 973 { 974 N: aws.String("7"), 975 }, 976 }, 977 fmtExpr: "$v", 978 }, 979 }, 980 fmtExpr: "$c IN ($c, $c)", 981 }, 982 }, 983 { 984 name: "basic method in for value", 985 input: Value(6).In(Value(5), Value(7)), 986 expectedNode: exprNode{ 987 children: []exprNode{ 988 { 989 values: []dynamodb.AttributeValue{ 990 { 991 N: aws.String("6"), 992 }, 993 }, 994 fmtExpr: "$v", 995 }, 996 { 997 values: []dynamodb.AttributeValue{ 998 { 999 N: aws.String("5"), 1000 }, 1001 }, 1002 fmtExpr: "$v", 1003 }, 1004 { 1005 values: []dynamodb.AttributeValue{ 1006 { 1007 N: aws.String("7"), 1008 }, 1009 }, 1010 fmtExpr: "$v", 1011 }, 1012 }, 1013 fmtExpr: "$c IN ($c, $c)", 1014 }, 1015 }, 1016 { 1017 name: "basic method in for size", 1018 input: Name("foo").Size().In(Value(5), Value(7)), 1019 expectedNode: exprNode{ 1020 children: []exprNode{ 1021 { 1022 names: []string{"foo"}, 1023 fmtExpr: "size ($n)", 1024 }, 1025 { 1026 values: []dynamodb.AttributeValue{ 1027 { 1028 N: aws.String("5"), 1029 }, 1030 }, 1031 fmtExpr: "$v", 1032 }, 1033 { 1034 values: []dynamodb.AttributeValue{ 1035 { 1036 N: aws.String("7"), 1037 }, 1038 }, 1039 fmtExpr: "$v", 1040 }, 1041 }, 1042 fmtExpr: "$c IN ($c, $c)", 1043 }, 1044 }, 1045 { 1046 name: "invalid operand error in", 1047 input: Name("[5]").In(Value(3), Name("foo..bar")), 1048 err: invalidConditionOperand, 1049 }, 1050 } 1051 1052 for _, c := range cases { 1053 t.Run(c.name, func(t *testing.T) { 1054 actual, err := c.input.buildTree() 1055 if c.err != noConditionError { 1056 if err == nil { 1057 t.Errorf("expect error %q, got no error", c.err) 1058 } else { 1059 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 1060 t.Errorf("expect %q error message to be in %q", e, a) 1061 } 1062 } 1063 } else { 1064 if err != nil { 1065 t.Errorf("expect no error, got unexpected Error %q", err) 1066 } 1067 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 1068 t.Errorf("expect %v, got %v", e, a) 1069 } 1070 } 1071 }) 1072 } 1073 } 1074 1075 func TestAttrExistsCondition(t *testing.T) { 1076 cases := []struct { 1077 name string 1078 input ConditionBuilder 1079 expectedNode exprNode 1080 err condErrorMode 1081 }{ 1082 { 1083 name: "basic attr exists", 1084 input: Name("foo").AttributeExists(), 1085 expectedNode: exprNode{ 1086 children: []exprNode{ 1087 { 1088 names: []string{"foo"}, 1089 fmtExpr: "$n", 1090 }, 1091 }, 1092 fmtExpr: "attribute_exists ($c)", 1093 }, 1094 }, 1095 { 1096 name: "basic attr not exists", 1097 input: Name("foo").AttributeNotExists(), 1098 expectedNode: exprNode{ 1099 children: []exprNode{ 1100 { 1101 names: []string{"foo"}, 1102 fmtExpr: "$n", 1103 }, 1104 }, 1105 fmtExpr: "attribute_not_exists ($c)", 1106 }, 1107 }, 1108 { 1109 name: "invalid operand error attr exists", 1110 input: AttributeExists(Name("")), 1111 err: invalidConditionOperand, 1112 }, 1113 { 1114 name: "invalid operand error attr not exists", 1115 input: AttributeNotExists(Name("foo..bar")), 1116 err: invalidConditionOperand, 1117 }, 1118 } 1119 1120 for _, c := range cases { 1121 t.Run(c.name, func(t *testing.T) { 1122 actual, err := c.input.buildTree() 1123 if c.err != noConditionError { 1124 if err == nil { 1125 t.Errorf("expect error %q, got no error", c.err) 1126 } else { 1127 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 1128 t.Errorf("expect %q error message to be in %q", e, a) 1129 } 1130 } 1131 } else { 1132 if err != nil { 1133 t.Errorf("expect no error, got unexpected Error %q", err) 1134 } 1135 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 1136 t.Errorf("expect %v, got %v", e, a) 1137 } 1138 } 1139 }) 1140 } 1141 } 1142 1143 func TestAttrTypeCondition(t *testing.T) { 1144 cases := []struct { 1145 name string 1146 input ConditionBuilder 1147 expectedNode exprNode 1148 err condErrorMode 1149 }{ 1150 { 1151 name: "attr type String", 1152 input: Name("foo").AttributeType(String), 1153 expectedNode: exprNode{ 1154 children: []exprNode{ 1155 { 1156 names: []string{"foo"}, 1157 fmtExpr: "$n", 1158 }, 1159 { 1160 values: []dynamodb.AttributeValue{ 1161 { 1162 S: aws.String("S"), 1163 }, 1164 }, 1165 fmtExpr: "$v", 1166 }, 1167 }, 1168 fmtExpr: "attribute_type ($c, $c)", 1169 }, 1170 }, 1171 { 1172 name: "attr type String", 1173 input: Name("foo").AttributeType(String), 1174 expectedNode: exprNode{ 1175 children: []exprNode{ 1176 { 1177 names: []string{"foo"}, 1178 fmtExpr: "$n", 1179 }, 1180 { 1181 values: []dynamodb.AttributeValue{ 1182 { 1183 S: aws.String("S"), 1184 }, 1185 }, 1186 fmtExpr: "$v", 1187 }, 1188 }, 1189 fmtExpr: "attribute_type ($c, $c)", 1190 }, 1191 }, 1192 { 1193 name: "attr type StringSet", 1194 input: Name("foo").AttributeType(StringSet), 1195 expectedNode: exprNode{ 1196 children: []exprNode{ 1197 { 1198 names: []string{"foo"}, 1199 fmtExpr: "$n", 1200 }, 1201 { 1202 values: []dynamodb.AttributeValue{ 1203 { 1204 S: aws.String("SS"), 1205 }, 1206 }, 1207 fmtExpr: "$v", 1208 }, 1209 }, 1210 fmtExpr: "attribute_type ($c, $c)", 1211 }, 1212 }, 1213 { 1214 name: "attr type Number", 1215 input: Name("foo").AttributeType(Number), 1216 expectedNode: exprNode{ 1217 children: []exprNode{ 1218 { 1219 names: []string{"foo"}, 1220 fmtExpr: "$n", 1221 }, 1222 { 1223 values: []dynamodb.AttributeValue{ 1224 { 1225 S: aws.String("N"), 1226 }, 1227 }, 1228 fmtExpr: "$v", 1229 }, 1230 }, 1231 fmtExpr: "attribute_type ($c, $c)", 1232 }, 1233 }, 1234 { 1235 name: "attr type NumberSet", 1236 input: Name("foo").AttributeType(NumberSet), 1237 expectedNode: exprNode{ 1238 children: []exprNode{ 1239 { 1240 names: []string{"foo"}, 1241 fmtExpr: "$n", 1242 }, 1243 { 1244 values: []dynamodb.AttributeValue{ 1245 { 1246 S: aws.String("NS"), 1247 }, 1248 }, 1249 fmtExpr: "$v", 1250 }, 1251 }, 1252 fmtExpr: "attribute_type ($c, $c)", 1253 }, 1254 }, 1255 { 1256 name: "attr type Binary", 1257 input: Name("foo").AttributeType(Binary), 1258 expectedNode: exprNode{ 1259 children: []exprNode{ 1260 { 1261 names: []string{"foo"}, 1262 fmtExpr: "$n", 1263 }, 1264 { 1265 values: []dynamodb.AttributeValue{ 1266 { 1267 S: aws.String("B"), 1268 }, 1269 }, 1270 fmtExpr: "$v", 1271 }, 1272 }, 1273 fmtExpr: "attribute_type ($c, $c)", 1274 }, 1275 }, 1276 { 1277 name: "attr type BinarySet", 1278 input: Name("foo").AttributeType(BinarySet), 1279 expectedNode: exprNode{ 1280 children: []exprNode{ 1281 { 1282 names: []string{"foo"}, 1283 fmtExpr: "$n", 1284 }, 1285 { 1286 values: []dynamodb.AttributeValue{ 1287 { 1288 S: aws.String("BS"), 1289 }, 1290 }, 1291 fmtExpr: "$v", 1292 }, 1293 }, 1294 fmtExpr: "attribute_type ($c, $c)", 1295 }, 1296 }, 1297 { 1298 name: "attr type Boolean", 1299 input: Name("foo").AttributeType(Boolean), 1300 expectedNode: exprNode{ 1301 children: []exprNode{ 1302 { 1303 names: []string{"foo"}, 1304 fmtExpr: "$n", 1305 }, 1306 { 1307 values: []dynamodb.AttributeValue{ 1308 { 1309 S: aws.String("BOOL"), 1310 }, 1311 }, 1312 fmtExpr: "$v", 1313 }, 1314 }, 1315 fmtExpr: "attribute_type ($c, $c)", 1316 }, 1317 }, 1318 { 1319 name: "attr type Null", 1320 input: Name("foo").AttributeType(Null), 1321 expectedNode: exprNode{ 1322 children: []exprNode{ 1323 { 1324 names: []string{"foo"}, 1325 fmtExpr: "$n", 1326 }, 1327 { 1328 values: []dynamodb.AttributeValue{ 1329 { 1330 S: aws.String("NULL"), 1331 }, 1332 }, 1333 fmtExpr: "$v", 1334 }, 1335 }, 1336 fmtExpr: "attribute_type ($c, $c)", 1337 }, 1338 }, 1339 { 1340 name: "attr type List", 1341 input: Name("foo").AttributeType(List), 1342 expectedNode: exprNode{ 1343 children: []exprNode{ 1344 { 1345 names: []string{"foo"}, 1346 fmtExpr: "$n", 1347 }, 1348 { 1349 values: []dynamodb.AttributeValue{ 1350 { 1351 S: aws.String("L"), 1352 }, 1353 }, 1354 fmtExpr: "$v", 1355 }, 1356 }, 1357 fmtExpr: "attribute_type ($c, $c)", 1358 }, 1359 }, 1360 { 1361 name: "attr type Map", 1362 input: Name("foo").AttributeType(Map), 1363 expectedNode: exprNode{ 1364 children: []exprNode{ 1365 { 1366 names: []string{"foo"}, 1367 fmtExpr: "$n", 1368 }, 1369 { 1370 values: []dynamodb.AttributeValue{ 1371 { 1372 S: aws.String("M"), 1373 }, 1374 }, 1375 fmtExpr: "$v", 1376 }, 1377 }, 1378 fmtExpr: "attribute_type ($c, $c)", 1379 }, 1380 }, 1381 { 1382 name: "attr type invalid operand", 1383 input: Name("").AttributeType(Map), 1384 err: invalidConditionOperand, 1385 }, 1386 } 1387 1388 for _, c := range cases { 1389 t.Run(c.name, func(t *testing.T) { 1390 actual, err := c.input.buildTree() 1391 if c.err != noConditionError { 1392 if err == nil { 1393 t.Errorf("expect error %q, got no error", c.err) 1394 } else { 1395 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 1396 t.Errorf("expect %q error message to be in %q", e, a) 1397 } 1398 } 1399 } else { 1400 if err != nil { 1401 t.Errorf("expect no error, got unexpected Error %q", err) 1402 } 1403 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 1404 t.Errorf("expect %v, got %v", e, a) 1405 } 1406 } 1407 }) 1408 } 1409 } 1410 1411 func TestBeginsWithCondition(t *testing.T) { 1412 cases := []struct { 1413 name string 1414 input ConditionBuilder 1415 expectedNode exprNode 1416 err condErrorMode 1417 }{ 1418 { 1419 name: "basic begins with", 1420 input: Name("foo").BeginsWith("bar"), 1421 expectedNode: exprNode{ 1422 children: []exprNode{ 1423 { 1424 names: []string{"foo"}, 1425 fmtExpr: "$n", 1426 }, 1427 { 1428 values: []dynamodb.AttributeValue{ 1429 { 1430 S: aws.String("bar"), 1431 }, 1432 }, 1433 fmtExpr: "$v", 1434 }, 1435 }, 1436 fmtExpr: "begins_with ($c, $c)", 1437 }, 1438 }, 1439 { 1440 name: "begins with invalid operand", 1441 input: Name("").BeginsWith("bar"), 1442 err: invalidConditionOperand, 1443 }, 1444 } 1445 1446 for _, c := range cases { 1447 t.Run(c.name, func(t *testing.T) { 1448 actual, err := c.input.buildTree() 1449 if c.err != noConditionError { 1450 if err == nil { 1451 t.Errorf("expect error %q, got no error", c.err) 1452 } else { 1453 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 1454 t.Errorf("expect %q error message to be in %q", e, a) 1455 } 1456 } 1457 } else { 1458 if err != nil { 1459 t.Errorf("expect no error, got unexpected Error %q", err) 1460 } 1461 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 1462 t.Errorf("expect %v, got %v", e, a) 1463 } 1464 } 1465 }) 1466 } 1467 } 1468 1469 func TestContainsCondition(t *testing.T) { 1470 cases := []struct { 1471 name string 1472 input ConditionBuilder 1473 expectedNode exprNode 1474 err condErrorMode 1475 }{ 1476 { 1477 name: "basic contains", 1478 input: Name("foo").Contains("bar"), 1479 expectedNode: exprNode{ 1480 children: []exprNode{ 1481 { 1482 names: []string{"foo"}, 1483 fmtExpr: "$n", 1484 }, 1485 { 1486 values: []dynamodb.AttributeValue{ 1487 { 1488 S: aws.String("bar"), 1489 }, 1490 }, 1491 fmtExpr: "$v", 1492 }, 1493 }, 1494 fmtExpr: "contains ($c, $c)", 1495 }, 1496 }, 1497 { 1498 name: "contains invalid operand", 1499 input: Name("").Contains("bar"), 1500 err: invalidConditionOperand, 1501 }, 1502 } 1503 1504 for _, c := range cases { 1505 t.Run(c.name, func(t *testing.T) { 1506 actual, err := c.input.buildTree() 1507 if c.err != noConditionError { 1508 if err == nil { 1509 t.Errorf("expect error %q, got no error", c.err) 1510 } else { 1511 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 1512 t.Errorf("expect %q error message to be in %q", e, a) 1513 } 1514 } 1515 } else { 1516 if err != nil { 1517 t.Errorf("expect no error, got unexpected Error %q", err) 1518 } 1519 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 1520 t.Errorf("expect %v, got %v", e, a) 1521 } 1522 } 1523 }) 1524 } 1525 } 1526 1527 func TestCompoundBuildCondition(t *testing.T) { 1528 cases := []struct { 1529 name string 1530 inputCond ConditionBuilder 1531 expected string 1532 }{ 1533 { 1534 name: "and", 1535 inputCond: ConditionBuilder{ 1536 conditionList: []ConditionBuilder{ 1537 {}, 1538 {}, 1539 {}, 1540 {}, 1541 }, 1542 mode: andCond, 1543 }, 1544 expected: "($c) AND ($c) AND ($c) AND ($c)", 1545 }, 1546 { 1547 name: "or", 1548 inputCond: ConditionBuilder{ 1549 conditionList: []ConditionBuilder{ 1550 {}, 1551 {}, 1552 {}, 1553 {}, 1554 {}, 1555 {}, 1556 {}, 1557 }, 1558 mode: orCond, 1559 }, 1560 expected: "($c) OR ($c) OR ($c) OR ($c) OR ($c) OR ($c) OR ($c)", 1561 }, 1562 } 1563 1564 for _, c := range cases { 1565 t.Run(c.name, func(t *testing.T) { 1566 en, err := compoundBuildCondition(c.inputCond, exprNode{}) 1567 if err != nil { 1568 t.Errorf("expect no error, got unexpected Error %q", err) 1569 } 1570 1571 if e, a := c.expected, en.fmtExpr; !reflect.DeepEqual(a, e) { 1572 t.Errorf("expect %v, got %v", e, a) 1573 } 1574 }) 1575 } 1576 } 1577 1578 func TestInBuildCondition(t *testing.T) { 1579 cases := []struct { 1580 name string 1581 inputCond ConditionBuilder 1582 expected string 1583 }{ 1584 { 1585 name: "in", 1586 inputCond: ConditionBuilder{ 1587 operandList: []OperandBuilder{ 1588 NameBuilder{}, 1589 NameBuilder{}, 1590 NameBuilder{}, 1591 NameBuilder{}, 1592 NameBuilder{}, 1593 NameBuilder{}, 1594 NameBuilder{}, 1595 }, 1596 mode: andCond, 1597 }, 1598 expected: "$c IN ($c, $c, $c, $c, $c, $c)", 1599 }, 1600 } 1601 1602 for _, c := range cases { 1603 t.Run(c.name, func(t *testing.T) { 1604 en, err := inBuildCondition(c.inputCond, exprNode{}) 1605 if err != nil { 1606 t.Errorf("expect no error, got unexpected Error %q", err) 1607 } 1608 1609 if e, a := c.expected, en.fmtExpr; !reflect.DeepEqual(a, e) { 1610 t.Errorf("expect %v, got %v", e, a) 1611 } 1612 }) 1613 } 1614 } 1615 1616 // If there is time implement mapEquals