marshaler_test.go 16 KB

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