| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622 |
- package dynamodbattribute
- import (
- "fmt"
- "reflect"
- "strconv"
- "testing"
- "time"
- "github.com/aws/aws-sdk-go/aws"
- "github.com/aws/aws-sdk-go/aws/awserr"
- "github.com/aws/aws-sdk-go/service/dynamodb"
- )
- func TestUnmarshalErrorTypes(t *testing.T) {
- var _ awserr.Error = (*UnmarshalTypeError)(nil)
- var _ awserr.Error = (*InvalidUnmarshalError)(nil)
- }
- func TestUnmarshalShared(t *testing.T) {
- for i, c := range sharedTestCases {
- err := Unmarshal(c.in, c.actual)
- assertConvertTest(t, i, c.actual, c.expected, err, c.err)
- }
- }
- func TestUnmarshal(t *testing.T) {
- cases := []struct {
- in *dynamodb.AttributeValue
- actual, expected interface{}
- err error
- }{
- //------------
- // Sets
- //------------
- {
- in: &dynamodb.AttributeValue{BS: [][]byte{
- {48, 49}, {50, 51},
- }},
- actual: &[][]byte{},
- expected: [][]byte{{48, 49}, {50, 51}},
- },
- {
- in: &dynamodb.AttributeValue{NS: []*string{
- aws.String("123"), aws.String("321"),
- }},
- actual: &[]int{},
- expected: []int{123, 321},
- },
- {
- in: &dynamodb.AttributeValue{NS: []*string{
- aws.String("123"), aws.String("321"),
- }},
- actual: &[]interface{}{},
- expected: []interface{}{123., 321.},
- },
- {
- in: &dynamodb.AttributeValue{SS: []*string{
- aws.String("abc"), aws.String("123"),
- }},
- actual: &[]string{},
- expected: &[]string{"abc", "123"},
- },
- {
- in: &dynamodb.AttributeValue{SS: []*string{
- aws.String("abc"), aws.String("123"),
- }},
- actual: &[]*string{},
- expected: &[]*string{aws.String("abc"), aws.String("123")},
- },
- //------------
- // Interfaces
- //------------
- {
- in: &dynamodb.AttributeValue{B: []byte{48, 49}},
- actual: func() interface{} {
- var v interface{}
- return &v
- }(),
- expected: []byte{48, 49},
- },
- {
- in: &dynamodb.AttributeValue{BS: [][]byte{
- {48, 49}, {50, 51},
- }},
- actual: func() interface{} {
- var v interface{}
- return &v
- }(),
- expected: [][]byte{{48, 49}, {50, 51}},
- },
- {
- in: &dynamodb.AttributeValue{BOOL: aws.Bool(true)},
- actual: func() interface{} {
- var v interface{}
- return &v
- }(),
- expected: bool(true),
- },
- {
- in: &dynamodb.AttributeValue{L: []*dynamodb.AttributeValue{
- {S: aws.String("abc")}, {S: aws.String("123")},
- }},
- actual: func() interface{} {
- var v interface{}
- return &v
- }(),
- expected: []interface{}{"abc", "123"},
- },
- {
- in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
- "123": {S: aws.String("abc")},
- "abc": {S: aws.String("123")},
- }},
- actual: func() interface{} {
- var v interface{}
- return &v
- }(),
- expected: map[string]interface{}{"123": "abc", "abc": "123"},
- },
- {
- in: &dynamodb.AttributeValue{N: aws.String("123")},
- actual: func() interface{} {
- var v interface{}
- return &v
- }(),
- expected: float64(123),
- },
- {
- in: &dynamodb.AttributeValue{NS: []*string{
- aws.String("123"), aws.String("321"),
- }},
- actual: func() interface{} {
- var v interface{}
- return &v
- }(),
- expected: []float64{123., 321.},
- },
- {
- in: &dynamodb.AttributeValue{S: aws.String("123")},
- actual: func() interface{} {
- var v interface{}
- return &v
- }(),
- expected: "123",
- },
- {
- in: &dynamodb.AttributeValue{SS: []*string{
- aws.String("123"), aws.String("321"),
- }},
- actual: func() interface{} {
- var v interface{}
- return &v
- }(),
- expected: []string{"123", "321"},
- },
- {
- in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
- "abc": {S: aws.String("123")},
- "Cba": {S: aws.String("321")},
- }},
- actual: &struct{ Abc, Cba string }{},
- expected: struct{ Abc, Cba string }{Abc: "123", Cba: "321"},
- },
- {
- in: &dynamodb.AttributeValue{N: aws.String("512")},
- actual: new(uint8),
- err: &UnmarshalTypeError{
- Value: fmt.Sprintf("number overflow, 512"),
- Type: reflect.TypeOf(uint8(0)),
- },
- },
- }
- for i, c := range cases {
- err := Unmarshal(c.in, c.actual)
- assertConvertTest(t, i, c.actual, c.expected, err, c.err)
- }
- }
- func TestInterfaceInput(t *testing.T) {
- var v interface{}
- expected := []interface{}{"abc", "123"}
- err := Unmarshal(&dynamodb.AttributeValue{L: []*dynamodb.AttributeValue{
- {S: aws.String("abc")}, {S: aws.String("123")},
- }}, &v)
- assertConvertTest(t, 0, v, expected, err, nil)
- }
- func TestUnmarshalError(t *testing.T) {
- cases := []struct {
- in *dynamodb.AttributeValue
- actual, expected interface{}
- err error
- }{
- {
- in: &dynamodb.AttributeValue{},
- actual: int(0),
- expected: nil,
- err: &InvalidUnmarshalError{Type: reflect.TypeOf(int(0))},
- },
- }
- for i, c := range cases {
- err := Unmarshal(c.in, c.actual)
- assertConvertTest(t, i, c.actual, c.expected, err, c.err)
- }
- }
- func TestUnmarshalListShared(t *testing.T) {
- for i, c := range sharedListTestCases {
- err := UnmarshalList(c.in, c.actual)
- assertConvertTest(t, i, c.actual, c.expected, err, c.err)
- }
- }
- func TestUnmarshalListError(t *testing.T) {
- cases := []struct {
- in []*dynamodb.AttributeValue
- actual, expected interface{}
- err error
- }{
- {
- in: []*dynamodb.AttributeValue{},
- actual: []interface{}{},
- expected: nil,
- err: &InvalidUnmarshalError{Type: reflect.TypeOf([]interface{}{})},
- },
- }
- for i, c := range cases {
- err := UnmarshalList(c.in, c.actual)
- assertConvertTest(t, i, c.actual, c.expected, err, c.err)
- }
- }
- func TestUnmarshalConvertToData(t *testing.T) {
- type T struct {
- Int int
- Str string
- ByteSlice []byte
- StrSlice []string
- }
- exp := T{
- Int: 42,
- Str: "foo",
- ByteSlice: []byte{42, 97, 83},
- StrSlice: []string{"cat", "dog"},
- }
- av, err := ConvertToMap(exp)
- if err != nil {
- t.Fatalf("expect no error, got %v", err)
- }
- var act T
- err = UnmarshalMap(av, &act)
- assertConvertTest(t, 0, act, exp, err, nil)
- }
- func TestUnmarshalMapShared(t *testing.T) {
- for i, c := range sharedMapTestCases {
- err := UnmarshalMap(c.in, c.actual)
- assertConvertTest(t, i, c.actual, c.expected, err, c.err)
- }
- }
- func TestUnmarshalMapError(t *testing.T) {
- cases := []struct {
- in map[string]*dynamodb.AttributeValue
- actual, expected interface{}
- err error
- }{
- {
- in: map[string]*dynamodb.AttributeValue{},
- actual: map[string]interface{}{},
- expected: nil,
- err: &InvalidUnmarshalError{Type: reflect.TypeOf(map[string]interface{}{})},
- },
- {
- in: map[string]*dynamodb.AttributeValue{
- "BOOL": {BOOL: aws.Bool(true)},
- },
- actual: &map[int]interface{}{},
- expected: nil,
- err: &UnmarshalTypeError{Value: "map string key", Type: reflect.TypeOf(int(0))},
- },
- }
- for i, c := range cases {
- err := UnmarshalMap(c.in, c.actual)
- assertConvertTest(t, i, c.actual, c.expected, err, c.err)
- }
- }
- func TestUnmarshalListOfMaps(t *testing.T) {
- type testItem struct {
- Value string
- Value2 int
- }
- cases := []struct {
- in []map[string]*dynamodb.AttributeValue
- actual, expected interface{}
- err error
- }{
- { // Simple map conversion.
- in: []map[string]*dynamodb.AttributeValue{
- {
- "Value": &dynamodb.AttributeValue{
- BOOL: aws.Bool(true),
- },
- },
- },
- actual: &[]map[string]interface{}{},
- expected: []map[string]interface{}{
- {
- "Value": true,
- },
- },
- },
- { // attribute to struct.
- in: []map[string]*dynamodb.AttributeValue{
- {
- "Value": &dynamodb.AttributeValue{
- S: aws.String("abc"),
- },
- "Value2": &dynamodb.AttributeValue{
- N: aws.String("123"),
- },
- },
- },
- actual: &[]testItem{},
- expected: []testItem{
- {
- Value: "abc",
- Value2: 123,
- },
- },
- },
- }
- for i, c := range cases {
- err := UnmarshalListOfMaps(c.in, c.actual)
- assertConvertTest(t, i, c.actual, c.expected, err, c.err)
- }
- }
- type unmarshalUnmarshaler struct {
- Value string
- Value2 int
- Value3 bool
- Value4 time.Time
- }
- func (u *unmarshalUnmarshaler) UnmarshalDynamoDBAttributeValue(av *dynamodb.AttributeValue) error {
- if av.M == nil {
- return fmt.Errorf("expected AttributeValue to be map")
- }
- if v, ok := av.M["abc"]; !ok {
- return fmt.Errorf("expected `abc` map key")
- } else if v.S == nil {
- return fmt.Errorf("expected `abc` map value string")
- } else {
- u.Value = *v.S
- }
- if v, ok := av.M["def"]; !ok {
- return fmt.Errorf("expected `def` map key")
- } else if v.N == nil {
- return fmt.Errorf("expected `def` map value number")
- } else {
- n, err := strconv.ParseInt(*v.N, 10, 64)
- if err != nil {
- return err
- }
- u.Value2 = int(n)
- }
- if v, ok := av.M["ghi"]; !ok {
- return fmt.Errorf("expected `ghi` map key")
- } else if v.BOOL == nil {
- return fmt.Errorf("expected `ghi` map value number")
- } else {
- u.Value3 = *v.BOOL
- }
- if v, ok := av.M["jkl"]; !ok {
- return fmt.Errorf("expected `jkl` map key")
- } else if v.S == nil {
- return fmt.Errorf("expected `jkl` map value string")
- } else {
- t, err := time.Parse(time.RFC3339, *v.S)
- if err != nil {
- return err
- }
- u.Value4 = t
- }
- return nil
- }
- func TestUnmarshalUnmashaler(t *testing.T) {
- u := &unmarshalUnmarshaler{}
- av := &dynamodb.AttributeValue{
- M: map[string]*dynamodb.AttributeValue{
- "abc": {S: aws.String("value")},
- "def": {N: aws.String("123")},
- "ghi": {BOOL: aws.Bool(true)},
- "jkl": {S: aws.String("2016-05-03T17:06:26.209072Z")},
- },
- }
- err := Unmarshal(av, u)
- if err != nil {
- t.Errorf("expect no error, got %v", err)
- }
- if e, a := "value", u.Value; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- if e, a := 123, u.Value2; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- if e, a := true, u.Value3; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- if e, a := testDate, u.Value4; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- }
- func TestDecodeUseNumber(t *testing.T) {
- u := map[string]interface{}{}
- av := &dynamodb.AttributeValue{
- M: map[string]*dynamodb.AttributeValue{
- "abc": {S: aws.String("value")},
- "def": {N: aws.String("123")},
- "ghi": {BOOL: aws.Bool(true)},
- },
- }
- decoder := NewDecoder(func(d *Decoder) {
- d.UseNumber = true
- })
- err := decoder.Decode(av, &u)
- if err != nil {
- t.Errorf("expect no error, got %v", err)
- }
- if e, a := "value", u["abc"]; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- n := u["def"].(Number)
- if e, a := "123", n.String(); e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- if e, a := true, u["ghi"]; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- }
- func TestDecodeUseNumberNumberSet(t *testing.T) {
- u := map[string]interface{}{}
- av := &dynamodb.AttributeValue{
- M: map[string]*dynamodb.AttributeValue{
- "ns": {
- NS: []*string{
- aws.String("123"), aws.String("321"),
- },
- },
- },
- }
- decoder := NewDecoder(func(d *Decoder) {
- d.UseNumber = true
- })
- err := decoder.Decode(av, &u)
- if err != nil {
- t.Errorf("expect no error, got %v", err)
- }
- ns := u["ns"].([]Number)
- if e, a := "123", ns[0].String(); e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- if e, a := "321", ns[1].String(); e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- }
- func TestDecodeEmbeddedPointerStruct(t *testing.T) {
- type B struct {
- Bint int
- }
- type C struct {
- Cint int
- }
- type A struct {
- Aint int
- *B
- *C
- }
- av := &dynamodb.AttributeValue{
- M: map[string]*dynamodb.AttributeValue{
- "Aint": {
- N: aws.String("321"),
- },
- "Bint": {
- N: aws.String("123"),
- },
- },
- }
- decoder := NewDecoder()
- a := A{}
- err := decoder.Decode(av, &a)
- if err != nil {
- t.Errorf("expect no error, got %v", err)
- }
- if e, a := 321, a.Aint; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- // Embedded pointer struct can be created automatically.
- if e, a := 123, a.Bint; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- // But not for absent fields.
- if a.C != nil {
- t.Errorf("expect nil, got %v", a.C)
- }
- }
- func TestDecodeBooleanOverlay(t *testing.T) {
- type BooleanOverlay bool
- av := &dynamodb.AttributeValue{
- BOOL: aws.Bool(true),
- }
- decoder := NewDecoder()
- var v BooleanOverlay
- err := decoder.Decode(av, &v)
- if err != nil {
- t.Errorf("expect no error, got %v", err)
- }
- if e, a := BooleanOverlay(true), v; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- }
- func TestDecodeUnixTime(t *testing.T) {
- type A struct {
- Normal time.Time
- Tagged time.Time `dynamodbav:",unixtime"`
- Typed UnixTime
- }
- expect := A{
- Normal: time.Unix(123, 0).UTC(),
- Tagged: time.Unix(456, 0),
- Typed: UnixTime(time.Unix(789, 0)),
- }
- input := &dynamodb.AttributeValue{
- M: map[string]*dynamodb.AttributeValue{
- "Normal": {
- S: aws.String("1970-01-01T00:02:03Z"),
- },
- "Tagged": {
- N: aws.String("456"),
- },
- "Typed": {
- N: aws.String("789"),
- },
- },
- }
- actual := A{}
- err := Unmarshal(input, &actual)
- if err != nil {
- t.Errorf("expect no error, got %v", err)
- }
- if e, a := expect, actual; e != a {
- t.Errorf("expect %v, got %v", e, a)
- }
- }
- func TestDecodeAliasedUnixTime(t *testing.T) {
- type A struct {
- Normal AliasedTime
- Tagged AliasedTime `dynamodbav:",unixtime"`
- }
- expect := A{
- Normal: AliasedTime(time.Unix(123, 0).UTC()),
- Tagged: AliasedTime(time.Unix(456, 0)),
- }
- input := &dynamodb.AttributeValue{
- M: map[string]*dynamodb.AttributeValue{
- "Normal": {
- S: aws.String("1970-01-01T00:02:03Z"),
- },
- "Tagged": {
- N: aws.String("456"),
- },
- },
- }
- actual := A{}
- err := Unmarshal(input, &actual)
- if err != nil {
- t.Errorf("expect no error, got %v", err)
- }
- if expect != actual {
- t.Errorf("expect %v, got %v", expect, actual)
- }
- }
|