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