httpimagestore 0.5.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+