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.
- data/Gemfile +10 -12
- data/Gemfile.lock +57 -55
- data/README.md +829 -0
- data/VERSION +1 -1
- data/bin/httpimagestore +114 -180
- data/features/cache-control.feature +26 -90
- data/features/compatibility.feature +129 -0
- data/features/error-reporting.feature +207 -0
- data/features/health-check.feature +30 -0
- data/features/s3-store-and-thumbnail.feature +65 -0
- data/features/step_definitions/httpimagestore_steps.rb +66 -26
- data/features/support/env.rb +32 -5
- data/features/support/test.empty +0 -0
- data/httpimagestore.gemspec +60 -47
- data/lib/httpimagestore/aws_sdk_regions_hack.rb +23 -0
- data/lib/httpimagestore/configuration/file.rb +120 -0
- data/lib/httpimagestore/configuration/handler.rb +239 -0
- data/lib/httpimagestore/configuration/output.rb +119 -0
- data/lib/httpimagestore/configuration/path.rb +77 -0
- data/lib/httpimagestore/configuration/s3.rb +194 -0
- data/lib/httpimagestore/configuration/thumbnailer.rb +244 -0
- data/lib/httpimagestore/configuration.rb +126 -29
- data/lib/httpimagestore/error_reporter.rb +36 -0
- data/lib/httpimagestore/ruby_string_template.rb +26 -0
- data/load_test/load_test.1k.23a022f6e.m1.small-comp.csv +3 -0
- data/load_test/load_test.1k.ec9bde794.m1.small.csv +4 -0
- data/load_test/load_test.jmx +344 -0
- data/load_test/thumbnail_specs.csv +11 -0
- data/spec/configuration_file_spec.rb +309 -0
- data/spec/configuration_handler_spec.rb +124 -0
- data/spec/configuration_output_spec.rb +338 -0
- data/spec/configuration_path_spec.rb +92 -0
- data/spec/configuration_s3_spec.rb +571 -0
- data/spec/configuration_spec.rb +80 -105
- data/spec/configuration_thumbnailer_spec.rb +417 -0
- data/spec/ruby_string_template_spec.rb +43 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/support/compute.jpg +0 -0
- data/spec/support/cuba_response_env.rb +40 -0
- data/spec/support/full.cfg +49 -0
- metadata +138 -84
- data/README.rdoc +0 -23
- data/features/httpimagestore.feature +0 -167
- data/lib/httpimagestore/image_path.rb +0 -54
- data/lib/httpimagestore/s3_service.rb +0 -37
- data/lib/httpimagestore/thumbnail_class.rb +0 -13
- data/spec/image_path_spec.rb +0 -72
- 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
|
+
|