update_test.go 15 KB

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