| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- package dynamodbattribute
- import (
- "math"
- "reflect"
- "testing"
- "github.com/aws/aws-sdk-go/aws"
- "github.com/aws/aws-sdk-go/aws/awserr"
- "github.com/aws/aws-sdk-go/aws/awsutil"
- "github.com/aws/aws-sdk-go/service/dynamodb"
- )
- type simpleMarshalStruct struct {
- Byte []byte
- String string
- Int int
- Uint uint
- Float32 float32
- Float64 float64
- Bool bool
- Null *interface{}
- }
- type complexMarshalStruct struct {
- Simple []simpleMarshalStruct
- }
- type myByteStruct struct {
- Byte []byte
- }
- type myByteSetStruct struct {
- ByteSet [][]byte
- }
- type marshallerTestInput struct {
- input interface{}
- expected interface{}
- err awserr.Error
- }
- var marshalerScalarInputs = []marshallerTestInput{
- {
- input: nil,
- expected: &dynamodb.AttributeValue{NULL: &trueValue},
- },
- {
- input: "some string",
- expected: &dynamodb.AttributeValue{S: aws.String("some string")},
- },
- {
- input: true,
- expected: &dynamodb.AttributeValue{BOOL: &trueValue},
- },
- {
- input: false,
- expected: &dynamodb.AttributeValue{BOOL: &falseValue},
- },
- {
- input: 3.14,
- expected: &dynamodb.AttributeValue{N: aws.String("3.14")},
- },
- {
- input: math.MaxFloat32,
- expected: &dynamodb.AttributeValue{N: aws.String("340282346638528860000000000000000000000")},
- },
- {
- input: math.MaxFloat64,
- expected: &dynamodb.AttributeValue{N: aws.String("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")},
- },
- {
- input: 12,
- expected: &dynamodb.AttributeValue{N: aws.String("12")},
- },
- {
- input: Number("12"),
- expected: &dynamodb.AttributeValue{N: aws.String("12")},
- },
- {
- input: simpleMarshalStruct{},
- expected: &dynamodb.AttributeValue{
- M: map[string]*dynamodb.AttributeValue{
- "Byte": {NULL: &trueValue},
- "Bool": {BOOL: &falseValue},
- "Float32": {N: aws.String("0")},
- "Float64": {N: aws.String("0")},
- "Int": {N: aws.String("0")},
- "Null": {NULL: &trueValue},
- "String": {NULL: &trueValue},
- "Uint": {N: aws.String("0")},
- },
- },
- },
- }
- var marshallerMapTestInputs = []marshallerTestInput{
- // Scalar tests
- {
- input: nil,
- expected: map[string]*dynamodb.AttributeValue{},
- },
- {
- input: map[string]interface{}{"string": "some string"},
- expected: map[string]*dynamodb.AttributeValue{"string": {S: aws.String("some string")}},
- },
- {
- input: map[string]interface{}{"bool": true},
- expected: map[string]*dynamodb.AttributeValue{"bool": {BOOL: &trueValue}},
- },
- {
- input: map[string]interface{}{"bool": false},
- expected: map[string]*dynamodb.AttributeValue{"bool": {BOOL: &falseValue}},
- },
- {
- input: map[string]interface{}{"null": nil},
- expected: map[string]*dynamodb.AttributeValue{"null": {NULL: &trueValue}},
- },
- {
- input: map[string]interface{}{"float": 3.14},
- expected: map[string]*dynamodb.AttributeValue{"float": {N: aws.String("3.14")}},
- },
- {
- input: map[string]interface{}{"float": math.MaxFloat32},
- expected: map[string]*dynamodb.AttributeValue{"float": {N: aws.String("340282346638528860000000000000000000000")}},
- },
- {
- input: map[string]interface{}{"float": math.MaxFloat64},
- expected: map[string]*dynamodb.AttributeValue{"float": {N: aws.String("179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")}},
- },
- {
- input: map[string]interface{}{"num": 12.},
- expected: map[string]*dynamodb.AttributeValue{"num": {N: aws.String("12")}},
- },
- {
- input: map[string]interface{}{"byte": []byte{48, 49}},
- expected: map[string]*dynamodb.AttributeValue{"byte": {B: []byte{48, 49}}},
- },
- {
- input: struct{ Byte []byte }{Byte: []byte{48, 49}},
- expected: map[string]*dynamodb.AttributeValue{"Byte": {B: []byte{48, 49}}},
- },
- {
- input: map[string]interface{}{"byte_set": [][]byte{{48, 49}, {50, 51}}},
- expected: map[string]*dynamodb.AttributeValue{"byte_set": {BS: [][]byte{{48, 49}, {50, 51}}}},
- },
- {
- input: struct{ ByteSet [][]byte }{ByteSet: [][]byte{{48, 49}, {50, 51}}},
- expected: map[string]*dynamodb.AttributeValue{"ByteSet": {BS: [][]byte{{48, 49}, {50, 51}}}},
- },
- // List
- {
- input: map[string]interface{}{"list": []interface{}{"a string", 12., 3.14, true, nil, false}},
- expected: map[string]*dynamodb.AttributeValue{
- "list": {
- L: []*dynamodb.AttributeValue{
- {S: aws.String("a string")},
- {N: aws.String("12")},
- {N: aws.String("3.14")},
- {BOOL: &trueValue},
- {NULL: &trueValue},
- {BOOL: &falseValue},
- },
- },
- },
- },
- // Map
- {
- input: map[string]interface{}{"map": map[string]interface{}{"nestednum": 12.}},
- expected: map[string]*dynamodb.AttributeValue{
- "map": {
- M: map[string]*dynamodb.AttributeValue{
- "nestednum": {
- N: aws.String("12"),
- },
- },
- },
- },
- },
- // Structs
- {
- input: simpleMarshalStruct{},
- expected: map[string]*dynamodb.AttributeValue{
- "Byte": {NULL: &trueValue},
- "Bool": {BOOL: &falseValue},
- "Float32": {N: aws.String("0")},
- "Float64": {N: aws.String("0")},
- "Int": {N: aws.String("0")},
- "Null": {NULL: &trueValue},
- "String": {NULL: &trueValue},
- "Uint": {N: aws.String("0")},
- },
- },
- {
- input: complexMarshalStruct{},
- expected: map[string]*dynamodb.AttributeValue{
- "Simple": {NULL: &trueValue},
- },
- },
- {
- input: struct {
- Simple []string `json:"simple"`
- }{},
- expected: map[string]*dynamodb.AttributeValue{
- "simple": {NULL: &trueValue},
- },
- },
- {
- input: struct {
- Simple []string `json:"simple,omitempty"`
- }{},
- expected: map[string]*dynamodb.AttributeValue{},
- },
- {
- input: struct {
- Simple []string `json:"-"`
- }{},
- expected: map[string]*dynamodb.AttributeValue{},
- },
- {
- input: complexMarshalStruct{Simple: []simpleMarshalStruct{{Int: -2}, {Uint: 5}}},
- expected: map[string]*dynamodb.AttributeValue{
- "Simple": {
- L: []*dynamodb.AttributeValue{
- {
- M: map[string]*dynamodb.AttributeValue{
- "Byte": {NULL: &trueValue},
- "Bool": {BOOL: &falseValue},
- "Float32": {N: aws.String("0")},
- "Float64": {N: aws.String("0")},
- "Int": {N: aws.String("-2")},
- "Null": {NULL: &trueValue},
- "String": {NULL: &trueValue},
- "Uint": {N: aws.String("0")},
- },
- },
- {
- M: map[string]*dynamodb.AttributeValue{
- "Byte": {NULL: &trueValue},
- "Bool": {BOOL: &falseValue},
- "Float32": {N: aws.String("0")},
- "Float64": {N: aws.String("0")},
- "Int": {N: aws.String("0")},
- "Null": {NULL: &trueValue},
- "String": {NULL: &trueValue},
- "Uint": {N: aws.String("5")},
- },
- },
- },
- },
- },
- },
- }
- var marshallerListTestInputs = []marshallerTestInput{
- {
- input: nil,
- expected: []*dynamodb.AttributeValue{},
- },
- {
- input: []interface{}{},
- expected: []*dynamodb.AttributeValue{},
- },
- {
- input: []simpleMarshalStruct{},
- expected: []*dynamodb.AttributeValue{},
- },
- {
- input: []interface{}{"a string", 12., 3.14, true, nil, false},
- expected: []*dynamodb.AttributeValue{
- {S: aws.String("a string")},
- {N: aws.String("12")},
- {N: aws.String("3.14")},
- {BOOL: &trueValue},
- {NULL: &trueValue},
- {BOOL: &falseValue},
- },
- },
- {
- input: []simpleMarshalStruct{{}},
- expected: []*dynamodb.AttributeValue{
- {
- M: map[string]*dynamodb.AttributeValue{
- "Byte": {NULL: &trueValue},
- "Bool": {BOOL: &falseValue},
- "Float32": {N: aws.String("0")},
- "Float64": {N: aws.String("0")},
- "Int": {N: aws.String("0")},
- "Null": {NULL: &trueValue},
- "String": {NULL: &trueValue},
- "Uint": {N: aws.String("0")},
- },
- },
- },
- },
- }
- func Test_New_Marshal(t *testing.T) {
- for _, test := range marshalerScalarInputs {
- testMarshal(t, test)
- }
- }
- func testMarshal(t *testing.T, test marshallerTestInput) {
- actual, err := Marshal(test.input)
- if test.err != nil {
- if err == nil {
- t.Errorf("Marshal with input %#v retured %#v, expected error `%s`", test.input, actual, test.err)
- } else if err.Error() != test.err.Error() {
- t.Errorf("Marshal with input %#v retured error `%s`, expected error `%s`", test.input, err, test.err)
- }
- } else {
- if err != nil {
- t.Errorf("Marshal with input %#v retured error `%s`", test.input, err)
- }
- compareObjects(t, test.expected, actual)
- }
- }
- func Test_New_Unmarshal(t *testing.T) {
- // Using the same inputs from Marshal, test the reverse mapping.
- for i, test := range marshalerScalarInputs {
- if test.input == nil {
- continue
- }
- actual := reflect.New(reflect.TypeOf(test.input)).Interface()
- if err := Unmarshal(test.expected.(*dynamodb.AttributeValue), actual); err != nil {
- t.Errorf("Unmarshal %d, with input %#v retured error `%s`", i+1, test.expected, err)
- }
- compareObjects(t, test.input, reflect.ValueOf(actual).Elem().Interface())
- }
- }
- func Test_New_UnmarshalError(t *testing.T) {
- // Test that we get an error using Unmarshal to convert to a nil value.
- expected := &InvalidUnmarshalError{Type: reflect.TypeOf(nil)}
- if err := Unmarshal(nil, nil); err == nil {
- t.Errorf("Unmarshal with input %T returned no error, expected error `%v`", nil, expected)
- } else if err.Error() != expected.Error() {
- t.Errorf("Unmarshal with input %T returned error `%v`, expected error `%v`", nil, err, expected)
- }
- // Test that we get an error using Unmarshal to convert to a non-pointer value.
- var actual map[string]interface{}
- expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual)}
- if err := Unmarshal(nil, actual); err == nil {
- t.Errorf("Unmarshal with input %T returned no error, expected error `%v`", actual, expected)
- } else if err.Error() != expected.Error() {
- t.Errorf("Unmarshal with input %T returned error `%v`, expected error `%v`", actual, err, expected)
- }
- // Test that we get an error using Unmarshal to convert to nil struct.
- var actual2 *struct{ A int }
- expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual2)}
- if err := Unmarshal(nil, actual2); err == nil {
- t.Errorf("Unmarshal with input %T returned no error, expected error `%v`", actual2, expected)
- } else if err.Error() != expected.Error() {
- t.Errorf("Unmarshal with input %T returned error `%v`, expected error `%v`", actual2, err, expected)
- }
- }
- func Test_New_MarshalMap(t *testing.T) {
- for _, test := range marshallerMapTestInputs {
- testMarshalMap(t, test)
- }
- }
- func testMarshalMap(t *testing.T, test marshallerTestInput) {
- actual, err := MarshalMap(test.input)
- if test.err != nil {
- if err == nil {
- t.Errorf("MarshalMap with input %#v retured %#v, expected error `%s`", test.input, actual, test.err)
- } else if err.Error() != test.err.Error() {
- t.Errorf("MarshalMap with input %#v retured error `%s`, expected error `%s`", test.input, err, test.err)
- }
- } else {
- if err != nil {
- t.Errorf("MarshalMap with input %#v retured error `%s`", test.input, err)
- }
- compareObjects(t, test.expected, actual)
- }
- }
- func Test_New_UnmarshalMap(t *testing.T) {
- // Using the same inputs from MarshalMap, test the reverse mapping.
- for i, test := range marshallerMapTestInputs {
- if test.input == nil {
- continue
- }
- actual := reflect.New(reflect.TypeOf(test.input)).Interface()
- if err := UnmarshalMap(test.expected.(map[string]*dynamodb.AttributeValue), actual); err != nil {
- t.Errorf("Unmarshal %d, with input %#v retured error `%s`", i+1, test.expected, err)
- }
- compareObjects(t, test.input, reflect.ValueOf(actual).Elem().Interface())
- }
- }
- func Test_New_UnmarshalMapError(t *testing.T) {
- // Test that we get an error using UnmarshalMap to convert to a nil value.
- expected := &InvalidUnmarshalError{Type: reflect.TypeOf(nil)}
- if err := UnmarshalMap(nil, nil); err == nil {
- t.Errorf("UnmarshalMap with input %T returned no error, expected error `%v`", nil, expected)
- } else if err.Error() != expected.Error() {
- t.Errorf("UnmarshalMap with input %T returned error `%v`, expected error `%v`", nil, err, expected)
- }
- // Test that we get an error using UnmarshalMap to convert to a non-pointer value.
- var actual map[string]interface{}
- expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual)}
- if err := UnmarshalMap(nil, actual); err == nil {
- t.Errorf("UnmarshalMap with input %T returned no error, expected error `%v`", actual, expected)
- } else if err.Error() != expected.Error() {
- t.Errorf("UnmarshalMap with input %T returned error `%v`, expected error `%v`", actual, err, expected)
- }
- // Test that we get an error using UnmarshalMap to convert to nil struct.
- var actual2 *struct{ A int }
- expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual2)}
- if err := UnmarshalMap(nil, actual2); err == nil {
- t.Errorf("UnmarshalMap with input %T returned no error, expected error `%v`", actual2, expected)
- } else if err.Error() != expected.Error() {
- t.Errorf("UnmarshalMap with input %T returned error `%v`, expected error `%v`", actual2, err, expected)
- }
- }
- func Test_New_MarshalList(t *testing.T) {
- for _, test := range marshallerListTestInputs {
- testMarshalList(t, test)
- }
- }
- func testMarshalList(t *testing.T, test marshallerTestInput) {
- actual, err := MarshalList(test.input)
- if test.err != nil {
- if err == nil {
- t.Errorf("MarshalList with input %#v retured %#v, expected error `%s`", test.input, actual, test.err)
- } else if err.Error() != test.err.Error() {
- t.Errorf("MarshalList with input %#v retured error `%s`, expected error `%s`", test.input, err, test.err)
- }
- } else {
- if err != nil {
- t.Errorf("MarshalList with input %#v retured error `%s`", test.input, err)
- }
- compareObjects(t, test.expected, actual)
- }
- }
- func Test_New_UnmarshalList(t *testing.T) {
- // Using the same inputs from MarshalList, test the reverse mapping.
- for i, test := range marshallerListTestInputs {
- if test.input == nil {
- continue
- }
- iv := reflect.ValueOf(test.input)
- actual := reflect.New(iv.Type())
- if iv.Kind() == reflect.Slice {
- actual.Elem().Set(reflect.MakeSlice(iv.Type(), iv.Len(), iv.Cap()))
- }
- if err := UnmarshalList(test.expected.([]*dynamodb.AttributeValue), actual.Interface()); err != nil {
- t.Errorf("Unmarshal %d, with input %#v retured error `%s`", i+1, test.expected, err)
- }
- compareObjects(t, test.input, actual.Elem().Interface())
- }
- }
- func Test_New_UnmarshalListError(t *testing.T) {
- // Test that we get an error using UnmarshalList to convert to a nil value.
- expected := &InvalidUnmarshalError{Type: reflect.TypeOf(nil)}
- if err := UnmarshalList(nil, nil); err == nil {
- t.Errorf("UnmarshalList with input %T returned no error, expected error `%v`", nil, expected)
- } else if err.Error() != expected.Error() {
- t.Errorf("UnmarshalList with input %T returned error `%v`, expected error `%v`", nil, err, expected)
- }
- // Test that we get an error using UnmarshalList to convert to a non-pointer value.
- var actual map[string]interface{}
- expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual)}
- if err := UnmarshalList(nil, actual); err == nil {
- t.Errorf("UnmarshalList with input %T returned no error, expected error `%v`", actual, expected)
- } else if err.Error() != expected.Error() {
- t.Errorf("UnmarshalList with input %T returned error `%v`, expected error `%v`", actual, err, expected)
- }
- // Test that we get an error using UnmarshalList to convert to nil struct.
- var actual2 *struct{ A int }
- expected = &InvalidUnmarshalError{Type: reflect.TypeOf(actual2)}
- if err := UnmarshalList(nil, actual2); err == nil {
- t.Errorf("UnmarshalList with input %T returned no error, expected error `%v`", actual2, expected)
- } else if err.Error() != expected.Error() {
- t.Errorf("UnmarshalList with input %T returned error `%v`, expected error `%v`", actual2, err, expected)
- }
- }
- func compareObjects(t *testing.T, expected interface{}, actual interface{}) {
- if !reflect.DeepEqual(expected, actual) {
- ev := reflect.ValueOf(expected)
- av := reflect.ValueOf(actual)
- t.Errorf("\nExpected kind(%s,%T):\n%s\nActual kind(%s,%T):\n%s\n",
- ev.Kind(),
- ev.Interface(),
- awsutil.Prettify(expected),
- av.Kind(),
- ev.Interface(),
- awsutil.Prettify(actual))
- }
- }
- func BenchmarkMarshal(b *testing.B) {
- d := simpleMarshalStruct{
- String: "abc",
- Int: 123,
- Uint: 123,
- Float32: 123.321,
- Float64: 123.321,
- Bool: true,
- Null: nil,
- }
- for i := 0; i < b.N; i++ {
- _, err := Marshal(d)
- if err != nil {
- b.Fatal("unexpected error", err)
- }
- }
- }
|