aws-sdk 2.0.1 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/.eslintrc +20 -0
  2. package/.gitignore +10 -0
  3. package/.travis.yml +20 -0
  4. package/.yardopts +20 -0
  5. package/.yardopts_guide +21 -0
  6. package/Gemfile +16 -0
  7. package/Gemfile.lock +34 -0
  8. package/README.md +5 -6
  9. package/Rakefile +14 -0
  10. package/UPGRADING.md +9 -4
  11. package/configuration.sample +5 -0
  12. package/dist/BUNDLE_LICENSE.txt +96 -0
  13. package/dist/aws-sdk.js +9594 -0
  14. package/dist/aws-sdk.min.js +21 -0
  15. package/dist-tools/.eslintrc +10 -0
  16. package/dist-tools/browser-builder.js +142 -0
  17. package/dist-tools/strategies/cache.js +68 -0
  18. package/dist-tools/strategies/default.js +165 -0
  19. package/dist-tools/test/browser-builder.mocha.spec.coffee +182 -0
  20. package/dist-tools/test/helpers.coffee +16 -0
  21. package/doc-src/guide/browser-building.md +93 -0
  22. package/doc-src/guide/browser-configuring-wif.md +287 -0
  23. package/doc-src/guide/browser-configuring.md +218 -0
  24. package/doc-src/guide/browser-examples.md +220 -0
  25. package/doc-src/guide/browser-intro.md +46 -0
  26. package/doc-src/guide/browser-making-requests.md +279 -0
  27. package/doc-src/guide/browser-services.md +75 -0
  28. package/doc-src/guide/index.md +41 -0
  29. package/doc-src/guide/node-configuring.md +272 -0
  30. package/doc-src/guide/node-examples.md +341 -0
  31. package/doc-src/guide/node-intro.md +32 -0
  32. package/doc-src/guide/node-making-requests.md +309 -0
  33. package/doc-src/guide/node-services.md +159 -0
  34. package/doc-src/templates/api-versions/model_documentor.rb +366 -0
  35. package/doc-src/templates/api-versions/plugin.rb +230 -0
  36. package/doc-src/templates/api-versions/templates/default/class/html/setup.rb +9 -0
  37. package/doc-src/templates/api-versions/templates/default/class/html/waiter_details_list.erb +7 -0
  38. package/doc-src/templates/api-versions/templates/default/class/html/waiter_summary.erb +7 -0
  39. package/doc-src/templates/api-versions/templates/default/docstring/html/experimental.erb +4 -0
  40. package/doc-src/templates/api-versions/templates/default/docstring/setup.rb +9 -0
  41. package/doc-src/templates/api-versions/templates/default/fulldoc/html/css/common.css +6 -0
  42. package/doc-src/templates/api-versions/templates/default/fulldoc/html/setup.rb +62 -0
  43. package/doc-src/templates/api-versions/templates/default/layout/html/services.erb +10 -0
  44. package/doc-src/templates/api-versions/templates/default/layout/html/setup.rb +28 -0
  45. package/doc-src/templates/api-versions/templates/default/module/html/box_info.erb +45 -0
  46. package/doc-src/templates/api-versions/templates/default/module/html/children.erb +8 -0
  47. package/doc-src/templates/api-versions/templates/default/tags/setup.rb +3 -0
  48. package/doc-src/templates/api-versions/templates/default/waiter_details/html/method_signature.erb +3 -0
  49. package/doc-src/templates/api-versions/templates/default/waiter_details/html/setup.rb +5 -0
  50. package/doc-src/templates/default/layout/html/footer.erb +31 -0
  51. package/doc-src/templates/default/layout/html/layout.erb +23 -0
  52. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/css/highlight.github.css +127 -0
  53. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/css/style.css +1192 -0
  54. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/img/logo.png +0 -0
  55. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/app.js +33 -0
  56. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/highlight.pack.js +27 -0
  57. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/AUTHORS +55 -0
  58. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/LICENSE +25 -0
  59. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/doctools.js +247 -0
  60. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/file.png +0 -0
  61. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/searchtools.js +568 -0
  62. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/underscore.js +23 -0
  63. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/search.erb +29 -0
  64. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/search_index.erb +1 -0
  65. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/setup.rb +75 -0
  66. package/doc-src/templates/flasky_sphinx_guide/layout/html/layout.erb +93 -0
  67. package/doc-src/templates/flasky_sphinx_guide/layout/html/setup.rb +9 -0
  68. package/doc-src/templates/flasky_sphinx_guide/layout/html/sidebar.erb +45 -0
  69. package/doc-src/templates/flasky_sphinx_guide/onefile/html/layout.erb +51 -0
  70. package/doc-src/templates/flasky_sphinx_guide/onefile/html/setup.rb +1 -0
  71. package/eslint-rules/no-require-in-service.js +10 -0
  72. package/features/autoscaling/autoscaling.feature +21 -0
  73. package/features/autoscaling/step_definitions/autoscaling.js +49 -0
  74. package/features/cloudformation/cloudformation.feature +22 -0
  75. package/features/cloudformation/step_definitions/cloudformation.js +26 -0
  76. package/features/cloudfront/cloudfront.feature +28 -0
  77. package/features/cloudfront/step_definitions/cloudfront-latest.js +54 -0
  78. package/features/cloudfront/step_definitions/cloudfront.js +21 -0
  79. package/features/cloudsearch/cloudsearch.feature +34 -0
  80. package/features/cloudsearch/step_definitions/cloudsearch.js +42 -0
  81. package/features/cloudtrail/cloudtrail.feature +17 -0
  82. package/features/cloudtrail/step_definitions/cloudtrail.js +14 -0
  83. package/features/cloudwatch/cloudwatch.feature +15 -0
  84. package/features/cloudwatch/step_definitions/cloudwatch.js +48 -0
  85. package/features/datapipeline/datapipeline.feature +23 -0
  86. package/features/datapipeline/step_definitions/datapipeline.js +79 -0
  87. package/features/directconnect/directconnect.feature +20 -0
  88. package/features/directconnect/step_definitions/directconnect.js +44 -0
  89. package/features/dynamodb/crc32.feature +18 -0
  90. package/features/dynamodb/step_definitions/dynamodb.js +154 -0
  91. package/features/dynamodb/tables.feature +50 -0
  92. package/features/ec2/ec2.feature +28 -0
  93. package/features/ec2/step_definitions/ec2.js +65 -0
  94. package/features/elasticache/elasticache.feature +20 -0
  95. package/features/elasticache/step_definitions/elasticache.js +34 -0
  96. package/features/elasticbeanstalk/elasticbeanstalk.feature +22 -0
  97. package/features/elasticbeanstalk/step_definitions/elasticbeanstalk.js +38 -0
  98. package/features/elastictranscoder/elastictranscoder.feature +24 -0
  99. package/features/elastictranscoder/step_definitions/elastictranscoder.js +56 -0
  100. package/features/elb/elb.feature +19 -0
  101. package/features/elb/step_definitions/elb.js +37 -0
  102. package/features/emr/emr.feature +16 -0
  103. package/features/emr/step_definitions/emr.js +45 -0
  104. package/features/extra/assertions.js +29 -0
  105. package/features/extra/dummy.feature +0 -0
  106. package/features/extra/fixtures/testfile.txt +1 -0
  107. package/features/extra/helpers.js +113 -0
  108. package/features/extra/hooks.js +107 -0
  109. package/features/extra/world.js +12 -0
  110. package/features/glacier/glacier.feature +47 -0
  111. package/features/glacier/step_definitions/glacier.js +112 -0
  112. package/features/iam/iam.feature +24 -0
  113. package/features/iam/step_definitions/iam.js +66 -0
  114. package/features/importexport/importexport.feature +53 -0
  115. package/features/importexport/step_definitions/importexport.js +42 -0
  116. package/features/kinesis/kinesis.feature +9 -0
  117. package/features/kinesis/step_definitions/kinesis.js +10 -0
  118. package/features/opsworks/opsworks.feature +26 -0
  119. package/features/opsworks/step_definitions/opsworks.js +42 -0
  120. package/features/rds/rds.feature +32 -0
  121. package/features/rds/step_definitions/rds.js +72 -0
  122. package/features/redshift/redshift.feature +20 -0
  123. package/features/redshift/step_definitions/redshift.js +33 -0
  124. package/features/route53/route53.feature +41 -0
  125. package/features/route53/step_definitions/route53.js +97 -0
  126. package/features/s3/buckets.feature +40 -0
  127. package/features/s3/objects.feature +122 -0
  128. package/features/s3/step_definitions/buckets.js +136 -0
  129. package/features/s3/step_definitions/hooks.js +39 -0
  130. package/features/s3/step_definitions/objects.js +204 -0
  131. package/features/s3/step_definitions/proxy.js +44 -0
  132. package/features/ses/ses.feature +20 -0
  133. package/features/ses/step_definitions/ses.js +22 -0
  134. package/features/simpledb/simpledb.feature +29 -0
  135. package/features/simpledb/step_definitions/simpledb.js +46 -0
  136. package/features/sns/sns.feature +15 -0
  137. package/features/sns/step_definitions/sns.js +33 -0
  138. package/features/sqs/messages.feature +21 -0
  139. package/features/sqs/queues.feature +18 -0
  140. package/features/sqs/step_definitions/messages.js +46 -0
  141. package/features/sqs/step_definitions/queues.js +33 -0
  142. package/features/sqs/step_definitions/sqs.js +7 -0
  143. package/features/storagegateway/step_definitions/storagegateway.js +16 -0
  144. package/features/storagegateway/storagegateway.feature +13 -0
  145. package/features/sts/step_definitions/sts.js +35 -0
  146. package/features/sts/sts.feature +29 -0
  147. package/features/support/step_definitions/support.js +35 -0
  148. package/features/support/support.feature +18 -0
  149. package/features/swf/step_definitions/swf.js +38 -0
  150. package/features/swf/swf.feature +15 -0
  151. package/index.js +2 -0
  152. package/lib/core.js +2 -2
  153. package/lib/credentials/shared_ini_file_credentials.js +0 -1
  154. package/lib/event_listeners.js +13 -1
  155. package/lib/http/node.js +19 -30
  156. package/lib/model/resource_waiter.js +0 -4
  157. package/lib/model/shape.js +2 -1
  158. package/lib/protocol/rest_xml.js +1 -1
  159. package/lib/region_config.js +31 -0
  160. package/lib/region_config.json +56 -0
  161. package/lib/request.js +37 -45
  162. package/lib/sequential_executor.js +17 -34
  163. package/lib/service.js +17 -44
  164. package/lib/services/cloudsearchdomain.js +69 -0
  165. package/lib/services/route53.js +0 -12
  166. package/lib/services/s3.js +3 -19
  167. package/lib/signers/v4.js +2 -1
  168. package/lib/util.js +28 -3
  169. package/package.json +3 -3
  170. package/scripts/console +11 -3
  171. package/scripts/coverage +126 -0
  172. package/tasks/apis.rake +122 -0
  173. package/tasks/browser.rake +89 -0
  174. package/tasks/docs.rake +36 -0
  175. package/tasks/lib/cucumber_generator.rb +40 -0
  176. package/tasks/util.rake +33 -0
  177. package/test/browser/js/jasmine-1.3.1.js +2600 -0
  178. package/test/browser/js/jasmine-html.js +681 -0
  179. package/test/browser/runner.html +109 -0
  180. package/test/browser/runner.js +92 -0
  181. package/test/browser/sample/appinfo.sample.js +15 -0
  182. package/test/browser/sample/console.html +429 -0
  183. package/test/browser/sample/css/smoothness/images/animated-overlay.gif +0 -0
  184. package/test/browser/sample/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  185. package/test/browser/sample/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  186. package/test/browser/sample/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  187. package/test/browser/sample/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  188. package/test/browser/sample/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  189. package/test/browser/sample/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  190. package/test/browser/sample/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  191. package/test/browser/sample/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  192. package/test/browser/sample/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
  193. package/test/browser/sample/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  194. package/test/browser/sample/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
  195. package/test/browser/sample/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
  196. package/test/browser/sample/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  197. package/test/browser/sample/css/smoothness/jquery-ui-1.10.1.custom.css +1175 -0
  198. package/test/browser/sample/css/smoothness/jquery-ui-1.10.1.custom.min.css +5 -0
  199. package/test/browser/sample/img/loading.gif +0 -0
  200. package/test/browser/sample/js/jquery-1.9.1.js +9597 -0
  201. package/test/browser/sample/js/jquery-ui-1.10.1.custom.js +14903 -0
  202. package/test/browser/sample/js/jquery-ui-1.10.1.custom.min.js +6 -0
  203. package/test/browser/sample/s3upload.html +111 -0
  204. package/test/browser.spec.coffee +207 -0
  205. package/test/config.spec.coffee +202 -0
  206. package/test/credential_provider_chain.spec.coffee +90 -0
  207. package/test/credentials.spec.coffee +452 -0
  208. package/test/endpoint.spec.coffee +80 -0
  209. package/test/event_listeners.spec.coffee +493 -0
  210. package/test/helpers.coffee +150 -0
  211. package/test/http_request.spec.coffee +55 -0
  212. package/test/json/builder.spec.coffee +129 -0
  213. package/test/json/parser.spec.coffee +108 -0
  214. package/test/metadata_service.spec.coffee +54 -0
  215. package/test/model/api.spec.coffee +67 -0
  216. package/test/model/shape.spec.coffee +23 -0
  217. package/test/node_http_client.spec.coffee +40 -0
  218. package/test/param_validator.spec.coffee +456 -0
  219. package/test/protocol/json.spec.coffee +167 -0
  220. package/test/protocol/query.spec.coffee +191 -0
  221. package/test/protocol/rest.spec.coffee +237 -0
  222. package/test/protocol/rest_json.spec.coffee +255 -0
  223. package/test/protocol/rest_xml.spec.coffee +329 -0
  224. package/test/query/query_param_serializer.spec.coffee +327 -0
  225. package/test/region_config.spec.coffee +50 -0
  226. package/test/request.spec.coffee +316 -0
  227. package/test/resource_waiter.spec.coffee +89 -0
  228. package/test/response.spec.coffee +81 -0
  229. package/test/sequential_executor.spec.coffee +118 -0
  230. package/test/service.spec.coffee +230 -0
  231. package/test/services/cloudfront.spec.coffee +44 -0
  232. package/test/services/cloudsearchdomain.spec.coffee +23 -0
  233. package/test/services/dynamodb.spec.coffee +32 -0
  234. package/test/services/ec2.spec.coffee +78 -0
  235. package/test/services/elastictranscoder.spec.coffee +43 -0
  236. package/test/services/glacier.spec.coffee +61 -0
  237. package/test/services/rds.spec.coffee +38 -0
  238. package/test/services/route53.spec.coffee +77 -0
  239. package/test/services/s3.spec.coffee +538 -0
  240. package/test/services/simpledb.spec.coffee +12 -0
  241. package/test/services/sqs.spec.coffee +130 -0
  242. package/test/services/sts.spec.coffee +72 -0
  243. package/test/services/swf.spec.coffee +6 -0
  244. package/test/signers/presign.spec.coffee +36 -0
  245. package/test/signers/s3.spec.coffee +297 -0
  246. package/test/signers/v2.spec.coffee +68 -0
  247. package/test/signers/v4.spec.coffee +135 -0
  248. package/test/util.spec.coffee +510 -0
  249. package/test/xml/builder.spec.coffee +529 -0
  250. package/test/xml/parser.spec.coffee +587 -0
  251. package/lib/services/simpledb.js +0 -15
