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