1package dynamodbattribute
2
3import (
4	"reflect"
5	"testing"
6
7	"github.com/aws/aws-sdk-go/aws"
8	"github.com/aws/aws-sdk-go/service/dynamodb"
9)
10
11type testEmptyCollectionsNumericalScalars struct {
12	String string
13
14	Uint8  uint8
15	Uint16 uint16
16	Uint32 uint32
17	Uint64 uint64
18
19	Int8  int8
20	Int16 int16
21	Int32 int32
22	Int64 int64
23
24	Float32 float32
25	Float64 float64
26}
27
28type testEmptyCollectionsOmittedNumericalScalars struct {
29	String string `dynamodbav:",omitempty"`
30
31	Uint8  uint8  `dynamodbav:",omitempty"`
32	Uint16 uint16 `dynamodbav:",omitempty"`
33	Uint32 uint32 `dynamodbav:",omitempty"`
34	Uint64 uint64 `dynamodbav:",omitempty"`
35
36	Int8  int8  `dynamodbav:",omitempty"`
37	Int16 int16 `dynamodbav:",omitempty"`
38	Int32 int32 `dynamodbav:",omitempty"`
39	Int64 int64 `dynamodbav:",omitempty"`
40
41	Float32 float32 `dynamodbav:",omitempty"`
42	Float64 float64 `dynamodbav:",omitempty"`
43}
44
45type testEmptyCollectionsPtrScalars struct {
46	PtrString *string
47
48	PtrUint8  *uint8
49	PtrUint16 *uint16
50	PtrUint32 *uint32
51	PtrUint64 *uint64
52
53	PtrInt8  *int8
54	PtrInt16 *int16
55	PtrInt32 *int32
56	PtrInt64 *int64
57
58	PtrFloat32 *float32
59	PtrFloat64 *float64
60}
61
62type testEmptyCollectionsOmittedPtrNumericalScalars struct {
63	PtrUint8  *uint8  `dynamodbav:",omitempty"`
64	PtrUint16 *uint16 `dynamodbav:",omitempty"`
65	PtrUint32 *uint32 `dynamodbav:",omitempty"`
66	PtrUint64 *uint64 `dynamodbav:",omitempty"`
67
68	PtrInt8  *int8  `dynamodbav:",omitempty"`
69	PtrInt16 *int16 `dynamodbav:",omitempty"`
70	PtrInt32 *int32 `dynamodbav:",omitempty"`
71	PtrInt64 *int64 `dynamodbav:",omitempty"`
72
73	PtrFloat32 *float32 `dynamodbav:",omitempty"`
74	PtrFloat64 *float64 `dynamodbav:",omitempty"`
75}
76
77type testEmptyCollectionTypes struct {
78	Map       map[string]string
79	Slice     []string
80	ByteSlice []byte
81	ByteArray [4]byte
82	ZeroArray [0]byte
83	BinarySet [][]byte `dynamodbav:",binaryset"`
84	NumberSet []int    `dynamodbav:",numberset"`
85	StringSet []string `dynamodbav:",stringset"`
86}
87
88type testEmptyCollectionTypesOmitted struct {
89	Map       map[string]string `dynamodbav:",omitempty"`
90	Slice     []string          `dynamodbav:",omitempty"`
91	ByteSlice []byte            `dynamodbav:",omitempty"`
92	ByteArray [4]byte           `dynamodbav:",omitempty"`
93	ZeroArray [0]byte           `dynamodbav:",omitempty"`
94	BinarySet [][]byte          `dynamodbav:",binaryset,omitempty"`
95	NumberSet []int             `dynamodbav:",numberset,omitempty"`
96	StringSet []string          `dynamodbav:",stringset,omitempty"`
97}
98
99type testEmptyCollectionStruct struct {
100	Int int
101}
102
103type testEmptyCollectionStructOmitted struct {
104	Slice []string `dynamodbav:",omitempty"`
105}
106
107var sharedEmptyCollectionsTestCases = []struct {
108	encoderOpts      func(encoder *Encoder)
109	in               *dynamodb.AttributeValue
110	actual, expected interface{}
111	err              error
112}{
113	// scalars with zero value
114	0: {
115		in: &dynamodb.AttributeValue{
116			M: map[string]*dynamodb.AttributeValue{
117				"String":  {NULL: aws.Bool(true)},
118				"Uint8":   {N: aws.String("0")},
119				"Uint16":  {N: aws.String("0")},
120				"Uint32":  {N: aws.String("0")},
121				"Uint64":  {N: aws.String("0")},
122				"Int8":    {N: aws.String("0")},
123				"Int16":   {N: aws.String("0")},
124				"Int32":   {N: aws.String("0")},
125				"Int64":   {N: aws.String("0")},
126				"Float32": {N: aws.String("0")},
127				"Float64": {N: aws.String("0")},
128			},
129		},
130		actual:   &testEmptyCollectionsNumericalScalars{},
131		expected: testEmptyCollectionsNumericalScalars{},
132	},
133	// scalars with non-zero values
134	1: {
135		in: &dynamodb.AttributeValue{
136			M: map[string]*dynamodb.AttributeValue{
137				"String":  {S: aws.String("test string")},
138				"Uint8":   {N: aws.String("1")},
139				"Uint16":  {N: aws.String("2")},
140				"Uint32":  {N: aws.String("3")},
141				"Uint64":  {N: aws.String("4")},
142				"Int8":    {N: aws.String("-5")},
143				"Int16":   {N: aws.String("-6")},
144				"Int32":   {N: aws.String("-7")},
145				"Int64":   {N: aws.String("-8")},
146				"Float32": {N: aws.String("9.9")},
147				"Float64": {N: aws.String("10.1")},
148			},
149		},
150		actual: &testEmptyCollectionsNumericalScalars{},
151		expected: testEmptyCollectionsNumericalScalars{
152			String:  "test string",
153			Uint8:   1,
154			Uint16:  2,
155			Uint32:  3,
156			Uint64:  4,
157			Int8:    -5,
158			Int16:   -6,
159			Int32:   -7,
160			Int64:   -8,
161			Float32: 9.9,
162			Float64: 10.1,
163		},
164	},
165	// omittable scalars with zero value
166	2: {
167		in:       &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{}},
168		actual:   &testEmptyCollectionsOmittedNumericalScalars{},
169		expected: testEmptyCollectionsOmittedNumericalScalars{},
170	},
171	// omittable scalars with non-zero value
172	3: {
173		in: &dynamodb.AttributeValue{
174			M: map[string]*dynamodb.AttributeValue{
175				"String":  {S: aws.String("test string")},
176				"Uint8":   {N: aws.String("1")},
177				"Uint16":  {N: aws.String("2")},
178				"Uint32":  {N: aws.String("3")},
179				"Uint64":  {N: aws.String("4")},
180				"Int8":    {N: aws.String("-5")},
181				"Int16":   {N: aws.String("-6")},
182				"Int32":   {N: aws.String("-7")},
183				"Int64":   {N: aws.String("-8")},
184				"Float32": {N: aws.String("9.9")},
185				"Float64": {N: aws.String("10.1")},
186			},
187		},
188		actual: &testEmptyCollectionsOmittedNumericalScalars{},
189		expected: testEmptyCollectionsOmittedNumericalScalars{
190			String:  "test string",
191			Uint8:   1,
192			Uint16:  2,
193			Uint32:  3,
194			Uint64:  4,
195			Int8:    -5,
196			Int16:   -6,
197			Int32:   -7,
198			Int64:   -8,
199			Float32: 9.9,
200			Float64: 10.1,
201		},
202	},
203	// nil pointer scalars
204	4: {
205		in: &dynamodb.AttributeValue{
206			M: map[string]*dynamodb.AttributeValue{
207				"PtrString":  {NULL: aws.Bool(true)},
208				"PtrUint8":   {NULL: aws.Bool(true)},
209				"PtrUint16":  {NULL: aws.Bool(true)},
210				"PtrUint32":  {NULL: aws.Bool(true)},
211				"PtrUint64":  {NULL: aws.Bool(true)},
212				"PtrInt8":    {NULL: aws.Bool(true)},
213				"PtrInt16":   {NULL: aws.Bool(true)},
214				"PtrInt32":   {NULL: aws.Bool(true)},
215				"PtrInt64":   {NULL: aws.Bool(true)},
216				"PtrFloat32": {NULL: aws.Bool(true)},
217				"PtrFloat64": {NULL: aws.Bool(true)},
218			},
219		},
220		actual:   &testEmptyCollectionsPtrScalars{},
221		expected: testEmptyCollectionsPtrScalars{},
222	},
223	// non-nil pointer to scalars with zero value
224	5: {
225		in: &dynamodb.AttributeValue{
226			M: map[string]*dynamodb.AttributeValue{
227				"PtrString":  {NULL: aws.Bool(true)},
228				"PtrUint8":   {N: aws.String("0")},
229				"PtrUint16":  {N: aws.String("0")},
230				"PtrUint32":  {N: aws.String("0")},
231				"PtrUint64":  {N: aws.String("0")},
232				"PtrInt8":    {N: aws.String("0")},
233				"PtrInt16":   {N: aws.String("0")},
234				"PtrInt32":   {N: aws.String("0")},
235				"PtrInt64":   {N: aws.String("0")},
236				"PtrFloat32": {N: aws.String("0")},
237				"PtrFloat64": {N: aws.String("0")},
238			},
239		},
240		actual: &testEmptyCollectionsPtrScalars{},
241		expected: testEmptyCollectionsPtrScalars{
242			PtrUint8:   aws.Uint8(0),
243			PtrUint16:  aws.Uint16(0),
244			PtrUint32:  aws.Uint32(0),
245			PtrUint64:  aws.Uint64(0),
246			PtrInt8:    aws.Int8(0),
247			PtrInt16:   aws.Int16(0),
248			PtrInt32:   aws.Int32(0),
249			PtrInt64:   aws.Int64(0),
250			PtrFloat32: aws.Float32(0),
251			PtrFloat64: aws.Float64(0),
252		},
253	},
254	// pointer scalars non-nil non-zero
255	6: {
256		in: &dynamodb.AttributeValue{
257			M: map[string]*dynamodb.AttributeValue{
258				"PtrString":  {S: aws.String("test string")},
259				"PtrUint8":   {N: aws.String("1")},
260				"PtrUint16":  {N: aws.String("2")},
261				"PtrUint32":  {N: aws.String("3")},
262				"PtrUint64":  {N: aws.String("4")},
263				"PtrInt8":    {N: aws.String("-5")},
264				"PtrInt16":   {N: aws.String("-6")},
265				"PtrInt32":   {N: aws.String("-7")},
266				"PtrInt64":   {N: aws.String("-8")},
267				"PtrFloat32": {N: aws.String("9.9")},
268				"PtrFloat64": {N: aws.String("10.1")},
269			},
270		},
271		actual: &testEmptyCollectionsPtrScalars{},
272		expected: testEmptyCollectionsPtrScalars{
273			PtrString:  aws.String("test string"),
274			PtrUint8:   aws.Uint8(1),
275			PtrUint16:  aws.Uint16(2),
276			PtrUint32:  aws.Uint32(3),
277			PtrUint64:  aws.Uint64(4),
278			PtrInt8:    aws.Int8(-5),
279			PtrInt16:   aws.Int16(-6),
280			PtrInt32:   aws.Int32(-7),
281			PtrInt64:   aws.Int64(-8),
282			PtrFloat32: aws.Float32(9.9),
283			PtrFloat64: aws.Float64(10.1),
284		},
285	},
286	// omittable nil pointer scalars
287	7: {
288		in: &dynamodb.AttributeValue{
289			M: map[string]*dynamodb.AttributeValue{},
290		},
291		actual:   &testEmptyCollectionsOmittedPtrNumericalScalars{},
292		expected: testEmptyCollectionsOmittedPtrNumericalScalars{},
293	},
294	// omittable non-nil pointer to scalars with zero value
295	8: {
296		in: &dynamodb.AttributeValue{
297			M: map[string]*dynamodb.AttributeValue{
298				"PtrUint8":   {N: aws.String("0")},
299				"PtrUint16":  {N: aws.String("0")},
300				"PtrUint32":  {N: aws.String("0")},
301				"PtrUint64":  {N: aws.String("0")},
302				"PtrInt8":    {N: aws.String("0")},
303				"PtrInt16":   {N: aws.String("0")},
304				"PtrInt32":   {N: aws.String("0")},
305				"PtrInt64":   {N: aws.String("0")},
306				"PtrFloat32": {N: aws.String("0")},
307				"PtrFloat64": {N: aws.String("0")},
308			},
309		},
310		actual: &testEmptyCollectionsOmittedPtrNumericalScalars{},
311		expected: testEmptyCollectionsOmittedPtrNumericalScalars{
312			PtrUint8:   aws.Uint8(0),
313			PtrUint16:  aws.Uint16(0),
314			PtrUint32:  aws.Uint32(0),
315			PtrUint64:  aws.Uint64(0),
316			PtrInt8:    aws.Int8(0),
317			PtrInt16:   aws.Int16(0),
318			PtrInt32:   aws.Int32(0),
319			PtrInt64:   aws.Int64(0),
320			PtrFloat32: aws.Float32(0),
321			PtrFloat64: aws.Float64(0),
322		},
323	},
324	// omittable non-nil pointer to non-zero scalar
325	9: {
326		in: &dynamodb.AttributeValue{
327			M: map[string]*dynamodb.AttributeValue{
328				"PtrUint8":   {N: aws.String("1")},
329				"PtrUint16":  {N: aws.String("2")},
330				"PtrUint32":  {N: aws.String("3")},
331				"PtrUint64":  {N: aws.String("4")},
332				"PtrInt8":    {N: aws.String("-5")},
333				"PtrInt16":   {N: aws.String("-6")},
334				"PtrInt32":   {N: aws.String("-7")},
335				"PtrInt64":   {N: aws.String("-8")},
336				"PtrFloat32": {N: aws.String("9.9")},
337				"PtrFloat64": {N: aws.String("10.1")},
338			},
339		},
340		actual: &testEmptyCollectionsOmittedPtrNumericalScalars{},
341		expected: testEmptyCollectionsOmittedPtrNumericalScalars{
342			PtrUint8:   aws.Uint8(1),
343			PtrUint16:  aws.Uint16(2),
344			PtrUint32:  aws.Uint32(3),
345			PtrUint64:  aws.Uint64(4),
346			PtrInt8:    aws.Int8(-5),
347			PtrInt16:   aws.Int16(-6),
348			PtrInt32:   aws.Int32(-7),
349			PtrInt64:   aws.Int64(-8),
350			PtrFloat32: aws.Float32(9.9),
351			PtrFloat64: aws.Float64(10.1),
352		},
353	},
354	// maps, slices nil values
355	10: {
356		in: &dynamodb.AttributeValue{
357			M: map[string]*dynamodb.AttributeValue{
358				"Map":       {NULL: aws.Bool(true)},
359				"Slice":     {NULL: aws.Bool(true)},
360				"ByteSlice": {NULL: aws.Bool(true)},
361				"ByteArray": {B: make([]byte, 4)},
362				"ZeroArray": {B: make([]byte, 0)},
363				"BinarySet": {NULL: aws.Bool(true)},
364				"NumberSet": {NULL: aws.Bool(true)},
365				"StringSet": {NULL: aws.Bool(true)},
366			},
367		},
368		actual:   &testEmptyCollectionTypes{},
369		expected: testEmptyCollectionTypes{},
370	},
371	// maps, slices zero values
372	11: {
373		in: &dynamodb.AttributeValue{
374			M: map[string]*dynamodb.AttributeValue{
375				"Map":       {M: map[string]*dynamodb.AttributeValue{}},
376				"Slice":     {L: []*dynamodb.AttributeValue{}},
377				"ByteSlice": {B: []byte{}},
378				"ByteArray": {B: make([]byte, 4)},
379				"ZeroArray": {B: make([]byte, 0)},
380				"BinarySet": {BS: [][]byte{}},
381				"NumberSet": {NS: []*string{}},
382				"StringSet": {SS: []*string{}},
383			},
384		},
385		actual: &testEmptyCollectionTypes{},
386		expected: testEmptyCollectionTypes{
387			Map:       map[string]string{},
388			Slice:     []string{},
389			ByteSlice: []byte{},
390			ByteArray: [4]byte{},
391			ZeroArray: [0]byte{},
392			BinarySet: [][]byte{},
393			NumberSet: []int{},
394			StringSet: []string{},
395		},
396	},
397	// maps, slices non-zero values
398	12: {
399		in: &dynamodb.AttributeValue{
400			M: map[string]*dynamodb.AttributeValue{
401				"Map": {
402					M: map[string]*dynamodb.AttributeValue{
403						"key": {S: aws.String("value")},
404					},
405				},
406				"Slice":     {L: []*dynamodb.AttributeValue{{S: aws.String("test")}, {S: aws.String("slice")}}},
407				"ByteSlice": {B: []byte{0, 1}},
408				"ByteArray": {B: []byte{0, 1, 2, 3}},
409				"ZeroArray": {B: make([]byte, 0)},
410				"BinarySet": {BS: [][]byte{{0, 1}, {2, 3}}},
411				"NumberSet": {NS: []*string{aws.String("0"), aws.String("1")}},
412				"StringSet": {SS: []*string{aws.String("test"), aws.String("slice")}},
413			},
414		},
415		actual: &testEmptyCollectionTypes{},
416		expected: testEmptyCollectionTypes{
417			Map:       map[string]string{"key": "value"},
418			Slice:     []string{"test", "slice"},
419			ByteSlice: []byte{0, 1},
420			ByteArray: [4]byte{0, 1, 2, 3},
421			ZeroArray: [0]byte{},
422			BinarySet: [][]byte{{0, 1}, {2, 3}},
423			NumberSet: []int{0, 1},
424			StringSet: []string{"test", "slice"},
425		},
426	},
427	// omittable maps, slices nil values
428	13: {
429		in: &dynamodb.AttributeValue{
430			M: map[string]*dynamodb.AttributeValue{
431				"ByteArray": {B: make([]byte, 4)},
432			},
433		},
434		actual:   &testEmptyCollectionTypesOmitted{},
435		expected: testEmptyCollectionTypesOmitted{},
436	},
437	// omittable maps, slices zero values
438	14: {
439		in: &dynamodb.AttributeValue{
440			M: map[string]*dynamodb.AttributeValue{
441				"Map":       {M: map[string]*dynamodb.AttributeValue{}},
442				"Slice":     {L: []*dynamodb.AttributeValue{}},
443				"ByteSlice": {B: []byte{}},
444				"ByteArray": {B: make([]byte, 4)},
445				"BinarySet": {BS: [][]byte{}},
446				"NumberSet": {NS: []*string{}},
447				"StringSet": {SS: []*string{}},
448			},
449		},
450		actual: &testEmptyCollectionTypesOmitted{},
451		expected: testEmptyCollectionTypesOmitted{
452			Map:       map[string]string{},
453			Slice:     []string{},
454			ByteSlice: []byte{},
455			ByteArray: [4]byte{},
456			BinarySet: [][]byte{},
457			NumberSet: []int{},
458			StringSet: []string{},
459		},
460	},
461	// omittable maps, slices non-zero values
462	15: {
463		in: &dynamodb.AttributeValue{
464			M: map[string]*dynamodb.AttributeValue{
465				"Map": {
466					M: map[string]*dynamodb.AttributeValue{
467						"key": {S: aws.String("value")},
468					},
469				},
470				"Slice":     {L: []*dynamodb.AttributeValue{{S: aws.String("test")}, {S: aws.String("slice")}}},
471				"ByteSlice": {B: []byte{0, 1}},
472				"ByteArray": {B: []byte{0, 1, 2, 3}},
473				"BinarySet": {BS: [][]byte{{0, 1}, {2, 3}}},
474				"NumberSet": {NS: []*string{aws.String("0"), aws.String("1")}},
475				"StringSet": {SS: []*string{aws.String("test"), aws.String("slice")}},
476			},
477		},
478		actual: &testEmptyCollectionTypesOmitted{},
479		expected: testEmptyCollectionTypesOmitted{
480			Map:       map[string]string{"key": "value"},
481			Slice:     []string{"test", "slice"},
482			ByteSlice: []byte{0, 1},
483			ByteArray: [4]byte{0, 1, 2, 3},
484			ZeroArray: [0]byte{},
485			BinarySet: [][]byte{{0, 1}, {2, 3}},
486			NumberSet: []int{0, 1},
487			StringSet: []string{"test", "slice"},
488		},
489	},
490	// structs with members zero
491	16: {
492		in: &dynamodb.AttributeValue{
493			M: map[string]*dynamodb.AttributeValue{
494				"Struct": {
495					M: map[string]*dynamodb.AttributeValue{
496						"Int": {N: aws.String("0")},
497					},
498				},
499				"PtrStruct": {NULL: aws.Bool(true)},
500			},
501		},
502		actual: &struct {
503			Struct    testEmptyCollectionStruct
504			PtrStruct *testEmptyCollectionStruct
505		}{},
506		expected: struct {
507			Struct    testEmptyCollectionStruct
508			PtrStruct *testEmptyCollectionStruct
509		}{},
510	},
511	// structs with members non-zero value
512	17: {
513		in: &dynamodb.AttributeValue{
514			M: map[string]*dynamodb.AttributeValue{
515				"Struct": {
516					M: map[string]*dynamodb.AttributeValue{
517						"Int": {N: aws.String("1")},
518					},
519				},
520				"PtrStruct": {
521					M: map[string]*dynamodb.AttributeValue{
522						"Int": {N: aws.String("1")},
523					},
524				},
525			},
526		},
527		actual: &struct {
528			Struct    testEmptyCollectionStruct
529			PtrStruct *testEmptyCollectionStruct
530		}{},
531		expected: struct {
532			Struct    testEmptyCollectionStruct
533			PtrStruct *testEmptyCollectionStruct
534		}{
535			Struct:    testEmptyCollectionStruct{Int: 1},
536			PtrStruct: &testEmptyCollectionStruct{Int: 1},
537		},
538	},
539	// struct with omittable members zero value
540	18: {
541		in: &dynamodb.AttributeValue{
542			M: map[string]*dynamodb.AttributeValue{
543				"Struct":    {M: map[string]*dynamodb.AttributeValue{}},
544				"PtrStruct": {NULL: aws.Bool(true)},
545			},
546		},
547		actual: &struct {
548			Struct    testEmptyCollectionStructOmitted
549			PtrStruct *testEmptyCollectionStructOmitted
550		}{},
551		expected: struct {
552			Struct    testEmptyCollectionStructOmitted
553			PtrStruct *testEmptyCollectionStructOmitted
554		}{},
555	},
556	// omittable struct with omittable members zero value
557	19: {
558		in: &dynamodb.AttributeValue{
559			M: map[string]*dynamodb.AttributeValue{
560				"Struct": {M: map[string]*dynamodb.AttributeValue{}},
561			},
562		},
563		actual: &struct {
564			Struct    testEmptyCollectionStructOmitted  `dynamodbav:",omitempty"`
565			PtrStruct *testEmptyCollectionStructOmitted `dynamodbav:",omitempty"`
566		}{},
567		expected: struct {
568			Struct    testEmptyCollectionStructOmitted  `dynamodbav:",omitempty"`
569			PtrStruct *testEmptyCollectionStructOmitted `dynamodbav:",omitempty"`
570		}{},
571	},
572	// omittable struct with omittable members non-zero value
573	20: {
574		in: &dynamodb.AttributeValue{
575			M: map[string]*dynamodb.AttributeValue{
576				"Struct": {
577					M: map[string]*dynamodb.AttributeValue{
578						"Slice": {L: []*dynamodb.AttributeValue{{S: aws.String("test")}}},
579					},
580				},
581				"InitPtrStruct": {
582					M: map[string]*dynamodb.AttributeValue{
583						"Slice": {L: []*dynamodb.AttributeValue{{S: aws.String("test")}}},
584					},
585				},
586			},
587		},
588		actual: &struct {
589			Struct        testEmptyCollectionStructOmitted  `dynamodbav:",omitempty"`
590			InitPtrStruct *testEmptyCollectionStructOmitted `dynamodbav:",omitempty"`
591		}{},
592		expected: struct {
593			Struct        testEmptyCollectionStructOmitted  `dynamodbav:",omitempty"`
594			InitPtrStruct *testEmptyCollectionStructOmitted `dynamodbav:",omitempty"`
595		}{
596			Struct:        testEmptyCollectionStructOmitted{Slice: []string{"test"}},
597			InitPtrStruct: &testEmptyCollectionStructOmitted{Slice: []string{"test"}},
598		},
599	},
600	21: { // empty slice and NullEmptyByteSlice disabled
601		encoderOpts: func(encoder *Encoder) {
602			encoder.NullEmptyByteSlice = false
603		},
604		in: &dynamodb.AttributeValue{
605			B: []byte{},
606		},
607		actual:   &[]byte{},
608		expected: []byte{},
609	},
610	22: { // empty slice and NullEmptyByteSlice disabled, and omitempty
611		encoderOpts: func(encoder *Encoder) {
612			encoder.NullEmptyByteSlice = false
613		},
614		in: &dynamodb.AttributeValue{
615			M: map[string]*dynamodb.AttributeValue{},
616		},
617		actual: &struct {
618			Value []byte `dynamodbav:",omitempty"`
619		}{},
620		expected: struct {
621			Value []byte `dynamodbav:",omitempty"`
622		}{},
623	},
624}
625
626func TestMarshalEmptyCollections(t *testing.T) {
627	for i, c := range sharedEmptyCollectionsTestCases {
628		encoder := NewEncoder(func(e *Encoder) {
629			e.EnableEmptyCollections = true
630			if c.encoderOpts != nil {
631				c.encoderOpts(e)
632			}
633		})
634		av, err := encoder.Encode(c.expected)
635		assertConvertTest(t, i, av, c.in, err, c.err)
636	}
637}
638
639func TestEmptyCollectionsSpecialCases(t *testing.T) {
640	// ptr string non nil with empty value
641
642	type SpecialCases struct {
643		PtrString        *string
644		OmittedPtrString *string `dynamodbav:",omitempty"`
645	}
646
647	expectedEncode := &dynamodb.AttributeValue{
648		M: map[string]*dynamodb.AttributeValue{
649			"PtrString": {NULL: aws.Bool(true)},
650		},
651	}
652	expectedDecode := SpecialCases{}
653
654	encoder := NewEncoder(func(encoder *Encoder) {
655		encoder.EnableEmptyCollections = true
656	})
657
658	actualEncode, err := encoder.Encode(&SpecialCases{
659		PtrString:        aws.String(""),
660		OmittedPtrString: aws.String(""),
661	})
662	if err != nil {
663		t.Fatalf("expected no err got %v", err)
664	}
665	if e, a := expectedEncode, actualEncode; !reflect.DeepEqual(e, a) {
666		t.Errorf("expected %v, got %v", e, a)
667	}
668
669	decoder := NewDecoder(func(decoder *Decoder) {
670		decoder.EnableEmptyCollections = true
671	})
672
673	var actualDecode SpecialCases
674	err = decoder.Decode(&dynamodb.AttributeValue{}, &actualDecode)
675	if err != nil {
676		t.Fatalf("expected no err got %v", err)
677	}
678	if e, a := expectedDecode, actualDecode; !reflect.DeepEqual(e, a) {
679		t.Errorf("expected %v, got %v", e, a)
680	}
681}
682
683func TestUnmarshalEmptyCollections(t *testing.T) {
684	for i, c := range sharedEmptyCollectionsTestCases {
685		decoder := NewDecoder(func(d *Decoder) {
686			d.EnableEmptyCollections = true
687		})
688		err := decoder.Decode(c.in, c.actual)
689		assertConvertTest(t, i, c.actual, c.expected, err, c.err)
690	}
691}
692