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