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