condition_test.go 32 KB

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