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
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'unicorn-cuba-base'
|
2
1
|
require 'httpthumbnailer-client'
|
3
2
|
require 'httpimagestore/ruby_string_template'
|
4
3
|
require 'httpimagestore/configuration/handler'
|
@@ -126,7 +125,7 @@ module Configuration
|
|
126
125
|
operation || 'fit',
|
127
126
|
width || 'input',
|
128
127
|
height || 'input',
|
129
|
-
format || '
|
128
|
+
format || 'input',
|
130
129
|
remaining || {},
|
131
130
|
matcher
|
132
131
|
)
|
@@ -134,7 +133,7 @@ module Configuration
|
|
134
133
|
|
135
134
|
matcher = InclusionMatcher.new(source_image_name, node.grab_attributes('if-image-name-on').first) if use_multipart_api
|
136
135
|
|
137
|
-
configuration.
|
136
|
+
configuration.processors << self.new(
|
138
137
|
configuration.global,
|
139
138
|
source_image_name,
|
140
139
|
specs,
|
@@ -158,12 +157,14 @@ module Configuration
|
|
158
157
|
@specs.select do |spec|
|
159
158
|
spec.included?(request_state)
|
160
159
|
end.each do |spec|
|
161
|
-
rendered_specs.merge! spec.render(request_state
|
160
|
+
rendered_specs.merge! spec.render(request_state)
|
162
161
|
end
|
163
162
|
source_image = request_state.images[@source_image_name]
|
164
163
|
|
165
164
|
thumbnails = {}
|
166
165
|
input_mime_type = nil
|
166
|
+
input_width = nil
|
167
|
+
input_height = nil
|
167
168
|
|
168
169
|
Thumbnail.stats.incr_total_thumbnail_requests
|
169
170
|
Thumbnail.stats.incr_total_thumbnail_requests_bytes source_image.data.bytesize
|
@@ -192,6 +193,8 @@ module Configuration
|
|
192
193
|
end
|
193
194
|
|
194
195
|
input_mime_type = thumbnails.input_mime_type
|
196
|
+
input_width = thumbnails.input_width
|
197
|
+
input_height = thumbnails.input_height
|
195
198
|
|
196
199
|
# check each thumbnail for errors
|
197
200
|
thumbnails = Hash[rendered_specs.keys.zip(thumbnails)]
|
@@ -205,7 +208,7 @@ module Configuration
|
|
205
208
|
|
206
209
|
# borrow from memory limit - note that we might have already used too much memory
|
207
210
|
thumbnails.each do |name, thumbnail|
|
208
|
-
request_state.memory_limit.borrow
|
211
|
+
request_state.memory_limit.borrow(thumbnail.data.bytesize, "thumbnail '#{name}'")
|
209
212
|
end
|
210
213
|
else
|
211
214
|
name, rendered_spec = *rendered_specs.first
|
@@ -213,8 +216,12 @@ module Configuration
|
|
213
216
|
|
214
217
|
begin
|
215
218
|
thumbnail = client.thumbnail(source_image.data, *rendered_spec)
|
216
|
-
request_state.memory_limit.borrow
|
219
|
+
request_state.memory_limit.borrow(thumbnail.data.bytesize, "thumbnail '#{name}'")
|
220
|
+
|
217
221
|
input_mime_type = thumbnail.input_mime_type
|
222
|
+
input_width = thumbnail.input_width
|
223
|
+
input_height = thumbnail.input_height
|
224
|
+
|
218
225
|
thumbnails[name] = thumbnail
|
219
226
|
rescue HTTPThumbnailerClient::HTTPThumbnailerClientError => error
|
220
227
|
log.warn 'got thumbnailer error', error
|
@@ -232,8 +239,10 @@ module Configuration
|
|
232
239
|
Thumbnail.stats.incr_total_thumbnail_thumbnails_bytes thumbnail.data.bytesize
|
233
240
|
end
|
234
241
|
|
235
|
-
#
|
236
|
-
source_image.mime_type = input_mime_type
|
242
|
+
# use httpthumbnailer provided information on input image mime type and size
|
243
|
+
source_image.mime_type = input_mime_type if input_mime_type
|
244
|
+
source_image.width = input_width if input_width
|
245
|
+
source_image.height = input_height if input_height
|
237
246
|
|
238
247
|
request_state.images.merge! thumbnails
|
239
248
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class ErrorReporter < Controler
|
2
2
|
self.define do
|
3
3
|
on error(
|
4
|
-
Rack::UnhandledRequest::UnhandledRequestError,
|
5
4
|
Configuration::S3NoSuchKeyError,
|
6
5
|
Configuration::NoSuchFileError
|
7
6
|
) do |error|
|
@@ -21,16 +20,7 @@ class ErrorReporter < Controler
|
|
21
20
|
write_error 400, error
|
22
21
|
end
|
23
22
|
|
24
|
-
|
25
|
-
log.error "unhandled error while processing request: #{env['REQUEST_METHOD']} #{env['SCRIPT_NAME']}[#{env["PATH_INFO"]}]", error
|
26
|
-
log.debug {
|
27
|
-
out = StringIO.new
|
28
|
-
PP::pp(env, out, 200)
|
29
|
-
"Request: \n" + out.string
|
30
|
-
}
|
31
|
-
|
32
|
-
write_error 500, error
|
33
|
-
end
|
23
|
+
run DefaultErrorReporter
|
34
24
|
end
|
35
25
|
end
|
36
26
|
|
data/load_test/load_test.jmx
CHANGED
@@ -42,7 +42,7 @@
|
|
42
42
|
<stringProp name="HTTPSampler.concurrentPool">4</stringProp>
|
43
43
|
</ConfigTestElement>
|
44
44
|
<hashTree/>
|
45
|
-
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Compatibility API" enabled="
|
45
|
+
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Compatibility API" enabled="false">
|
46
46
|
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
47
47
|
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
48
48
|
<boolProp name="LoopController.continue_forever">false</boolProp>
|
@@ -78,7 +78,7 @@
|
|
78
78
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
79
79
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
80
80
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
81
|
-
<stringProp name="HTTPSampler.path">/thumbnail/original,search,search_thumb,brochure,brochure_thumb,admin,dmin_thumb,treatment_thumb,staff_member_thumb,consultation,clinic_google_map_thumb</stringProp>
|
81
|
+
<stringProp name="HTTPSampler.path">/thumbnail/original,search,search_thumb,brochure,brochure_thumb,admin,dmin_thumb,treatment_thumb,staff_member_thumb,consultation,clinic_google_map_thumb/randwick.jpg</stringProp>
|
82
82
|
<stringProp name="HTTPSampler.method">PUT</stringProp>
|
83
83
|
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
|
84
84
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
@@ -108,7 +108,7 @@
|
|
108
108
|
<hashTree/>
|
109
109
|
</hashTree>
|
110
110
|
</hashTree>
|
111
|
-
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="API
|
111
|
+
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Flexi API" enabled="true">
|
112
112
|
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
113
113
|
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
114
114
|
<boolProp name="LoopController.continue_forever">false</boolProp>
|
@@ -144,8 +144,8 @@
|
|
144
144
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
145
145
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
146
146
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
147
|
-
<stringProp name="HTTPSampler.path">/
|
148
|
-
<stringProp name="HTTPSampler.method">
|
147
|
+
<stringProp name="HTTPSampler.path">/iss/v2/pictures</stringProp>
|
148
|
+
<stringProp name="HTTPSampler.method">POST</stringProp>
|
149
149
|
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
|
150
150
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
151
151
|
<boolProp name="HTTPSampler.use_keepalive">false</boolProp>
|
@@ -172,9 +172,9 @@
|
|
172
172
|
<stringProp name="RegexExtractor.match_number">1</stringProp>
|
173
173
|
</RegexExtractor>
|
174
174
|
<hashTree/>
|
175
|
-
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="contains path" enabled="true">
|
175
|
+
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="contains .jpg file path" enabled="true">
|
176
176
|
<collectionProp name="Asserion.test_strings">
|
177
|
-
<stringProp name="
|
177
|
+
<stringProp name="1475827">.jpg</stringProp>
|
178
178
|
</collectionProp>
|
179
179
|
<stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
|
180
180
|
<boolProp name="Assertion.assume_success">false</boolProp>
|
@@ -195,7 +195,7 @@
|
|
195
195
|
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="thumbnail specs" enabled="true">
|
196
196
|
<stringProp name="filename">thumbnail_specs.csv</stringProp>
|
197
197
|
<stringProp name="fileEncoding"></stringProp>
|
198
|
-
<stringProp name="variableNames">method,width,height,options</stringProp>
|
198
|
+
<stringProp name="variableNames">type,method,width,height,options</stringProp>
|
199
199
|
<stringProp name="delimiter">,</stringProp>
|
200
200
|
<boolProp name="quotedData">true</boolProp>
|
201
201
|
<boolProp name="recycle">true</boolProp>
|
@@ -213,7 +213,7 @@
|
|
213
213
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
214
214
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
215
215
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
216
|
-
<stringProp name="HTTPSampler.path">/
|
216
|
+
<stringProp name="HTTPSampler.path">/iss/v2/pictures/${__BeanShell(vars.get("store_path").replace("/:"\, "%2f"))}?type=${type}</stringProp>
|
217
217
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
218
218
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
219
219
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
@@ -238,13 +238,13 @@
|
|
238
238
|
<DebugPostProcessor guiclass="TestBeanGUI" testclass="DebugPostProcessor" testname="Debug PostProcessor" enabled="false">
|
239
239
|
<boolProp name="displayJMeterProperties">false</boolProp>
|
240
240
|
<boolProp name="displayJMeterVariables">true</boolProp>
|
241
|
-
<boolProp name="displaySamplerProperties">
|
241
|
+
<boolProp name="displaySamplerProperties">true</boolProp>
|
242
242
|
<boolProp name="displaySystemProperties">false</boolProp>
|
243
243
|
</DebugPostProcessor>
|
244
244
|
<hashTree/>
|
245
245
|
</hashTree>
|
246
246
|
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
|
247
|
-
<boolProp name="ResultCollector.error_logging">
|
247
|
+
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
248
248
|
<objProp>
|
249
249
|
<name>saveConfig</name>
|
250
250
|
<value class="SampleSaveConfiguration">
|
@@ -1,11 +1,11 @@
|
|
1
|
-
"crop","input","input","background-color:0xF0F0F0"
|
2
|
-
"pad","162","162","background-color:0xF0F0F0"
|
3
|
-
"pad","28","28","background-color:0xF0F0F0"
|
4
|
-
"pad","264","264","background-color:0xF0F0F0"
|
5
|
-
"pad","40","40","background-color:0xF0F0F0"
|
6
|
-
"pad","160","160","background-color:0xF0F0F0"
|
7
|
-
"pad","65","65","background-color:0xF0F0F0"
|
8
|
-
"pad","80","60","background-color:0xF0F0F0"
|
9
|
-
"pad","50","50","background-color:0xF0F0F0"
|
10
|
-
"pad","126","126","background-color:0xF0F0F0"
|
11
|
-
"pad","74","74","background-color:0xF0F0F0"
|
1
|
+
"original","crop","input","input","background-color:0xF0F0F0"
|
2
|
+
"search","pad","162","162","background-color:0xF0F0F0"
|
3
|
+
"search_thumb","pad","28","28","background-color:0xF0F0F0"
|
4
|
+
"brochure","pad","264","264","background-color:0xF0F0F0"
|
5
|
+
"brochure_thumb","pad","40","40","background-color:0xF0F0F0"
|
6
|
+
"admin","pad","160","160","background-color:0xF0F0F0"
|
7
|
+
"admin_thumb","pad","65","65","background-color:0xF0F0F0"
|
8
|
+
"treatment_thumb","pad","80","60","background-color:0xF0F0F0"
|
9
|
+
"staff_member_thumb","pad","50","50","background-color:0xF0F0F0"
|
10
|
+
"consultation","pad","126","126","background-color:0xF0F0F0"
|
11
|
+
"clinic_google_map_thumb","pad","74","74","background-color:0xF0F0F0"
|
@@ -36,21 +36,21 @@ describe Configuration do
|
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'should source image from file using path spec' do
|
39
|
-
subject.handlers[0].
|
40
|
-
subject.handlers[0].
|
39
|
+
subject.handlers[0].sources[0].should be_a Configuration::FileSource
|
40
|
+
subject.handlers[0].sources[0].realize(state)
|
41
41
|
|
42
42
|
state.images["original"].should_not be_nil
|
43
43
|
state.images["original"].data.should == 'abc'
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'should have nil mime type' do
|
47
|
-
subject.handlers[0].
|
47
|
+
subject.handlers[0].sources[0].realize(state)
|
48
48
|
|
49
49
|
state.images["original"].mime_type.should be_nil
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'should have source path and url' do
|
53
|
-
subject.handlers[0].
|
53
|
+
subject.handlers[0].sources[0].realize(state)
|
54
54
|
|
55
55
|
state.images['original'].source_path.should == "test.in"
|
56
56
|
state.images['original'].source_url.should == "file://test.in"
|
@@ -65,16 +65,16 @@ describe Configuration do
|
|
65
65
|
|
66
66
|
subject do
|
67
67
|
Configuration.read(<<-EOF)
|
68
|
-
path "
|
68
|
+
path "image_name" "\#{image_name}.jpg"
|
69
69
|
|
70
70
|
get "small" {
|
71
|
-
source_file "test-image-name" root="/tmp" path="
|
71
|
+
source_file "test-image-name" root="/tmp" path="image_name"
|
72
72
|
}
|
73
73
|
EOF
|
74
74
|
end
|
75
75
|
|
76
|
-
it 'should provide image name to be used as #{
|
77
|
-
subject.handlers[0].
|
76
|
+
it 'should provide image name to be used as #{image_name}' do
|
77
|
+
subject.handlers[0].sources[0].realize(state)
|
78
78
|
state.images['test-image-name'].source_path.should == 'test-image-name.jpg'
|
79
79
|
state.images['test-image-name'].data.should == 'hello world'
|
80
80
|
end
|
@@ -93,7 +93,7 @@ describe Configuration do
|
|
93
93
|
EOF
|
94
94
|
|
95
95
|
expect {
|
96
|
-
subject.handlers[0].
|
96
|
+
subject.handlers[0].sources[0].realize(state)
|
97
97
|
}.to raise_error Configuration::FileStorageOutsideOfRootDirError, %{error while processing image 'original': file storage path '../test.in' outside of root direcotry}
|
98
98
|
end
|
99
99
|
|
@@ -109,7 +109,7 @@ describe Configuration do
|
|
109
109
|
EOF
|
110
110
|
|
111
111
|
expect {
|
112
|
-
subject.handlers[0].
|
112
|
+
subject.handlers[0].sources[0].realize(state)
|
113
113
|
}.to raise_error Configuration::NoSuchFileError, %{error while processing image 'original': file 'blah' not found}
|
114
114
|
end
|
115
115
|
|
@@ -151,7 +151,7 @@ describe Configuration do
|
|
151
151
|
|
152
152
|
it 'should rais MemoryLimit::MemoryLimitedExceededError error if limit exceeded runing file sourcing' do
|
153
153
|
expect {
|
154
|
-
subject.handlers[0].
|
154
|
+
subject.handlers[0].sources[0].realize(state)
|
155
155
|
}.to raise_error MemoryLimit::MemoryLimitedExceededError, 'memory limit exceeded'
|
156
156
|
end
|
157
157
|
end
|
@@ -176,7 +176,7 @@ describe Configuration do
|
|
176
176
|
|
177
177
|
before :each do
|
178
178
|
out_file.unlink if out_file.exist?
|
179
|
-
subject.handlers[0].
|
179
|
+
subject.handlers[0].sources[0].realize(state)
|
180
180
|
end
|
181
181
|
|
182
182
|
after :each do
|
@@ -227,33 +227,33 @@ describe Configuration do
|
|
227
227
|
describe 'context locals' do
|
228
228
|
subject do
|
229
229
|
Configuration.read(<<-'EOF')
|
230
|
-
path "
|
231
|
-
path "
|
230
|
+
path "image_name" "#{image_name}.jpg"
|
231
|
+
path "image_mime_extension" "test-store-file.#{image_mime_extension}"
|
232
232
|
|
233
233
|
post "small" {
|
234
|
-
store_file "input" root="/tmp" path="
|
235
|
-
store_file "input" root="/tmp" path="
|
234
|
+
store_file "input" root="/tmp" path="image_name"
|
235
|
+
store_file "input" root="/tmp" path="image_mime_extension"
|
236
236
|
}
|
237
237
|
EOF
|
238
238
|
end
|
239
239
|
|
240
|
-
it 'should provide image name to be used as #{
|
240
|
+
it 'should provide image name to be used as #{image_name}' do
|
241
241
|
subject.handlers[0].stores[0].realize(state)
|
242
242
|
|
243
243
|
state.images['input'].store_path.should == 'input.jpg'
|
244
244
|
end
|
245
245
|
|
246
|
-
it 'should provide image mime type based file extension to be used as #{
|
246
|
+
it 'should provide image mime type based file extension to be used as #{image_mime_extension}' do
|
247
247
|
state.images['input'].mime_type = 'image/jpeg'
|
248
248
|
subject.handlers[0].stores[1].realize(state)
|
249
249
|
|
250
250
|
state.images['input'].store_path.should == 'test-store-file.jpg'
|
251
251
|
end
|
252
252
|
|
253
|
-
it 'should raise
|
253
|
+
it 'should raise PathRenderingError if there is on mime type for image defined and path contains #{image_mime_extension}' do
|
254
254
|
expect {
|
255
255
|
subject.handlers[0].stores[1].realize(state)
|
256
|
-
}.to raise_error Configuration::
|
256
|
+
}.to raise_error Configuration::PathRenderingError, %q{cannot generate path 'image_mime_extension' from template 'test-store-file.#{image_mime_extension}': image 'input' does not have data for variable 'image_mime_extension'}
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
@@ -39,6 +39,156 @@ describe Configuration do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
describe Configuration::RequestState do
|
42
|
+
subject do
|
43
|
+
Configuration::RequestState.new(
|
44
|
+
'test',
|
45
|
+
{operation: 'pad'},
|
46
|
+
'/hello/world.jpg',
|
47
|
+
{width: '123', height: '321'}
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should behave like hash' do
|
52
|
+
subject['a'] = 'b'
|
53
|
+
subject['a'].should == 'b'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should provide body' do
|
57
|
+
subject.body.should == 'test'
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'variables' do
|
61
|
+
it 'should provide path' do
|
62
|
+
subject[:path].should == '/hello/world.jpg'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should provide query string params' do
|
66
|
+
subject[:width].should == '123'
|
67
|
+
subject[:height].should == '321'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should provide matches' do
|
71
|
+
subject[:operation].should == 'pad'
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should provide query_string_options' do
|
75
|
+
subject[:query_string_options].should == 'height:321,width:123'
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should provide request body digest' do
|
79
|
+
subject[:digest].should == '9f86d081884c7d65' # deprecated
|
80
|
+
subject[:input_digest].should == '9f86d081884c7d65'
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should provide request body full sha256 checsum' do
|
84
|
+
subject[:input_sha256].should == '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should provide input image mime extension' do
|
88
|
+
subject.images['input'] = Struct.new(:data, :mime_type).new('image body', 'image/jpeg')
|
89
|
+
subject.images['input'].extend Configuration::ImageMetaData
|
90
|
+
subject[:input_image_mime_extension].should == 'jpg'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should provide input image width and height' do
|
94
|
+
subject.images['input'] = Struct.new(:data, :width, :height).new('image body', 128, 256)
|
95
|
+
subject[:input_image_width].should == 128
|
96
|
+
subject[:input_image_height].should == 256
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should provide image body digest' do
|
100
|
+
subject.images['abc'] = Struct.new(:data).new('image body')
|
101
|
+
subject.with_locals(image_name: 'abc')[:image_digest].should == 'f5288dd892bb007b'
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should provide image body full sha256 checsum' do
|
105
|
+
subject.images['abc'] = Struct.new(:data).new('image body')
|
106
|
+
subject.with_locals(image_name: 'abc')[:image_sha256].should == 'f5288dd892bb007b607304a8fb20c91ea769dcd04d82cc8ddf3239602867eb4d'
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should provide uuid' do
|
110
|
+
subject[:uuid].should_not be_empty
|
111
|
+
subject[:uuid].should == subject[:uuid]
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should provide extension' do
|
115
|
+
subject[:extension].should == 'jpg'
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should provide dirname' do
|
119
|
+
subject[:dirname].should == '/hello'
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should provide basename' do
|
123
|
+
subject[:basename].should == 'world'
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should provide image mime extension' do
|
127
|
+
subject.images['abc'] = Struct.new(:data, :mime_type).new('image body', 'image/jpeg')
|
128
|
+
subject.images['abc'].extend Configuration::ImageMetaData
|
129
|
+
subject.with_locals(image_name: 'abc')[:mimeextension].should == 'jpg' # deprecated
|
130
|
+
subject.with_locals(image_name: 'abc')[:image_mime_extension].should == 'jpg'
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should provide image width and height' do
|
134
|
+
subject.images['abc'] = Struct.new(:data, :width, :height).new('image body', 128, 256)
|
135
|
+
subject.with_locals(image_name: 'abc')[:image_width].should == 128
|
136
|
+
subject.with_locals(image_name: 'abc')[:image_height].should == 256
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "error handling" do
|
140
|
+
it 'should raise NoRequestBodyToGenerateMetaVariableError when empty body was provided and body needed for variable calculation' do
|
141
|
+
subject = Configuration::RequestState.new(
|
142
|
+
'',
|
143
|
+
{operation: 'pad'},
|
144
|
+
'/hello/world.jpg',
|
145
|
+
{width: '123', height: '321'}
|
146
|
+
)
|
147
|
+
|
148
|
+
expect {
|
149
|
+
subject[:input_digest]
|
150
|
+
}.to raise_error Configuration::NoRequestBodyToGenerateMetaVariableError, %q{need not empty request body to generate value for 'input_digest'}
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should raise ImageNotLoadedError when asking for image related variable of not loaded image' do
|
154
|
+
expect {
|
155
|
+
subject.with_locals(image_name: 'abc')[:image_mime_extension]
|
156
|
+
}.to raise_error Configuration::ImageNotLoadedError, %q{image 'abc' not loaded}
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should raise NoVariableToGenerateMetaVariableError when no image_name was defined' do
|
160
|
+
expect {
|
161
|
+
subject.with_locals({})[:image_mime_extension]
|
162
|
+
}.to raise_error Configuration::NoVariableToGenerateMetaVariableError, %q{need 'image_name' variable to generate value for 'image_mime_extension'}
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should raise NoVariableToGenerateMetaVariableError when no path was defined' do
|
166
|
+
expect {
|
167
|
+
subject.delete(:path)
|
168
|
+
subject[:basename]
|
169
|
+
}.to raise_error Configuration::NoVariableToGenerateMetaVariableError, %q{need 'path' variable to generate value for 'basename'}
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should raise NoImageDataForVariableError when image has no mime type' do
|
173
|
+
subject.images['abc'] = Struct.new(:data, :mime_type).new('image body', nil)
|
174
|
+
subject.images['abc'].extend Configuration::ImageMetaData
|
175
|
+
expect {
|
176
|
+
subject.with_locals(image_name: 'abc')[:image_mime_extension]
|
177
|
+
}.to raise_error Configuration::NoImageDataForVariableError, %q{image 'abc' does not have data for variable 'image_mime_extension'}
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'should raise NoImageDataForVariableError when image has no known width or height' do
|
181
|
+
subject.images['abc'] = Struct.new(:data, :width, :height).new('image body', nil, nil)
|
182
|
+
expect {
|
183
|
+
subject.with_locals(image_name: 'abc')[:image_width]
|
184
|
+
}.to raise_error Configuration::NoImageDataForVariableError, %q{image 'abc' does not have data for variable 'image_width'}
|
185
|
+
expect {
|
186
|
+
subject.with_locals(image_name: 'abc')[:image_height]
|
187
|
+
}.to raise_error Configuration::NoImageDataForVariableError, %q{image 'abc' does not have data for variable 'image_height'}
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
42
192
|
it 'should raise ImageNotLoadedError if image lookup fails' do
|
43
193
|
expect {
|
44
194
|
Configuration::RequestState.new.images['test']
|
@@ -72,27 +222,27 @@ describe Configuration do
|
|
72
222
|
|
73
223
|
describe 'sources' do
|
74
224
|
it 'should have implicit InputSource on non get handlers' do
|
75
|
-
subject.handlers[0].
|
76
|
-
subject.handlers[1].
|
77
|
-
subject.handlers[2].
|
225
|
+
subject.handlers[0].sources.first.should_not be_a Configuration::InputSource
|
226
|
+
subject.handlers[1].sources.first.should be_a Configuration::InputSource
|
227
|
+
subject.handlers[2].sources.first.should be_a Configuration::InputSource
|
78
228
|
end
|
79
229
|
|
80
230
|
describe Configuration::InputSource do
|
81
231
|
it 'should copy input data to "input" image when realized' do
|
82
232
|
state = Configuration::RequestState.new('abc')
|
83
|
-
input_source = subject.handlers[1].
|
233
|
+
input_source = subject.handlers[1].sources[0].realize(state)
|
84
234
|
state.images['input'].data.should == 'abc'
|
85
235
|
end
|
86
236
|
|
87
237
|
it 'should have nil mime type' do
|
88
238
|
state = Configuration::RequestState.new('abc')
|
89
|
-
input_source = subject.handlers[1].
|
239
|
+
input_source = subject.handlers[1].sources[0].realize(state)
|
90
240
|
state.images['input'].mime_type.should be_nil
|
91
241
|
end
|
92
242
|
|
93
243
|
it 'should have nil source path and url' do
|
94
244
|
state = Configuration::RequestState.new('abc')
|
95
|
-
input_source = subject.handlers[1].
|
245
|
+
input_source = subject.handlers[1].sources[0].realize(state)
|
96
246
|
state.images['input'].source_path.should be_nil
|
97
247
|
state.images['input'].source_url.should be_nil
|
98
248
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'httpimagestore/configuration'
|
3
|
+
Configuration::Scope.logger = Logger.new('/dev/null')
|
4
|
+
|
5
|
+
require 'httpimagestore/configuration/thumbnailer'
|
6
|
+
require 'httpimagestore/configuration/identify'
|
7
|
+
MemoryLimit.logger = Logger.new('/dev/null')
|
8
|
+
|
9
|
+
describe Configuration do
|
10
|
+
describe 'thumbnailer' do
|
11
|
+
before :all do
|
12
|
+
log = support_dir + 'server.log'
|
13
|
+
start_server(
|
14
|
+
"httpthumbnailer -f -d -l #{log}",
|
15
|
+
'/tmp/httpthumbnailer.pid',
|
16
|
+
log,
|
17
|
+
'http://localhost:3100/'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
let :state do
|
22
|
+
Configuration::RequestState.new(
|
23
|
+
(support_dir + 'compute.jpg').read
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
subject do
|
28
|
+
Configuration.read(<<-'EOF')
|
29
|
+
put {
|
30
|
+
identify "input"
|
31
|
+
}
|
32
|
+
EOF
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should provide input image mime type' do
|
36
|
+
subject.handlers[0].sources[0].realize(state)
|
37
|
+
state.images['input'].mime_type.should be_nil
|
38
|
+
|
39
|
+
subject.handlers[0].processors[0].realize(state)
|
40
|
+
state.images['input'].mime_type.should == 'image/jpeg'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
@@ -27,7 +27,7 @@ describe Configuration do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
before :each do
|
30
|
-
subject.handlers[0].
|
30
|
+
subject.handlers[0].sources[0].realize(state)
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'should provide given image' do
|
@@ -112,7 +112,7 @@ describe Configuration do
|
|
112
112
|
}
|
113
113
|
EOF
|
114
114
|
|
115
|
-
subject.handlers[0].
|
115
|
+
subject.handlers[0].sources[0].realize(state)
|
116
116
|
subject.handlers[0].stores[0].realize(state)
|
117
117
|
subject.handlers[0].output.realize(state)
|
118
118
|
|
@@ -142,8 +142,8 @@ describe Configuration do
|
|
142
142
|
}
|
143
143
|
EOF
|
144
144
|
|
145
|
-
subject.handlers[0].
|
146
|
-
subject.handlers[0].
|
145
|
+
subject.handlers[0].sources[0].realize(state)
|
146
|
+
subject.handlers[0].sources[1].realize(state)
|
147
147
|
subject.handlers[0].stores[0].realize(state)
|
148
148
|
subject.handlers[0].stores[1].realize(state)
|
149
149
|
subject.handlers[0].output.realize(state)
|
@@ -185,9 +185,9 @@ describe Configuration do
|
|
185
185
|
end
|
186
186
|
|
187
187
|
it 'should output store path only for images that names match if-image-name-on list' do
|
188
|
-
subject.handlers[0].
|
189
|
-
subject.handlers[0].
|
190
|
-
subject.handlers[0].
|
188
|
+
subject.handlers[0].sources[0].realize(state)
|
189
|
+
subject.handlers[0].sources[1].realize(state)
|
190
|
+
subject.handlers[0].sources[2].realize(state)
|
191
191
|
subject.handlers[0].stores[0].realize(state)
|
192
192
|
subject.handlers[0].stores[1].realize(state)
|
193
193
|
subject.handlers[0].stores[2].realize(state)
|
@@ -207,7 +207,7 @@ describe Configuration do
|
|
207
207
|
}
|
208
208
|
EOF
|
209
209
|
|
210
|
-
subject.handlers[0].
|
210
|
+
subject.handlers[0].sources[0].realize(state)
|
211
211
|
|
212
212
|
expect {
|
213
213
|
subject.handlers[0].output.realize(state)
|
@@ -230,7 +230,7 @@ describe Configuration do
|
|
230
230
|
}
|
231
231
|
EOF
|
232
232
|
|
233
|
-
subject.handlers[0].
|
233
|
+
subject.handlers[0].sources[0].realize(state)
|
234
234
|
subject.handlers[0].stores[0].realize(state)
|
235
235
|
subject.handlers[0].output.realize(state)
|
236
236
|
|
@@ -260,8 +260,8 @@ describe Configuration do
|
|
260
260
|
}
|
261
261
|
EOF
|
262
262
|
|
263
|
-
subject.handlers[0].
|
264
|
-
subject.handlers[0].
|
263
|
+
subject.handlers[0].sources[0].realize(state)
|
264
|
+
subject.handlers[0].sources[1].realize(state)
|
265
265
|
subject.handlers[0].stores[0].realize(state)
|
266
266
|
subject.handlers[0].stores[1].realize(state)
|
267
267
|
subject.handlers[0].output.realize(state)
|
@@ -303,9 +303,9 @@ describe Configuration do
|
|
303
303
|
end
|
304
304
|
|
305
305
|
it 'should output store url only for images that names match if-image-name-on list' do
|
306
|
-
subject.handlers[0].
|
307
|
-
subject.handlers[0].
|
308
|
-
subject.handlers[0].
|
306
|
+
subject.handlers[0].sources[0].realize(state)
|
307
|
+
subject.handlers[0].sources[1].realize(state)
|
308
|
+
subject.handlers[0].sources[2].realize(state)
|
309
309
|
subject.handlers[0].stores[0].realize(state)
|
310
310
|
subject.handlers[0].stores[1].realize(state)
|
311
311
|
subject.handlers[0].stores[2].realize(state)
|
@@ -325,7 +325,7 @@ describe Configuration do
|
|
325
325
|
}
|
326
326
|
EOF
|
327
327
|
|
328
|
-
subject.handlers[0].
|
328
|
+
subject.handlers[0].sources[0].realize(state)
|
329
329
|
|
330
330
|
expect {
|
331
331
|
subject.handlers[0].output.realize(state)
|