1package attributevalue
2
3import (
4	"reflect"
5	"strconv"
6	"testing"
7	"time"
8
9	"github.com/aws/aws-sdk-go-v2/aws"
10	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
11	"github.com/google/go-cmp/cmp"
12)
13
14func TestMarshalShared(t *testing.T) {
15	for name, c := range sharedTestCases {
16		t.Run(name, func(t *testing.T) {
17			av, err := Marshal(c.expected)
18			assertConvertTest(t, av, c.in, err, c.err)
19		})
20	}
21}
22
23func TestMarshalListShared(t *testing.T) {
24	for name, c := range sharedListTestCases {
25		t.Run(name, func(t *testing.T) {
26			av, err := MarshalList(c.expected)
27			assertConvertTest(t, av, c.in, err, c.err)
28		})
29	}
30}
31
32func TestMarshalMapShared(t *testing.T) {
33	for name, c := range sharedMapTestCases {
34		t.Run(name, func(t *testing.T) {
35			av, err := MarshalMap(c.expected)
36			assertConvertTest(t, av, c.in, err, c.err)
37		})
38	}
39}
40
41type marshalMarshaler struct {
42	Value  string
43	Value2 int
44	Value3 bool
45	Value4 time.Time
46}
47
48func (m *marshalMarshaler) MarshalDynamoDBAttributeValue() (types.AttributeValue, error) {
49	return &types.AttributeValueMemberM{
50		Value: map[string]types.AttributeValue{
51			"abc": &types.AttributeValueMemberS{Value: m.Value},
52			"def": &types.AttributeValueMemberN{Value: strconv.Itoa(m.Value2)},
53			"ghi": &types.AttributeValueMemberBOOL{Value: m.Value3},
54			"jkl": &types.AttributeValueMemberS{Value: m.Value4.Format(time.RFC3339Nano)},
55		},
56	}, nil
57}
58
59func TestMarshalMashaler(t *testing.T) {
60	m := &marshalMarshaler{
61		Value:  "value",
62		Value2: 123,
63		Value3: true,
64		Value4: testDate,
65	}
66
67	expect := &types.AttributeValueMemberM{
68		Value: map[string]types.AttributeValue{
69			"abc": &types.AttributeValueMemberS{Value: "value"},
70			"def": &types.AttributeValueMemberN{Value: "123"},
71			"ghi": &types.AttributeValueMemberBOOL{Value: true},
72			"jkl": &types.AttributeValueMemberS{Value: "2016-05-03T17:06:26.209072Z"},
73		},
74	}
75
76	actual, err := Marshal(m)
77	if err != nil {
78		t.Errorf("expect nil, got %v", err)
79	}
80
81	if e, a := expect, actual; !reflect.DeepEqual(e, a) {
82		t.Errorf("expect %v, got %v", e, a)
83	}
84}
85
86type testOmitEmptyElemListStruct struct {
87	Values []string `dynamodbav:",omitemptyelem"`
88}
89
90type testOmitEmptyElemMapStruct struct {
91	Values map[string]interface{} `dynamodbav:",omitemptyelem"`
92}
93
94func TestMarshalListOmitEmptyElem(t *testing.T) {
95	expect := &types.AttributeValueMemberM{
96		Value: map[string]types.AttributeValue{
97			"Values": &types.AttributeValueMemberL{Value: []types.AttributeValue{
98				&types.AttributeValueMemberS{Value: "abc"},
99				&types.AttributeValueMemberS{Value: "123"},
100			}},
101		},
102	}
103
104	m := testOmitEmptyElemListStruct{Values: []string{"abc", "", "123"}}
105
106	actual, err := Marshal(m)
107	if err != nil {
108		t.Errorf("expect nil, got %v", err)
109	}
110	if diff := cmp.Diff(expect, actual); len(diff) != 0 {
111		t.Errorf("expect match\n%s", diff)
112	}
113}
114
115func TestMarshalMapOmitEmptyElem(t *testing.T) {
116	expect := &types.AttributeValueMemberM{
117		Value: map[string]types.AttributeValue{
118			"Values": &types.AttributeValueMemberM{Value: map[string]types.AttributeValue{
119				"abc": &types.AttributeValueMemberN{Value: "123"},
120				"hij": &types.AttributeValueMemberS{Value: ""},
121				"klm": &types.AttributeValueMemberS{Value: "abc"},
122				"qrs": &types.AttributeValueMemberS{Value: "abc"},
123			}},
124		},
125	}
126
127	m := testOmitEmptyElemMapStruct{Values: map[string]interface{}{
128		"abc": 123.,
129		"efg": nil,
130		"hij": "",
131		"klm": "abc",
132		"nop": func() interface{} {
133			var v *string
134			return v
135		}(),
136		"qrs": func() interface{} {
137			v := "abc"
138			return &v
139		}(),
140	}}
141
142	actual, err := Marshal(m)
143	if err != nil {
144		t.Errorf("expect nil, got %v", err)
145	}
146	if diff := cmp.Diff(expect, actual); len(diff) != 0 {
147		t.Errorf("expect match\n%s", diff)
148	}
149}
150
151type testNullEmptyElemListStruct struct {
152	Values []string `dynamodbav:",nullemptyelem"`
153}
154
155type testNullEmptyElemMapStruct struct {
156	Values map[string]interface{} `dynamodbav:",nullemptyelem"`
157}
158
159func TestMarshalListNullEmptyElem(t *testing.T) {
160	expect := &types.AttributeValueMemberM{
161		Value: map[string]types.AttributeValue{
162			"Values": &types.AttributeValueMemberL{Value: []types.AttributeValue{
163				&types.AttributeValueMemberS{Value: "abc"},
164				&types.AttributeValueMemberNULL{Value: true},
165				&types.AttributeValueMemberS{Value: "123"},
166			}},
167		},
168	}
169
170	m := testNullEmptyElemListStruct{Values: []string{"abc", "", "123"}}
171
172	actual, err := Marshal(m)
173	if err != nil {
174		t.Errorf("expect nil, got %v", err)
175	}
176	if diff := cmp.Diff(expect, actual); len(diff) != 0 {
177		t.Errorf("expect match\n%s", diff)
178	}
179}
180
181func TestMarshalMapNullEmptyElem(t *testing.T) {
182	expect := &types.AttributeValueMemberM{
183		Value: map[string]types.AttributeValue{
184			"Values": &types.AttributeValueMemberM{Value: map[string]types.AttributeValue{
185				"abc": &types.AttributeValueMemberN{Value: "123"},
186				"efg": &types.AttributeValueMemberNULL{Value: true},
187				"hij": &types.AttributeValueMemberS{Value: ""},
188				"klm": &types.AttributeValueMemberS{Value: "abc"},
189				"nop": &types.AttributeValueMemberNULL{Value: true},
190				"qrs": &types.AttributeValueMemberS{Value: "abc"},
191			}},
192		},
193	}
194
195	m := testNullEmptyElemMapStruct{Values: map[string]interface{}{
196		"abc": 123.,
197		"efg": nil,
198		"hij": "",
199		"klm": "abc",
200		"nop": func() interface{} {
201			var v *string
202			return v
203		}(),
204		"qrs": func() interface{} {
205			v := "abc"
206			return &v
207		}(),
208	}}
209
210	actual, err := Marshal(m)
211	if err != nil {
212		t.Errorf("expect nil, got %v", err)
213	}
214	if diff := cmp.Diff(expect, actual); len(diff) != 0 {
215		t.Errorf("expect match\n%s", diff)
216	}
217}
218
219type testOmitEmptyScalar struct {
220	IntZero       int  `dynamodbav:",omitempty"`
221	IntPtrNil     *int `dynamodbav:",omitempty"`
222	IntPtrSetZero *int `dynamodbav:",omitempty"`
223}
224
225func TestMarshalOmitEmpty(t *testing.T) {
226	expect := &types.AttributeValueMemberM{
227		Value: map[string]types.AttributeValue{
228			"IntPtrSetZero": &types.AttributeValueMemberN{Value: "0"},
229		},
230	}
231
232	m := testOmitEmptyScalar{IntPtrSetZero: aws.Int(0)}
233
234	actual, err := Marshal(m)
235	if err != nil {
236		t.Errorf("expect nil, got %v", err)
237	}
238	if e, a := expect, actual; !reflect.DeepEqual(e, a) {
239		t.Errorf("expect %v, got %v", e, a)
240	}
241}
242
243func TestEncodeEmbeddedPointerStruct(t *testing.T) {
244	type B struct {
245		Bint int
246	}
247	type C struct {
248		Cint int
249	}
250	type A struct {
251		Aint int
252		*B
253		*C
254	}
255	a := A{Aint: 321, B: &B{123}}
256	if e, a := 321, a.Aint; e != a {
257		t.Errorf("expect %v, got %v", e, a)
258	}
259	if e, a := 123, a.Bint; e != a {
260		t.Errorf("expect %v, got %v", e, a)
261	}
262	if a.C != nil {
263		t.Errorf("expect nil, got %v", a.C)
264	}
265
266	actual, err := Marshal(a)
267	if err != nil {
268		t.Errorf("expect nil, got %v", err)
269	}
270	expect := &types.AttributeValueMemberM{
271		Value: map[string]types.AttributeValue{
272			"Aint": &types.AttributeValueMemberN{Value: "321"},
273			"Bint": &types.AttributeValueMemberN{Value: "123"},
274		},
275	}
276	if e, a := expect, actual; !reflect.DeepEqual(e, a) {
277		t.Errorf("expect %v, got %v", e, a)
278	}
279}
280
281func TestEncodeUnixTime(t *testing.T) {
282	type A struct {
283		Normal time.Time
284		Tagged time.Time `dynamodbav:",unixtime"`
285		Typed  UnixTime
286	}
287
288	a := A{
289		Normal: time.Unix(123, 0).UTC(),
290		Tagged: time.Unix(456, 0),
291		Typed:  UnixTime(time.Unix(789, 0)),
292	}
293
294	actual, err := Marshal(a)
295	if err != nil {
296		t.Errorf("expect nil, got %v", err)
297	}
298	expect := &types.AttributeValueMemberM{
299		Value: map[string]types.AttributeValue{
300			"Normal": &types.AttributeValueMemberS{Value: "1970-01-01T00:02:03Z"},
301			"Tagged": &types.AttributeValueMemberN{Value: "456"},
302			"Typed":  &types.AttributeValueMemberN{Value: "789"},
303		},
304	}
305	if e, a := expect, actual; !reflect.DeepEqual(e, a) {
306		t.Errorf("expect %v, got %v", e, a)
307	}
308}
309
310type AliasedTime time.Time
311
312func TestEncodeAliasedUnixTime(t *testing.T) {
313	type A struct {
314		Normal AliasedTime
315		Tagged AliasedTime `dynamodbav:",unixtime"`
316	}
317
318	a := A{
319		Normal: AliasedTime(time.Unix(123, 0).UTC()),
320		Tagged: AliasedTime(time.Unix(456, 0)),
321	}
322
323	actual, err := Marshal(a)
324	if err != nil {
325		t.Errorf("expect no err, got %v", err)
326	}
327	expect := &types.AttributeValueMemberM{
328		Value: map[string]types.AttributeValue{
329			"Normal": &types.AttributeValueMemberS{Value: "1970-01-01T00:02:03Z"},
330			"Tagged": &types.AttributeValueMemberN{Value: "456"},
331		},
332	}
333	if e, a := expect, actual; !reflect.DeepEqual(e, a) {
334		t.Errorf("expect %v, got %v", e, a)
335	}
336}
337
338func TestEncoderFieldByIndex(t *testing.T) {
339	type (
340		Middle struct{ Inner int }
341		Outer  struct{ *Middle }
342	)
343
344	// nil embedded struct
345	outer := Outer{}
346	outerFields := unionStructFields(reflect.TypeOf(outer), structFieldOptions{})
347	innerField, _ := outerFields.FieldByName("Inner")
348
349	_, found := encoderFieldByIndex(reflect.ValueOf(&outer).Elem(), innerField.Index)
350	if found != false {
351		t.Error("expected found to be false when embedded struct is nil")
352	}
353
354	// non-nil embedded struct
355	outer = Outer{Middle: &Middle{Inner: 3}}
356	outerFields = unionStructFields(reflect.TypeOf(outer), structFieldOptions{})
357	innerField, _ = outerFields.FieldByName("Inner")
358
359	f, found := encoderFieldByIndex(reflect.ValueOf(&outer).Elem(), innerField.Index)
360	if !found {
361		t.Error("expected found to be true")
362	}
363	if f.Kind() != reflect.Int || f.Int() != int64(outer.Inner) {
364		t.Error("expected f to be of kind Int with value equal to outer.Inner")
365	}
366}
367