github.com/aavshr/aws-sdk-go@v1.41.3/service/dynamodb/expression/update_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 // updateErrorMode will help with error cases and checking error types 16 type updateErrorMode string 17 18 const ( 19 noUpdateError updateErrorMode = "" 20 invalidUpdateOperand = "BuildOperand error" 21 unsetSetValue = "unset parameter: SetValueBuilder" 22 unsetUpdate = "unset parameter: UpdateBuilder" 23 emptyOperationBuilderList = "operationBuilder list is empty" 24 ) 25 26 func TestBuildOperation(t *testing.T) { 27 cases := []struct { 28 name string 29 input operationBuilder 30 expected exprNode 31 err updateErrorMode 32 }{ 33 { 34 name: "set operation", 35 input: operationBuilder{ 36 name: Name("foo"), 37 value: Value(5), 38 mode: setOperation, 39 }, 40 expected: exprNode{ 41 children: []exprNode{ 42 { 43 names: []string{"foo"}, 44 fmtExpr: "$n", 45 }, 46 { 47 values: []dynamodb.AttributeValue{ 48 { 49 N: aws.String("5"), 50 }, 51 }, 52 fmtExpr: "$v", 53 }, 54 }, 55 fmtExpr: "$c = $c", 56 }, 57 }, 58 { 59 name: "add operation", 60 input: operationBuilder{ 61 name: Name("foo"), 62 value: Value(5), 63 mode: addOperation, 64 }, 65 expected: exprNode{ 66 children: []exprNode{ 67 { 68 names: []string{"foo"}, 69 fmtExpr: "$n", 70 }, 71 { 72 values: []dynamodb.AttributeValue{ 73 { 74 N: aws.String("5"), 75 }, 76 }, 77 fmtExpr: "$v", 78 }, 79 }, 80 fmtExpr: "$c $c", 81 }, 82 }, 83 { 84 name: "remove operation", 85 input: operationBuilder{ 86 name: Name("foo"), 87 mode: removeOperation, 88 }, 89 expected: exprNode{ 90 children: []exprNode{ 91 { 92 names: []string{"foo"}, 93 fmtExpr: "$n", 94 }, 95 }, 96 fmtExpr: "$c", 97 }, 98 }, 99 { 100 name: "invalid operand", 101 input: operationBuilder{ 102 name: Name(""), 103 mode: removeOperation, 104 }, 105 err: invalidUpdateOperand, 106 }, 107 } 108 for _, c := range cases { 109 t.Run(c.name, func(t *testing.T) { 110 actual, err := c.input.buildOperation() 111 if c.err != noUpdateError { 112 if err == nil { 113 t.Errorf("expect error %q, got no error", c.err) 114 } else { 115 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 116 t.Errorf("expect %q error message to be in %q", e, a) 117 } 118 } 119 } else { 120 if err != nil { 121 t.Errorf("expect no error, got unexpected Error %q", err) 122 } 123 124 if e, a := c.expected, actual; !reflect.DeepEqual(a, e) { 125 t.Errorf("expect %v, got %v", e, a) 126 } 127 } 128 }) 129 } 130 } 131 132 func TestUpdateTree(t *testing.T) { 133 cases := []struct { 134 name string 135 input UpdateBuilder 136 expectedNode exprNode 137 err updateErrorMode 138 }{ 139 { 140 name: "set update", 141 input: Set(Name("foo"), Value(5)), 142 expectedNode: exprNode{ 143 children: []exprNode{ 144 { 145 children: []exprNode{ 146 { 147 children: []exprNode{ 148 { 149 names: []string{"foo"}, 150 fmtExpr: "$n", 151 }, 152 { 153 values: []dynamodb.AttributeValue{ 154 { 155 N: aws.String("5"), 156 }, 157 }, 158 fmtExpr: "$v", 159 }, 160 }, 161 fmtExpr: "$c = $c", 162 }, 163 }, 164 fmtExpr: "$c", 165 }, 166 }, 167 fmtExpr: "SET $c\n", 168 }, 169 }, 170 { 171 name: "remove update", 172 input: Remove(Name("foo")), 173 expectedNode: exprNode{ 174 children: []exprNode{ 175 { 176 children: []exprNode{ 177 { 178 children: []exprNode{ 179 { 180 names: []string{"foo"}, 181 fmtExpr: "$n", 182 }, 183 }, 184 fmtExpr: "$c", 185 }, 186 }, 187 fmtExpr: "$c", 188 }, 189 }, 190 fmtExpr: "REMOVE $c\n", 191 }, 192 }, 193 { 194 name: "add update", 195 input: Add(Name("foo"), Value(5)), 196 expectedNode: exprNode{ 197 children: []exprNode{ 198 { 199 children: []exprNode{ 200 { 201 children: []exprNode{ 202 { 203 names: []string{"foo"}, 204 fmtExpr: "$n", 205 }, 206 { 207 values: []dynamodb.AttributeValue{ 208 { 209 N: aws.String("5"), 210 }, 211 }, 212 fmtExpr: "$v", 213 }, 214 }, 215 fmtExpr: "$c $c", 216 }, 217 }, 218 fmtExpr: "$c", 219 }, 220 }, 221 fmtExpr: "ADD $c\n", 222 }, 223 }, 224 { 225 name: "delete update", 226 input: Delete(Name("foo"), Value(5)), 227 expectedNode: exprNode{ 228 children: []exprNode{ 229 { 230 children: []exprNode{ 231 { 232 children: []exprNode{ 233 { 234 names: []string{"foo"}, 235 fmtExpr: "$n", 236 }, 237 { 238 values: []dynamodb.AttributeValue{ 239 { 240 N: aws.String("5"), 241 }, 242 }, 243 fmtExpr: "$v", 244 }, 245 }, 246 fmtExpr: "$c $c", 247 }, 248 }, 249 fmtExpr: "$c", 250 }, 251 }, 252 fmtExpr: "DELETE $c\n", 253 }, 254 }, 255 { 256 name: "multiple sets", 257 input: Set(Name("foo"), Value(5)).Set(Name("bar"), Value(6)).Set(Name("baz"), Name("qux")), 258 expectedNode: exprNode{ 259 fmtExpr: "SET $c\n", 260 children: []exprNode{ 261 { 262 fmtExpr: "$c, $c, $c", 263 children: []exprNode{ 264 { 265 fmtExpr: "$c = $c", 266 children: []exprNode{ 267 { 268 fmtExpr: "$n", 269 names: []string{"foo"}, 270 }, 271 { 272 fmtExpr: "$v", 273 values: []dynamodb.AttributeValue{ 274 { 275 N: aws.String("5"), 276 }, 277 }, 278 }, 279 }, 280 }, 281 { 282 fmtExpr: "$c = $c", 283 children: []exprNode{ 284 { 285 fmtExpr: "$n", 286 names: []string{"bar"}, 287 }, 288 { 289 fmtExpr: "$v", 290 values: []dynamodb.AttributeValue{ 291 { 292 N: aws.String("6"), 293 }, 294 }, 295 }, 296 }, 297 }, 298 { 299 fmtExpr: "$c = $c", 300 children: []exprNode{ 301 { 302 fmtExpr: "$n", 303 names: []string{"baz"}, 304 }, 305 { 306 fmtExpr: "$n", 307 names: []string{"qux"}, 308 }, 309 }, 310 }, 311 }, 312 }, 313 }, 314 }, 315 }, 316 { 317 name: "compound update", 318 input: Add(Name("foo"), Value(5)).Set(Name("foo"), Value(5)).Delete(Name("foo"), Value(5)).Remove(Name("foo")), 319 expectedNode: exprNode{ 320 children: []exprNode{ 321 { 322 children: []exprNode{ 323 { 324 children: []exprNode{ 325 { 326 names: []string{"foo"}, 327 fmtExpr: "$n", 328 }, 329 { 330 values: []dynamodb.AttributeValue{ 331 { 332 N: aws.String("5"), 333 }, 334 }, 335 fmtExpr: "$v", 336 }, 337 }, 338 fmtExpr: "$c $c", 339 }, 340 }, 341 fmtExpr: "$c", 342 }, 343 { 344 children: []exprNode{ 345 { 346 children: []exprNode{ 347 { 348 names: []string{"foo"}, 349 fmtExpr: "$n", 350 }, 351 { 352 values: []dynamodb.AttributeValue{ 353 { 354 N: aws.String("5"), 355 }, 356 }, 357 fmtExpr: "$v", 358 }, 359 }, 360 fmtExpr: "$c $c", 361 }, 362 }, 363 fmtExpr: "$c", 364 }, 365 { 366 children: []exprNode{ 367 { 368 children: []exprNode{ 369 { 370 names: []string{"foo"}, 371 fmtExpr: "$n", 372 }, 373 }, 374 fmtExpr: "$c", 375 }, 376 }, 377 fmtExpr: "$c", 378 }, 379 { 380 children: []exprNode{ 381 { 382 children: []exprNode{ 383 { 384 names: []string{"foo"}, 385 fmtExpr: "$n", 386 }, 387 { 388 values: []dynamodb.AttributeValue{ 389 { 390 N: aws.String("5"), 391 }, 392 }, 393 fmtExpr: "$v", 394 }, 395 }, 396 fmtExpr: "$c = $c", 397 }, 398 }, 399 fmtExpr: "$c", 400 }, 401 }, 402 fmtExpr: "ADD $c\nDELETE $c\nREMOVE $c\nSET $c\n", 403 }, 404 }, 405 { 406 name: "empty UpdateBuilder", 407 input: UpdateBuilder{}, 408 err: unsetUpdate, 409 }, 410 } 411 for _, c := range cases { 412 t.Run(c.name, func(t *testing.T) { 413 actual, err := c.input.buildTree() 414 if c.err != noUpdateError { 415 if err == nil { 416 t.Errorf("expect error %q, got no error", c.err) 417 } else { 418 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 419 t.Errorf("expect %q error message to be in %q", e, a) 420 } 421 } 422 } else { 423 if err != nil { 424 t.Errorf("expect no error, got unexpected Error %q", err) 425 } 426 427 if e, a := c.expectedNode, actual; !reflect.DeepEqual(a, e) { 428 t.Errorf("expect %v, got %v", e, a) 429 } 430 } 431 }) 432 } 433 } 434 435 func TestSetValueBuilder(t *testing.T) { 436 cases := []struct { 437 name string 438 input SetValueBuilder 439 expected exprNode 440 err updateErrorMode 441 }{ 442 { 443 name: "name plus name", 444 input: Name("foo").Plus(Name("bar")), 445 expected: exprNode{ 446 children: []exprNode{ 447 { 448 names: []string{"foo"}, 449 fmtExpr: "$n", 450 }, 451 { 452 names: []string{"bar"}, 453 fmtExpr: "$n", 454 }, 455 }, 456 fmtExpr: "$c + $c", 457 }, 458 }, 459 { 460 name: "name minus name", 461 input: Name("foo").Minus(Name("bar")), 462 expected: exprNode{ 463 children: []exprNode{ 464 { 465 names: []string{"foo"}, 466 fmtExpr: "$n", 467 }, 468 { 469 names: []string{"bar"}, 470 fmtExpr: "$n", 471 }, 472 }, 473 fmtExpr: "$c - $c", 474 }, 475 }, 476 { 477 name: "list append name and name", 478 input: Name("foo").ListAppend(Name("bar")), 479 expected: exprNode{ 480 children: []exprNode{ 481 { 482 names: []string{"foo"}, 483 fmtExpr: "$n", 484 }, 485 { 486 names: []string{"bar"}, 487 fmtExpr: "$n", 488 }, 489 }, 490 fmtExpr: "list_append($c, $c)", 491 }, 492 }, 493 { 494 name: "if not exists name and name", 495 input: Name("foo").IfNotExists(Name("bar")), 496 expected: exprNode{ 497 children: []exprNode{ 498 { 499 names: []string{"foo"}, 500 fmtExpr: "$n", 501 }, 502 { 503 names: []string{"bar"}, 504 fmtExpr: "$n", 505 }, 506 }, 507 fmtExpr: "if_not_exists($c, $c)", 508 }, 509 }, 510 { 511 name: "value plus name", 512 input: Value(5).Plus(Name("bar")), 513 expected: exprNode{ 514 children: []exprNode{ 515 { 516 values: []dynamodb.AttributeValue{ 517 { 518 N: aws.String("5"), 519 }, 520 }, 521 fmtExpr: "$v", 522 }, 523 { 524 names: []string{"bar"}, 525 fmtExpr: "$n", 526 }, 527 }, 528 fmtExpr: "$c + $c", 529 }, 530 }, 531 { 532 name: "value minus name", 533 input: Value(5).Minus(Name("bar")), 534 expected: exprNode{ 535 children: []exprNode{ 536 { 537 values: []dynamodb.AttributeValue{ 538 { 539 N: aws.String("5"), 540 }, 541 }, 542 fmtExpr: "$v", 543 }, 544 { 545 names: []string{"bar"}, 546 fmtExpr: "$n", 547 }, 548 }, 549 fmtExpr: "$c - $c", 550 }, 551 }, 552 { 553 name: "list append list and name", 554 input: Value([]int{1, 2, 3}).ListAppend(Name("bar")), 555 expected: exprNode{ 556 children: []exprNode{ 557 { 558 values: []dynamodb.AttributeValue{ 559 { 560 L: []*dynamodb.AttributeValue{ 561 { 562 N: aws.String("1"), 563 }, 564 { 565 N: aws.String("2"), 566 }, 567 { 568 N: aws.String("3"), 569 }, 570 }, 571 }, 572 }, 573 fmtExpr: "$v", 574 }, 575 { 576 names: []string{"bar"}, 577 fmtExpr: "$n", 578 }, 579 }, 580 fmtExpr: "list_append($c, $c)", 581 }, 582 }, 583 { 584 name: "unset SetValueBuilder", 585 input: SetValueBuilder{}, 586 err: unsetSetValue, 587 }, 588 { 589 name: "invalid operand error", 590 input: Name("").Plus(Name("foo")), 591 err: invalidUpdateOperand, 592 }, 593 { 594 name: "invalid operand error", 595 input: Name("foo").Plus(Name("")), 596 err: invalidUpdateOperand, 597 }, 598 } 599 for _, c := range cases { 600 t.Run(c.name, func(t *testing.T) { 601 actual, err := c.input.BuildOperand() 602 if c.err != noUpdateError { 603 if err == nil { 604 t.Errorf("expect error %q, got no error", c.err) 605 } else { 606 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 607 t.Errorf("expect %q error message to be in %q", e, a) 608 } 609 } 610 } else { 611 if err != nil { 612 t.Errorf("expect no error, got unexpected Error %q", err) 613 } 614 615 if e, a := c.expected, actual.exprNode; !reflect.DeepEqual(a, e) { 616 t.Errorf("expect %v, got %v", e, a) 617 } 618 } 619 }) 620 } 621 } 622 623 func TestUpdateBuildChildNodes(t *testing.T) { 624 cases := []struct { 625 name string 626 input []operationBuilder 627 expected exprNode 628 err updateErrorMode 629 }{ 630 { 631 name: "set operand builder", 632 input: []operationBuilder{ 633 { 634 mode: setOperation, 635 name: NameBuilder{ 636 name: "foo", 637 }, 638 value: ValueBuilder{ 639 value: 5, 640 }, 641 }, 642 { 643 mode: setOperation, 644 name: NameBuilder{ 645 name: "bar", 646 }, 647 value: ValueBuilder{ 648 value: 6, 649 }, 650 }, 651 { 652 mode: setOperation, 653 name: NameBuilder{ 654 name: "baz", 655 }, 656 value: ValueBuilder{ 657 value: 7, 658 }, 659 }, 660 { 661 mode: setOperation, 662 name: NameBuilder{ 663 name: "qux", 664 }, 665 value: ValueBuilder{ 666 value: 8, 667 }, 668 }, 669 }, 670 expected: exprNode{ 671 fmtExpr: "$c, $c, $c, $c", 672 children: []exprNode{ 673 { 674 fmtExpr: "$c = $c", 675 children: []exprNode{ 676 { 677 fmtExpr: "$n", 678 names: []string{"foo"}, 679 }, 680 { 681 fmtExpr: "$v", 682 values: []dynamodb.AttributeValue{ 683 { 684 N: aws.String("5"), 685 }, 686 }, 687 }, 688 }, 689 }, 690 { 691 fmtExpr: "$c = $c", 692 children: []exprNode{ 693 { 694 fmtExpr: "$n", 695 names: []string{"bar"}, 696 }, 697 { 698 fmtExpr: "$v", 699 values: []dynamodb.AttributeValue{ 700 { 701 N: aws.String("6"), 702 }, 703 }, 704 }, 705 }, 706 }, 707 { 708 fmtExpr: "$c = $c", 709 children: []exprNode{ 710 { 711 fmtExpr: "$n", 712 names: []string{"baz"}, 713 }, 714 { 715 fmtExpr: "$v", 716 values: []dynamodb.AttributeValue{ 717 { 718 N: aws.String("7"), 719 }, 720 }, 721 }, 722 }, 723 }, 724 { 725 fmtExpr: "$c = $c", 726 children: []exprNode{ 727 { 728 fmtExpr: "$n", 729 names: []string{"qux"}, 730 }, 731 { 732 fmtExpr: "$v", 733 values: []dynamodb.AttributeValue{ 734 { 735 N: aws.String("8"), 736 }, 737 }, 738 }, 739 }, 740 }, 741 }, 742 }, 743 }, 744 { 745 name: "empty operationBuilder list", 746 input: []operationBuilder{}, 747 err: emptyOperationBuilderList, 748 }, 749 } 750 for _, c := range cases { 751 t.Run(c.name, func(t *testing.T) { 752 actual, err := buildChildNodes(c.input) 753 if c.err != noUpdateError { 754 if err == nil { 755 t.Errorf("expect error %q, got no error", c.err) 756 } else { 757 if e, a := string(c.err), err.Error(); !strings.Contains(a, e) { 758 t.Errorf("expect %q error message to be in %q", e, a) 759 } 760 } 761 } else { 762 if err != nil { 763 t.Errorf("expect no error, got unexpected Error %q", err) 764 } 765 766 if e, a := c.expected, actual; !reflect.DeepEqual(a, e) { 767 t.Errorf("expect %v, got %v", e, a) 768 } 769 } 770 }) 771 } 772 }