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