httpimagestore 0.5.0 → 1.0.0

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 (48) hide show
  1. data/Gemfile +10 -12
  2. data/Gemfile.lock +57 -55
  3. data/README.md +829 -0
  4. data/VERSION +1 -1
  5. data/bin/httpimagestore +114 -180
  6. data/features/cache-control.feature +26 -90
  7. data/features/compatibility.feature +129 -0
  8. data/features/error-reporting.feature +207 -0
  9. data/features/health-check.feature +30 -0
  10. data/features/s3-store-and-thumbnail.feature +65 -0
  11. data/features/step_definitions/httpimagestore_steps.rb +66 -26
  12. data/features/support/env.rb +32 -5
  13. data/features/support/test.empty +0 -0
  14. data/httpimagestore.gemspec +60 -47
  15. data/lib/httpimagestore/aws_sdk_regions_hack.rb +23 -0
  16. data/lib/httpimagestore/configuration/file.rb +120 -0
  17. data/lib/httpimagestore/configuration/handler.rb +239 -0
  18. data/lib/httpimagestore/configuration/output.rb +119 -0
  19. data/lib/httpimagestore/configuration/path.rb +77 -0
  20. data/lib/httpimagestore/configuration/s3.rb +194 -0
  21. data/lib/httpimagestore/configuration/thumbnailer.rb +244 -0
  22. data/lib/httpimagestore/configuration.rb +126 -29
  23. data/lib/httpimagestore/error_reporter.rb +36 -0
  24. data/lib/httpimagestore/ruby_string_template.rb +26 -0
  25. data/load_test/load_test.1k.23a022f6e.m1.small-comp.csv +3 -0
  26. data/load_test/load_test.1k.ec9bde794.m1.small.csv +4 -0
  27. data/load_test/load_test.jmx +344 -0
  28. data/load_test/thumbnail_specs.csv +11 -0
  29. data/spec/configuration_file_spec.rb +309 -0
  30. data/spec/configuration_handler_spec.rb +124 -0
  31. data/spec/configuration_output_spec.rb +338 -0
  32. data/spec/configuration_path_spec.rb +92 -0
  33. data/spec/configuration_s3_spec.rb +571 -0
  34. data/spec/configuration_spec.rb +80 -105
  35. data/spec/configuration_thumbnailer_spec.rb +417 -0
  36. data/spec/ruby_string_template_spec.rb +43 -0
  37. data/spec/spec_helper.rb +61 -0
  38. data/spec/support/compute.jpg +0 -0
  39. data/spec/support/cuba_response_env.rb +40 -0
  40. data/spec/support/full.cfg +49 -0
  41. metadata +138 -84
  42. data/README.rdoc +0 -23
  43. data/features/httpimagestore.feature +0 -167
  44. data/lib/httpimagestore/image_path.rb +0 -54
  45. data/lib/httpimagestore/s3_service.rb +0 -37
  46. data/lib/httpimagestore/thumbnail_class.rb +0 -13
  47. data/spec/image_path_spec.rb +0 -72
  48. data/spec/test.cfg +0 -8
