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

     1  package dynamodbattribute
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  
     7  	"github.com/aavshr/aws-sdk-go/aws"
     8  	"github.com/aavshr/aws-sdk-go/aws/awserr"
     9  	"github.com/aavshr/aws-sdk-go/service/dynamodb"
    10  )
    11  
    12  type mySimpleStruct struct {
    13  	String  string
    14  	Int     int
    15  	Uint    uint
    16  	Float32 float32
    17  	Float64 float64
    18  	Bool    bool
    19  	Null    *interface{}
    20  }
    21  
    22  type myComplexStruct struct {
    23  	Simple []mySimpleStruct
    24  }
    25  
    26  type converterTestInput struct {
    27  	input     interface{}
    28  	expected  interface{}
    29  	err       awserr.Error
    30  	inputType string // "enum" of types
    31  }
    32  
    33  var trueValue = true
    34  var falseValue = false
    35  
    36  var converterScalarInputs = []converterTestInput{
    37  	{
    38  		input:    nil,
    39  		expected: &dynamodb.AttributeValue{NULL: &trueValue},
    40  	},
    41  	{
    42  		input:    "some string",
    43  		expected: &dynamodb.AttributeValue{S: aws.String("some string")},
    44  	},
    45  	{
    46  		input:    true,
    47  		expected: &dynamodb.AttributeValue{BOOL: &trueValue},
    48  	},
    49  	{
    50  		input:    false,
    51  		expected: &dynamodb.AttributeValue{BOOL: &falseValue},
    52  	},
    53  	{
    54  		input:    3.14,
    55  		expected: &dynamodb.AttributeValue{N: aws.String("3.14")},
    56  	},
    57  	{
    58  		input:    math.MaxFloat32,
    59  		expected: &dynamodb.AttributeValue{N: aws.String("340282346638528860000000000000000000000")},
    60  	},
    61  	{
    62  		input:    math.MaxFloat64,
    63  		expected: &dynamodb.AttributeValue{N: aws.String("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")},
    64  	},
    65  	{
    66  		input:    12,
    67  		expected: &dynamodb.AttributeValue{N: aws.String("12")},
    68  	},
    69  	{
    70  		input: mySimpleStruct{},
    71  		expected: &dynamodb.AttributeValue{
    72  			M: map[string]*dynamodb.AttributeValue{
    73  				"Bool":    {BOOL: &falseValue},
    74  				"Float32": {N: aws.String("0")},
    75  				"Float64": {N: aws.String("0")},
    76  				"Int":     {N: aws.String("0")},
    77  				"Null":    {NULL: &trueValue},
    78  				"String":  {S: aws.String("")},
    79  				"Uint":    {N: aws.String("0")},
    80  			},
    81  		},
    82  		inputType: "mySimpleStruct",
    83  	},
    84  }
    85  
    86  var converterMapTestInputs = []converterTestInput{
    87  	// Scalar tests
    88  	{
    89  		input: nil,
    90  		err:   awserr.New("SerializationError", "in must be a map[string]interface{} or struct, got <nil>", nil),
    91  	},
    92  	{
    93  		input:    map[string]interface{}{"string": "some string"},
    94  		expected: map[string]*dynamodb.AttributeValue{"string": {S: aws.String("some string")}},
    95  	},
    96  	{
    97  		input:    map[string]interface{}{"bool": true},
    98  		expected: map[string]*dynamodb.AttributeValue{"bool": {BOOL: &trueValue}},
    99  	},
   100  	{
   101  		input:    map[string]interface{}{"bool": false},
   102  		expected: map[string]*dynamodb.AttributeValue{"bool": {BOOL: &falseValue}},
   103  	},
   104  	{
   105  		input:    map[string]interface{}{"null": nil},
   106  		expected: map[string]*dynamodb.AttributeValue{"null": {NULL: &trueValue}},
   107  	},
   108  	{
   109  		input:    map[string]interface{}{"float": 3.14},
   110  		expected: map[string]*dynamodb.AttributeValue{"float": {N: aws.String("3.14")}},
   111  	},
   112  	{
   113  		input:    map[string]interface{}{"float": math.MaxFloat32},
   114  		expected: map[string]*dynamodb.AttributeValue{"float": {N: aws.String("340282346638528860000000000000000000000")}},
   115  	},
   116  	{
   117  		input:    map[string]interface{}{"float": math.MaxFloat64},
   118  		expected: map[string]*dynamodb.AttributeValue{"float": {N: aws.String("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")}},
   119  	},
   120  	{
   121  		input:    map[string]interface{}{"int": int(12)},
   122  		expected: map[string]*dynamodb.AttributeValue{"int": {N: aws.String("12")}},
   123  	},
   124  	{
   125  		input:    map[string]interface{}{"byte": []byte{48, 49}},
   126  		expected: map[string]*dynamodb.AttributeValue{"byte": {B: []byte{48, 49}}},
   127  	},
   128  	// List
   129  	{
   130  		input: map[string]interface{}{"list": []interface{}{"a string", 12, 3.14, true, nil, false}},
   131  		expected: map[string]*dynamodb.AttributeValue{
   132  			"list": {
   133  				L: []*dynamodb.AttributeValue{
   134  					{S: aws.String("a string")},
   135  					{N: aws.String("12")},
   136  					{N: aws.String("3.14")},
   137  					{BOOL: &trueValue},
   138  					{NULL: &trueValue},
   139  					{BOOL: &falseValue},
   140  				},
   141  			},
   142  		},
   143  	},
   144  	// Map
   145  	{
   146  		input: map[string]interface{}{"map": map[string]interface{}{"nestedint": 12}},
   147  		expected: map[string]*dynamodb.AttributeValue{
   148  			"map": {
   149  				M: map[string]*dynamodb.AttributeValue{
   150  					"nestedint": {
   151  						N: aws.String("12"),
   152  					},
   153  				},
   154  			},
   155  		},
   156  	},
   157  	// Structs
   158  	{
   159  		input: mySimpleStruct{},
   160  		expected: map[string]*dynamodb.AttributeValue{
   161  			"Bool":    {BOOL: &falseValue},
   162  			"Float32": {N: aws.String("0")},
   163  			"Float64": {N: aws.String("0")},
   164  			"Int":     {N: aws.String("0")},
   165  			"Null":    {NULL: &trueValue},
   166  			"String":  {S: aws.String("")},
   167  			"Uint":    {N: aws.String("0")},
   168  		},
   169  		inputType: "mySimpleStruct",
   170  	},
   171  	{
   172  		input: myComplexStruct{},
   173  		expected: map[string]*dynamodb.AttributeValue{
   174  			"Simple": {NULL: &trueValue},
   175  		},
   176  		inputType: "myComplexStruct",
   177  	},
   178  	{
   179  		input: myComplexStruct{Simple: []mySimpleStruct{{Int: -2}, {Uint: 5}}},
   180  		expected: map[string]*dynamodb.AttributeValue{
   181  			"Simple": {
   182  				L: []*dynamodb.AttributeValue{
   183  					{
   184  						M: map[string]*dynamodb.AttributeValue{
   185  							"Bool":    {BOOL: &falseValue},
   186  							"Float32": {N: aws.String("0")},
   187  							"Float64": {N: aws.String("0")},
   188  							"Int":     {N: aws.String("-2")},
   189  							"Null":    {NULL: &trueValue},
   190  							"String":  {S: aws.String("")},
   191  							"Uint":    {N: aws.String("0")},
   192  						},
   193  					},
   194  					{
   195  						M: map[string]*dynamodb.AttributeValue{
   196  							"Bool":    {BOOL: &falseValue},
   197  							"Float32": {N: aws.String("0")},
   198  							"Float64": {N: aws.String("0")},
   199  							"Int":     {N: aws.String("0")},
   200  							"Null":    {NULL: &trueValue},
   201  							"String":  {S: aws.String("")},
   202  							"Uint":    {N: aws.String("5")},
   203  						},
   204  					},
   205  				},
   206  			},
   207  		},
   208  		inputType: "myComplexStruct",
   209  	},
   210  }
   211  
   212  var converterListTestInputs = []converterTestInput{
   213  	{
   214  		input: nil,
   215  		err:   awserr.New("SerializationError", "in must be an array or slice, got <nil>", nil),
   216  	},
   217  	{
   218  		input:    []interface{}{},
   219  		expected: []*dynamodb.AttributeValue{},
   220  	},
   221  	{
   222  		input: []interface{}{"a string", 12, 3.14, true, nil, false},
   223  		expected: []*dynamodb.AttributeValue{
   224  			{S: aws.String("a string")},
   225  			{N: aws.String("12")},
   226  			{N: aws.String("3.14")},
   227  			{BOOL: &trueValue},
   228  			{NULL: &trueValue},
   229  			{BOOL: &falseValue},
   230  		},
   231  	},
   232  	{
   233  		input: []mySimpleStruct{{}},
   234  		expected: []*dynamodb.AttributeValue{
   235  			{
   236  				M: map[string]*dynamodb.AttributeValue{
   237  					"Bool":    {BOOL: &falseValue},
   238  					"Float32": {N: aws.String("0")},
   239  					"Float64": {N: aws.String("0")},
   240  					"Int":     {N: aws.String("0")},
   241  					"Null":    {NULL: &trueValue},
   242  					"String":  {S: aws.String("")},
   243  					"Uint":    {N: aws.String("0")},
   244  				},
   245  			},
   246  		},
   247  		inputType: "mySimpleStruct",
   248  	},
   249  }
   250  
   251  func TestConvertTo(t *testing.T) {
   252  	for _, test := range converterScalarInputs {
   253  		testConvertTo(t, test)
   254  	}
   255  }
   256  
   257  func testConvertTo(t *testing.T, test converterTestInput) {
   258  	actual, err := ConvertTo(test.input)
   259  	if test.err != nil {
   260  		if err == nil {
   261  			t.Errorf("ConvertTo with input %#v retured %#v, expected error `%s`", test.input, actual, test.err)
   262  		} else if err.Error() != test.err.Error() {
   263  			t.Errorf("ConvertTo with input %#v retured error `%s`, expected error `%s`", test.input, err, test.err)
   264  		}
   265  	} else {
   266  		if err != nil {
   267  			t.Errorf("ConvertTo with input %#v retured error `%s`", test.input, err)
   268  		}
   269  		compareObjects(t, test.expected, actual)
   270  	}
   271  }
   272  
   273  func TestConvertFrom(t *testing.T) {
   274  	// Using the same inputs from TestConvertTo, test the reverse mapping.
   275  	for _, test := range converterScalarInputs {
   276  		if test.expected != nil {
   277  			testConvertFrom(t, test)
   278  		}
   279  	}
   280  }
   281  
   282  func testConvertFrom(t *testing.T, test converterTestInput) {
   283  	switch test.inputType {
   284  	case "mySimpleStruct":
   285  		var actual mySimpleStruct
   286  		if err := ConvertFrom(test.expected.(*dynamodb.AttributeValue), &actual); err != nil {
   287  			t.Errorf("ConvertFrom with input %#v retured error `%s`", test.expected, err)
   288  		}
   289  		compareObjects(t, test.input, actual)
   290  	case "myComplexStruct":
   291  		var actual myComplexStruct
   292  		if err := ConvertFrom(test.expected.(*dynamodb.AttributeValue), &actual); err != nil {
   293  			t.Errorf("ConvertFrom with input %#v retured error `%s`", test.expected, err)
   294  		}
   295  		compareObjects(t, test.input, actual)
   296  	default:
   297  		var actual interface{}
   298  		if err := ConvertFrom(test.expected.(*dynamodb.AttributeValue), &actual); err != nil {
   299  			t.Errorf("ConvertFrom with input %#v retured error `%s`", test.expected, err)
   300  		}
   301  		compareObjects(t, test.input, actual)
   302  	}
   303  }
   304  
   305  func TestConvertFromError(t *testing.T) {
   306  	// Test that we get an error using ConvertFrom to convert to a map.
   307  	var actual map[string]interface{}
   308  	expected := awserr.New("SerializationError", `v must be a non-nil pointer to an interface{} or struct, got *map[string]interface {}`, nil).Error()
   309  	if err := ConvertFrom(nil, &actual); err == nil {
   310  		t.Errorf("ConvertFrom with input %#v returned no error, expected error `%s`", nil, expected)
   311  	} else if err.Error() != expected {
   312  		t.Errorf("ConvertFrom with input %#v returned error `%s`, expected error `%s`", nil, err, expected)
   313  	}
   314  
   315  	// Test that we get an error using ConvertFrom to convert to a list.
   316  	var actual2 []interface{}
   317  	expected = awserr.New("SerializationError", `v must be a non-nil pointer to an interface{} or struct, got *[]interface {}`, nil).Error()
   318  	if err := ConvertFrom(nil, &actual2); err == nil {
   319  		t.Errorf("ConvertFrom with input %#v returned no error, expected error `%s`", nil, expected)
   320  	} else if err.Error() != expected {
   321  		t.Errorf("ConvertFrom with input %#v returned error `%s`, expected error `%s`", nil, err, expected)
   322  	}
   323  }
   324  
   325  func TestConvertToMap(t *testing.T) {
   326  	for _, test := range converterMapTestInputs {
   327  		testConvertToMap(t, test)
   328  	}
   329  }
   330  
   331  func testConvertToMap(t *testing.T, test converterTestInput) {
   332  	actual, err := ConvertToMap(test.input)
   333  	if test.err != nil {
   334  		if err == nil {
   335  			t.Errorf("ConvertToMap with input %#v retured %#v, expected error `%s`", test.input, actual, test.err)
   336  		} else if err.Error() != test.err.Error() {
   337  			t.Errorf("ConvertToMap with input %#v retured error `%s`, expected error `%s`", test.input, err, test.err)
   338  		}
   339  	} else {
   340  		if err != nil {
   341  			t.Errorf("ConvertToMap with input %#v retured error `%s`", test.input, err)
   342  		}
   343  		compareObjects(t, test.expected, actual)
   344  	}
   345  }
   346  
   347  func TestConvertFromMap(t *testing.T) {
   348  	// Using the same inputs from TestConvertToMap, test the reverse mapping.
   349  	for _, test := range converterMapTestInputs {
   350  		if test.expected != nil {
   351  			testConvertFromMap(t, test)
   352  		}
   353  	}
   354  }
   355  
   356  func testConvertFromMap(t *testing.T, test converterTestInput) {
   357  	switch test.inputType {
   358  	case "mySimpleStruct":
   359  		var actual mySimpleStruct
   360  		if err := ConvertFromMap(test.expected.(map[string]*dynamodb.AttributeValue), &actual); err != nil {
   361  			t.Errorf("ConvertFromMap with input %#v retured error `%s`", test.expected, err)
   362  		}
   363  		compareObjects(t, test.input, actual)
   364  	case "myComplexStruct":
   365  		var actual myComplexStruct
   366  		if err := ConvertFromMap(test.expected.(map[string]*dynamodb.AttributeValue), &actual); err != nil {
   367  			t.Errorf("ConvertFromMap with input %#v retured error `%s`", test.expected, err)
   368  		}
   369  		compareObjects(t, test.input, actual)
   370  	default:
   371  		var actual map[string]interface{}
   372  		if err := ConvertFromMap(test.expected.(map[string]*dynamodb.AttributeValue), &actual); err != nil {
   373  			t.Errorf("ConvertFromMap with input %#v retured error `%s`", test.expected, err)
   374  		}
   375  		compareObjects(t, test.input, actual)
   376  	}
   377  }
   378  
   379  func TestConvertFromMapError(t *testing.T) {
   380  	// Test that we get an error using ConvertFromMap to convert to an interface{}.
   381  	var actual interface{}
   382  	expected := awserr.New("SerializationError", `v must be a non-nil pointer to a map[string]interface{} or struct, got *interface {}`, nil).Error()
   383  	if err := ConvertFromMap(nil, &actual); err == nil {
   384  		t.Errorf("ConvertFromMap with input %#v returned no error, expected error `%s`", nil, expected)
   385  	} else if err.Error() != expected {
   386  		t.Errorf("ConvertFromMap with input %#v returned error `%s`, expected error `%s`", nil, err, expected)
   387  	}
   388  
   389  	// Test that we get an error using ConvertFromMap to convert to a slice.
   390  	var actual2 []interface{}
   391  	expected = awserr.New("SerializationError", `v must be a non-nil pointer to a map[string]interface{} or struct, got *[]interface {}`, nil).Error()
   392  	if err := ConvertFromMap(nil, &actual2); err == nil {
   393  		t.Errorf("ConvertFromMap with input %#v returned no error, expected error `%s`", nil, expected)
   394  	} else if err.Error() != expected {
   395  		t.Errorf("ConvertFromMap with input %#v returned error `%s`, expected error `%s`", nil, err, expected)
   396  	}
   397  }
   398  
   399  func TestConvertToList(t *testing.T) {
   400  	for _, test := range converterListTestInputs {
   401  		testConvertToList(t, test)
   402  	}
   403  }
   404  
   405  func testConvertToList(t *testing.T, test converterTestInput) {
   406  	actual, err := ConvertToList(test.input)
   407  	if test.err != nil {
   408  		if err == nil {
   409  			t.Errorf("ConvertToList with input %#v retured %#v, expected error `%s`", test.input, actual, test.err)
   410  		} else if err.Error() != test.err.Error() {
   411  			t.Errorf("ConvertToList with input %#v retured error `%s`, expected error `%s`", test.input, err, test.err)
   412  		}
   413  	} else {
   414  		if err != nil {
   415  			t.Errorf("ConvertToList with input %#v retured error `%s`", test.input, err)
   416  		}
   417  		compareObjects(t, test.expected, actual)
   418  	}
   419  }
   420  
   421  func TestConvertFromList(t *testing.T) {
   422  	// Using the same inputs from TestConvertToList, test the reverse mapping.
   423  	for _, test := range converterListTestInputs {
   424  		if test.expected != nil {
   425  			testConvertFromList(t, test)
   426  		}
   427  	}
   428  }
   429  
   430  func testConvertFromList(t *testing.T, test converterTestInput) {
   431  	switch test.inputType {
   432  	case "mySimpleStruct":
   433  		var actual []mySimpleStruct
   434  		if err := ConvertFromList(test.expected.([]*dynamodb.AttributeValue), &actual); err != nil {
   435  			t.Errorf("ConvertFromList with input %#v retured error `%s`", test.expected, err)
   436  		}
   437  		compareObjects(t, test.input, actual)
   438  	case "myComplexStruct":
   439  		var actual []myComplexStruct
   440  		if err := ConvertFromList(test.expected.([]*dynamodb.AttributeValue), &actual); err != nil {
   441  			t.Errorf("ConvertFromList with input %#v retured error `%s`", test.expected, err)
   442  		}
   443  		compareObjects(t, test.input, actual)
   444  	default:
   445  		var actual []interface{}
   446  		if err := ConvertFromList(test.expected.([]*dynamodb.AttributeValue), &actual); err != nil {
   447  			t.Errorf("ConvertFromList with input %#v retured error `%s`", test.expected, err)
   448  		}
   449  		compareObjects(t, test.input, actual)
   450  	}
   451  }
   452  
   453  func TestConvertFromListError(t *testing.T) {
   454  	// Test that we get an error using ConvertFromList to convert to a map.
   455  	var actual map[string]interface{}
   456  	expected := awserr.New("SerializationError", `v must be a non-nil pointer to an array or slice, got *map[string]interface {}`, nil).Error()
   457  	if err := ConvertFromList(nil, &actual); err == nil {
   458  		t.Errorf("ConvertFromList with input %#v returned no error, expected error `%s`", nil, expected)
   459  	} else if err.Error() != expected {
   460  		t.Errorf("ConvertFromList with input %#v returned error `%s`, expected error `%s`", nil, err, expected)
   461  	}
   462  
   463  	// Test that we get an error using ConvertFromList to convert to a struct.
   464  	var actual2 myComplexStruct
   465  	expected = awserr.New("SerializationError", `v must be a non-nil pointer to an array or slice, got *dynamodbattribute.myComplexStruct`, nil).Error()
   466  	if err := ConvertFromList(nil, &actual2); err == nil {
   467  		t.Errorf("ConvertFromList with input %#v returned no error, expected error `%s`", nil, expected)
   468  	} else if err.Error() != expected {
   469  		t.Errorf("ConvertFromList with input %#v returned error `%s`, expected error `%s`", nil, err, expected)
   470  	}
   471  
   472  	// Test that we get an error using ConvertFromList to convert to an interface{}.
   473  	var actual3 interface{}
   474  	expected = awserr.New("SerializationError", `v must be a non-nil pointer to an array or slice, got *interface {}`, nil).Error()
   475  	if err := ConvertFromList(nil, &actual3); err == nil {
   476  		t.Errorf("ConvertFromList with input %#v returned no error, expected error `%s`", nil, expected)
   477  	} else if err.Error() != expected {
   478  		t.Errorf("ConvertFromList with input %#v returned error `%s`, expected error `%s`", nil, err, expected)
   479  	}
   480  }
   481  
   482  func BenchmarkConvertTo(b *testing.B) {
   483  	d := mySimpleStruct{
   484  		String:  "abc",
   485  		Int:     123,
   486  		Uint:    123,
   487  		Float32: 123.321,
   488  		Float64: 123.321,
   489  		Bool:    true,
   490  		Null:    nil,
   491  	}
   492  	for i := 0; i < b.N; i++ {
   493  		_, err := ConvertTo(d)
   494  		if err != nil {
   495  			b.Fatal("unexpected error", err)
   496  		}
   497  	}
   498  }