converter_test.go 16 KB

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