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

     1  package dynamodbattribute
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/aavshr/aws-sdk-go/aws"
    10  	"github.com/aavshr/aws-sdk-go/service/dynamodb"
    11  )
    12  
    13  type testBinarySetStruct struct {
    14  	Binarys [][]byte `dynamodbav:",binaryset"`
    15  }
    16  type testNumberSetStruct struct {
    17  	Numbers []int `dynamodbav:",numberset"`
    18  }
    19  type testStringSetStruct struct {
    20  	Strings []string `dynamodbav:",stringset"`
    21  }
    22  
    23  type testIntAsStringStruct struct {
    24  	Value int `dynamodbav:",string"`
    25  }
    26  
    27  type testOmitEmptyStruct struct {
    28  	Value  string  `dynamodbav:",omitempty"`
    29  	Value2 *string `dynamodbav:",omitempty"`
    30  	Value3 int
    31  }
    32  
    33  type testAliasedString string
    34  type testAliasedStringSlice []string
    35  type testAliasedInt int
    36  type testAliasedIntSlice []int
    37  type testAliasedMap map[string]int
    38  type testAliasedSlice []string
    39  type testAliasedByteSlice []byte
    40  type testAliasedBool bool
    41  type testAliasedBoolSlice []bool
    42  
    43  type testAliasedStruct struct {
    44  	Value  testAliasedString
    45  	Value2 testAliasedInt
    46  	Value3 testAliasedMap
    47  	Value4 testAliasedSlice
    48  
    49  	Value5 testAliasedByteSlice
    50  	Value6 []testAliasedInt
    51  	Value7 []testAliasedString
    52  
    53  	Value8  []testAliasedByteSlice `dynamodbav:",binaryset"`
    54  	Value9  []testAliasedInt       `dynamodbav:",numberset"`
    55  	Value10 []testAliasedString    `dynamodbav:",stringset"`
    56  
    57  	Value11 testAliasedIntSlice
    58  	Value12 testAliasedStringSlice
    59  
    60  	Value13 testAliasedBool
    61  	Value14 testAliasedBoolSlice
    62  
    63  	Value15 map[testAliasedString]string
    64  }
    65  
    66  type testNamedPointer *int
    67  
    68  var testDate, _ = time.Parse(time.RFC3339, "2016-05-03T17:06:26.209072Z")
    69  
    70  var sharedTestCases = []struct {
    71  	in                   *dynamodb.AttributeValue
    72  	actual, expected     interface{}
    73  	encoderOpts          func(encoder *Encoder)
    74  	enableEmptyString    bool
    75  	enableEmptyByteSlice bool
    76  	err                  error
    77  }{
    78  	0: { // Binary slice
    79  		in:       &dynamodb.AttributeValue{B: []byte{48, 49}},
    80  		actual:   &[]byte{},
    81  		expected: []byte{48, 49},
    82  	},
    83  	1: { // Binary slice oversized
    84  		in: &dynamodb.AttributeValue{B: []byte{48, 49}},
    85  		actual: func() *[]byte {
    86  			v := make([]byte, 0, 10)
    87  			return &v
    88  		}(),
    89  		expected: []byte{48, 49},
    90  	},
    91  	2: { // Binary slice pointer
    92  		in: &dynamodb.AttributeValue{B: []byte{48, 49}},
    93  		actual: func() **[]byte {
    94  			v := make([]byte, 0, 10)
    95  			v2 := &v
    96  			return &v2
    97  		}(),
    98  		expected: []byte{48, 49},
    99  	},
   100  	3: { // Bool
   101  		in:       &dynamodb.AttributeValue{BOOL: aws.Bool(true)},
   102  		actual:   new(bool),
   103  		expected: true,
   104  	},
   105  	4: { // List
   106  		in: &dynamodb.AttributeValue{L: []*dynamodb.AttributeValue{
   107  			{N: aws.String("123")},
   108  		}},
   109  		actual:   &[]int{},
   110  		expected: []int{123},
   111  	},
   112  	5: { // Map, interface
   113  		in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
   114  			"abc": {N: aws.String("123")},
   115  		}},
   116  		actual:   &map[string]int{},
   117  		expected: map[string]int{"abc": 123},
   118  	},
   119  	6: { // Map, struct
   120  		in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
   121  			"Abc": {N: aws.String("123")},
   122  		}},
   123  		actual:   &struct{ Abc int }{},
   124  		expected: struct{ Abc int }{Abc: 123},
   125  	},
   126  	7: { // Map, struct
   127  		in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
   128  			"abc": {N: aws.String("123")},
   129  		}},
   130  		actual: &struct {
   131  			Abc int `json:"abc" dynamodbav:"abc"`
   132  		}{},
   133  		expected: struct {
   134  			Abc int `json:"abc" dynamodbav:"abc"`
   135  		}{Abc: 123},
   136  	},
   137  	8: { // Number, int
   138  		in:       &dynamodb.AttributeValue{N: aws.String("123")},
   139  		actual:   new(int),
   140  		expected: 123,
   141  	},
   142  	9: { // Number, Float
   143  		in:       &dynamodb.AttributeValue{N: aws.String("123.1")},
   144  		actual:   new(float64),
   145  		expected: float64(123.1),
   146  	},
   147  	10: { // Null string
   148  		in:       &dynamodb.AttributeValue{NULL: aws.Bool(true)},
   149  		actual:   new(string),
   150  		expected: "",
   151  	},
   152  	11: { // Null ptr string
   153  		in:       &dynamodb.AttributeValue{NULL: aws.Bool(true)},
   154  		actual:   new(*string),
   155  		expected: nil,
   156  	},
   157  	12: { // Non-Empty String
   158  		in:       &dynamodb.AttributeValue{S: aws.String("abc")},
   159  		actual:   new(string),
   160  		expected: "abc",
   161  	},
   162  	13: { // Binary Set
   163  		in: &dynamodb.AttributeValue{
   164  			M: map[string]*dynamodb.AttributeValue{
   165  				"Binarys": {BS: [][]byte{{48, 49}, {50, 51}}},
   166  			},
   167  		},
   168  		actual:   &testBinarySetStruct{},
   169  		expected: testBinarySetStruct{Binarys: [][]byte{{48, 49}, {50, 51}}},
   170  	},
   171  	14: { // Number Set
   172  		in: &dynamodb.AttributeValue{
   173  			M: map[string]*dynamodb.AttributeValue{
   174  				"Numbers": {NS: []*string{aws.String("123"), aws.String("321")}},
   175  			},
   176  		},
   177  		actual:   &testNumberSetStruct{},
   178  		expected: testNumberSetStruct{Numbers: []int{123, 321}},
   179  	},
   180  	15: { // String Set
   181  		in: &dynamodb.AttributeValue{
   182  			M: map[string]*dynamodb.AttributeValue{
   183  				"Strings": {SS: []*string{aws.String("abc"), aws.String("efg")}},
   184  			},
   185  		},
   186  		actual:   &testStringSetStruct{},
   187  		expected: testStringSetStruct{Strings: []string{"abc", "efg"}},
   188  	},
   189  	16: { // Int value as string
   190  		in: &dynamodb.AttributeValue{
   191  			M: map[string]*dynamodb.AttributeValue{
   192  				"Value": {S: aws.String("123")},
   193  			},
   194  		},
   195  		actual:   &testIntAsStringStruct{},
   196  		expected: testIntAsStringStruct{Value: 123},
   197  	},
   198  	17: { // Omitempty
   199  		in: &dynamodb.AttributeValue{
   200  			M: map[string]*dynamodb.AttributeValue{
   201  				"Value3": {N: aws.String("0")},
   202  			},
   203  		},
   204  		actual:   &testOmitEmptyStruct{},
   205  		expected: testOmitEmptyStruct{Value: "", Value2: nil, Value3: 0},
   206  	},
   207  	18: { // aliased type
   208  		in: &dynamodb.AttributeValue{
   209  			M: map[string]*dynamodb.AttributeValue{
   210  				"Value":  {S: aws.String("123")},
   211  				"Value2": {N: aws.String("123")},
   212  				"Value3": {M: map[string]*dynamodb.AttributeValue{
   213  					"Key": {N: aws.String("321")},
   214  				}},
   215  				"Value4": {L: []*dynamodb.AttributeValue{
   216  					{S: aws.String("1")},
   217  					{S: aws.String("2")},
   218  					{S: aws.String("3")},
   219  				}},
   220  				"Value5": {B: []byte{0, 1, 2}},
   221  				"Value6": {L: []*dynamodb.AttributeValue{
   222  					{N: aws.String("1")},
   223  					{N: aws.String("2")},
   224  					{N: aws.String("3")},
   225  				}},
   226  				"Value7": {L: []*dynamodb.AttributeValue{
   227  					{S: aws.String("1")},
   228  					{S: aws.String("2")},
   229  					{S: aws.String("3")},
   230  				}},
   231  				"Value8": {BS: [][]byte{
   232  					{0, 1, 2}, {3, 4, 5},
   233  				}},
   234  				"Value9": {NS: []*string{
   235  					aws.String("1"),
   236  					aws.String("2"),
   237  					aws.String("3"),
   238  				}},
   239  				"Value10": {SS: []*string{
   240  					aws.String("1"),
   241  					aws.String("2"),
   242  					aws.String("3"),
   243  				}},
   244  				"Value11": {L: []*dynamodb.AttributeValue{
   245  					{N: aws.String("1")},
   246  					{N: aws.String("2")},
   247  					{N: aws.String("3")},
   248  				}},
   249  				"Value12": {L: []*dynamodb.AttributeValue{
   250  					{S: aws.String("1")},
   251  					{S: aws.String("2")},
   252  					{S: aws.String("3")},
   253  				}},
   254  				"Value13": {BOOL: aws.Bool(true)},
   255  				"Value14": {L: []*dynamodb.AttributeValue{
   256  					{BOOL: aws.Bool(true)},
   257  					{BOOL: aws.Bool(false)},
   258  					{BOOL: aws.Bool(true)},
   259  				}},
   260  				"Value15": {M: map[string]*dynamodb.AttributeValue{
   261  					"TestKey": {S: aws.String("TestElement")},
   262  				}},
   263  			},
   264  		},
   265  		actual: &testAliasedStruct{},
   266  		expected: testAliasedStruct{
   267  			Value: "123", Value2: 123,
   268  			Value3: testAliasedMap{
   269  				"Key": 321,
   270  			},
   271  			Value4: testAliasedSlice{"1", "2", "3"},
   272  			Value5: testAliasedByteSlice{0, 1, 2},
   273  			Value6: []testAliasedInt{1, 2, 3},
   274  			Value7: []testAliasedString{"1", "2", "3"},
   275  			Value8: []testAliasedByteSlice{
   276  				{0, 1, 2},
   277  				{3, 4, 5},
   278  			},
   279  			Value9:  []testAliasedInt{1, 2, 3},
   280  			Value10: []testAliasedString{"1", "2", "3"},
   281  			Value11: testAliasedIntSlice{1, 2, 3},
   282  			Value12: testAliasedStringSlice{"1", "2", "3"},
   283  			Value13: true,
   284  			Value14: testAliasedBoolSlice{true, false, true},
   285  			Value15: map[testAliasedString]string{
   286  				"TestKey": "TestElement",
   287  			},
   288  		},
   289  	},
   290  	19: {
   291  		in:       &dynamodb.AttributeValue{N: aws.String("123")},
   292  		actual:   new(testNamedPointer),
   293  		expected: testNamedPointer(aws.Int(123)),
   294  	},
   295  	20: { // time.Time
   296  		in:       &dynamodb.AttributeValue{S: aws.String("2016-05-03T17:06:26.209072Z")},
   297  		actual:   new(time.Time),
   298  		expected: testDate,
   299  	},
   300  	21: { // time.Time List
   301  		in: &dynamodb.AttributeValue{L: []*dynamodb.AttributeValue{
   302  			{S: aws.String("2016-05-03T17:06:26.209072Z")},
   303  			{S: aws.String("2016-05-04T17:06:26.209072Z")},
   304  		}},
   305  		actual:   new([]time.Time),
   306  		expected: []time.Time{testDate, testDate.Add(24 * time.Hour)},
   307  	},
   308  	22: { // time.Time struct
   309  		in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
   310  			"abc": {S: aws.String("2016-05-03T17:06:26.209072Z")},
   311  		}},
   312  		actual: &struct {
   313  			Abc time.Time `json:"abc" dynamodbav:"abc"`
   314  		}{},
   315  		expected: struct {
   316  			Abc time.Time `json:"abc" dynamodbav:"abc"`
   317  		}{Abc: testDate},
   318  	},
   319  	23: { // time.Time ptr struct
   320  		in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
   321  			"abc": {S: aws.String("2016-05-03T17:06:26.209072Z")},
   322  		}},
   323  		actual: &struct {
   324  			Abc *time.Time `json:"abc" dynamodbav:"abc"`
   325  		}{},
   326  		expected: struct {
   327  			Abc *time.Time `json:"abc" dynamodbav:"abc"`
   328  		}{Abc: &testDate},
   329  	},
   330  	24: { // empty string and NullEmptyString off
   331  		encoderOpts: func(encoder *Encoder) {
   332  			encoder.NullEmptyString = false
   333  		},
   334  		in:       &dynamodb.AttributeValue{S: aws.String("")},
   335  		actual:   new(string),
   336  		expected: "",
   337  	},
   338  	25: { // empty ptr string and NullEmptyString off
   339  		encoderOpts: func(encoder *Encoder) {
   340  			encoder.NullEmptyString = false
   341  		},
   342  		in:       &dynamodb.AttributeValue{S: aws.String("")},
   343  		actual:   new(*string),
   344  		expected: "",
   345  	},
   346  	26: { // string set with empty string and NullEmptyString off
   347  		encoderOpts: func(encoder *Encoder) {
   348  			encoder.NullEmptyString = false
   349  		},
   350  		in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
   351  			"Value": {SS: []*string{aws.String("one"), aws.String(""), aws.String("three")}},
   352  		}},
   353  		actual: &struct {
   354  			Value []string `dynamodbav:",stringset"`
   355  		}{},
   356  		expected: struct {
   357  			Value []string `dynamodbav:",stringset"`
   358  		}{
   359  			Value: []string{"one", "", "three"},
   360  		},
   361  	},
   362  	27: { // empty byte slice and NullEmptyString off
   363  		encoderOpts: func(encoder *Encoder) {
   364  			encoder.NullEmptyByteSlice = false
   365  		},
   366  		in:       &dynamodb.AttributeValue{B: []byte{}},
   367  		actual:   &[]byte{},
   368  		expected: []byte{},
   369  	},
   370  	28: { // byte slice set with empty values and NullEmptyString off
   371  		encoderOpts: func(encoder *Encoder) {
   372  			encoder.NullEmptyByteSlice = false
   373  		},
   374  		in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{"Value": {BS: [][]byte{{0x0}, {}, {0x2}}}}},
   375  		actual: &struct {
   376  			Value [][]byte `dynamodbav:",binaryset"`
   377  		}{},
   378  		expected: struct {
   379  			Value [][]byte `dynamodbav:",binaryset"`
   380  		}{
   381  			Value: [][]byte{{0x0}, {}, {0x2}},
   382  		},
   383  	},
   384  	29: { // empty byte slice and NullEmptyByteSlice disabled, and omitempty
   385  		encoderOpts: func(encoder *Encoder) {
   386  			encoder.NullEmptyByteSlice = false
   387  		},
   388  		in: &dynamodb.AttributeValue{
   389  			NULL: aws.Bool(true),
   390  		},
   391  		actual: &struct {
   392  			Value []byte `dynamodbav:",omitempty"`
   393  		}{
   394  			Value: []byte{},
   395  		},
   396  		expected: &struct {
   397  			Value []byte `dynamodbav:",omitempty"`
   398  		}{},
   399  	},
   400  }
   401  
   402  var sharedListTestCases = []struct {
   403  	in               []*dynamodb.AttributeValue
   404  	actual, expected interface{}
   405  	err              error
   406  }{
   407  	{
   408  		in: []*dynamodb.AttributeValue{
   409  			{B: []byte{48, 49}},
   410  			{BOOL: aws.Bool(true)},
   411  			{N: aws.String("123")},
   412  			{S: aws.String("123")},
   413  		},
   414  		actual: func() *[]interface{} {
   415  			v := []interface{}{}
   416  			return &v
   417  		}(),
   418  		expected: []interface{}{[]byte{48, 49}, true, 123., "123"},
   419  	},
   420  	{
   421  		in: []*dynamodb.AttributeValue{
   422  			{N: aws.String("1")},
   423  			{N: aws.String("2")},
   424  			{N: aws.String("3")},
   425  		},
   426  		actual:   &[]interface{}{},
   427  		expected: []interface{}{1., 2., 3.},
   428  	},
   429  }
   430  
   431  var sharedMapTestCases = []struct {
   432  	in               map[string]*dynamodb.AttributeValue
   433  	actual, expected interface{}
   434  	err              error
   435  }{
   436  	{
   437  		in: map[string]*dynamodb.AttributeValue{
   438  			"B":    {B: []byte{48, 49}},
   439  			"BOOL": {BOOL: aws.Bool(true)},
   440  			"N":    {N: aws.String("123")},
   441  			"S":    {S: aws.String("123")},
   442  		},
   443  		actual: &map[string]interface{}{},
   444  		expected: map[string]interface{}{
   445  			"B": []byte{48, 49}, "BOOL": true,
   446  			"N": 123., "S": "123",
   447  		},
   448  	},
   449  }
   450  
   451  func assertConvertTest(t *testing.T, i int, actual, expected interface{}, err, expectedErr error) {
   452  	if expectedErr != nil {
   453  		if err != nil {
   454  			if e, a := expectedErr, err; !strings.Contains(a.Error(), e.Error()) {
   455  				t.Errorf("case %d expect %v, got %v", i, e, a)
   456  			}
   457  		} else {
   458  			t.Fatalf("case %d, expected error, %v", i, expectedErr)
   459  		}
   460  	} else if err != nil {
   461  		t.Fatalf("case %d, expect no error, got %v", i, err)
   462  	} else {
   463  		if e, a := ptrToValue(expected), ptrToValue(actual); !reflect.DeepEqual(e, a) {
   464  			t.Errorf("case %d, expect %v, got %v", i, e, a)
   465  		}
   466  	}
   467  }
   468  
   469  func ptrToValue(in interface{}) interface{} {
   470  	v := reflect.ValueOf(in)
   471  	if v.Kind() == reflect.Ptr {
   472  		v = v.Elem()
   473  	}
   474  	if !v.IsValid() {
   475  		return nil
   476  	}
   477  	if v.Kind() == reflect.Ptr {
   478  		return ptrToValue(v.Interface())
   479  	}
   480  	return v.Interface()
   481  }