github.com/aavshr/aws-sdk-go@v1.41.3/service/dynamodb/dynamodbattribute/marshaler_test.go (about)

     1  package dynamodbattribute
     2  
     3  import (
     4  	"encoding/json"
     5  	"math"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/aavshr/aws-sdk-go/aws"
    10  	"github.com/aavshr/aws-sdk-go/aws/awserr"
    11  	"github.com/aavshr/aws-sdk-go/aws/awsutil"
    12  	"github.com/aavshr/aws-sdk-go/service/dynamodb"
    13  )
    14  
    15  type simpleMarshalStruct struct {
    16  	Byte    []byte
    17  	String  string
    18  	Int     int
    19  	Uint    uint
    20  	Float32 float32
    21  	Float64 float64
    22  	Bool    bool
    23  	Null    *interface{}
    24  }
    25  
    26  type complexMarshalStruct struct {
    27  	Simple []simpleMarshalStruct
    28  }
    29  
    30  type myByteStruct struct {
    31  	Byte []byte
    32  }
    33  
    34  type myByteSetStruct struct {
    35  	ByteSet [][]byte
    36  }
    37  
    38  type marshallerTestInput struct {
    39  	input    interface{}
    40  	expected interface{}
    41  	err      awserr.Error
    42  }
    43  
    44  var marshalerScalarInputs = []marshallerTestInput{
    45  	{
    46  		input:    nil,
    47  		expected: &dynamodb.AttributeValue{NULL: &trueValue},
    48  	},
    49  	{
    50  		input:    "some string",
    51  		expected: &dynamodb.AttributeValue{S: aws.String("some string")},
    52  	},
    53  	{
    54  		input:    true,
    55  		expected: &dynamodb.AttributeValue{BOOL: &trueValue},
    56  	},
    57  	{
    58  		input:    false,
    59  		expected: &dynamodb.AttributeValue{BOOL: &falseValue},
    60  	},
    61  	{
    62  		input:    3.14,
    63  		expected: &dynamodb.AttributeValue{N: aws.String("3.14")},
    64  	},
    65  	{
    66  		input:    math.MaxFloat32,
    67  		expected: &dynamodb.AttributeValue{N: aws.String("340282346638528860000000000000000000000")},
    68  	},
    69  	{
    70  		input:    math.MaxFloat64,
    71  		expected: &dynamodb.AttributeValue{N: aws.String("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")},
    72  	},
    73  	{
    74  		input:    12,
    75  		expected: &dynamodb.AttributeValue{N: aws.String("12")},
    76  	},
    77  	{
    78  		input:    Number("12"),
    79  		expected: &dynamodb.AttributeValue{N: aws.String("12")},
    80  	},
    81  	{
    82  		input:    json.Number("456"),
    83  		expected: &dynamodb.AttributeValue{N: aws.String("456")},
    84  	},
    85  	{
    86  		input: simpleMarshalStruct{},
    87  		expected: &dynamodb.AttributeValue{
    88  			M: map[string]*dynamodb.AttributeValue{
    89  				"Byte":    {NULL: &trueValue},
    90  				"Bool":    {BOOL: &falseValue},
    91  				"Float32": {N: aws.String("0")},
    92  				"Float64": {N: aws.String("0")},
    93  				"Int":     {N: aws.String("0")},
    94  				"Null":    {NULL: &trueValue},
    95  				"String":  {NULL: &trueValue},
    96  				"Uint":    {N: aws.String("0")},
    97  			},
    98  		},
    99  	},
   100  }
   101  
   102  var marshallerMapTestInputs = []marshallerTestInput{
   103  	// Scalar tests
   104  	{
   105  		input:    nil,
   106  		expected: map[string]*dynamodb.AttributeValue{},
   107  	},
   108  	{
   109  		input:    map[string]interface{}{"string": "some string"},
   110  		expected: map[string]*dynamodb.AttributeValue{"string": {S: aws.String("some string")}},
   111  	},
   112  	{
   113  		input:    map[string]interface{}{"bool": true},
   114  		expected: map[string]*dynamodb.AttributeValue{"bool": {BOOL: &trueValue}},
   115  	},
   116  	{
   117  		input:    map[string]interface{}{"bool": false},
   118  		expected: map[string]*dynamodb.AttributeValue{"bool": {BOOL: &falseValue}},
   119  	},
   120  	{
   121  		input:    map[string]interface{}{"null": nil},
   122  		expected: map[string]*dynamodb.AttributeValue{"null": {NULL: &trueValue}},
   123  	},
   124  	{
   125  		input:    map[string]interface{}{"float": 3.14},
   126  		expected: map[string]*dynamodb.AttributeValue{"float": {N: aws.String("3.14")}},
   127  	},
   128  	{
   129  		input:    map[string]interface{}{"float": math.MaxFloat32},
   130  		expected: map[string]*dynamodb.AttributeValue{"float": {N: aws.String("340282346638528860000000000000000000000")}},
   131  	},
   132  	{
   133  		input:    map[string]interface{}{"float": math.MaxFloat64},
   134  		expected: map[string]*dynamodb.AttributeValue{"float": {N: aws.String("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")}},
   135  	},
   136  	{
   137  		input:    map[string]interface{}{"num": 12.},
   138  		expected: map[string]*dynamodb.AttributeValue{"num": {N: aws.String("12")}},
   139  	},
   140  	{
   141  		input:    map[string]interface{}{"byte": []byte{48, 49}},
   142  		expected: map[string]*dynamodb.AttributeValue{"byte": {B: []byte{48, 49}}},
   143  	},
   144  	{
   145  		input:    struct{ Byte []byte }{Byte: []byte{48, 49}},
   146  		expected: map[string]*dynamodb.AttributeValue{"Byte": {B: []byte{48, 49}}},
   147  	},
   148  	{
   149  		input:    map[string]interface{}{"byte_set": [][]byte{{48, 49}, {50, 51}}},
   150  		expected: map[string]*dynamodb.AttributeValue{"byte_set": {BS: [][]byte{{48, 49}, {50, 51}}}},
   151  	},
   152  	{
   153  		input:    struct{ ByteSet [][]byte }{ByteSet: [][]byte{{48, 49}, {50, 51}}},
   154  		expected: map[string]*dynamodb.AttributeValue{"ByteSet": {BS: [][]byte{{48, 49}, {50, 51}}}},
   155  	},
   156  	// List
   157  	{
   158  		input: map[string]interface{}{"list": []interface{}{"a string", 12., 3.14, true, nil, false}},
   159  		expected: map[string]*dynamodb.AttributeValue{
   160  			"list": {
   161  				L: []*dynamodb.AttributeValue{
   162  					{S: aws.String("a string")},
   163  					{N: aws.String("12")},
   164  					{N: aws.String("3.14")},
   165  					{BOOL: &trueValue},
   166  					{NULL: &trueValue},
   167  					{BOOL: &falseValue},
   168  				},
   169  			},
   170  		},
   171  	},
   172  	// Map
   173  	{
   174  		input: map[string]interface{}{"map": map[string]interface{}{"nestednum": 12.}},
   175  		expected: map[string]*dynamodb.AttributeValue{
   176  			"map": {
   177  				M: map[string]*dynamodb.AttributeValue{
   178  					"nestednum": {
   179  						N: aws.String("12"),
   180  					},
   181  				},
   182  			},
   183  		},
   184  	},
   185  	// Structs
   186  	{
   187  		input: simpleMarshalStruct{},
   188  		expected: map[string]*dynamodb.AttributeValue{
   189  			"Byte":    {NULL: &trueValue},
   190  			"Bool":    {BOOL: &falseValue},
   191  			"Float32": {N: aws.String("0")},
   192  			"Float64": {N: aws.String("0")},
   193  			"Int":     {N: aws.String("0")},
   194  			"Null":    {NULL: &trueValue},
   195  			"String":  {NULL: &trueValue},
   196  			"Uint":    {N: aws.String("0")},
   197  		},
   198  	},
   199  	{
   200  		input: complexMarshalStruct{},
   201  		expected: map[string]*dynamodb.AttributeValue{
   202  			"Simple": {NULL: &trueValue},
   203  		},
   204  	},
   205  	{
   206  		input: struct {
   207  			Simple []string `json:"simple"`
   208  		}{},
   209  		expected: map[string]*dynamodb.AttributeValue{
   210  			"simple": {NULL: &trueValue},
   211  		},
   212  	},
   213  	{
   214  		input: struct {
   215  			Simple []string `json:"simple,omitempty"`
   216  		}{},
   217  		expected: map[string]*dynamodb.AttributeValue{},
   218  	},
   219  	{
   220  		input: struct {
   221  			Simple []string `json:"-"`
   222  		}{},
   223  		expected: map[string]*dynamodb.AttributeValue{},
   224  	},
   225  	{
   226  		input: complexMarshalStruct{Simple: []simpleMarshalStruct{{Int: -2}, {Uint: 5}}},
   227  		expected: map[string]*dynamodb.AttributeValue{
   228  			"Simple": {
   229  				L: []*dynamodb.AttributeValue{
   230  					{
   231  						M: map[string]*dynamodb.AttributeValue{
   232  							"Byte":    {NULL: &trueValue},
   233  							"Bool":    {BOOL: &falseValue},
   234  							"Float32": {N: aws.String("0")},
   235  							"Float64": {N: aws.String("0")},
   236  							"Int":     {N: aws.String("-2")},
   237  							"Null":    {NULL: &trueValue},
   238  							"String":  {NULL: &trueValue},
   239  							"Uint":    {N: aws.String("0")},
   240  						},
   241  					},
   242  					{
   243  						M: map[string]*dynamodb.AttributeValue{
   244  							"Byte":    {NULL: &trueValue},
   245  							"Bool":    {BOOL: &falseValue},
   246  							"Float32": {N: aws.String("0")},
   247  							"Float64": {N: aws.String("0")},
   248  							"Int":     {N: aws.String("0")},
   249  							"Null":    {NULL: &trueValue},
   250  							"String":  {NULL: &trueValue},
   251  							"Uint":    {N: aws.String("5")},
   252  						},
   253  					},
   254  				},
   255  			},
   256  		},
   257  	},
   258  }
   259  
   260  var marshallerListTestInputs = []marshallerTestInput{
   261  	{
   262  		input:    nil,
   263  		expected: []*dynamodb.AttributeValue{},
   264  	},
   265  	{
   266  		input:    []interface{}{},
   267  		expected: []*dynamodb.AttributeValue{},
   268  	},
   269  	{
   270  		input:    []simpleMarshalStruct{},
   271  		expected: []*dynamodb.AttributeValue{},
   272  	},
   273  	{
   274  		input: []interface{}{"a string", 12., 3.14, true, nil, false},
   275  		expected: []*dynamodb.AttributeValue{
   276  			{S: aws.String("a string")},
   277  			{N: aws.String("12")},
   278  			{N: aws.String("3.14")},
   279  			{BOOL: &trueValue},
   280  			{NULL: &trueValue},
   281  			{BOOL: &falseValue},
   282  		},
   283  	},
   284  	{
   285  		input: []simpleMarshalStruct{{}},
   286  		expected: []*dynamodb.AttributeValue{
   287  			{
   288  				M: map[string]*dynamodb.AttributeValue{
   289  					"Byte":    {NULL: &trueValue},
   290  					"Bool":    {BOOL: &falseValue},
   291  					"Float32": {N: aws.String("0")},
   292  					"Float64": {N: aws.String("0")},
   293  					"Int":     {N: aws.String("0")},
   294  					"Null":    {NULL: &trueValue},
   295  					"String":  {NULL: &trueValue},
   296  					"Uint":    {N: aws.String("0")},
   297  				},
   298  			},
   299  		},
   300  	},
   301  }
   302  
   303  func Test_New_Marshal(t *testing.T) {
   304  	for _, test := range marshalerScalarInputs {
   305  		testMarshal(t, test)
   306  	}
   307  }
   308  
   309  func testMarshal(t *testing.T, test marshallerTestInput) {
   310  	actual, err := Marshal(test.input)
   311  	if test.err != nil {
   312  		if err == nil {
   313  			t.Errorf("Marshal with input %#v retured %#v, expected error `%s`", test.input, actual, test.err)
   314  		} else if err.Error() != test.err.Error() {
   315  			t.Errorf("Marshal with input %#v retured error `%s`, expected error `%s`", test.input, err, test.err)
   316  		}
   317  	} else {
   318  		if err != nil {
   319  			t.Errorf("Marshal with input %#v retured error `%s`", test.input, err)
   320  		}
   321  		compareObjects(t, test.expected, actual)
   322  	}
   323  }
   324  
   325  func Test_New_Unmarshal(t *testing.T) {
   326  	// Using the same inputs from Marshal, test the reverse mapping.
   327  	for i, test := range marshalerScalarInputs {
   328  		if test.input == nil {
   329  			continue
   330  		}
   331  		actual := reflect.New(reflect.TypeOf(test.input)).Interface()
   332  		if err := Unmarshal(test.expected.(*dynamodb.AttributeValue), actual); err != nil {
   333  			t.Errorf("Unmarshal %d, with input %#v retured error `%s`", i+1, test.expected, err)
   334  		}
   335  		compareObjects(t, test.input, reflect.ValueOf(actual).Elem().Interface())
   336  	}
   337  }
   338  
   339  func Test_New_UnmarshalError(t *testing.T) {
   340  	// Test that we get an error using Unmarshal to convert to a nil value.
   341  	expected := &InvalidUnmarshalError{Type: reflect.TypeOf(nil)}
   342  	if err := Unmarshal(nil, nil); err == nil {
   343  		t.Errorf("Unmarshal with input %T returned no error, expected error `%v`", nil, expected)
   344  	} else if err.Error() != expected.Error() {
   345  		t.Errorf("Unmarshal with input %T returned error `%v`, expected error `%v`", nil, err, expected)
   346  	}
   347  
   348  	// Test that we get an error using Unmarshal to convert to a non-pointer value.
   349  	var actual map[string]interface{}
   350  	expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual)}
   351  	if err := Unmarshal(nil, actual); err == nil {
   352  		t.Errorf("Unmarshal with input %T returned no error, expected error `%v`", actual, expected)
   353  	} else if err.Error() != expected.Error() {
   354  		t.Errorf("Unmarshal with input %T returned error `%v`, expected error `%v`", actual, err, expected)
   355  	}
   356  
   357  	// Test that we get an error using Unmarshal to convert to nil struct.
   358  	var actual2 *struct{ A int }
   359  	expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual2)}
   360  	if err := Unmarshal(nil, actual2); err == nil {
   361  		t.Errorf("Unmarshal with input %T returned no error, expected error `%v`", actual2, expected)
   362  	} else if err.Error() != expected.Error() {
   363  		t.Errorf("Unmarshal with input %T returned error `%v`, expected error `%v`", actual2, err, expected)
   364  	}
   365  }
   366  
   367  func Test_New_MarshalMap(t *testing.T) {
   368  	for _, test := range marshallerMapTestInputs {
   369  		testMarshalMap(t, test)
   370  	}
   371  }
   372  
   373  func testMarshalMap(t *testing.T, test marshallerTestInput) {
   374  	actual, err := MarshalMap(test.input)
   375  	if test.err != nil {
   376  		if err == nil {
   377  			t.Errorf("MarshalMap with input %#v retured %#v, expected error `%s`", test.input, actual, test.err)
   378  		} else if err.Error() != test.err.Error() {
   379  			t.Errorf("MarshalMap with input %#v retured error `%s`, expected error `%s`", test.input, err, test.err)
   380  		}
   381  	} else {
   382  		if err != nil {
   383  			t.Errorf("MarshalMap with input %#v retured error `%s`", test.input, err)
   384  		}
   385  		compareObjects(t, test.expected, actual)
   386  	}
   387  }
   388  
   389  func Test_New_UnmarshalMap(t *testing.T) {
   390  	// Using the same inputs from MarshalMap, test the reverse mapping.
   391  	for i, test := range marshallerMapTestInputs {
   392  		if test.input == nil {
   393  			continue
   394  		}
   395  		actual := reflect.New(reflect.TypeOf(test.input)).Interface()
   396  		if err := UnmarshalMap(test.expected.(map[string]*dynamodb.AttributeValue), actual); err != nil {
   397  			t.Errorf("Unmarshal %d, with input %#v retured error `%s`", i+1, test.expected, err)
   398  		}
   399  		compareObjects(t, test.input, reflect.ValueOf(actual).Elem().Interface())
   400  	}
   401  }
   402  
   403  func Test_New_UnmarshalMapError(t *testing.T) {
   404  	// Test that we get an error using UnmarshalMap to convert to a nil value.
   405  	expected := &InvalidUnmarshalError{Type: reflect.TypeOf(nil)}
   406  	if err := UnmarshalMap(nil, nil); err == nil {
   407  		t.Errorf("UnmarshalMap with input %T returned no error, expected error `%v`", nil, expected)
   408  	} else if err.Error() != expected.Error() {
   409  		t.Errorf("UnmarshalMap with input %T returned error `%v`, expected error `%v`", nil, err, expected)
   410  	}
   411  
   412  	// Test that we get an error using UnmarshalMap to convert to a non-pointer value.
   413  	var actual map[string]interface{}
   414  	expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual)}
   415  	if err := UnmarshalMap(nil, actual); err == nil {
   416  		t.Errorf("UnmarshalMap with input %T returned no error, expected error `%v`", actual, expected)
   417  	} else if err.Error() != expected.Error() {
   418  		t.Errorf("UnmarshalMap with input %T returned error `%v`, expected error `%v`", actual, err, expected)
   419  	}
   420  
   421  	// Test that we get an error using UnmarshalMap to convert to nil struct.
   422  	var actual2 *struct{ A int }
   423  	expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual2)}
   424  	if err := UnmarshalMap(nil, actual2); err == nil {
   425  		t.Errorf("UnmarshalMap with input %T returned no error, expected error `%v`", actual2, expected)
   426  	} else if err.Error() != expected.Error() {
   427  		t.Errorf("UnmarshalMap with input %T returned error `%v`, expected error `%v`", actual2, err, expected)
   428  	}
   429  }
   430  
   431  func Test_New_MarshalList(t *testing.T) {
   432  	for _, test := range marshallerListTestInputs {
   433  		testMarshalList(t, test)
   434  	}
   435  }
   436  
   437  func testMarshalList(t *testing.T, test marshallerTestInput) {
   438  	actual, err := MarshalList(test.input)
   439  	if test.err != nil {
   440  		if err == nil {
   441  			t.Errorf("MarshalList with input %#v retured %#v, expected error `%s`", test.input, actual, test.err)
   442  		} else if err.Error() != test.err.Error() {
   443  			t.Errorf("MarshalList with input %#v retured error `%s`, expected error `%s`", test.input, err, test.err)
   444  		}
   445  	} else {
   446  		if err != nil {
   447  			t.Errorf("MarshalList with input %#v retured error `%s`", test.input, err)
   448  		}
   449  		compareObjects(t, test.expected, actual)
   450  	}
   451  }
   452  
   453  func Test_New_UnmarshalList(t *testing.T) {
   454  	// Using the same inputs from MarshalList, test the reverse mapping.
   455  	for i, test := range marshallerListTestInputs {
   456  		if test.input == nil {
   457  			continue
   458  		}
   459  		iv := reflect.ValueOf(test.input)
   460  
   461  		actual := reflect.New(iv.Type())
   462  		if iv.Kind() == reflect.Slice {
   463  			actual.Elem().Set(reflect.MakeSlice(iv.Type(), iv.Len(), iv.Cap()))
   464  		}
   465  
   466  		if err := UnmarshalList(test.expected.([]*dynamodb.AttributeValue), actual.Interface()); err != nil {
   467  			t.Errorf("Unmarshal %d, with input %#v retured error `%s`", i+1, test.expected, err)
   468  		}
   469  		compareObjects(t, test.input, actual.Elem().Interface())
   470  	}
   471  }
   472  
   473  func Test_New_UnmarshalListError(t *testing.T) {
   474  	// Test that we get an error using UnmarshalList to convert to a nil value.
   475  	expected := &InvalidUnmarshalError{Type: reflect.TypeOf(nil)}
   476  	if err := UnmarshalList(nil, nil); err == nil {
   477  		t.Errorf("UnmarshalList with input %T returned no error, expected error `%v`", nil, expected)
   478  	} else if err.Error() != expected.Error() {
   479  		t.Errorf("UnmarshalList with input %T returned error `%v`, expected error `%v`", nil, err, expected)
   480  	}
   481  
   482  	// Test that we get an error using UnmarshalList to convert to a non-pointer value.
   483  	var actual map[string]interface{}
   484  	expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual)}
   485  	if err := UnmarshalList(nil, actual); err == nil {
   486  		t.Errorf("UnmarshalList with input %T returned no error, expected error `%v`", actual, expected)
   487  	} else if err.Error() != expected.Error() {
   488  		t.Errorf("UnmarshalList with input %T returned error `%v`, expected error `%v`", actual, err, expected)
   489  	}
   490  
   491  	// Test that we get an error using UnmarshalList to convert to nil struct.
   492  	var actual2 *struct{ A int }
   493  	expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual2)}
   494  	if err := UnmarshalList(nil, actual2); err == nil {
   495  		t.Errorf("UnmarshalList with input %T returned no error, expected error `%v`", actual2, expected)
   496  	} else if err.Error() != expected.Error() {
   497  		t.Errorf("UnmarshalList with input %T returned error `%v`, expected error `%v`", actual2, err, expected)
   498  	}
   499  }
   500  
   501  // see github issue #1594
   502  func TestDecodeArrayType(t *testing.T) {
   503  	cases := []struct {
   504  		to, from interface{}
   505  	}{
   506  		{
   507  			&[2]int{1, 2},
   508  			&[2]int{},
   509  		},
   510  		{
   511  			&[2]int64{1, 2},
   512  			&[2]int64{},
   513  		},
   514  		{
   515  			&[2]byte{1, 2},
   516  			&[2]byte{},
   517  		},
   518  		{
   519  			&[2]bool{true, false},
   520  			&[2]bool{},
   521  		},
   522  		{
   523  			&[2]string{"1", "2"},
   524  			&[2]string{},
   525  		},
   526  		{
   527  			&[2][]string{{"1", "2"}},
   528  			&[2][]string{},
   529  		},
   530  	}
   531  
   532  	for _, c := range cases {
   533  		marshaled, err := Marshal(c.to)
   534  		if err != nil {
   535  			t.Errorf("expected no error, but received %v", err)
   536  		}
   537  
   538  		if err = Unmarshal(marshaled, c.from); err != nil {
   539  			t.Errorf("expected no error, but received %v", err)
   540  		}
   541  
   542  		if !reflect.DeepEqual(c.to, c.from) {
   543  			t.Errorf("expected %v, but received %v", c.to, c.from)
   544  		}
   545  	}
   546  }
   547  
   548  func compareObjects(t *testing.T, expected interface{}, actual interface{}) {
   549  	if !reflect.DeepEqual(expected, actual) {
   550  		ev := reflect.ValueOf(expected)
   551  		av := reflect.ValueOf(actual)
   552  		t.Errorf("\nExpected kind(%s,%T):\n%s\nActual kind(%s,%T):\n%s\n",
   553  			ev.Kind(),
   554  			ev.Interface(),
   555  			awsutil.Prettify(expected),
   556  			av.Kind(),
   557  			ev.Interface(),
   558  			awsutil.Prettify(actual))
   559  	}
   560  }
   561  
   562  func BenchmarkMarshalOneMember(b *testing.B) {
   563  	fieldCache = fieldCacher{}
   564  
   565  	simple := simpleMarshalStruct{
   566  		String:  "abc",
   567  		Int:     123,
   568  		Uint:    123,
   569  		Float32: 123.321,
   570  		Float64: 123.321,
   571  		Bool:    true,
   572  		Null:    nil,
   573  	}
   574  
   575  	type MyCompositeStruct struct {
   576  		A simpleMarshalStruct `dynamodbav:"a"`
   577  	}
   578  	b.RunParallel(func(pb *testing.PB) {
   579  		for pb.Next() {
   580  			if _, err := Marshal(MyCompositeStruct{
   581  				A: simple,
   582  			}); err != nil {
   583  				b.Error("unexpected error:", err)
   584  			}
   585  		}
   586  	})
   587  }
   588  
   589  func BenchmarkMarshalTwoMembers(b *testing.B) {
   590  	fieldCache = fieldCacher{}
   591  
   592  	simple := simpleMarshalStruct{
   593  		String:  "abc",
   594  		Int:     123,
   595  		Uint:    123,
   596  		Float32: 123.321,
   597  		Float64: 123.321,
   598  		Bool:    true,
   599  		Null:    nil,
   600  	}
   601  
   602  	type MyCompositeStruct struct {
   603  		A simpleMarshalStruct `dynamodbav:"a"`
   604  		B simpleMarshalStruct `dynamodbav:"b"`
   605  	}
   606  	b.RunParallel(func(pb *testing.PB) {
   607  		for pb.Next() {
   608  			if _, err := Marshal(MyCompositeStruct{
   609  				A: simple,
   610  				B: simple,
   611  			}); err != nil {
   612  				b.Error("unexpected error:", err)
   613  			}
   614  		}
   615  	})
   616  }
   617  
   618  func BenchmarkUnmarshalOneMember(b *testing.B) {
   619  	fieldCache = fieldCacher{}
   620  
   621  	myStructAVMap, _ := Marshal(simpleMarshalStruct{
   622  		String:  "abc",
   623  		Int:     123,
   624  		Uint:    123,
   625  		Float32: 123.321,
   626  		Float64: 123.321,
   627  		Bool:    true,
   628  		Null:    nil,
   629  	})
   630  
   631  	type MyCompositeStructOne struct {
   632  		A simpleMarshalStruct `dynamodbav:"a"`
   633  	}
   634  	var out MyCompositeStructOne
   635  	avMap := map[string]*dynamodb.AttributeValue{
   636  		"a": myStructAVMap,
   637  	}
   638  	b.RunParallel(func(pb *testing.PB) {
   639  		for pb.Next() {
   640  			if err := Unmarshal(&dynamodb.AttributeValue{M: avMap}, &out); err != nil {
   641  				b.Error("unexpected error:", err)
   642  			}
   643  		}
   644  	})
   645  }
   646  
   647  func BenchmarkUnmarshalTwoMembers(b *testing.B) {
   648  	fieldCache = fieldCacher{}
   649  
   650  	myStructAVMap, _ := Marshal(simpleMarshalStruct{
   651  		String:  "abc",
   652  		Int:     123,
   653  		Uint:    123,
   654  		Float32: 123.321,
   655  		Float64: 123.321,
   656  		Bool:    true,
   657  		Null:    nil,
   658  	})
   659  
   660  	type MyCompositeStructTwo struct {
   661  		A simpleMarshalStruct `dynamodbav:"a"`
   662  		B simpleMarshalStruct `dynamodbav:"b"`
   663  	}
   664  	var out MyCompositeStructTwo
   665  	avMap := map[string]*dynamodb.AttributeValue{
   666  		"a": myStructAVMap,
   667  		"b": myStructAVMap,
   668  	}
   669  	b.RunParallel(func(pb *testing.PB) {
   670  		for pb.Next() {
   671  			if err := Unmarshal(&dynamodb.AttributeValue{M: avMap}, &out); err != nil {
   672  				b.Error("unexpected error:", err)
   673  			}
   674  		}
   675  	})
   676  }
   677  
   678  func Test_Encode_YAML_TagKey(t *testing.T) {
   679  	input := struct {
   680  		String      string         `yaml:"string"`
   681  		EmptyString string         `yaml:"empty"`
   682  		OmitString  string         `yaml:"omitted,omitempty"`
   683  		Ignored     string         `yaml:"-"`
   684  		Byte        []byte         `yaml:"byte"`
   685  		Float32     float32        `yaml:"float32"`
   686  		Float64     float64        `yaml:"float64"`
   687  		Int         int            `yaml:"int"`
   688  		Uint        uint           `yaml:"uint"`
   689  		Slice       []string       `yaml:"slice"`
   690  		Map         map[string]int `yaml:"map"`
   691  		NoTag       string
   692  	}{
   693  		String:  "String",
   694  		Ignored: "Ignored",
   695  		Slice:   []string{"one", "two"},
   696  		Map: map[string]int{
   697  			"one": 1,
   698  			"two": 2,
   699  		},
   700  		NoTag: "NoTag",
   701  	}
   702  
   703  	expected := &dynamodb.AttributeValue{
   704  		M: map[string]*dynamodb.AttributeValue{
   705  			"string":  {S: aws.String("String")},
   706  			"empty":   {NULL: &trueValue},
   707  			"byte":    {NULL: &trueValue},
   708  			"float32": {N: aws.String("0")},
   709  			"float64": {N: aws.String("0")},
   710  			"int":     {N: aws.String("0")},
   711  			"uint":    {N: aws.String("0")},
   712  			"slice": {
   713  				L: []*dynamodb.AttributeValue{
   714  					{S: aws.String("one")},
   715  					{S: aws.String("two")},
   716  				},
   717  			},
   718  			"map": {
   719  				M: map[string]*dynamodb.AttributeValue{
   720  					"one": {N: aws.String("1")},
   721  					"two": {N: aws.String("2")},
   722  				},
   723  			},
   724  			"NoTag": {S: aws.String("NoTag")},
   725  		},
   726  	}
   727  
   728  	enc := NewEncoder(func(e *Encoder) {
   729  		e.TagKey = "yaml"
   730  	})
   731  
   732  	actual, err := enc.Encode(input)
   733  	if err != nil {
   734  		t.Errorf("Encode with input %#v retured error `%s`, expected nil", input, err)
   735  	}
   736  
   737  	compareObjects(t, expected, actual)
   738  }