@@ -0,0 +1,571 @@
1
+ require_relative 'spec_helper'
2
+ require 'aws-sdk'
3
+ require 'httpimagestore/configuration'
4
+ Configuration::Scope.logger = Logger.new('/dev/null')
5
+
6
+ require 'httpimagestore/configuration/s3'
7
+ MemoryLimit.logger = Logger.new('/dev/null')
8
+
9
+ unless ENV['AWS_ACCESS_KEY_ID'] and ENV['AWS_SECRET_ACCESS_KEY'] and ENV['AWS_S3_TEST_BUCKET']
10
+ puts "AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY or AWS_S3_TEST_BUCKET environment variables not set - Skipping S3 specs"
11
+ else
12
+ describe Configuration do
13
+ describe Configuration::S3 do
14
+ subject do
15
+ Configuration.read(<<-EOF)
16
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
17
+ EOF
18
+ end
19
+
20
+ it 'should provide S3 key and secret' do
21
+ subject.s3.config.access_key_id.should == ENV['AWS_ACCESS_KEY_ID']
22
+ subject.s3.config.secret_access_key.should == ENV['AWS_SECRET_ACCESS_KEY']
23
+ end
24
+
25
+ it 'should use SSL by default' do
26
+ subject.s3.config.use_ssl.should be_true
27
+ end
28
+
29
+ it 'should allow disabling SSL' do
30
+ subject = Configuration.read(<<-EOF)
31
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
32
+ EOF
33
+
34
+ subject.s3.config.use_ssl.should be_false
35
+ end
36
+
37
+ it 'should provide S3 client' do
38
+ subject.s3.should be_a AWS::S3
39
+ end
40
+
41
+ describe 'error handling' do
42
+ it 'should raise StatementCollisionError on duplicate s3 statement' do
43
+ expect {
44
+ Configuration.read(<<-EOF)
45
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
46
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
47
+ EOF
48
+ }.to raise_error Configuration::StatementCollisionError, %{syntax error while parsing 's3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"': only one s3 type statement can be specified within context}
49
+ end
50
+
51
+ it 'should raise NoAttributeError on missing key attribute' do
52
+ expect {
53
+ Configuration.read(<<-EOF)
54
+ s3 secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
55
+ EOF
56
+ }.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 's3 secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"': expected 'key' attribute to be set}
57
+ end
58
+
59
+ it 'should raise NoAttributeError on missing secret attribute' do
60
+ expect {
61
+ Configuration.read(<<-EOF)
62
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}"
63
+ EOF
64
+ }.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 's3 key="#{ENV['AWS_ACCESS_KEY_ID']}"': expected 'secret' attribute to be set}
65
+ end
66
+ end
67
+ end
68
+
69
+ describe Configuration::S3Source do
70
+ let :state do
71
+ Configuration::RequestState.new('abc', test_image: 'test.jpg')
72
+ end
73
+
74
+ subject do
75
+ Configuration.read(<<-EOF)
76
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
77
+ path "hash" "\#{test_image}"
78
+ get {
79
+ source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
80
+ }
81
+ EOF
82
+ end
83
+
84
+ before :all do
85
+ @test_data = (support_dir + 'compute.jpg').read.force_encoding('ASCII-8BIT')
86
+
87
+ s3_client = AWS::S3.new(use_ssl: false)
88
+ s3_test_bucket = s3_client.buckets[ENV['AWS_S3_TEST_BUCKET']]
89
+ s3_test_bucket.objects['test.jpg'].write(@test_data, content_type: 'image/jpeg')
90
+ end
91
+
92
+ it 'should source image from S3 using path spec' do
93
+ subject.handlers[0].image_sources[0].should be_a Configuration::S3Source
94
+ subject.handlers[0].image_sources[0].realize(state)
95
+
96
+ state.images['original'].data.should == @test_data
97
+ end
98
+
99
+ it 'should use S3 object content type for mime type' do
100
+ subject.handlers[0].image_sources[0].realize(state)
101
+
102
+ state.images['original'].mime_type.should == 'image/jpeg'
103
+ end
104
+
105
+ it 'should provide source path and HTTPS url' do
106
+ subject.handlers[0].image_sources[0].realize(state)
107
+
108
+ state.images['original'].source_path.should == "test.jpg"
109
+ state.images['original'].source_url.should start_with "https://"
110
+ state.images['original'].source_url.should include ENV['AWS_S3_TEST_BUCKET']
111
+ state.images['original'].source_url.should include "/test.jpg"
112
+ state.images['original'].source_url.should include ENV['AWS_ACCESS_KEY_ID']
113
+ status(state.images['original'].source_url).should == 200
114
+ end
115
+
116
+ describe 'non encrypted connection mode' do
117
+ subject do
118
+ Configuration.read(<<-EOF)
119
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
120
+ path "hash" "\#{test_image}"
121
+ get {
122
+ source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
123
+ }
124
+ EOF
125
+ end
126
+
127
+ it 'should source image from S3 using path spec' do
128
+ subject.handlers[0].image_sources[0].should be_a Configuration::S3Source
129
+ subject.handlers[0].image_sources[0].realize(state)
130
+
131
+ state.images['original'].data.should == @test_data
132
+ end
133
+
134
+ it 'should provide source HTTP url' do
135
+ subject.handlers[0].image_sources[0].realize(state)
136
+ state.images['original'].source_url.should start_with "http://"
137
+ state.images['original'].source_url.should include ENV['AWS_S3_TEST_BUCKET']
138
+ state.images['original'].source_url.should include "/test.jpg"
139
+ state.images['original'].source_url.should include ENV['AWS_ACCESS_KEY_ID']
140
+ status(state.images['original'].source_url).should == 200
141
+ end
142
+ end
143
+
144
+ describe 'context locals' do
145
+ before :all do
146
+ s3_client = AWS::S3.new(use_ssl: false)
147
+ s3_test_bucket = s3_client.buckets[ENV['AWS_S3_TEST_BUCKET']]
148
+ s3_test_bucket.objects['test-image-name.jpg'].write('hello world', content_type: 'image/jpeg')
149
+ s3_test_bucket.objects["#{ENV['AWS_S3_TEST_BUCKET']}.jpg"].write('hello bucket', content_type: 'image/jpeg')
150
+ end
151
+
152
+ subject do
153
+ Configuration.read(<<-EOF)
154
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
155
+ path "imagename" "\#{imagename}.jpg"
156
+ path "bucket" "\#{bucket}.jpg"
157
+ get {
158
+ source_s3 "test-image-name" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="imagename"
159
+ source_s3 "bucket" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="bucket"
160
+ }
161
+ EOF
162
+ end
163
+
164
+ it 'should provide image name to be used as #{imagename}' do
165
+ subject.handlers[0].image_sources[0].realize(state)
166
+ state.images['test-image-name'].source_path.should == 'test-image-name.jpg'
167
+ state.images['test-image-name'].data.should == 'hello world'
168
+ end
169
+
170
+ it 'should provide bucket to be used as #{bucket}' do
171
+ subject.handlers[0].image_sources[1].realize(state)
172
+ state.images['bucket'].source_path.should == "#{ENV['AWS_S3_TEST_BUCKET']}.jpg"
173
+ state.images['bucket'].data.should == 'hello bucket'
174
+ end
175
+ end
176
+
177
+ describe 'error handling' do
178
+ it 'should raise NoValueError on missing image name' do
179
+ expect {
180
+ Configuration.read(<<-EOF)
181
+ get "test" {
182
+ source_s3 bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
183
+ }
184
+ EOF
185
+ }.to raise_error Configuration::NoValueError, %{syntax error while parsing 'source_s3 bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"': expected image name}
186
+ end
187
+
188
+ it 'should raise NoAttributeError on missing bucket name' do
189
+ expect {
190
+ Configuration.read(<<-EOF)
191
+ get "test" {
192
+ source_s3 "original" path="hash"
193
+ }
194
+ EOF
195
+ }.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 'source_s3 "original" path="hash"': expected 'bucket' attribute to be set}
196
+ end
197
+
198
+ it 'should raise NoAttributeError on missing path' do
199
+ expect {
200
+ Configuration.read(<<-EOF)
201
+ get "test" {
202
+ source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}"
203
+ }
204
+ EOF
205
+ }.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 'source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}"': expected 'path' attribute to be set}
206
+ end
207
+
208
+ it 'should raise S3NotConfiguredError if used but no s3 statement was used' do
209
+ subject = Configuration.read(<<-'EOF')
210
+ path "hash" "#{test_image}"
211
+ get "test" {
212
+ source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
213
+ }
214
+ EOF
215
+ expect {
216
+ subject.handlers[0].image_sources[0].realize(state)
217
+ }.to raise_error Configuration::S3NotConfiguredError, 'S3 client not configured'
218
+ end
219
+
220
+ it 'should raise S3NoSuchBucketError if bucket was not found on S3' do
221
+ subject = Configuration.read(<<-EOF)
222
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
223
+ path "hash" "\#{test_image}"
224
+ get "test" {
225
+ source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}X" path="hash"
226
+ }
227
+ EOF
228
+ expect {
229
+ subject.handlers[0].image_sources[0].realize(state)
230
+ }.to raise_error Configuration::S3NoSuchBucketError, %{S3 bucket '#{ENV['AWS_S3_TEST_BUCKET']}X' does not exist}
231
+ end
232
+
233
+ it 'should raise S3NoSuchKeyError if object was not found on S3' do
234
+ subject = Configuration.read(<<-EOF)
235
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
236
+ path "hash" "blah"
237
+ get "test" {
238
+ source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
239
+ }
240
+ EOF
241
+ expect {
242
+ subject.handlers[0].image_sources[0].realize(state)
243
+ }.to raise_error Configuration::S3NoSuchKeyError, %{S3 bucket '#{ENV['AWS_S3_TEST_BUCKET']}' does not contain key 'blah'}
244
+ end
245
+
246
+ it 'should raise S3AccessDenied if bucket was not found on S3' do
247
+ subject = Configuration.read(<<-EOF)
248
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
249
+ path "hash" "\#{test_image}"
250
+ get "test" {
251
+ source_s3 "original" bucket="blah" path="hash"
252
+ }
253
+ EOF
254
+ expect {
255
+ subject.handlers[0].image_sources[0].realize(state)
256
+ }.to raise_error Configuration::S3AccessDenied, %{access to S3 bucket 'blah' or key 'test.jpg' was denied}
257
+ end
258
+ end
259
+
260
+ describe 'memory limit' do
261
+ let :state do
262
+ Configuration::RequestState.new('abc', {test_image: 'test.jpg'}, MemoryLimit.new(10))
263
+ end
264
+
265
+ it 'should raise MemoryLimit::MemoryLimitedExceededError when sourcing bigger image than limit' do
266
+ expect {
267
+ subject.handlers[0].image_sources[0].realize(state)
268
+ }.to raise_error MemoryLimit::MemoryLimitedExceededError
269
+ end
270
+ end
271
+ end
272
+
273
+ describe Configuration::S3Store do
274
+ let :state do
275
+ Configuration::RequestState.new(@test_data, test_image: 'test_out.jpg')
276
+ end
277
+
278
+ subject do
279
+ Configuration.read(<<-EOF)
280
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
281
+ path "hash" "\#{test_image}"
282
+ post {
283
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
284
+ }
285
+ EOF
286
+ end
287
+
288
+ before :all do
289
+ @test_data = (support_dir + 'compute.jpg').read.force_encoding('ASCII-8BIT')
290
+
291
+ s3_client = AWS::S3.new(use_ssl: false)
292
+ s3_test_bucket = s3_client.buckets[ENV['AWS_S3_TEST_BUCKET']]
293
+ @test_object = s3_test_bucket.objects['test_out.jpg']
294
+ @test_object.delete
295
+ end
296
+
297
+ before :each do
298
+ subject.handlers[0].image_sources[0].realize(state)
299
+ end
300
+
301
+ it 'should source image from S3 using path spec' do
302
+ subject.handlers[0].stores[0].should be_a Configuration::S3Store
303
+ subject.handlers[0].stores[0].realize(state)
304
+
305
+ @test_object.read.should == @test_data
306
+ end
307
+
308
+ it 'should use image mime type as S3 object content type' do
309
+ state.images['input'].mime_type = 'image/jpeg'
310
+ subject.handlers[0].stores[0].realize(state)
311
+
312
+ @test_object.head[:content_type].should == 'image/jpeg'
313
+ end
314
+
315
+ it 'should provide source path and HTTPS url' do
316
+ subject.handlers[0].stores[0].realize(state)
317
+
318
+ state.images['input'].store_path.should == "test_out.jpg"
319
+ state.images['input'].store_url.should start_with "https://"
320
+ state.images['input'].store_url.should include ENV['AWS_S3_TEST_BUCKET']
321
+ state.images['input'].store_url.should include "/test_out.jpg"
322
+ state.images['input'].store_url.should include ENV['AWS_ACCESS_KEY_ID']
323
+ status(state.images['input'].store_url).should == 200
324
+ end
325
+
326
+ describe 'non encrypted connection mode' do
327
+ subject do
328
+ Configuration.read(<<-EOF)
329
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
330
+ path "hash" "\#{test_image}"
331
+ post {
332
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
333
+ }
334
+ EOF
335
+ end
336
+
337
+ it 'should source image from S3 using path spec' do
338
+ subject.handlers[0].stores[0].should be_a Configuration::S3Store
339
+ subject.handlers[0].stores[0].realize(state)
340
+
341
+ @test_object.read.should == @test_data
342
+ end
343
+
344
+ it 'should provide source HTTP url' do
345
+ subject.handlers[0].stores[0].realize(state)
346
+
347
+ state.images['input'].store_url.should start_with "http://"
348
+ state.images['input'].store_url.should include ENV['AWS_S3_TEST_BUCKET']
349
+ state.images['input'].store_url.should include "/test_out.jpg"
350
+ state.images['input'].store_url.should include ENV['AWS_ACCESS_KEY_ID']
351
+ status(state.images['input'].store_url).should == 200
352
+ end
353
+ end
354
+
355
+ describe 'permission control' do
356
+ it 'should store images that are not accessible by public by default' do
357
+ subject.handlers[0].stores[0].realize(state)
358
+ status(state.images['input'].store_url[/^[^\?]*/]).should == 403
359
+ end
360
+
361
+ describe 'public' do
362
+ subject do
363
+ Configuration.read(<<-EOF)
364
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
365
+ path "hash" "\#{test_image}"
366
+ post {
367
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" public=true
368
+ }
369
+ EOF
370
+ end
371
+
372
+ it 'should store image accessible for public' do
373
+ subject.handlers[0].stores[0].realize(state)
374
+
375
+ get(state.images['input'].store_url).should == @test_data
376
+ end
377
+
378
+ it 'should provide public source HTTPS url' do
379
+ subject.handlers[0].stores[0].realize(state)
380
+
381
+ state.images['input'].store_url.should start_with "https://"
382
+ state.images['input'].store_url.should include ENV['AWS_S3_TEST_BUCKET']
383
+ state.images['input'].store_url.should include "/test_out.jpg"
384
+ state.images['input'].store_url.should_not include ENV['AWS_ACCESS_KEY_ID']
385
+ status(state.images['input'].store_url).should == 200
386
+ end
387
+
388
+ describe 'non encrypted connection mode' do
389
+ subject do
390
+ Configuration.read(<<-EOF)
391
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
392
+ path "hash" "\#{test_image}"
393
+ post {
394
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" public=true
395
+ }
396
+ EOF
397
+ end
398
+
399
+ it 'should provide public source HTTP url' do
400
+ subject.handlers[0].stores[0].realize(state)
401
+
402
+ state.images['input'].store_url.should start_with "http://"
403
+ state.images['input'].store_url.should include ENV['AWS_S3_TEST_BUCKET']
404
+ state.images['input'].store_url.should include "/test_out.jpg"
405
+ state.images['input'].store_url.should_not include ENV['AWS_ACCESS_KEY_ID']
406
+ status(state.images['input'].store_url).should == 200
407
+ end
408
+ end
409
+ end
410
+ end
411
+
412
+ describe 'cache control' do
413
+ it 'should have no cache control set by default' do
414
+ subject.handlers[0].stores[0].realize(state)
415
+ headers(state.images['input'].store_url)["Cache-Control"].should be_nil
416
+ end
417
+
418
+ describe 'set' do
419
+ subject do
420
+ Configuration.read(<<-EOF)
421
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
422
+ path "hash" "\#{test_image}"
423
+ post {
424
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" public=true cache-control="public, max-age=3600"
425
+ }
426
+ EOF
427
+ end
428
+
429
+ it 'should have given cahce control header set on the object' do
430
+ subject.handlers[0].stores[0].realize(state)
431
+ headers(state.images['input'].store_url)["Cache-Control"].should == 'public, max-age=3600'
432
+ end
433
+ end
434
+ end
435
+
436
+ describe 'conditional inclusion support' do
437
+ let :state do
438
+ Configuration::RequestState.new(@test_data, test_image: 'test_out.jpg', list: 'input,input2')
439
+ end
440
+
441
+ subject do
442
+ Configuration.read(<<-EOF)
443
+ post {
444
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" if-image-name-on="\#{list}"
445
+ store_s3 "input1" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" if-image-name-on="\#{list}"
446
+ store_s3 "input2" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" if-image-name-on="\#{list}"
447
+ }
448
+ EOF
449
+ end
450
+
451
+ it 'should mark sores to be included when image name match if-image-name-on list' do
452
+ subject.handlers[0].stores[0].excluded?(state).should be_false
453
+ subject.handlers[0].stores[1].excluded?(state).should be_true
454
+ subject.handlers[0].stores[2].excluded?(state).should be_false
455
+ end
456
+ end
457
+
458
+ describe 'context locals' do
459
+ subject do
460
+ Configuration.read(<<-EOF)
461
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
462
+ path "imagename" "\#{imagename}"
463
+ path "bucket" "\#{bucket}"
464
+ path "mimeextension" "\#{mimeextension}"
465
+ post {
466
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="imagename"
467
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="bucket"
468
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="mimeextension"
469
+ }
470
+ EOF
471
+ end
472
+
473
+ it 'should provide image name to be used as #{imagename}' do
474
+ subject.handlers[0].stores[0].realize(state)
475
+
476
+ state.images['input'].store_path.should == 'input'
477
+ end
478
+
479
+ it 'should provide bucket to be used as #{bucket}' do
480
+ subject.handlers[0].stores[1].realize(state)
481
+
482
+ state.images['input'].store_path.should == ENV['AWS_S3_TEST_BUCKET']
483
+ end
484
+
485
+ it 'should provide image mime type based file extension to be used as #{mimeextension}' do
486
+ state.images['input'].mime_type = 'image/jpeg'
487
+ subject.handlers[0].stores[2].realize(state)
488
+
489
+ state.images['input'].store_path.should == 'jpg'
490
+ end
491
+
492
+ it 'should raise NoValueForPathTemplatePlaceholerError if there is on mime type for image defined and path contains #{mimeextension}' do
493
+ expect {
494
+ subject.handlers[0].stores[2].realize(state)
495
+ }.to raise_error Configuration::NoValueForPathTemplatePlaceholerError, %q{cannot generate path 'mimeextension' from template '#{mimeextension}': no value for '#{mimeextension}'}
496
+ end
497
+ end
498
+
499
+ describe 'error handling' do
500
+ it 'should raise NoValueError on missing image name' do
501
+ expect {
502
+ Configuration.read(<<-EOF)
503
+ post "test" {
504
+ store_s3 bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
505
+ }
506
+ EOF
507
+ }.to raise_error Configuration::NoValueError, %{syntax error while parsing 'store_s3 bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"': expected image name}
508
+ end
509
+
510
+ it 'should raise NoAttributeError on missing bucket name' do
511
+ expect {
512
+ Configuration.read(<<-EOF)
513
+ post "test" {
514
+ store_s3 "input" path="hash"
515
+ }
516
+ EOF
517
+ }.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 'store_s3 "input" path="hash"': expected 'bucket' attribute to be set}
518
+ end
519
+
520
+ it 'should raise NoAttributeError on missing path' do
521
+ expect {
522
+ Configuration.read(<<-EOF)
523
+ post "test" {
524
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}"
525
+ }
526
+ EOF
527
+ }.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 'store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}"': expected 'path' attribute to be set}
528
+ end
529
+
530
+ it 'should raise S3NotConfiguredError if used but no s3 statement was used' do
531
+ subject = Configuration.read(<<-EOF)
532
+ path "hash" "\#{test_image}"
533
+ post "test" {
534
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
535
+ }
536
+ EOF
537
+ expect {
538
+ subject.handlers[0].stores[0].realize(state)
539
+ }.to raise_error Configuration::S3NotConfiguredError, 'S3 client not configured'
540
+ end
541
+
542
+ it 'should raise S3NoSuchBucketError if bucket was not found on S3' do
543
+ subject = Configuration.read(<<-EOF)
544
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
545
+ path "hash" "\#{test_image}"
546
+ post "test" {
547
+ store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}X" path="hash"
548
+ }
549
+ EOF
550
+ expect {
551
+ subject.handlers[0].stores[0].realize(state)
552
+ }.to raise_error Configuration::S3NoSuchBucketError, %{S3 bucket '#{ENV['AWS_S3_TEST_BUCKET']}X' does not exist}
553
+ end
554
+
555
+ it 'should raise S3AccessDenied if bucket was not found on S3' do
556
+ subject = Configuration.read(<<-EOF)
557
+ s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
558
+ path "hash" "\#{test_image}"
559
+ post "test" {
560
+ store_s3 "input" bucket="blah" path="hash"
561
+ }
562
+ EOF
563
+ expect {
564
+ subject.handlers[0].stores[0].realize(state)
565
+ }.to raise_error Configuration::S3AccessDenied, %{access to S3 bucket 'blah' or key 'test_out.jpg' was denied}
566
+ end
567
+ end
568
+ end
569
+ end
570
+ end
571
+