@@ -0,0 +1,538 @@
1
+ helpers = require('../helpers')
2
+ AWS = helpers.AWS
3
+ Stream = require('stream').Stream
4
+ Buffer = AWS.util.Buffer
5
+
6
+ describe 'AWS.S3', ->
7
+
8
+ s3 = null
9
+ request = (operation, params) -> s3.makeRequest(operation, params)
10
+
11
+ beforeEach ->
12
+ s3 = new AWS.S3(region: undefined)
13
+
14
+ describe 'dnsCompatibleBucketName', ->
15
+
16
+ it 'must be at least 3 characters', ->
17
+ expect(s3.dnsCompatibleBucketName('aa')).toBe(false)
18
+
19
+ it 'must not be longer than 63 characters', ->
20
+ b = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
21
+ expect(s3.dnsCompatibleBucketName(b)).toBe(false)
22
+
23
+ it 'must start with a lower-cased letter or number', ->
24
+ expect(s3.dnsCompatibleBucketName('Abc')).toBe(false)
25
+ expect(s3.dnsCompatibleBucketName('-bc')).toBe(false)
26
+ expect(s3.dnsCompatibleBucketName('abc')).toBe(true)
27
+
28
+ it 'must end with a lower-cased letter or number', ->
29
+ expect(s3.dnsCompatibleBucketName('abC')).toBe(false)
30
+ expect(s3.dnsCompatibleBucketName('ab-')).toBe(false)
31
+ expect(s3.dnsCompatibleBucketName('abc')).toBe(true)
32
+
33
+ it 'may not contain multiple contiguous dots', ->
34
+ expect(s3.dnsCompatibleBucketName('abc.123')).toBe(true)
35
+ expect(s3.dnsCompatibleBucketName('abc..123')).toBe(false)
36
+
37
+ it 'may only contain letters numbers and dots', ->
38
+ expect(s3.dnsCompatibleBucketName('abc123')).toBe(true)
39
+ expect(s3.dnsCompatibleBucketName('abc_123')).toBe(false)
40
+
41
+ it 'must not look like an ip address', ->
42
+ expect(s3.dnsCompatibleBucketName('1.2.3.4')).toBe(false)
43
+ expect(s3.dnsCompatibleBucketName('a.b.c.d')).toBe(true)
44
+
45
+ describe 'endpoint', ->
46
+
47
+ it 'sets hostname to s3.amazonaws.com when region is un-specified', ->
48
+ s3 = new AWS.S3(region: undefined)
49
+ expect(s3.endpoint.hostname).toEqual('s3.amazonaws.com')
50
+
51
+ it 'sets hostname to s3.amazonaws.com when region is us-east-1', ->
52
+ s3 = new AWS.S3(region: 'us-east-1')
53
+ expect(s3.endpoint.hostname).toEqual('s3.amazonaws.com')
54
+
55
+ it 'sets region to us-east-1 when unspecified', ->
56
+ s3 = new AWS.S3(region: 'us-east-1')
57
+ expect(s3.config.region).toEqual('us-east-1')
58
+
59
+ it 'combines the region with s3 in the endpoint using a - instead of .', ->
60
+ s3 = new AWS.S3(region: 'us-west-1')
61
+ expect(s3.endpoint.hostname).toEqual('s3-us-west-1.amazonaws.com')
62
+
63
+ describe 'building a request', ->
64
+ build = (operation, params) ->
65
+ request(operation, params).build().httpRequest
66
+
67
+ it 'obeys the configuration for s3ForcePathStyle', ->
68
+ config = new AWS.Config(s3ForcePathStyle: true, accessKeyId: 'AKID', secretAccessKey: 'SECRET')
69
+ s3 = new AWS.S3(config)
70
+ expect(s3.config.s3ForcePathStyle).toEqual(true)
71
+ req = build('headObject', {Bucket:'bucket', Key:'key'})
72
+ expect(req.endpoint.hostname).toEqual('s3.amazonaws.com')
73
+ expect(req.path).toEqual('/bucket/key')
74
+
75
+ describe 'uri escaped params', ->
76
+ it 'uri-escapes path and querystring params', ->
77
+ # bucket param ends up as part of the hostname
78
+ params = { Bucket: 'bucket', Key: 'a b c', VersionId: 'a&b' }
79
+ req = build('headObject', params)
80
+ expect(req.path).toEqual('/a%20b%20c?versionId=a%26b')
81
+
82
+ it 'does not uri-escape forward slashes in the path', ->
83
+ params = { Bucket: 'bucket', Key: 'k e/y' }
84
+ req = build('headObject', params)
85
+ expect(req.path).toEqual('/k%20e/y')
86
+
87
+ it 'ensures a single forward slash exists', ->
88
+ req = build('listObjects', { Bucket: 'bucket' })
89
+ expect(req.path).toEqual('/')
90
+
91
+ req = build('listObjects', { Bucket: 'bucket', MaxKeys:123 })
92
+ expect(req.path).toEqual('/?max-keys=123')
93
+
94
+ describe 'adding Content-Type', ->
95
+ beforeEach -> spyOn(AWS.util, 'isBrowser').andReturn(true)
96
+
97
+ it 'adds default content-type when not supplied', ->
98
+ req = build('putObject', Bucket: 'bucket', Key: 'key', Body: 'body')
99
+ expect(req.headers['Content-Type']).toEqual('application/octet-stream; charset=UTF-8')
100
+
101
+ it 'adds charset to existing content-type if not supplied', ->
102
+ req = build('putObject', Bucket: 'bucket', Key: 'key', Body: 'body', ContentType: 'text/html')
103
+ expect(req.headers['Content-Type']).toEqual('text/html; charset=UTF-8')
104
+
105
+ it 'normalized charset to uppercase', ->
106
+ req = build('putObject', Bucket: 'bucket', Key: 'key', Body: 'body', ContentType: 'text/html; charset=utf-8')
107
+ expect(req.headers['Content-Type']).toEqual('text/html; charset=UTF-8')
108
+
109
+ it 'does not add charset to non-string data', ->
110
+ req = build('putObject', Bucket: 'bucket', Key: 'key', Body: new Buffer('body'), ContentType: 'image/png')
111
+ expect(req.headers['Content-Type']).toEqual('image/png')
112
+
113
+ describe 'virtual-hosted vs path-style bucket requests', ->
114
+
115
+ describe 'HTTPS', ->
116
+
117
+ beforeEach ->
118
+ s3 = new AWS.S3(sslEnabled: true, region: undefined)
119
+
120
+ it 'puts dns-compat bucket names in the hostname', ->
121
+ req = build('headObject', {Bucket:'bucket-name',Key:'abc'})
122
+ expect(req.method).toEqual('HEAD')
123
+ expect(req.endpoint.hostname).toEqual('bucket-name.s3.amazonaws.com')
124
+ expect(req.path).toEqual('/abc')
125
+
126
+ it 'ensures the path contains / at a minimum when moving bucket', ->
127
+ req = build('listObjects', {Bucket:'bucket-name'})
128
+ expect(req.endpoint.hostname).toEqual('bucket-name.s3.amazonaws.com')
129
+ expect(req.path).toEqual('/')
130
+
131
+ it 'puts dns-compat bucket names in path if they contain a dot', ->
132
+ req = build('listObjects', {Bucket:'bucket.name'})
133
+ expect(req.endpoint.hostname).toEqual('s3.amazonaws.com')
134
+ expect(req.path).toEqual('/bucket.name')
135
+
136
+ it 'puts dns-compat bucket names in path if configured to do so', ->
137
+ s3 = new AWS.S3(sslEnabled: true, s3ForcePathStyle: true, region: undefined)
138
+ req = build('listObjects', {Bucket:'bucket-name'})
139
+ expect(req.endpoint.hostname).toEqual('s3.amazonaws.com')
140
+ expect(req.path).toEqual('/bucket-name')
141
+
142
+ it 'puts dns-incompat bucket names in path', ->
143
+ req = build('listObjects', {Bucket:'bucket_name'})
144
+ expect(req.endpoint.hostname).toEqual('s3.amazonaws.com')
145
+ expect(req.path).toEqual('/bucket_name')
146
+
147
+ describe 'HTTP', ->
148
+
149
+ beforeEach ->
150
+ s3 = new AWS.S3(sslEnabled: false, region: undefined)
151
+
152
+ it 'puts dns-compat bucket names in the hostname', ->
153
+ req = build('listObjects', {Bucket:'bucket-name'})
154
+ expect(req.endpoint.hostname).toEqual('bucket-name.s3.amazonaws.com')
155
+ expect(req.path).toEqual('/')
156
+
157
+ it 'puts dns-compat bucket names in the hostname if they contain a dot', ->
158
+ req = build('listObjects', {Bucket:'bucket.name'})
159
+ expect(req.endpoint.hostname).toEqual('bucket.name.s3.amazonaws.com')
160
+ expect(req.path).toEqual('/')
161
+
162
+ it 'puts dns-incompat bucket names in path', ->
163
+ req = build('listObjects', {Bucket:'bucket_name'})
164
+ expect(req.endpoint.hostname).toEqual('s3.amazonaws.com')
165
+ expect(req.path).toEqual('/bucket_name')
166
+
167
+ describe 'SSE support', ->
168
+ beforeEach -> s3 = new AWS.S3
169
+
170
+ it 'encodes SSECustomerKey and fills in MD5', ->
171
+ req = s3.putObject
172
+ Bucket: 'bucket', Key: 'key', Body: 'data'
173
+ SSECustomerKey: 'KEY', SSECustomerAlgorithm: 'AES256'
174
+ req.build()
175
+ expect(req.httpRequest.headers['x-amz-server-side-encryption-customer-key']).
176
+ toEqual('S0VZ')
177
+ expect(req.httpRequest.headers['x-amz-server-side-encryption-customer-key-MD5']).
178
+ toEqual('TzFsSjRNSnJoM1o2UjFLaWR0NlZjQT09')
179
+
180
+ it 'encodes CopySourceSSECustomerKey and fills in MD5', ->
181
+ req = s3.copyObject
182
+ Bucket: 'bucket', Key: 'key', CopySource: 'bucket/oldkey', Body: 'data'
183
+ CopySourceSSECustomerKey: 'KEY', CopySourceSSECustomerAlgorithm: 'AES256'
184
+ req.build()
185
+ expect(req.httpRequest.headers['x-amz-copy-source-server-side-encryption-customer-key']).
186
+ toEqual('S0VZ')
187
+ expect(req.httpRequest.headers['x-amz-copy-source-server-side-encryption-customer-key-MD5']).
188
+ toEqual('TzFsSjRNSnJoM1o2UjFLaWR0NlZjQT09')
189
+
190
+ describe 'retry behavior', ->
191
+ it 'retries RequestTimeout errors', ->
192
+ s3.config.maxRetries = 3
193
+ helpers.mockHttpResponse 400, {},
194
+ '<xml><Code>RequestTimeout</Code><Message>message</Message></xml>'
195
+ s3.putObject (err, data) ->
196
+ expect(@retryCount).toEqual(s3.config.maxRetries)
197
+
198
+ # S3 returns a handful of errors without xml bodies (to match the
199
+ # http spec) these tests ensure we give meaningful codes/messages for these.
200
+ describe 'errors with no XML body', ->
201
+
202
+ extractError = (statusCode, body) ->
203
+ req = request('operation')
204
+ resp = new AWS.Response(req)
205
+ resp.httpResponse.body = new Buffer(body || '')
206
+ resp.httpResponse.statusCode = statusCode
207
+ req.emit('extractError', [resp])
208
+ resp.error
209
+
210
+ it 'handles 304 errors', ->
211
+ error = extractError(304)
212
+ expect(error.code).toEqual('NotModified')
213
+ expect(error.message).toEqual(null)
214
+
215
+ it 'handles 400 errors', ->
216
+ error = extractError(400)
217
+ expect(error.code).toEqual('BadRequest')
218
+ expect(error.message).toEqual(null)
219
+
220
+ it 'handles 403 errors', ->
221
+ error = extractError(403)
222
+ expect(error.code).toEqual('Forbidden')
223
+ expect(error.message).toEqual(null)
224
+
225
+ it 'handles 404 errors', ->
226
+ error = extractError(404)
227
+ expect(error.code).toEqual('NotFound')
228
+ expect(error.message).toEqual(null)
229
+
230
+ it 'misc errors not known to return an empty body', ->
231
+ error = extractError(412) # made up
232
+ expect(error.code).toEqual(412)
233
+ expect(error.message).toEqual(null)
234
+
235
+ it 'uses canned errors only when the body is empty', ->
236
+ body = """
237
+ <xml>
238
+ <Code>ErrorCode</Code>
239
+ <Message>ErrorMessage</Message>
240
+ </xml>
241
+ """
242
+ error = extractError(403, body)
243
+ expect(error.code).toEqual('ErrorCode')
244
+ expect(error.message).toEqual('ErrorMessage')
245
+
246
+ # tests from this point on are "special cases" for specific aws operations
247
+
248
+ describe 'getBucketAcl', ->
249
+ it 'correctly parses the ACL XML document', ->
250
+ headers = { 'x-amz-request-id' : 'request-id' }
251
+ body =
252
+ """
253
+ <AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
254
+ <AccessControlList>
255
+ <Grant>
256
+ <Grantee xsi:type="CanonicalUser" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
257
+ <DisplayName>aws-sdk</DisplayName>
258
+ <ID>id</ID>
259
+ </Grantee>
260
+ <Permission>FULL_CONTROL</Permission>
261
+ </Grant>
262
+ <Grant>
263
+ <Grantee xsi:type="Group" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
264
+ <URI>uri</URI>
265
+ </Grantee>
266
+ <Permission>READ</Permission>
267
+ </Grant>
268
+ </AccessControlList>
269
+ <Owner>
270
+ <DisplayName>aws-sdk</DisplayName>
271
+ <ID>id</ID>
272
+ </Owner>
273
+ </AccessControlPolicy>
274
+ """
275
+ helpers.mockHttpResponse 200, headers, body
276
+ s3.getBucketAcl (error, data) ->
277
+ expect(error).toBe(null)
278
+ expect(data).toEqual({
279
+ Owner:
280
+ DisplayName: 'aws-sdk',
281
+ ID: 'id'
282
+ Grants: [
283
+ {
284
+ Permission: 'FULL_CONTROL'
285
+ Grantee:
286
+ Type: 'CanonicalUser'
287
+ DisplayName: 'aws-sdk'
288
+ ID: 'id'
289
+ },
290
+ {
291
+ Permission : 'READ'
292
+ Grantee:
293
+ Type: 'Group'
294
+ URI: 'uri'
295
+ }
296
+ ]
297
+ })
298
+
299
+ describe 'putBucketAcl', ->
300
+ it 'correctly builds the ACL XML document', ->
301
+ xml =
302
+ """
303
+ <AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
304
+ <AccessControlList>
305
+ <Grant>
306
+ <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
307
+ <DisplayName>aws-sdk</DisplayName>
308
+ <ID>id</ID>
309
+ </Grantee>
310
+ <Permission>FULL_CONTROL</Permission>
311
+ </Grant>
312
+ <Grant>
313
+ <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
314
+ <URI>uri</URI>
315
+ </Grantee>
316
+ <Permission>READ</Permission>
317
+ </Grant>
318
+ </AccessControlList>
319
+ <Owner>
320
+ <DisplayName>aws-sdk</DisplayName>
321
+ <ID>id</ID>
322
+ </Owner>
323
+ </AccessControlPolicy>
324
+ """
325
+ helpers.mockHttpResponse 200, {}, ''
326
+ params =
327
+ AccessControlPolicy:
328
+ Owner:
329
+ DisplayName: 'aws-sdk',
330
+ ID: 'id'
331
+ Grants: [
332
+ {
333
+ Permission: 'FULL_CONTROL'
334
+ Grantee:
335
+ Type: 'CanonicalUser',
336
+ DisplayName: 'aws-sdk'
337
+ ID: 'id'
338
+ },
339
+ {
340
+ Permission : 'READ'
341
+ Grantee:
342
+ Type: 'Group',
343
+ URI: 'uri'
344
+ }
345
+ ]
346
+ s3.putBucketAcl params, (err, data) ->
347
+ helpers.matchXML(this.request.httpRequest.body, xml)
348
+
349
+ describe 'completeMultipartUpload', ->
350
+
351
+ it 'returns data when the resp is 200 with valid response', ->
352
+ headers =
353
+ 'x-amz-id-2': 'Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg=='
354
+ 'x-amz-request-id': '656c76696e6727732072657175657374'
355
+ body =
356
+ """
357
+ <?xml version="1.0" encoding="UTF-8"?>
358
+ <CompleteMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
359
+ <Location>http://Example-Bucket.s3.amazonaws.com/Example-Object</Location>
360
+ <Bucket>Example-Bucket</Bucket>
361
+ <Key>Example-Object</Key>
362
+ <ETag>"3858f62230ac3c915f300c664312c11f-9"</ETag>
363
+ </CompleteMultipartUploadResult>
364
+ """
365
+
366
+ helpers.mockHttpResponse 200, headers, body
367
+ s3.completeMultipartUpload (error, data) ->
368
+ expect(error).toBe(null)
369
+ expect(data).toEqual({
370
+ Location: 'http://Example-Bucket.s3.amazonaws.com/Example-Object'
371
+ Bucket: 'Example-Bucket'
372
+ Key: 'Example-Object'
373
+ ETag: '"3858f62230ac3c915f300c664312c11f-9"'
374
+ })
375
+ expect(this.requestId).toEqual('656c76696e6727732072657175657374')
376
+
377
+ it 'returns an error when the resp is 200 with an error xml document', ->
378
+ body =
379
+ """
380
+ <?xml version="1.0" encoding="UTF-8"?>
381
+ <Error>
382
+ <Code>InternalError</Code>
383
+ <Message>We encountered an internal error. Please try again.</Message>
384
+ <RequestId>656c76696e6727732072657175657374</RequestId>
385
+ <HostId>Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==</HostId>
386
+ </Error>
387
+ """
388
+
389
+ helpers.mockHttpResponse 200, {}, body
390
+ s3.completeMultipartUpload (error, data) ->
391
+ expect(error instanceof Error).toBeTruthy()
392
+ expect(error.code).toEqual('InternalError')
393
+ expect(error.message).toEqual('We encountered an internal error. Please try again.')
394
+ expect(error.statusCode).toEqual(200)
395
+ expect(error.retryable).toEqual(true)
396
+ expect(data).toEqual(null)
397
+
398
+ describe 'getBucketLocation', ->
399
+
400
+ it 'returns null for the location constraint when not present', ->
401
+ body = '<?xml version="1.0" encoding="UTF-8"?>\n<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/"/>'
402
+ helpers.mockHttpResponse 200, {}, body
403
+ s3.getBucketLocation (error, data) ->
404
+ expect(error).toBe(null)
405
+ expect(data).toEqual({})
406
+
407
+ it 'parses the location constraint from the root xml', ->
408
+ headers = { 'x-amz-request-id': 'abcxyz' }
409
+ body = '<?xml version="1.0" encoding="UTF-8"?>\n<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">EU</LocationConstraint>'
410
+ helpers.mockHttpResponse 200, headers, body
411
+ s3.getBucketLocation (error, data) ->
412
+ expect(error).toBe(null)
413
+ expect(data).toEqual(LocationConstraint: 'EU')
414
+ expect(this.requestId).toEqual('abcxyz')
415
+
416
+ describe 'createBucket', ->
417
+ it 'auto-populates the LocationConstraint based on the region', ->
418
+ loc = null
419
+ s3 = new AWS.S3(region:'eu-west-1')
420
+ s3.makeRequest = (op, params) ->
421
+ loc = params.CreateBucketConfiguration.LocationConstraint
422
+ s3.createBucket(Bucket:'name')
423
+ expect(loc).toEqual('eu-west-1')
424
+
425
+ it 'correctly builds the xml', ->
426
+
427
+ AWS.util.each AWS.S3.prototype.computableChecksumOperations, (operation) ->
428
+ describe operation, ->
429
+ it 'forces Content-MD5 header parameter', ->
430
+ req = s3[operation](Bucket: 'bucket', ContentMD5: '000').build()
431
+ hash = AWS.util.crypto.md5(req.httpRequest.body, 'base64')
432
+ expect(req.httpRequest.headers['Content-MD5']).toEqual(hash)
433
+
434
+ describe 'willComputeChecksums', ->
435
+ willCompute = (operation, opts) ->
436
+ compute = opts.computeChecksums
437
+ s3 = new AWS.S3(computeChecksums: compute)
438
+ req = s3.makeRequest(operation, Bucket: 'example', ContentMD5: opts.hash).build()
439
+ checksum = req.httpRequest.headers['Content-MD5']
440
+ if opts.hash != undefined
441
+ expect(checksum).toEqual(opts.hash)
442
+ else
443
+ realChecksum = AWS.util.crypto.md5(req.httpRequest.body, 'base64')
444
+ expect(checksum).toEqual(realChecksum)
445
+
446
+ it 'computes checksums if the operation requires it', ->
447
+ willCompute 'deleteObjects', computeChecksums: true
448
+ willCompute 'putBucketCors', computeChecksums: true
449
+ willCompute 'putBucketLifecycle', computeChecksums: true
450
+ willCompute 'putBucketTagging', computeChecksums: true
451
+
452
+ it 'computes checksums if computeChecksums is off and operation requires it', ->
453
+ willCompute 'deleteObjects', computeChecksums: false
454
+ willCompute 'putBucketCors', computeChecksums: false
455
+ willCompute 'putBucketLifecycle', computeChecksums: false
456
+ willCompute 'putBucketTagging', computeChecksums: false
457
+
458
+ it 'does not compute checksums if computeChecksums is off', ->
459
+ willCompute 'putObject', computeChecksums: false, hash: null
460
+
461
+ it 'does not compute checksums if computeChecksums is on and ContentMD5 is provided', ->
462
+ willCompute 'putBucketAcl', computeChecksums: true, hash: '000'
463
+
464
+ it 'does not compute checksums for Stream objects', ->
465
+ s3 = new AWS.S3(computeChecksums: true)
466
+ req = s3.putObject(Bucket: 'example', Key: 'foo', Body: new Stream)
467
+ expect(req.build(->).httpRequest.headers['Content-MD5']).toEqual(undefined)
468
+
469
+ it 'computes checksums if computeChecksums is on and ContentMD5 is not provided',->
470
+ willCompute 'putBucketAcl', computeChecksums: true
471
+
472
+ describe 'getSignedUrl', ->
473
+ date = null
474
+ beforeEach ->
475
+ date = AWS.util.date.getDate
476
+ AWS.util.date.getDate = -> new Date(0)
477
+ afterEach ->
478
+ AWS.util.date.getDate = date
479
+
480
+ it 'gets a signed URL for getObject', ->
481
+ url = s3.getSignedUrl('getObject', Bucket: 'bucket', Key: 'key')
482
+ expect(url).toEqual('https://bucket.s3.amazonaws.com/key?AWSAccessKeyId=akid&Expires=900&Signature=4mlYnRmz%2BBFEPrgYz5tXcl9Wc4w%3D&x-amz-security-token=session')
483
+
484
+ it 'gets a signed URL with Expires time', ->
485
+ url = s3.getSignedUrl('getObject', Bucket: 'bucket', Key: 'key', Expires: 60)
486
+ expect(url).toEqual('https://bucket.s3.amazonaws.com/key?AWSAccessKeyId=akid&Expires=60&Signature=kH2pMK%2Fgm7cCZKVG8GHVTRGXKzY%3D&x-amz-security-token=session')
487
+
488
+ it 'gets a signed URL with expiration and bound bucket parameters', ->
489
+ s3 = new AWS.S3(paramValidation: true, region: undefined, params: Bucket: 'bucket')
490
+ url = s3.getSignedUrl('getObject', Key: 'key', Expires: 60)
491
+ expect(url).toEqual('https://bucket.s3.amazonaws.com/key?AWSAccessKeyId=akid&Expires=60&Signature=kH2pMK%2Fgm7cCZKVG8GHVTRGXKzY%3D&x-amz-security-token=session')
492
+
493
+ it 'generates the right URL with a custom endpoint', ->
494
+ s3 = new AWS.S3(endpoint: 'https://foo.bar.baz:555/prefix', params: Bucket: 'bucket')
495
+ url = s3.getSignedUrl('getObject', Key: 'key', Expires: 60)
496
+ expect(url).toEqual('https://bucket.foo.bar.baz:555/prefix/key?AWSAccessKeyId=akid&Expires=60&Signature=zA6k0cQqDkTZgLamfoYLOd%2Bqfg8%3D&x-amz-security-token=session')
497
+
498
+ it 'gets a signed URL with callback', ->
499
+ url = null
500
+ runs ->
501
+ s3.getSignedUrl 'getObject', Bucket: 'bucket', Key: 'key', (err, value) -> url = value
502
+ waitsFor -> url
503
+ runs ->
504
+ expect(url).toEqual('https://bucket.s3.amazonaws.com/key?AWSAccessKeyId=akid&Expires=900&Signature=4mlYnRmz%2BBFEPrgYz5tXcl9Wc4w%3D&x-amz-security-token=session')
505
+
506
+ it 'gets a signed URL for putObject with no body', ->
507
+ url = s3.getSignedUrl('putObject', Bucket: 'bucket', Key: 'key')
508
+ expect(url).toEqual('https://bucket.s3.amazonaws.com/key?AWSAccessKeyId=akid&Expires=900&Signature=J%2BnWZ0lPUfLV0kio8ONhJmAttGc%3D&x-amz-security-token=session')
509
+
510
+ it 'gets a signed URL for putObject with special characters', ->
511
+ url = s3.getSignedUrl('putObject', Bucket: 'bucket', Key: '!@#$%^&*();\':"{}[],./?`~')
512
+ expect(url).toEqual('https://bucket.s3.amazonaws.com/%21%40%23%24%25%5E%26%2A%28%29%3B%27%3A%22%7B%7D%5B%5D%2C./%3F%60~?AWSAccessKeyId=akid&Expires=900&Signature=9nEltJACZKsriZqU2cmRel6g8LQ%3D&x-amz-security-token=session')
513
+
514
+ it 'gets a signed URL for putObject with a body (and checksum)', ->
515
+ url = s3.getSignedUrl('putObject', Bucket: 'bucket', Key: 'key', Body: 'body')
516
+ expect(url).toEqual('https://bucket.s3.amazonaws.com/key?AWSAccessKeyId=akid&Content-MD5=hBotaJrYa9FhFEdFPCLG%2FA%3D%3D&Expires=900&Signature=4ycA2tpHKxfFnNCdqnK1d5BG8gc%3D&x-amz-security-token=session')
517
+
518
+ it 'gets a signed URL and appends to existing query parameters', ->
519
+ url = s3.getSignedUrl('listObjects', Bucket: 'bucket', Prefix: 'prefix')
520
+ expect(url).toEqual('https://bucket.s3.amazonaws.com/?AWSAccessKeyId=akid&Expires=900&Signature=8W3pwZPfgucCyPNg1MsoYq8h5zw%3D&prefix=prefix&x-amz-security-token=session')
521
+
522
+ it 'gets a signed URL for getObject using SigV4', ->
523
+ s3 = new AWS.S3(signatureVersion: 'v4', region: undefined)
524
+ url = s3.getSignedUrl('getObject', Bucket: 'bucket', Key: 'object')
525
+ expect(url).toEqual('https://bucket.s3.amazonaws.com/object?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=akid%2F19700101%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=19700101T000000Z&X-Amz-Expires=900&X-Amz-Security-Token=session&X-Amz-Signature=05ae40d2d22c93549a1de0686232ff56baf556876ec497d0d8349431f98b8dfe&X-Amz-SignedHeaders=host')
526
+
527
+ it 'errors when expiry time is greater than a week out on SigV4', ->
528
+ err = null; data = null
529
+ s3 = new AWS.S3(signatureVersion: 'v4', region: undefined)
530
+ params = Bucket: 'bucket', Key: 'object', Expires: 60 * 60 * 24 * 7 + 120
531
+ error = 'Presigning does not support expiry time greater than a week with SigV4 signing.'
532
+ runs ->
533
+ s3.getSignedUrl 'getObject', params, (e, d) -> data = d; err = e
534
+ waitsFor -> err || data
535
+ runs ->
536
+ expect(err).not.toEqual(null)
537
+ expect(err.message).toEqual(error)
538
+ #expect(-> s3.getSignedUrl('getObject', params)).toThrow(error) # sync mode
@@ -0,0 +1,12 @@
1
+ helpers = require('../helpers')
2
+ AWS = helpers.AWS
3
+
4
+ describe 'AWS.SimpleDB', ->
5
+ describe 'setEndpoint', ->
6
+ it 'uses global endpoint if region is us-east-1', ->
7
+ service = new AWS.SimpleDB(region: 'us-east-1')
8
+ expect(service.endpoint.host).toEqual('sdb.amazonaws.com')
9
+
10
+ it 'uses normal setEndpoint functionality if region is not us-east-1', ->
11
+ service = new AWS.SimpleDB(region: 'us-west-2')
12
+ expect(service.endpoint.host).toEqual('sdb.us-west-2.amazonaws.com')
@@ -0,0 +1,130 @@
1
+ helpers = require('../helpers')
2
+ AWS = helpers.AWS
3
+
4
+ describe 'AWS.SQS', ->
5
+ sqs = null
6
+ beforeEach ->
7
+ sqs = new AWS.SQS params: QueueUrl: 'http://url'
8
+
9
+ checksumValidate = (operation, input, response, shouldPass, cb) ->
10
+ output = null
11
+ helpers.mockHttpResponse 200, {}, response
12
+ runs ->
13
+ sqs[operation](input, (e, d) -> output = error: e, data: d)
14
+ waitsFor -> output
15
+ runs ->
16
+ if shouldPass
17
+ expect(output.error).toEqual(null)
18
+ else
19
+ expect(output.error).not.toEqual(null)
20
+ if cb
21
+ cb(output.error, output.data)
22
+
23
+ describe 'buildEndpoint', ->
24
+ it 'should detect correct region from QueueUrl', ->
25
+ sqs = new AWS.SQS
26
+ computeChecksums: false
27
+ params: QueueUrl: 'http://sqs.region-1.amazonaws.com/queue'
28
+ helpers.mockHttpResponse 200, {}, ''
29
+ req = sqs.sendMessage(MessageBody: 'foo')
30
+ req.build()
31
+ expect(req.httpRequest.region).toEqual('region-1')
32
+
33
+ describe 'sendMessage', ->
34
+ input = MessageBody: 'foo'
35
+ md5 = 'acbd18db4cc2f85cedef654fccc4a4d8'
36
+ payload = (md5) ->
37
+ """
38
+ <SendMessageResponse><SendMessageResult>
39
+ <MD5OfMessageBody>#{md5}</MD5OfMessageBody>
40
+ <MessageId>MSGID</MessageId>
41
+ </SendMessageResult></SendMessageResponse>
42
+ """
43
+
44
+ it 'correctly validates MD5 of message input', ->
45
+ checksumValidate 'sendMessage', input, payload(md5), true, (err, data) ->
46
+ expect(data.MD5OfMessageBody).toEqual(md5)
47
+
48
+ it 'raises InvalidChecksum if MD5 does not match message input', ->
49
+ checksumValidate 'sendMessage', input, payload('000'), false, (err) ->
50
+ expect(err.message).toMatch('Got "000", expecting "acbd18db4cc2f85cedef654fccc4a4d8"')
51
+ expect(err.messageIds).toEqual(['MSGID'])
52
+
53
+ it 'ignores checksum errors if computeChecksums is false', ->
54
+ sqs.config.computeChecksums = false
55
+ checksumValidate 'sendMessage', input, payload('000'), true
56
+
57
+ describe 'sendMessageBatch', ->
58
+ input = Entries: [
59
+ { Id: 'a', MessageBody: 'foo' },
60
+ { Id: 'b', MessageBody: 'bar' },
61
+ { Id: 'c', MessageBody: 'bar' }
62
+ ]
63
+ md5foo = 'acbd18db4cc2f85cedef654fccc4a4d8'
64
+ md5bar = '37b51d194a7513e45b56f6524f2d51f2'
65
+ payload = (md5a, md5b, md5c) ->
66
+ """
67
+ <SendMessageBatchResponse><SendMessageBatchResult>
68
+ <SendMessageBatchResultEntry>
69
+ <Id>a</Id>
70
+ <MessageId>MSGID1</MessageId>
71
+ <MD5OfMessageBody>#{md5a}</MD5OfMessageBody>
72
+ </SendMessageBatchResultEntry>
73
+ <SendMessageBatchResultEntry>
74
+ <Id>b</Id>
75
+ <MessageId>MSGID2</MessageId>
76
+ <MD5OfMessageBody>#{md5b}</MD5OfMessageBody>
77
+ </SendMessageBatchResultEntry>
78
+ <SendMessageBatchResultEntry>
79
+ <Id>c</Id>
80
+ <MessageId>MSGID3</MessageId>
81
+ <MD5OfMessageBody>#{md5c}</MD5OfMessageBody>
82
+ </SendMessageBatchResultEntry>
83
+ </SendMessageBatchResult></SendMessageBatchResponse>
84
+ """
85
+
86
+ it 'correctly validates MD5 of operation', ->
87
+ output = payload(md5foo, md5bar, md5bar)
88
+ checksumValidate 'sendMessageBatch', input, output, true, (err, data) ->
89
+ expect(data.Successful[0].MD5OfMessageBody).toEqual(md5foo)
90
+ expect(data.Successful[1].MD5OfMessageBody).toEqual(md5bar)
91
+ expect(data.Successful[2].MD5OfMessageBody).toEqual(md5bar)
92
+
93
+ it 'raises InvalidChecksum with relevent message IDs', ->
94
+ output = payload('000', md5bar, '000')
95
+ checksumValidate 'sendMessageBatch', input, output, false, (err, data) ->
96
+ expect(err.message).toMatch('Invalid messages: a, c')
97
+ expect(err.messageIds).toEqual(['MSGID1', 'MSGID3'])
98
+
99
+ it 'ignores checksum errors if computeChecksums is false', ->
100
+ output = payload(md5foo, '000', md5bar)
101
+ sqs.config.computeChecksums = false
102
+ checksumValidate 'sendMessageBatch', input, output, true
103
+
104
+ describe 'receiveMessage', ->
105
+ md5 = 'acbd18db4cc2f85cedef654fccc4a4d8'
106
+ payload = (body, md5, id) ->
107
+ """
108
+ <ReceiveMessageResponse><ReceiveMessageResult><Message>
109
+ <Body>#{body}</Body>
110
+ <MD5OfBody>#{md5}</MD5OfBody>
111
+ <MessageId>#{id}</MessageId>
112
+ </Message></ReceiveMessageResult></ReceiveMessageResponse>
113
+ """
114
+
115
+ it 'correctly validates MD5 of operation', ->
116
+ output = payload('foo', md5, 'MSGID')
117
+ checksumValidate 'receiveMessage', {}, output, true, (err, data) ->
118
+ expect(data.Messages[0].MD5OfBody).toEqual(md5)
119
+
120
+ it 'raises InvalidChecksum with relevent message IDs', ->
121
+ output = payload('foo', '000', 'MSGID')
122
+ checksumValidate 'receiveMessage', {}, output, false, (err, data) ->
123
+ expect(err.message).toMatch('Invalid messages: MSGID')
124
+ expect(err.messageIds).toEqual(['MSGID'])
125
+
126
+ it 'ignores checksum errors if computeChecksums is false', ->
127
+ output = payload('foo', '000', 'MSGID')
128
+ sqs.config.computeChecksums = false
129
+ checksumValidate 'receiveMessage', {}, output, true, (err, data) ->
130
+ expect(data.Messages[0].MD5OfBody).not.toEqual(md5)