decode_test.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. package dynamodbattribute
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strconv"
  6. "testing"
  7. "time"
  8. "github.com/aws/aws-sdk-go/aws"
  9. "github.com/aws/aws-sdk-go/aws/awserr"
  10. "github.com/aws/aws-sdk-go/service/dynamodb"
  11. "github.com/stretchr/testify/assert"
  12. )
  13. func TestUnmarshalErrorTypes(t *testing.T) {
  14. var _ awserr.Error = (*UnmarshalTypeError)(nil)
  15. var _ awserr.Error = (*InvalidUnmarshalError)(nil)
  16. }
  17. func TestUnmarshalShared(t *testing.T) {
  18. for i, c := range sharedTestCases {
  19. err := Unmarshal(c.in, c.actual)
  20. assertConvertTest(t, i, c.actual, c.expected, err, c.err)
  21. }
  22. }
  23. func TestUnmarshal(t *testing.T) {
  24. cases := []struct {
  25. in *dynamodb.AttributeValue
  26. actual, expected interface{}
  27. err error
  28. }{
  29. //------------
  30. // Sets
  31. //------------
  32. {
  33. in: &dynamodb.AttributeValue{BS: [][]byte{
  34. {48, 49}, {50, 51},
  35. }},
  36. actual: &[][]byte{},
  37. expected: [][]byte{{48, 49}, {50, 51}},
  38. },
  39. {
  40. in: &dynamodb.AttributeValue{NS: []*string{
  41. aws.String("123"), aws.String("321"),
  42. }},
  43. actual: &[]int{},
  44. expected: []int{123, 321},
  45. },
  46. {
  47. in: &dynamodb.AttributeValue{NS: []*string{
  48. aws.String("123"), aws.String("321"),
  49. }},
  50. actual: &[]interface{}{},
  51. expected: []interface{}{123., 321.},
  52. },
  53. {
  54. in: &dynamodb.AttributeValue{SS: []*string{
  55. aws.String("abc"), aws.String("123"),
  56. }},
  57. actual: &[]string{},
  58. expected: &[]string{"abc", "123"},
  59. },
  60. {
  61. in: &dynamodb.AttributeValue{SS: []*string{
  62. aws.String("abc"), aws.String("123"),
  63. }},
  64. actual: &[]*string{},
  65. expected: &[]*string{aws.String("abc"), aws.String("123")},
  66. },
  67. //------------
  68. // Interfaces
  69. //------------
  70. {
  71. in: &dynamodb.AttributeValue{B: []byte{48, 49}},
  72. actual: func() interface{} {
  73. var v interface{}
  74. return &v
  75. }(),
  76. expected: []byte{48, 49},
  77. },
  78. {
  79. in: &dynamodb.AttributeValue{BS: [][]byte{
  80. {48, 49}, {50, 51},
  81. }},
  82. actual: func() interface{} {
  83. var v interface{}
  84. return &v
  85. }(),
  86. expected: [][]byte{{48, 49}, {50, 51}},
  87. },
  88. {
  89. in: &dynamodb.AttributeValue{BOOL: aws.Bool(true)},
  90. actual: func() interface{} {
  91. var v interface{}
  92. return &v
  93. }(),
  94. expected: bool(true),
  95. },
  96. {
  97. in: &dynamodb.AttributeValue{L: []*dynamodb.AttributeValue{
  98. {S: aws.String("abc")}, {S: aws.String("123")},
  99. }},
  100. actual: func() interface{} {
  101. var v interface{}
  102. return &v
  103. }(),
  104. expected: []interface{}{"abc", "123"},
  105. },
  106. {
  107. in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
  108. "123": {S: aws.String("abc")},
  109. "abc": {S: aws.String("123")},
  110. }},
  111. actual: func() interface{} {
  112. var v interface{}
  113. return &v
  114. }(),
  115. expected: map[string]interface{}{"123": "abc", "abc": "123"},
  116. },
  117. {
  118. in: &dynamodb.AttributeValue{N: aws.String("123")},
  119. actual: func() interface{} {
  120. var v interface{}
  121. return &v
  122. }(),
  123. expected: float64(123),
  124. },
  125. {
  126. in: &dynamodb.AttributeValue{NS: []*string{
  127. aws.String("123"), aws.String("321"),
  128. }},
  129. actual: func() interface{} {
  130. var v interface{}
  131. return &v
  132. }(),
  133. expected: []float64{123., 321.},
  134. },
  135. {
  136. in: &dynamodb.AttributeValue{S: aws.String("123")},
  137. actual: func() interface{} {
  138. var v interface{}
  139. return &v
  140. }(),
  141. expected: "123",
  142. },
  143. {
  144. in: &dynamodb.AttributeValue{SS: []*string{
  145. aws.String("123"), aws.String("321"),
  146. }},
  147. actual: func() interface{} {
  148. var v interface{}
  149. return &v
  150. }(),
  151. expected: []string{"123", "321"},
  152. },
  153. {
  154. in: &dynamodb.AttributeValue{M: map[string]*dynamodb.AttributeValue{
  155. "abc": {S: aws.String("123")},
  156. "Cba": {S: aws.String("321")},
  157. }},
  158. actual: &struct{ Abc, Cba string }{},
  159. expected: struct{ Abc, Cba string }{Abc: "123", Cba: "321"},
  160. },
  161. {
  162. in: &dynamodb.AttributeValue{N: aws.String("512")},
  163. actual: new(uint8),
  164. err: &UnmarshalTypeError{
  165. Value: fmt.Sprintf("number overflow, 512"),
  166. Type: reflect.TypeOf(uint8(0)),
  167. },
  168. },
  169. }
  170. for i, c := range cases {
  171. err := Unmarshal(c.in, c.actual)
  172. assertConvertTest(t, i, c.actual, c.expected, err, c.err)
  173. }
  174. }
  175. func TestInterfaceInput(t *testing.T) {
  176. var v interface{}
  177. expected := []interface{}{"abc", "123"}
  178. err := Unmarshal(&dynamodb.AttributeValue{L: []*dynamodb.AttributeValue{
  179. {S: aws.String("abc")}, {S: aws.String("123")},
  180. }}, &v)
  181. assertConvertTest(t, 0, v, expected, err, nil)
  182. }
  183. func TestUnmarshalError(t *testing.T) {
  184. cases := []struct {
  185. in *dynamodb.AttributeValue
  186. actual, expected interface{}
  187. err error
  188. }{
  189. {
  190. in: &dynamodb.AttributeValue{},
  191. actual: int(0),
  192. expected: nil,
  193. err: &InvalidUnmarshalError{Type: reflect.TypeOf(int(0))},
  194. },
  195. }
  196. for i, c := range cases {
  197. err := Unmarshal(c.in, c.actual)
  198. assertConvertTest(t, i, c.actual, c.expected, err, c.err)
  199. }
  200. }
  201. func TestUnmarshalListShared(t *testing.T) {
  202. for i, c := range sharedListTestCases {
  203. err := UnmarshalList(c.in, c.actual)
  204. assertConvertTest(t, i, c.actual, c.expected, err, c.err)
  205. }
  206. }
  207. func TestUnmarshalListError(t *testing.T) {
  208. cases := []struct {
  209. in []*dynamodb.AttributeValue
  210. actual, expected interface{}
  211. err error
  212. }{
  213. {
  214. in: []*dynamodb.AttributeValue{},
  215. actual: []interface{}{},
  216. expected: nil,
  217. err: &InvalidUnmarshalError{Type: reflect.TypeOf([]interface{}{})},
  218. },
  219. }
  220. for i, c := range cases {
  221. err := UnmarshalList(c.in, c.actual)
  222. assertConvertTest(t, i, c.actual, c.expected, err, c.err)
  223. }
  224. }
  225. func TestUnmarshalMapShared(t *testing.T) {
  226. for i, c := range sharedMapTestCases {
  227. err := UnmarshalMap(c.in, c.actual)
  228. assertConvertTest(t, i, c.actual, c.expected, err, c.err)
  229. }
  230. }
  231. func TestUnmarshalMapError(t *testing.T) {
  232. cases := []struct {
  233. in map[string]*dynamodb.AttributeValue
  234. actual, expected interface{}
  235. err error
  236. }{
  237. {
  238. in: map[string]*dynamodb.AttributeValue{},
  239. actual: map[string]interface{}{},
  240. expected: nil,
  241. err: &InvalidUnmarshalError{Type: reflect.TypeOf(map[string]interface{}{})},
  242. },
  243. {
  244. in: map[string]*dynamodb.AttributeValue{
  245. "BOOL": {BOOL: aws.Bool(true)},
  246. },
  247. actual: &map[int]interface{}{},
  248. expected: nil,
  249. err: &UnmarshalTypeError{Value: "map string key", Type: reflect.TypeOf(int(0))},
  250. },
  251. }
  252. for i, c := range cases {
  253. err := UnmarshalMap(c.in, c.actual)
  254. assertConvertTest(t, i, c.actual, c.expected, err, c.err)
  255. }
  256. }
  257. type unmarshalUnmarshaler struct {
  258. Value string
  259. Value2 int
  260. Value3 bool
  261. Value4 time.Time
  262. }
  263. func (u *unmarshalUnmarshaler) UnmarshalDynamoDBAttributeValue(av *dynamodb.AttributeValue) error {
  264. if av.M == nil {
  265. return fmt.Errorf("expected AttributeValue to be map")
  266. }
  267. if v, ok := av.M["abc"]; !ok {
  268. return fmt.Errorf("expected `abc` map key")
  269. } else if v.S == nil {
  270. return fmt.Errorf("expected `abc` map value string")
  271. } else {
  272. u.Value = *v.S
  273. }
  274. if v, ok := av.M["def"]; !ok {
  275. return fmt.Errorf("expected `def` map key")
  276. } else if v.N == nil {
  277. return fmt.Errorf("expected `def` map value number")
  278. } else {
  279. n, err := strconv.ParseInt(*v.N, 10, 64)
  280. if err != nil {
  281. return err
  282. }
  283. u.Value2 = int(n)
  284. }
  285. if v, ok := av.M["ghi"]; !ok {
  286. return fmt.Errorf("expected `ghi` map key")
  287. } else if v.BOOL == nil {
  288. return fmt.Errorf("expected `ghi` map value number")
  289. } else {
  290. u.Value3 = *v.BOOL
  291. }
  292. if v, ok := av.M["jkl"]; !ok {
  293. return fmt.Errorf("expected `jkl` map key")
  294. } else if v.S == nil {
  295. return fmt.Errorf("expected `jkl` map value string")
  296. } else {
  297. t, err := time.Parse(time.RFC3339, *v.S)
  298. if err != nil {
  299. return err
  300. }
  301. u.Value4 = t
  302. }
  303. return nil
  304. }
  305. func TestUnmarshalUnmashaler(t *testing.T) {
  306. u := &unmarshalUnmarshaler{}
  307. av := &dynamodb.AttributeValue{
  308. M: map[string]*dynamodb.AttributeValue{
  309. "abc": {S: aws.String("value")},
  310. "def": {N: aws.String("123")},
  311. "ghi": {BOOL: aws.Bool(true)},
  312. "jkl": {S: aws.String("2016-05-03T17:06:26.209072Z")},
  313. },
  314. }
  315. err := Unmarshal(av, u)
  316. assert.NoError(t, err)
  317. assert.Equal(t, "value", u.Value)
  318. assert.Equal(t, 123, u.Value2)
  319. assert.Equal(t, true, u.Value3)
  320. assert.Equal(t, testDate, u.Value4)
  321. }
  322. func TestDecodeUseNumber(t *testing.T) {
  323. u := map[string]interface{}{}
  324. av := &dynamodb.AttributeValue{
  325. M: map[string]*dynamodb.AttributeValue{
  326. "abc": {S: aws.String("value")},
  327. "def": {N: aws.String("123")},
  328. "ghi": {BOOL: aws.Bool(true)},
  329. },
  330. }
  331. decoder := NewDecoder(func(d *Decoder) {
  332. d.UseNumber = true
  333. })
  334. err := decoder.Decode(av, &u)
  335. assert.NoError(t, err)
  336. assert.Equal(t, "value", u["abc"])
  337. n, ok := u["def"].(Number)
  338. assert.True(t, ok)
  339. assert.Equal(t, "123", n.String())
  340. assert.Equal(t, true, u["ghi"])
  341. }
  342. func TestDecodeUseNumberNumberSet(t *testing.T) {
  343. u := map[string]interface{}{}
  344. av := &dynamodb.AttributeValue{
  345. M: map[string]*dynamodb.AttributeValue{
  346. "ns": {
  347. NS: []*string{
  348. aws.String("123"), aws.String("321"),
  349. },
  350. },
  351. },
  352. }
  353. decoder := NewDecoder(func(d *Decoder) {
  354. d.UseNumber = true
  355. })
  356. err := decoder.Decode(av, &u)
  357. assert.NoError(t, err)
  358. ns, ok := u["ns"].([]Number)
  359. assert.True(t, ok)
  360. assert.Equal(t, "123", ns[0].String())
  361. assert.Equal(t, "321", ns[1].String())
  362. }