httpimagestore 1.2.0 → 1.3.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 +5 -2
- data/Gemfile.lock +24 -14
- data/README.md +161 -105
- data/bin/httpimagestore +6 -2
- data/features/cache-control.feature +2 -2
- data/features/compatibility.feature +4 -4
- data/features/error-reporting.feature +49 -3
- data/features/flexi.feature +260 -0
- data/features/s3-store-and-thumbnail.feature +1 -1
- data/features/step_definitions/httpimagestore_steps.rb +24 -0
- data/features/storage.feature +199 -0
- data/features/support/test-large.jpg +0 -0
- data/features/support/test.png +0 -0
- data/httpimagestore.gemspec +13 -9
- data/lib/httpimagestore/configuration/file.rb +1 -1
- data/lib/httpimagestore/configuration/handler.rb +114 -20
- data/lib/httpimagestore/configuration/identify.rb +56 -0
- data/lib/httpimagestore/configuration/path.rb +7 -28
- data/lib/httpimagestore/configuration/s3.rb +230 -12
- data/lib/httpimagestore/configuration/thumbnailer.rb +17 -8
- data/lib/httpimagestore/error_reporter.rb +1 -11
- data/load_test/load_test.jmx +11 -11
- data/load_test/thumbnail_specs.csv +11 -11
- data/spec/configuration_file_spec.rb +20 -20
- data/spec/configuration_handler_spec.rb +156 -6
- data/spec/configuration_identify_spec.rb +45 -0
- data/spec/configuration_output_spec.rb +15 -15
- data/spec/configuration_path_spec.rb +70 -23
- data/spec/configuration_s3_spec.rb +187 -28
- data/spec/configuration_thumbnailer_spec.rb +22 -22
- data/spec/spec_helper.rb +11 -2
- data/spec/support/full.cfg +4 -4
- metadata +12 -8
- data/features/facebook.feature +0 -149
@@ -2,26 +2,28 @@ require_relative 'spec_helper'
|
|
2
2
|
require 'httpimagestore/configuration'
|
3
3
|
Configuration::Scope.logger = Logger.new('/dev/null')
|
4
4
|
|
5
|
+
require 'httpimagestore/configuration/handler'
|
5
6
|
require 'httpimagestore/configuration/path'
|
7
|
+
MemoryLimit.logger = Logger.new('/dev/null')
|
6
8
|
|
7
9
|
describe Configuration do
|
8
10
|
describe 'path rendering' do
|
9
11
|
it 'should load paths form single line and multi line declarations and render spec templates' do
|
10
12
|
subject = Configuration.read(<<-'EOF')
|
11
13
|
path "uri" "#{path}"
|
12
|
-
path "hash" "#{
|
14
|
+
path "hash" "#{input_digest}.#{extension}"
|
13
15
|
path {
|
14
|
-
"hash-name" "#{
|
15
|
-
"structured" "#{dirname}/#{
|
16
|
-
"structured-name" "#{dirname}/#{
|
16
|
+
"hash-name" "#{input_digest}/#{image_name}.#{extension}"
|
17
|
+
"structured" "#{dirname}/#{input_digest}/#{basename}.#{extension}"
|
18
|
+
"structured-name" "#{dirname}/#{input_digest}/#{basename}-#{image_name}.#{extension}"
|
17
19
|
}
|
18
20
|
EOF
|
19
21
|
|
20
22
|
subject.paths['uri'].render(path: 'test/abc.jpg').should == 'test/abc.jpg'
|
21
|
-
subject.paths['hash'].render(
|
22
|
-
subject.paths['hash-name'].render(
|
23
|
-
subject.paths['structured'].render(
|
24
|
-
subject.paths['structured-name'].render(
|
23
|
+
subject.paths['hash'].render(input_digest: '2cf24dba5fb0a30e', extension: 'jpg').should == '2cf24dba5fb0a30e.jpg'
|
24
|
+
subject.paths['hash-name'].render(input_digest: '2cf24dba5fb0a30e', image_name: 'xbrna', extension: 'jpg').should == '2cf24dba5fb0a30e/xbrna.jpg'
|
25
|
+
subject.paths['structured'].render(dirname: 'test', input_digest: '2cf24dba5fb0a30e', basename: 'abc', extension: 'jpg').should == 'test/2cf24dba5fb0a30e/abc.jpg'
|
26
|
+
subject.paths['structured-name'].render(dirname: 'test', input_digest: '2cf24dba5fb0a30e', basename: 'abc', extension: 'jpg', image_name: 'xbrna').should == 'test/2cf24dba5fb0a30e/abc-xbrna.jpg'
|
25
27
|
end
|
26
28
|
|
27
29
|
describe 'error handling' do
|
@@ -62,29 +64,74 @@ describe Configuration do
|
|
62
64
|
subject.paths['test'].render
|
63
65
|
}.to raise_error Configuration::NoValueForPathTemplatePlaceholerError, %q{cannot generate path 'test' from template '#{abc}#{xyz}': no value for '#{abc}'}
|
64
66
|
end
|
67
|
+
end
|
65
68
|
|
66
|
-
|
67
|
-
|
69
|
+
describe 'rendering from RequestState' do
|
70
|
+
let :state do
|
71
|
+
Configuration::RequestState.new(
|
72
|
+
'test',
|
73
|
+
{operation: 'pad'},
|
74
|
+
'test/abc.jpg',
|
75
|
+
{width: '123', height: '321'}
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
subject do
|
80
|
+
Configuration.read(<<-'EOF')
|
81
|
+
path "uri" "#{path}"
|
82
|
+
path "hash" "#{input_digest}.#{extension}"
|
68
83
|
path {
|
69
|
-
"
|
84
|
+
"hash-name" "#{input_digest}/#{image_name}.#{extension}"
|
85
|
+
"structured" "#{dirname}/#{input_digest}/#{basename}.#{extension}"
|
86
|
+
"structured-name" "#{dirname}/#{input_digest}/#{basename}-#{image_name}.#{extension}"
|
70
87
|
}
|
88
|
+
path "name" "#{image_name}"
|
89
|
+
path "base" "#{basename}"
|
71
90
|
EOF
|
91
|
+
end
|
72
92
|
|
73
|
-
|
74
|
-
|
75
|
-
|
93
|
+
it 'should render path using meta variables and locals' do
|
94
|
+
|
95
|
+
subject.paths['uri'].render(state).should == 'test/abc.jpg'
|
96
|
+
subject.paths['hash'].render(state).should == '9f86d081884c7d65.jpg'
|
97
|
+
subject.paths['hash-name'].render(state.with_locals(image_name: 'xbrna')).should == '9f86d081884c7d65/xbrna.jpg'
|
98
|
+
subject.paths['structured'].render(state).should == 'test/9f86d081884c7d65/abc.jpg'
|
99
|
+
subject.paths['structured-name'].render(state.with_locals(image_name: 'xbrna')).should == 'test/9f86d081884c7d65/abc-xbrna.jpg'
|
76
100
|
end
|
77
101
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
102
|
+
describe 'error handling' do
|
103
|
+
let :state do
|
104
|
+
Configuration::RequestState.new(
|
105
|
+
'',
|
106
|
+
{operation: 'pad'},
|
107
|
+
'test/abc.jpg',
|
108
|
+
{width: '123', height: '321'}
|
109
|
+
)
|
110
|
+
end
|
84
111
|
|
85
|
-
|
86
|
-
|
87
|
-
|
112
|
+
it 'should raise PathRenderingError if body was expected but not provided' do
|
113
|
+
expect {
|
114
|
+
subject.paths['hash'].render(state)
|
115
|
+
}.to raise_error Configuration::PathRenderingError, %q{cannot generate path 'hash' from template '#{input_digest}.#{extension}': need not empty request body to generate value for 'input_digest'}
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should raise PathRenderingError if variable not defined' do
|
119
|
+
expect {
|
120
|
+
subject.paths['name'].render(state)
|
121
|
+
}.to raise_error Configuration::PathRenderingError, %q{cannot generate path 'name' from template '#{image_name}': variable 'image_name' not defined}
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should raise PathRenderingError if meta variable dependent variable not defined' do
|
125
|
+
state = Configuration::RequestState.new(
|
126
|
+
'',
|
127
|
+
{operation: 'pad'},
|
128
|
+
nil,
|
129
|
+
{width: '123', height: '321'}
|
130
|
+
)
|
131
|
+
expect {
|
132
|
+
subject.paths['base'].render(state)
|
133
|
+
}.to raise_error Configuration::PathRenderingError, %q{cannot generate path 'base' from template '#{basename}': need 'path' variable to generate value for 'basename'}
|
134
|
+
end
|
88
135
|
end
|
89
136
|
end
|
90
137
|
end
|
@@ -66,6 +66,39 @@ else
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
describe Configuration::S3SourceStoreBase::CacheRoot do
|
70
|
+
subject do
|
71
|
+
Configuration::S3SourceStoreBase::CacheRoot.new('/tmp')
|
72
|
+
end
|
73
|
+
|
74
|
+
before do
|
75
|
+
@cached_object = Pathname.new('/tmp/0d/bf/50c256d6b6efe55d11d0b6b50600')
|
76
|
+
@cached_object.dirname.mkpath
|
77
|
+
@cached_object.open('w') do |io|
|
78
|
+
io.write 'abc'
|
79
|
+
end
|
80
|
+
|
81
|
+
test2 = Pathname.new('/tmp/46/b9/7a454d831d7570abbb833330d9fb')
|
82
|
+
test2.unlink if test2.exist?
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should build cache file location for storage location from bucket and key' do
|
86
|
+
subject.cache_file('mybucket', 'hello/world.jpg').should == "0d/bf/50c256d6b6efe55d11d0b6b50600"
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should look up object stored on disk by bucket and key' do
|
90
|
+
subject.open('mybucket', 'hello/world.jpg') do |io|
|
91
|
+
io.read.should == 'abc'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should create cache object for bucket and key if it does not exist' do
|
96
|
+
subject.open('mybucket', 'hello/world2.jpg') do |io|
|
97
|
+
io.read.should == ''
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
69
102
|
describe Configuration::S3Source do
|
70
103
|
let :state do
|
71
104
|
Configuration::RequestState.new('abc', {test_image: 'test.jpg'})
|
@@ -91,20 +124,20 @@ else
|
|
91
124
|
end
|
92
125
|
|
93
126
|
it 'should source image from S3 using path spec' do
|
94
|
-
subject.handlers[0].
|
95
|
-
subject.handlers[0].
|
127
|
+
subject.handlers[0].sources[0].should be_a Configuration::S3Source
|
128
|
+
subject.handlers[0].sources[0].realize(state)
|
96
129
|
|
97
130
|
state.images['original'].data.should == @test_data
|
98
131
|
end
|
99
132
|
|
100
133
|
it 'should use S3 object content type for mime type' do
|
101
|
-
subject.handlers[0].
|
134
|
+
subject.handlers[0].sources[0].realize(state)
|
102
135
|
|
103
136
|
state.images['original'].mime_type.should == 'image/jpeg'
|
104
137
|
end
|
105
138
|
|
106
139
|
it 'should provide source path and HTTPS url' do
|
107
|
-
subject.handlers[0].
|
140
|
+
subject.handlers[0].sources[0].realize(state)
|
108
141
|
|
109
142
|
state.images['original'].source_path.should == "test.jpg"
|
110
143
|
state.images['original'].source_url.should start_with "https://"
|
@@ -126,7 +159,7 @@ else
|
|
126
159
|
end
|
127
160
|
|
128
161
|
it 'should still provide valid HTTPS URL incliding prefix' do
|
129
|
-
subject.handlers[0].
|
162
|
+
subject.handlers[0].sources[0].realize(state)
|
130
163
|
|
131
164
|
state.images['original'].source_url.should start_with "https://"
|
132
165
|
state.images['original'].source_url.should include ENV['AWS_S3_TEST_BUCKET']
|
@@ -136,12 +169,138 @@ else
|
|
136
169
|
end
|
137
170
|
|
138
171
|
it 'should provide source path without prefix' do
|
139
|
-
subject.handlers[0].
|
172
|
+
subject.handlers[0].sources[0].realize(state)
|
140
173
|
|
141
174
|
state.images['original'].source_path.should == "test.jpg"
|
142
175
|
end
|
143
176
|
end
|
144
177
|
|
178
|
+
describe 'object cache' do
|
179
|
+
let :state do
|
180
|
+
Configuration::RequestState.new('abc', {test_image: 'test/ghost.jpg'})
|
181
|
+
end
|
182
|
+
|
183
|
+
subject do
|
184
|
+
Configuration.read(<<-EOF)
|
185
|
+
s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
|
186
|
+
path "hash" "\#{test_image}"
|
187
|
+
get {
|
188
|
+
source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" cache-root="/tmp"
|
189
|
+
source_s3 "original_cached" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" cache-root="/tmp"
|
190
|
+
source_s3 "original_cached_public" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" cache-root="/tmp" public="true"
|
191
|
+
source_s3 "original_cached_public2" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" cache-root="/tmp" public="true"
|
192
|
+
}
|
193
|
+
EOF
|
194
|
+
end
|
195
|
+
|
196
|
+
before do
|
197
|
+
@cached_object = Pathname.new('/tmp/ce/26/b196585e28aa99f55b1260b629e2')
|
198
|
+
@cached_object.dirname.mkpath
|
199
|
+
@cached_object.open('w') do |io|
|
200
|
+
header = MessagePack.pack(
|
201
|
+
'private_url' => 'https://s3-eu-west-1.amazonaws.com/test/ghost.jpg?' + ENV['AWS_ACCESS_KEY_ID'],
|
202
|
+
'public_url' => 'https://s3-eu-west-1.amazonaws.com/test/ghost.jpg',
|
203
|
+
'content_type' => 'image/jpeg'
|
204
|
+
)
|
205
|
+
io.write [header.length].pack('L') # header length
|
206
|
+
io.write header
|
207
|
+
io.write 'abc'
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should use cache when configured' do
|
212
|
+
subject.handlers[0].sources[0].should be_a Configuration::S3Source
|
213
|
+
subject.handlers[0].sources[0].realize(state)
|
214
|
+
|
215
|
+
state.images['original'].data.should == 'abc'
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should keep mime type' do
|
219
|
+
subject.handlers[0].sources[0].realize(state)
|
220
|
+
|
221
|
+
state.images['original'].mime_type.should == 'image/jpeg'
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should keep private source URL' do
|
225
|
+
subject.handlers[0].sources[0].realize(state)
|
226
|
+
|
227
|
+
state.images['original'].source_url.should == 'https://s3-eu-west-1.amazonaws.com/test/ghost.jpg?' + ENV['AWS_ACCESS_KEY_ID']
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should keep public source URL' do
|
231
|
+
subject = Configuration.read(<<-EOF)
|
232
|
+
s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
|
233
|
+
path "hash" "\#{test_image}"
|
234
|
+
get {
|
235
|
+
source_s3 "original" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash" cache-root="/tmp" public=true
|
236
|
+
}
|
237
|
+
EOF
|
238
|
+
subject.handlers[0].sources[0].realize(state)
|
239
|
+
|
240
|
+
state.images['original'].source_url.should == 'https://s3-eu-west-1.amazonaws.com/test/ghost.jpg'
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'shluld use object stored in S3 when not found in the cache' do
|
244
|
+
cache_file = Pathname.new('/tmp/af/a3/5eaf0a614693e2d1ed455ac1cedb')
|
245
|
+
cache_file.unlink if cache_file.exist?
|
246
|
+
|
247
|
+
state = Configuration::RequestState.new('abc', {test_image: 'test.jpg'})
|
248
|
+
subject.handlers[0].sources[0].realize(state)
|
249
|
+
|
250
|
+
cache_file.should exist
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'should use cached object writen when it was initialy read' do
|
254
|
+
cache_file = Pathname.new('/tmp/af/a3/5eaf0a614693e2d1ed455ac1cedb')
|
255
|
+
cache_file.unlink if cache_file.exist?
|
256
|
+
|
257
|
+
state = Configuration::RequestState.new('abc', {test_image: 'test.jpg'})
|
258
|
+
subject.handlers[0].sources[0].realize(state)
|
259
|
+
|
260
|
+
cache_file.should exist
|
261
|
+
|
262
|
+
subject.handlers[0].sources[1].realize(state)
|
263
|
+
|
264
|
+
state.images['original'].data.should == @test_data
|
265
|
+
state.images['original'].mime_type.should == 'image/jpeg'
|
266
|
+
|
267
|
+
state.images['original_cached'].data.should == @test_data
|
268
|
+
state.images['original_cached'].mime_type.should == 'image/jpeg'
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'should use update cached object when new properties are read from S3' do
|
272
|
+
cache_file = Pathname.new('/tmp/af/a3/5eaf0a614693e2d1ed455ac1cedb')
|
273
|
+
cache_file.unlink if cache_file.exist?
|
274
|
+
|
275
|
+
state = Configuration::RequestState.new('abc', {test_image: 'test.jpg'})
|
276
|
+
|
277
|
+
## cache with private URL
|
278
|
+
subject.handlers[0].sources[0].realize(state)
|
279
|
+
|
280
|
+
cache_file.should exist
|
281
|
+
sum = Digest::SHA2.new.update(cache_file.read).to_s
|
282
|
+
|
283
|
+
## read from cache with private URL
|
284
|
+
subject.handlers[0].sources[1].realize(state)
|
285
|
+
|
286
|
+
# no change
|
287
|
+
Digest::SHA2.new.update(cache_file.read).to_s.should == sum
|
288
|
+
|
289
|
+
## read from cache; add public URL
|
290
|
+
subject.handlers[0].sources[2].realize(state)
|
291
|
+
|
292
|
+
# should get updated
|
293
|
+
Digest::SHA2.new.update(cache_file.read).to_s.should_not == sum
|
294
|
+
|
295
|
+
sum = Digest::SHA2.new.update(cache_file.read).to_s
|
296
|
+
## read from cahce
|
297
|
+
subject.handlers[0].sources[3].realize(state)
|
298
|
+
|
299
|
+
# no change
|
300
|
+
Digest::SHA2.new.update(cache_file.read).to_s.should == sum
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
145
304
|
describe 'non encrypted connection mode' do
|
146
305
|
subject do
|
147
306
|
Configuration.read(<<-EOF)
|
@@ -154,14 +313,14 @@ else
|
|
154
313
|
end
|
155
314
|
|
156
315
|
it 'should source image from S3 using path spec' do
|
157
|
-
subject.handlers[0].
|
158
|
-
subject.handlers[0].
|
316
|
+
subject.handlers[0].sources[0].should be_a Configuration::S3Source
|
317
|
+
subject.handlers[0].sources[0].realize(state)
|
159
318
|
|
160
319
|
state.images['original'].data.should == @test_data
|
161
320
|
end
|
162
321
|
|
163
322
|
it 'should provide source HTTP url' do
|
164
|
-
subject.handlers[0].
|
323
|
+
subject.handlers[0].sources[0].realize(state)
|
165
324
|
state.images['original'].source_url.should start_with "http://"
|
166
325
|
state.images['original'].source_url.should include ENV['AWS_S3_TEST_BUCKET']
|
167
326
|
state.images['original'].source_url.should include "/test.jpg"
|
@@ -181,23 +340,23 @@ else
|
|
181
340
|
subject do
|
182
341
|
Configuration.read(<<-EOF)
|
183
342
|
s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
|
184
|
-
path "
|
343
|
+
path "image_name" "\#{image_name}.jpg"
|
185
344
|
path "bucket" "\#{bucket}.jpg"
|
186
345
|
get {
|
187
|
-
source_s3 "test-image-name" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="
|
346
|
+
source_s3 "test-image-name" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="image_name"
|
188
347
|
source_s3 "bucket" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="bucket"
|
189
348
|
}
|
190
349
|
EOF
|
191
350
|
end
|
192
351
|
|
193
|
-
it 'should provide image name to be used as #{
|
194
|
-
subject.handlers[0].
|
352
|
+
it 'should provide image name to be used as #{image_name}' do
|
353
|
+
subject.handlers[0].sources[0].realize(state)
|
195
354
|
state.images['test-image-name'].source_path.should == 'test-image-name.jpg'
|
196
355
|
state.images['test-image-name'].data.should == 'hello world'
|
197
356
|
end
|
198
357
|
|
199
358
|
it 'should provide bucket to be used as #{bucket}' do
|
200
|
-
subject.handlers[0].
|
359
|
+
subject.handlers[0].sources[1].realize(state)
|
201
360
|
state.images['bucket'].source_path.should == "#{ENV['AWS_S3_TEST_BUCKET']}.jpg"
|
202
361
|
state.images['bucket'].data.should == 'hello bucket'
|
203
362
|
end
|
@@ -242,7 +401,7 @@ else
|
|
242
401
|
}
|
243
402
|
EOF
|
244
403
|
expect {
|
245
|
-
subject.handlers[0].
|
404
|
+
subject.handlers[0].sources[0].realize(state)
|
246
405
|
}.to raise_error Configuration::S3NotConfiguredError, 'S3 client not configured'
|
247
406
|
end
|
248
407
|
|
@@ -255,7 +414,7 @@ else
|
|
255
414
|
}
|
256
415
|
EOF
|
257
416
|
expect {
|
258
|
-
subject.handlers[0].
|
417
|
+
subject.handlers[0].sources[0].realize(state)
|
259
418
|
}.to raise_error Configuration::S3NoSuchBucketError, %{S3 bucket '#{ENV['AWS_S3_TEST_BUCKET']}X' does not exist}
|
260
419
|
end
|
261
420
|
|
@@ -268,7 +427,7 @@ else
|
|
268
427
|
}
|
269
428
|
EOF
|
270
429
|
expect {
|
271
|
-
subject.handlers[0].
|
430
|
+
subject.handlers[0].sources[0].realize(state)
|
272
431
|
}.to raise_error Configuration::S3NoSuchKeyError, %{S3 bucket '#{ENV['AWS_S3_TEST_BUCKET']}' does not contain key 'blah'}
|
273
432
|
end
|
274
433
|
|
@@ -281,7 +440,7 @@ else
|
|
281
440
|
}
|
282
441
|
EOF
|
283
442
|
expect {
|
284
|
-
subject.handlers[0].
|
443
|
+
subject.handlers[0].sources[0].realize(state)
|
285
444
|
}.to raise_error Configuration::S3AccessDenied, %{access to S3 bucket 'blah' or key 'test.jpg' was denied}
|
286
445
|
end
|
287
446
|
end
|
@@ -293,7 +452,7 @@ else
|
|
293
452
|
|
294
453
|
it 'should raise MemoryLimit::MemoryLimitedExceededError when sourcing bigger image than limit' do
|
295
454
|
expect {
|
296
|
-
subject.handlers[0].
|
455
|
+
subject.handlers[0].sources[0].realize(state)
|
297
456
|
}.to raise_error MemoryLimit::MemoryLimitedExceededError
|
298
457
|
end
|
299
458
|
end
|
@@ -326,7 +485,7 @@ else
|
|
326
485
|
end
|
327
486
|
|
328
487
|
before :each do
|
329
|
-
subject.handlers[0].
|
488
|
+
subject.handlers[0].sources[0].realize(state)
|
330
489
|
end
|
331
490
|
|
332
491
|
it 'should source image from S3 using path spec' do
|
@@ -518,18 +677,18 @@ else
|
|
518
677
|
subject do
|
519
678
|
Configuration.read(<<-EOF)
|
520
679
|
s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}" ssl=false
|
521
|
-
path "
|
680
|
+
path "image_name" "\#{image_name}"
|
522
681
|
path "bucket" "\#{bucket}"
|
523
|
-
path "
|
682
|
+
path "image_mime_extension" "\#{image_mime_extension}"
|
524
683
|
post {
|
525
|
-
store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="
|
684
|
+
store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="image_name"
|
526
685
|
store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="bucket"
|
527
|
-
store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="
|
686
|
+
store_s3 "input" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="image_mime_extension"
|
528
687
|
}
|
529
688
|
EOF
|
530
689
|
end
|
531
690
|
|
532
|
-
it 'should provide image name to be used as #{
|
691
|
+
it 'should provide image name to be used as #{image_name}' do
|
533
692
|
subject.handlers[0].stores[0].realize(state)
|
534
693
|
|
535
694
|
state.images['input'].store_path.should == 'input'
|
@@ -541,17 +700,17 @@ else
|
|
541
700
|
state.images['input'].store_path.should == ENV['AWS_S3_TEST_BUCKET']
|
542
701
|
end
|
543
702
|
|
544
|
-
it 'should provide image mime type based file extension to be used as #{
|
703
|
+
it 'should provide image mime type based file extension to be used as #{image_mime_extension}' do
|
545
704
|
state.images['input'].mime_type = 'image/jpeg'
|
546
705
|
subject.handlers[0].stores[2].realize(state)
|
547
706
|
|
548
707
|
state.images['input'].store_path.should == 'jpg'
|
549
708
|
end
|
550
709
|
|
551
|
-
it 'should raise
|
710
|
+
it 'should raise PathRenderingError if there is on mime type for image defined and path contains #{image_mime_extension}' do
|
552
711
|
expect {
|
553
712
|
subject.handlers[0].stores[2].realize(state)
|
554
|
-
}.to raise_error Configuration::
|
713
|
+
}.to raise_error Configuration::PathRenderingError, %q{cannot generate path 'image_mime_extension' from template '#{image_mime_extension}': image 'input' does not have data for variable 'image_mime_extension'}
|
555
714
|
end
|
556
715
|
end
|
557
716
|
|
@@ -146,7 +146,7 @@ describe Configuration do
|
|
146
146
|
end
|
147
147
|
|
148
148
|
before :each do
|
149
|
-
subject.handlers[0].
|
149
|
+
subject.handlers[0].sources[0].realize(state)
|
150
150
|
end
|
151
151
|
|
152
152
|
describe 'thumbnailing to single spec' do
|
@@ -164,23 +164,23 @@ describe Configuration do
|
|
164
164
|
end
|
165
165
|
|
166
166
|
it 'should provide thumbnail data' do
|
167
|
-
subject.handlers[0].
|
167
|
+
subject.handlers[0].processors[0].realize(state)
|
168
168
|
state.images['original'].data.should_not be_nil
|
169
169
|
end
|
170
170
|
|
171
171
|
it 'should set thumbnail mime type' do
|
172
|
-
subject.handlers[0].
|
172
|
+
subject.handlers[0].processors[0].realize(state)
|
173
173
|
state.images['original'].mime_type.should == 'image/jpeg'
|
174
174
|
end
|
175
175
|
|
176
176
|
it 'should use input image source path and url' do
|
177
|
-
subject.handlers[0].
|
177
|
+
subject.handlers[0].processors[0].realize(state)
|
178
178
|
state.images['original'].source_path.should == 'test.in'
|
179
179
|
state.images['original'].source_url.should == 'file://test.in'
|
180
180
|
end
|
181
181
|
|
182
182
|
it 'should set input image mime type' do
|
183
|
-
subject.handlers[0].
|
183
|
+
subject.handlers[0].processors[0].realize(state)
|
184
184
|
state.images['input'].mime_type.should == 'image/jpeg'
|
185
185
|
end
|
186
186
|
|
@@ -202,7 +202,7 @@ describe Configuration do
|
|
202
202
|
|
203
203
|
it 'should raise MemoryLimit::MemoryLimitedExceededError when limit is exceeded' do
|
204
204
|
expect {
|
205
|
-
subject.handlers[0].
|
205
|
+
subject.handlers[0].processors[0].realize(state)
|
206
206
|
}.to raise_error MemoryLimit::MemoryLimitedExceededError
|
207
207
|
end
|
208
208
|
end
|
@@ -220,8 +220,8 @@ describe Configuration do
|
|
220
220
|
)
|
221
221
|
|
222
222
|
expect {
|
223
|
-
subject.handlers[0].
|
224
|
-
subject.handlers[0].
|
223
|
+
subject.handlers[0].sources[0].realize(state)
|
224
|
+
subject.handlers[0].processors[0].realize(state)
|
225
225
|
}.to raise_error Configuration::Thumbnail::ThumbnailingError # WTF?, "thumbnailing of 'input' into 'original' failed: at least one image dimension is zero: 0x10"
|
226
226
|
end
|
227
227
|
|
@@ -266,26 +266,26 @@ describe Configuration do
|
|
266
266
|
end
|
267
267
|
|
268
268
|
it 'should provide thumbnail data' do
|
269
|
-
subject.handlers[0].
|
269
|
+
subject.handlers[0].processors[0].realize(state)
|
270
270
|
state.images['original'].data.should_not be_nil
|
271
271
|
state.images['small'].data.should_not be_nil
|
272
272
|
state.images['padded'].data.should_not be_nil
|
273
273
|
end
|
274
274
|
|
275
275
|
it 'should set thumbnail mime type' do
|
276
|
-
subject.handlers[0].
|
276
|
+
subject.handlers[0].processors[0].realize(state)
|
277
277
|
state.images['original'].mime_type.should == 'image/jpeg'
|
278
278
|
state.images['small'].mime_type.should == 'image/jpeg'
|
279
279
|
state.images['padded'].mime_type.should == 'image/png'
|
280
280
|
end
|
281
281
|
|
282
282
|
it 'should set input image mime type' do
|
283
|
-
subject.handlers[0].
|
283
|
+
subject.handlers[0].processors[0].realize(state)
|
284
284
|
state.images['input'].mime_type.should == 'image/jpeg'
|
285
285
|
end
|
286
286
|
|
287
287
|
it 'should use input image source path and url' do
|
288
|
-
subject.handlers[0].
|
288
|
+
subject.handlers[0].processors[0].realize(state)
|
289
289
|
state.images['original'].source_path.should == 'test.in'
|
290
290
|
state.images['original'].source_url.should == 'file://test.in'
|
291
291
|
state.images['small'].source_path.should == 'test.in'
|
@@ -312,7 +312,7 @@ describe Configuration do
|
|
312
312
|
|
313
313
|
it 'should raise MemoryLimit::MemoryLimitedExceededError when limit is exceeded' do
|
314
314
|
expect {
|
315
|
-
subject.handlers[0].
|
315
|
+
subject.handlers[0].processors[0].realize(state)
|
316
316
|
}.to raise_error MemoryLimit::MemoryLimitedExceededError
|
317
317
|
end
|
318
318
|
end
|
@@ -344,7 +344,7 @@ describe Configuration do
|
|
344
344
|
end
|
345
345
|
|
346
346
|
it 'should provide thumbnails that name match if-image-name-on list' do
|
347
|
-
subject.handlers[0].
|
347
|
+
subject.handlers[0].processors[0].realize(state)
|
348
348
|
state.images.should_not include 'original'
|
349
349
|
state.images['small'].data.should_not be_nil
|
350
350
|
state.images['padded'].data.should_not be_nil
|
@@ -363,10 +363,10 @@ describe Configuration do
|
|
363
363
|
}
|
364
364
|
)
|
365
365
|
|
366
|
-
subject.handlers[0].
|
366
|
+
subject.handlers[0].sources[0].realize(state)
|
367
367
|
|
368
368
|
expect {
|
369
|
-
subject.handlers[0].
|
369
|
+
subject.handlers[0].processors[0].realize(state)
|
370
370
|
}.to raise_error Configuration::Thumbnail::ThumbnailingError, "thumbnailing of 'input' into 'original' failed: at least one image dimension is zero: 0x10"
|
371
371
|
end
|
372
372
|
|
@@ -416,12 +416,12 @@ describe Configuration do
|
|
416
416
|
end
|
417
417
|
|
418
418
|
it 'should mark source to be included when output image name in oneline and destination image name in multiline statement match if-image-name-on list' do
|
419
|
-
subject.handlers[0].
|
420
|
-
subject.handlers[0].
|
421
|
-
subject.handlers[0].
|
422
|
-
subject.handlers[0].
|
423
|
-
subject.handlers[0].
|
424
|
-
subject.handlers[0].
|
419
|
+
subject.handlers[0].processors[0].excluded?(state).should be_false
|
420
|
+
subject.handlers[0].processors[1].excluded?(state).should be_true
|
421
|
+
subject.handlers[0].processors[2].excluded?(state).should be_true
|
422
|
+
subject.handlers[0].processors[3].excluded?(state).should be_false
|
423
|
+
subject.handlers[0].processors[4].excluded?(state).should be_true
|
424
|
+
subject.handlers[0].processors[5].excluded?(state).should be_false
|
425
425
|
end
|
426
426
|
end
|
427
427
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -28,21 +28,28 @@ def headers(url)
|
|
28
28
|
HTTPClient.new.get(url).headers
|
29
29
|
end
|
30
30
|
|
31
|
+
@@running_cmd = {}
|
31
32
|
def start_server(cmd, pid_file, log_file, test_url)
|
33
|
+
if @@running_cmd[pid_file]
|
34
|
+
return if @@running_cmd[pid_file] == cmd
|
35
|
+
stop_server(pid_file)
|
36
|
+
end
|
37
|
+
|
32
38
|
fork do
|
33
39
|
Daemon.daemonize(pid_file, log_file)
|
34
40
|
log_file = Pathname.new(log_file)
|
35
41
|
log_file.truncate(0) if log_file.exist?
|
36
42
|
exec(cmd)
|
37
43
|
end
|
38
|
-
|
44
|
+
|
45
|
+
@@running_cmd[pid_file] = cmd
|
39
46
|
|
40
47
|
ppid = Process.pid
|
41
48
|
at_exit do
|
42
49
|
stop_server(pid_file) if Process.pid == ppid
|
43
50
|
end
|
44
51
|
|
45
|
-
Timeout.timeout(
|
52
|
+
Timeout.timeout(10) do
|
46
53
|
begin
|
47
54
|
get test_url
|
48
55
|
rescue Errno::ECONNREFUSED
|
@@ -56,6 +63,7 @@ def stop_server(pid_file)
|
|
56
63
|
pid_file = Pathname.new(pid_file)
|
57
64
|
return unless pid_file.exist?
|
58
65
|
|
66
|
+
STDERR.puts http_client.get_content("http://localhost:3000/stats") if pid_file.to_s.include? 'httpimagestore'
|
59
67
|
pid = pid_file.read.strip.to_i
|
60
68
|
|
61
69
|
Timeout.timeout(20) do
|
@@ -65,6 +73,7 @@ def stop_server(pid_file)
|
|
65
73
|
sleep 0.1
|
66
74
|
end
|
67
75
|
rescue Errno::ESRCH
|
76
|
+
@@running_cmd.delete pid_file.to_s
|
68
77
|
pid_file.unlink
|
69
78
|
end
|
70
79
|
end
|