httpimagestore 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -3
- data/Gemfile.lock +12 -16
- data/README.md +73 -0
- data/VERSION +1 -1
- data/bin/httpimagestore +7 -7
- data/features/data-uri.feature +55 -0
- data/features/error-reporting.feature +21 -3
- data/features/source-failover.feature +71 -0
- data/features/step_definitions/httpimagestore_steps.rb +27 -12
- data/features/storage.feature +26 -25
- data/features/support/tiny.png +0 -0
- data/features/xid-forwarding.feature +49 -0
- data/httpimagestore.gemspec +19 -23
- data/lib/httpimagestore/configuration/file.rb +6 -0
- data/lib/httpimagestore/configuration/handler.rb +4 -1
- data/lib/httpimagestore/configuration/identify.rb +2 -2
- data/lib/httpimagestore/configuration/output.rb +20 -1
- data/lib/httpimagestore/configuration/s3.rb +15 -9
- data/lib/httpimagestore/configuration/source_failover.rb +51 -0
- data/lib/httpimagestore/configuration/thumbnailer.rb +7 -7
- data/lib/httpimagestore/error_reporter.rb +9 -1
- data/lib/httpimagestore/ruby_string_template.rb +12 -7
- data/load_test/load_test.jmx +50 -77
- data/load_test/thumbnail_specs_v2.csv +10 -0
- data/spec/configuration_file_spec.rb +27 -2
- data/spec/configuration_identify_spec.rb +25 -2
- data/spec/configuration_s3_spec.rb +29 -3
- data/spec/configuration_source_failover_spec.rb +101 -0
- data/spec/configuration_thumbnailer_spec.rb +63 -8
- data/spec/ruby_string_template_spec.rb +4 -0
- data/spec/support/full.cfg +167 -33
- metadata +19 -23
- data/.idea/.name +0 -1
- data/.idea/.rakeTasks +0 -7
- data/.idea/codeStyleSettings.xml +0 -13
- data/.idea/dictionaries/wcc.xml +0 -8
- data/.idea/encodings.xml +0 -5
- data/.idea/httpimagestore.iml +0 -69
- data/.idea/jenkinsSettings.xml +0 -9
- data/.idea/misc.xml +0 -5
- data/.idea/modules.xml +0 -9
- data/.idea/scopes/scope_settings.xml +0 -5
- data/.idea/vcs.xml +0 -7
data/load_test/load_test.jmx
CHANGED
@@ -9,12 +9,12 @@
|
|
9
9
|
<collectionProp name="Arguments.arguments">
|
10
10
|
<elementProp name="iss_server" elementType="Argument">
|
11
11
|
<stringProp name="Argument.name">iss_server</stringProp>
|
12
|
-
<stringProp name="Argument.value">
|
12
|
+
<stringProp name="Argument.value">localhost</stringProp>
|
13
13
|
<stringProp name="Argument.metadata">=</stringProp>
|
14
14
|
</elementProp>
|
15
15
|
<elementProp name="test_data_dir" elementType="Argument">
|
16
16
|
<stringProp name="Argument.name">test_data_dir</stringProp>
|
17
|
-
<stringProp name="Argument.value">${__property(user.dir)}/
|
17
|
+
<stringProp name="Argument.value">${__property(user.dir)}/test_data</stringProp>
|
18
18
|
<stringProp name="Argument.metadata">=</stringProp>
|
19
19
|
</elementProp>
|
20
20
|
<elementProp name="test_data_list" elementType="Argument">
|
@@ -42,72 +42,6 @@
|
|
42
42
|
<stringProp name="HTTPSampler.concurrentPool">4</stringProp>
|
43
43
|
</ConfigTestElement>
|
44
44
|
<hashTree/>
|
45
|
-
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Compatibility API" enabled="false">
|
46
|
-
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
47
|
-
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
48
|
-
<boolProp name="LoopController.continue_forever">false</boolProp>
|
49
|
-
<intProp name="LoopController.loops">-1</intProp>
|
50
|
-
</elementProp>
|
51
|
-
<stringProp name="ThreadGroup.num_threads">5</stringProp>
|
52
|
-
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
|
53
|
-
<longProp name="ThreadGroup.start_time">1373912751000</longProp>
|
54
|
-
<longProp name="ThreadGroup.end_time">1373912751000</longProp>
|
55
|
-
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
56
|
-
<stringProp name="ThreadGroup.duration"></stringProp>
|
57
|
-
<stringProp name="ThreadGroup.delay"></stringProp>
|
58
|
-
</ThreadGroup>
|
59
|
-
<hashTree>
|
60
|
-
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="test files" enabled="true">
|
61
|
-
<stringProp name="delimiter">,</stringProp>
|
62
|
-
<stringProp name="fileEncoding"></stringProp>
|
63
|
-
<stringProp name="filename">${test_data_dir}/${test_data_list}</stringProp>
|
64
|
-
<boolProp name="quotedData">true</boolProp>
|
65
|
-
<boolProp name="recycle">false</boolProp>
|
66
|
-
<stringProp name="shareMode">All threads</stringProp>
|
67
|
-
<boolProp name="stopThread">true</boolProp>
|
68
|
-
<stringProp name="variableNames">test_file</stringProp>
|
69
|
-
</CSVDataSet>
|
70
|
-
<hashTree/>
|
71
|
-
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="classes" enabled="true">
|
72
|
-
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
73
|
-
<collectionProp name="Arguments.arguments"/>
|
74
|
-
</elementProp>
|
75
|
-
<stringProp name="HTTPSampler.domain"></stringProp>
|
76
|
-
<stringProp name="HTTPSampler.port"></stringProp>
|
77
|
-
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
78
|
-
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
79
|
-
<stringProp name="HTTPSampler.protocol"></stringProp>
|
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/randwick.jpg</stringProp>
|
82
|
-
<stringProp name="HTTPSampler.method">PUT</stringProp>
|
83
|
-
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
|
84
|
-
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
85
|
-
<boolProp name="HTTPSampler.use_keepalive">false</boolProp>
|
86
|
-
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
87
|
-
<elementProp name="HTTPsampler.Files" elementType="HTTPFileArgs">
|
88
|
-
<collectionProp name="HTTPFileArgs.files">
|
89
|
-
<elementProp name="${test_data_dir}/${test_file}" elementType="HTTPFileArg">
|
90
|
-
<stringProp name="File.path">${test_data_dir}/${test_file}</stringProp>
|
91
|
-
<stringProp name="File.paramname"></stringProp>
|
92
|
-
<stringProp name="File.mimetype">image/jpeg</stringProp>
|
93
|
-
</elementProp>
|
94
|
-
</collectionProp>
|
95
|
-
</elementProp>
|
96
|
-
<boolProp name="HTTPSampler.monitor">false</boolProp>
|
97
|
-
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
98
|
-
</HTTPSamplerProxy>
|
99
|
-
<hashTree>
|
100
|
-
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="contains path" enabled="true">
|
101
|
-
<collectionProp name="Asserion.test_strings">
|
102
|
-
<stringProp name="-1604815031">dev/httpimagestore/compat</stringProp>
|
103
|
-
</collectionProp>
|
104
|
-
<stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
|
105
|
-
<boolProp name="Assertion.assume_success">false</boolProp>
|
106
|
-
<intProp name="Assertion.test_type">16</intProp>
|
107
|
-
</ResponseAssertion>
|
108
|
-
<hashTree/>
|
109
|
-
</hashTree>
|
110
|
-
</hashTree>
|
111
45
|
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Flexi API" enabled="true">
|
112
46
|
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
113
47
|
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
@@ -144,7 +78,7 @@
|
|
144
78
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
145
79
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
146
80
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
147
|
-
<stringProp name="HTTPSampler.path">/iss/v2/pictures</stringProp>
|
81
|
+
<stringProp name="HTTPSampler.path">/iss/v2/thumbnails/pictures</stringProp>
|
148
82
|
<stringProp name="HTTPSampler.method">POST</stringProp>
|
149
83
|
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
|
150
84
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
@@ -184,18 +118,18 @@
|
|
184
118
|
</hashTree>
|
185
119
|
<LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="iterations" enabled="true">
|
186
120
|
<boolProp name="LoopController.continue_forever">true</boolProp>
|
187
|
-
<stringProp name="LoopController.loops">
|
121
|
+
<stringProp name="LoopController.loops">1</stringProp>
|
188
122
|
</LoopController>
|
189
123
|
<hashTree>
|
190
124
|
<LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="specs counter" enabled="true">
|
191
125
|
<boolProp name="LoopController.continue_forever">true</boolProp>
|
192
|
-
<stringProp name="LoopController.loops">
|
126
|
+
<stringProp name="LoopController.loops">10</stringProp>
|
193
127
|
</LoopController>
|
194
128
|
<hashTree>
|
195
129
|
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="thumbnail specs" enabled="true">
|
196
|
-
<stringProp name="filename">
|
130
|
+
<stringProp name="filename">thumbnail_specs_v2.csv</stringProp>
|
197
131
|
<stringProp name="fileEncoding"></stringProp>
|
198
|
-
<stringProp name="variableNames">type,
|
132
|
+
<stringProp name="variableNames">type,operation,width,height,options</stringProp>
|
199
133
|
<stringProp name="delimiter">,</stringProp>
|
200
134
|
<boolProp name="quotedData">true</boolProp>
|
201
135
|
<boolProp name="recycle">true</boolProp>
|
@@ -203,7 +137,7 @@
|
|
203
137
|
<stringProp name="shareMode">Current thread</stringProp>
|
204
138
|
</CSVDataSet>
|
205
139
|
<hashTree/>
|
206
|
-
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="thumbnail
|
140
|
+
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="query string param thumbnail" enabled="true">
|
207
141
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
208
142
|
<collectionProp name="Arguments.arguments"/>
|
209
143
|
</elementProp>
|
@@ -213,9 +147,9 @@
|
|
213
147
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
214
148
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
215
149
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
216
|
-
<stringProp name="HTTPSampler.path">/iss/v2/
|
150
|
+
<stringProp name="HTTPSampler.path">/iss/v2/thumbnails/${__BeanShell(vars.get("store_path").replace("/:"\, "%2f"))}?operation=${operation}&width=${width}&height=${height}&options=${options}</stringProp>
|
217
151
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
218
|
-
<boolProp name="HTTPSampler.follow_redirects">
|
152
|
+
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
|
219
153
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
220
154
|
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
221
155
|
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
@@ -223,7 +157,7 @@
|
|
223
157
|
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
224
158
|
</HTTPSamplerProxy>
|
225
159
|
<hashTree>
|
226
|
-
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="
|
160
|
+
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="image type" enabled="true">
|
227
161
|
<collectionProp name="Asserion.test_strings">
|
228
162
|
<stringProp name="-722923736">Content-Type: image/</stringProp>
|
229
163
|
</collectionProp>
|
@@ -233,6 +167,45 @@
|
|
233
167
|
</ResponseAssertion>
|
234
168
|
<hashTree/>
|
235
169
|
</hashTree>
|
170
|
+
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="query string param thumbnail - data URI output" enabled="true">
|
171
|
+
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
172
|
+
<collectionProp name="Arguments.arguments"/>
|
173
|
+
</elementProp>
|
174
|
+
<stringProp name="HTTPSampler.domain"></stringProp>
|
175
|
+
<stringProp name="HTTPSampler.port"></stringProp>
|
176
|
+
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
177
|
+
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
178
|
+
<stringProp name="HTTPSampler.protocol"></stringProp>
|
179
|
+
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
180
|
+
<stringProp name="HTTPSampler.path">/iss/v2/thumbnails/${__BeanShell(vars.get("store_path").replace("/:"\, "%2f"))}?operation=${operation}&width=${width}&height=${height}&options=${options}&data-uri=true</stringProp>
|
181
|
+
<stringProp name="HTTPSampler.method">GET</stringProp>
|
182
|
+
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
|
183
|
+
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
184
|
+
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
185
|
+
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
186
|
+
<boolProp name="HTTPSampler.monitor">false</boolProp>
|
187
|
+
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
188
|
+
</HTTPSamplerProxy>
|
189
|
+
<hashTree>
|
190
|
+
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="text/uri-list" enabled="true">
|
191
|
+
<collectionProp name="Asserion.test_strings">
|
192
|
+
<stringProp name="530900877">Content-Type: text/uri-list</stringProp>
|
193
|
+
</collectionProp>
|
194
|
+
<stringProp name="Assertion.test_field">Assertion.response_headers</stringProp>
|
195
|
+
<boolProp name="Assertion.assume_success">false</boolProp>
|
196
|
+
<intProp name="Assertion.test_type">16</intProp>
|
197
|
+
</ResponseAssertion>
|
198
|
+
<hashTree/>
|
199
|
+
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="base64 response" enabled="true">
|
200
|
+
<collectionProp name="Asserion.test_strings">
|
201
|
+
<stringProp name="-586062814">;base64,</stringProp>
|
202
|
+
</collectionProp>
|
203
|
+
<stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
|
204
|
+
<boolProp name="Assertion.assume_success">false</boolProp>
|
205
|
+
<intProp name="Assertion.test_type">16</intProp>
|
206
|
+
</ResponseAssertion>
|
207
|
+
<hashTree/>
|
208
|
+
</hashTree>
|
236
209
|
</hashTree>
|
237
210
|
</hashTree>
|
238
211
|
<DebugPostProcessor guiclass="TestBeanGUI" testclass="DebugPostProcessor" testname="Debug PostProcessor" enabled="false">
|
@@ -0,0 +1,10 @@
|
|
1
|
+
"storage","limit","2160","2160","quality:95"
|
2
|
+
"crop","pad","98","148",""
|
3
|
+
"float-crop","crop","242","162","float-x:0.8,float-y:0.2"
|
4
|
+
"pad","pad","220","220","background-color:0xeeede8"
|
5
|
+
"pad2","pad","40","40","background-color:0xF0F0F0"
|
6
|
+
"pad3","pad","65","65","background-color:0xF0F0F0"
|
7
|
+
"fit","fit","65","65",""
|
8
|
+
"limit","limit","80","60",""
|
9
|
+
"float-pad","pad","50","50","float-x:0.8,float-y:0.2"
|
10
|
+
"large","limit","1200","900","background-color:white,interlace:PlaneInterlace"
|
@@ -3,6 +3,7 @@ require 'httpimagestore/configuration'
|
|
3
3
|
Configuration::Scope.logger = Logger.new('/dev/null')
|
4
4
|
|
5
5
|
require 'httpimagestore/configuration/file'
|
6
|
+
require 'httpimagestore/configuration/output'
|
6
7
|
MemoryLimit.logger = Logger.new('/dev/null')
|
7
8
|
|
8
9
|
describe Configuration do
|
@@ -62,7 +63,7 @@ describe Configuration do
|
|
62
63
|
io.write('hello world')
|
63
64
|
end
|
64
65
|
end
|
65
|
-
|
66
|
+
|
66
67
|
subject do
|
67
68
|
Configuration.read(<<-EOF)
|
68
69
|
path "image_name" "\#{image_name}.jpg"
|
@@ -155,6 +156,30 @@ describe Configuration do
|
|
155
156
|
}.to raise_error MemoryLimit::MemoryLimitedExceededError, 'memory limit exceeded'
|
156
157
|
end
|
157
158
|
end
|
159
|
+
|
160
|
+
context 'in failover context' do
|
161
|
+
subject do
|
162
|
+
Configuration.read(<<-EOF)
|
163
|
+
path "in" "test.in"
|
164
|
+
|
165
|
+
get "test" {
|
166
|
+
source_failover {
|
167
|
+
source_file "first_fail_1" root="/tmp/bogous" path="in"
|
168
|
+
source_file "first_fail_2" root="/tmp" path="in"
|
169
|
+
}
|
170
|
+
}
|
171
|
+
EOF
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'should source second image' do
|
175
|
+
subject.handlers[0].sources[0].should be_a Configuration::SourceFailover
|
176
|
+
subject.handlers[0].sources[0].realize(state)
|
177
|
+
|
178
|
+
state.images.keys.should == ['first_fail_2']
|
179
|
+
state.images['first_fail_2'].should_not be_nil
|
180
|
+
state.images['first_fail_2'].data.should == 'abc'
|
181
|
+
end
|
182
|
+
end
|
158
183
|
end
|
159
184
|
|
160
185
|
describe Configuration::FileStore do
|
@@ -242,7 +267,7 @@ describe Configuration do
|
|
242
267
|
|
243
268
|
state.images['input'].store_path.should == 'input.jpg'
|
244
269
|
end
|
245
|
-
|
270
|
+
|
246
271
|
it 'should provide image mime type based file extension to be used as #{image_mime_extension}' do
|
247
272
|
state.images['input'].mime_type = 'image/jpeg'
|
248
273
|
subject.handlers[0].stores[1].realize(state)
|
@@ -2,16 +2,17 @@ require_relative 'spec_helper'
|
|
2
2
|
require 'httpimagestore/configuration'
|
3
3
|
Configuration::Scope.logger = Logger.new('/dev/null')
|
4
4
|
|
5
|
+
require 'httpimagestore/configuration/output'
|
5
6
|
require 'httpimagestore/configuration/thumbnailer'
|
6
7
|
require 'httpimagestore/configuration/identify'
|
7
8
|
MemoryLimit.logger = Logger.new('/dev/null')
|
8
9
|
|
9
10
|
describe Configuration do
|
10
|
-
describe '
|
11
|
+
describe 'identify' do
|
11
12
|
before :all do
|
12
13
|
log = support_dir + 'server.log'
|
13
14
|
start_server(
|
14
|
-
"httpthumbnailer -f -d -l #{log}",
|
15
|
+
"httpthumbnailer -f -d -x XID -l #{log}",
|
15
16
|
'/tmp/httpthumbnailer.pid',
|
16
17
|
log,
|
17
18
|
'http://localhost:3100/'
|
@@ -39,6 +40,28 @@ describe Configuration do
|
|
39
40
|
subject.handlers[0].processors[0].realize(state)
|
40
41
|
state.images['input'].mime_type.should == 'image/jpeg'
|
41
42
|
end
|
43
|
+
|
44
|
+
describe 'passing HTTP headers to thumbnailer' do
|
45
|
+
let :xid do
|
46
|
+
rand(0..1000)
|
47
|
+
end
|
48
|
+
|
49
|
+
let :state do
|
50
|
+
Configuration::RequestState.new(
|
51
|
+
(support_dir + 'compute.jpg').read,
|
52
|
+
{}, '', {}, MemoryLimit.new,
|
53
|
+
{'XID' => xid}
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should pass headers provided with request state' do
|
58
|
+
subject.handlers[0].sources[0].realize(state)
|
59
|
+
subject.handlers[0].processors[0].realize(state)
|
60
|
+
state.images['input'].mime_type.should == 'image/jpeg'
|
61
|
+
|
62
|
+
(support_dir + 'server.log').read.should include "xid=\"#{xid}\""
|
63
|
+
end
|
64
|
+
end
|
42
65
|
end
|
43
66
|
end
|
44
67
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative 'spec_helper'
|
2
2
|
require 'aws-sdk'
|
3
3
|
require 'httpimagestore/configuration'
|
4
|
+
require 'httpimagestore/configuration/output'
|
4
5
|
Configuration::Scope.logger = Logger.new('/dev/null')
|
5
6
|
|
6
7
|
require 'httpimagestore/configuration/s3'
|
@@ -286,7 +287,7 @@ else
|
|
286
287
|
|
287
288
|
# should get updated
|
288
289
|
Digest::SHA2.new.update(cache_file.read).to_s.should_not == sum
|
289
|
-
|
290
|
+
|
290
291
|
sum = Digest::SHA2.new.update(cache_file.read).to_s
|
291
292
|
## read from cahce
|
292
293
|
subject.handlers[0].sources[3].realize(state)
|
@@ -299,7 +300,7 @@ else
|
|
299
300
|
let :state do
|
300
301
|
Configuration::RequestState.new('abc', {test_image: 'test.jpg'})
|
301
302
|
end
|
302
|
-
|
303
|
+
|
303
304
|
before :each do
|
304
305
|
@cache_file = Pathname.new('/tmp/af/a3/5eaf0a614693e2d1ed455ac1cedb')
|
305
306
|
@cache_file.dirname.mkpath
|
@@ -352,7 +353,7 @@ else
|
|
352
353
|
cache_file = Pathname.new('/tmp/a2/fd/4261e9a7586ed772d0c78bb51c9d')
|
353
354
|
cache_file.unlink if cache_file.exist?
|
354
355
|
|
355
|
-
state = Configuration::RequestState.new('abc', {test_image: '
|
356
|
+
state = Configuration::RequestState.new('abc', {test_image: 'bogus.jpg'})
|
356
357
|
|
357
358
|
expect {
|
358
359
|
subject.handlers[0].sources[0].realize(state)
|
@@ -551,6 +552,31 @@ else
|
|
551
552
|
}.to raise_error MemoryLimit::MemoryLimitedExceededError
|
552
553
|
end
|
553
554
|
end
|
555
|
+
|
556
|
+
context 'in failover context' do
|
557
|
+
subject do
|
558
|
+
Configuration.read(<<-EOF)
|
559
|
+
s3 key="#{ENV['AWS_ACCESS_KEY_ID']}" secret="#{ENV['AWS_SECRET_ACCESS_KEY']}"
|
560
|
+
path "hash" "\#{test_image}"
|
561
|
+
path "bogus" "bogus"
|
562
|
+
get {
|
563
|
+
source_failover {
|
564
|
+
source_s3 "first_fail_1" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="bogus"
|
565
|
+
source_s3 "first_fail_2" bucket="#{ENV['AWS_S3_TEST_BUCKET']}" path="hash"
|
566
|
+
}
|
567
|
+
}
|
568
|
+
EOF
|
569
|
+
end
|
570
|
+
|
571
|
+
it 'should source second image' do
|
572
|
+
subject.handlers[0].sources[0].should be_a Configuration::SourceFailover
|
573
|
+
subject.handlers[0].sources[0].realize(state)
|
574
|
+
|
575
|
+
state.images.keys.should == ['first_fail_2']
|
576
|
+
state.images['first_fail_2'].should_not be_nil
|
577
|
+
state.images['first_fail_2'].data.should == @test_data
|
578
|
+
end
|
579
|
+
end
|
554
580
|
end
|
555
581
|
|
556
582
|
describe Configuration::S3Store do
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'httpimagestore/configuration'
|
3
|
+
Configuration::Scope.logger = Logger.new('/dev/null')
|
4
|
+
|
5
|
+
require 'httpimagestore/configuration/source_failover'
|
6
|
+
require 'httpimagestore/configuration/file'
|
7
|
+
require 'httpimagestore/configuration/output'
|
8
|
+
MemoryLimit.logger = Logger.new('/dev/null')
|
9
|
+
|
10
|
+
describe Configuration do
|
11
|
+
let :state do
|
12
|
+
Configuration::RequestState.new('abc')
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Configuration::FileSource do
|
16
|
+
subject do
|
17
|
+
Configuration.read(<<-EOF)
|
18
|
+
path "in" "test.in"
|
19
|
+
|
20
|
+
get "test" {
|
21
|
+
source_failover {
|
22
|
+
source_file "no_fail_1" root="/tmp" path="in"
|
23
|
+
source_file "no_fail_2" root="/tmp" path="in"
|
24
|
+
}
|
25
|
+
|
26
|
+
source_failover {
|
27
|
+
source_file "first_fail_1" root="/tmp/bogous" path="in"
|
28
|
+
source_file "first_fail_2" root="/tmp" path="in"
|
29
|
+
}
|
30
|
+
|
31
|
+
source_failover {
|
32
|
+
source_file "all_fail" root="/tmp/bogous" path="in"
|
33
|
+
source_file "all_fail" root="/tmp/bogous2" path="in"
|
34
|
+
}
|
35
|
+
|
36
|
+
source_failover {
|
37
|
+
source_file "deep_fail_1" root="/tmp/bogous" path="in"
|
38
|
+
source_file "deep_fail_2" root="/tmp/bogous" path="in"
|
39
|
+
source_file "deep_fail_3" root="/tmp" path="in"
|
40
|
+
source_file "deep_fail_4" root="/tmp/bogous" path="in"
|
41
|
+
}
|
42
|
+
}
|
43
|
+
EOF
|
44
|
+
end
|
45
|
+
|
46
|
+
let :in_file do
|
47
|
+
Pathname.new("/tmp/test.in")
|
48
|
+
end
|
49
|
+
|
50
|
+
before :each do
|
51
|
+
in_file.open('w'){|io| io.write('abc')}
|
52
|
+
end
|
53
|
+
|
54
|
+
after :each do
|
55
|
+
in_file.unlink
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'first source is OK' do
|
59
|
+
it 'should source first image' do
|
60
|
+
subject.handlers[0].sources[0].should be_a Configuration::SourceFailover
|
61
|
+
subject.handlers[0].sources[0].realize(state)
|
62
|
+
|
63
|
+
state.images.keys.should == ['no_fail_1']
|
64
|
+
state.images['no_fail_1'].should_not be_nil
|
65
|
+
state.images['no_fail_1'].data.should == 'abc'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'second source is OK' do
|
70
|
+
it 'should source second image' do
|
71
|
+
subject.handlers[0].sources[1].should be_a Configuration::SourceFailover
|
72
|
+
subject.handlers[0].sources[1].realize(state)
|
73
|
+
|
74
|
+
state.images.keys.should == ['first_fail_2']
|
75
|
+
state.images['first_fail_2'].should_not be_nil
|
76
|
+
state.images['first_fail_2'].data.should == 'abc'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'all sources fail' do
|
81
|
+
it 'should raise error Configuration::SourceFailoverAllFailedError' do
|
82
|
+
subject.handlers[0].sources[2].should be_a Configuration::SourceFailover
|
83
|
+
|
84
|
+
expect {
|
85
|
+
subject.handlers[0].sources[2].realize(state)
|
86
|
+
}.to raise_error Configuration::SourceFailoverAllFailedError, "all sources failed: FileSource[image_name: 'all_fail' root_dir: '/tmp/bogous' path_spec: 'in'](Configuration::NoSuchFileError: error while processing image 'all_fail': file 'test.in' not found), FileSource[image_name: 'all_fail' root_dir: '/tmp/bogous2' path_spec: 'in'](Configuration::NoSuchFileError: error while processing image 'all_fail': file 'test.in' not found)"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'tird source is OK' do
|
91
|
+
it 'should source second image' do
|
92
|
+
subject.handlers[0].sources[3].should be_a Configuration::SourceFailover
|
93
|
+
subject.handlers[0].sources[3].realize(state)
|
94
|
+
|
95
|
+
state.images.keys.should == ['deep_fail_3']
|
96
|
+
state.images['deep_fail_3'].should_not be_nil
|
97
|
+
state.images['deep_fail_3'].data.should == 'abc'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative 'spec_helper'
|
2
2
|
require 'httpimagestore/configuration'
|
3
|
+
require 'httpimagestore/configuration/output'
|
3
4
|
Configuration::Scope.logger = Logger.new('/dev/null')
|
4
5
|
|
5
6
|
require 'httpimagestore/configuration/thumbnailer'
|
@@ -30,7 +31,7 @@ describe Configuration do
|
|
30
31
|
subject = Configuration.read(<<-EOF, thumbnailer_url: 'http://1.1.1.1:8080')
|
31
32
|
thumbnailer url="http://2.2.2.2:1000"
|
32
33
|
EOF
|
33
|
-
|
34
|
+
|
34
35
|
subject.thumbnailer.server_url.should == 'http://2.2.2.2:1000'
|
35
36
|
end
|
36
37
|
|
@@ -58,7 +59,7 @@ describe Configuration do
|
|
58
59
|
describe 'should provide thumbnail spec array based on given locals' do
|
59
60
|
it 'where image name is hash key pointing to array where all values are string and options is hash' do
|
60
61
|
Configuration::Thumbnail::ThumbnailSpec.new('small', 'pad', 100, 100, 'jpeg', 'background-color' => 'red').render.should == {
|
61
|
-
'small' =>
|
62
|
+
'small' =>
|
62
63
|
['pad', '100', '100', 'jpeg', {'background-color' => 'red'}]
|
63
64
|
}
|
64
65
|
end
|
@@ -74,7 +75,7 @@ describe Configuration do
|
|
74
75
|
}
|
75
76
|
|
76
77
|
Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}', 'background-color' => '#{bg}').render(locals).should == {
|
77
|
-
'small' =>
|
78
|
+
'small' =>
|
78
79
|
['fit', '99', '66', 'png', {'background-color' => 'white'}]
|
79
80
|
}
|
80
81
|
end
|
@@ -89,7 +90,7 @@ describe Configuration do
|
|
89
90
|
}
|
90
91
|
|
91
92
|
Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}', 'options' => '#{opts}', 'background-color' => 'red').render(locals).should == {
|
92
|
-
'small' =>
|
93
|
+
'small' =>
|
93
94
|
['fit', '99', '66', 'png', {'background-color' => 'red', 'quality' => '100'}]
|
94
95
|
}
|
95
96
|
end
|
@@ -126,7 +127,7 @@ describe Configuration do
|
|
126
127
|
before :all do
|
127
128
|
log = support_dir + 'server.log'
|
128
129
|
start_server(
|
129
|
-
"httpthumbnailer -f -d -l #{log}",
|
130
|
+
"httpthumbnailer -f -d -x XID -l #{log}",
|
130
131
|
'/tmp/httpthumbnailer.pid',
|
131
132
|
log,
|
132
133
|
'http://localhost:3100/'
|
@@ -207,6 +208,32 @@ describe Configuration do
|
|
207
208
|
end
|
208
209
|
end
|
209
210
|
|
211
|
+
describe 'passing HTTP headers to thumbnailer' do
|
212
|
+
let :xid do
|
213
|
+
rand(0..1000)
|
214
|
+
end
|
215
|
+
|
216
|
+
let :state do
|
217
|
+
Configuration::RequestState.new(
|
218
|
+
(support_dir + 'compute.jpg').read,
|
219
|
+
{
|
220
|
+
operation: 'pad',
|
221
|
+
width: '10',
|
222
|
+
height: '10',
|
223
|
+
options: 'background-color:green'
|
224
|
+
},
|
225
|
+
'', {}, MemoryLimit.new,
|
226
|
+
{'XID' => xid}
|
227
|
+
)
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should pass headers provided with request state' do
|
231
|
+
subject.handlers[0].processors[0].realize(state)
|
232
|
+
|
233
|
+
(support_dir + 'server.log').read.should include "xid=\"#{xid}\""
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
210
237
|
describe 'error handling' do
|
211
238
|
it 'should raise Thumbnail::ThumbnailingError on realization of bad thumbnail sepc' do
|
212
239
|
state = Configuration::RequestState.new(
|
@@ -318,7 +345,7 @@ describe Configuration do
|
|
318
345
|
end
|
319
346
|
|
320
347
|
describe 'conditional inclusion support' do
|
321
|
-
subject do
|
348
|
+
subject do
|
322
349
|
Configuration.read(<<-'EOF')
|
323
350
|
put {
|
324
351
|
thumbnail "input" {
|
@@ -351,6 +378,34 @@ describe Configuration do
|
|
351
378
|
end
|
352
379
|
end
|
353
380
|
|
381
|
+
describe 'passing HTTP headers to thumbnailer' do
|
382
|
+
let :xid do
|
383
|
+
rand(0..1000)
|
384
|
+
end
|
385
|
+
|
386
|
+
let :state do
|
387
|
+
Configuration::RequestState.new(
|
388
|
+
(support_dir + 'compute.jpg').read,
|
389
|
+
{
|
390
|
+
operation: 'pad',
|
391
|
+
width: '10',
|
392
|
+
height: '10',
|
393
|
+
options: 'background-color:green'
|
394
|
+
},
|
395
|
+
'', {}, MemoryLimit.new,
|
396
|
+
{'XID' => xid}
|
397
|
+
)
|
398
|
+
end
|
399
|
+
|
400
|
+
it 'should pass headers provided with request state' do
|
401
|
+
subject.handlers[0].processors[0].realize(state)
|
402
|
+
state.images.keys.should include 'original'
|
403
|
+
state.images.keys.should include 'small'
|
404
|
+
|
405
|
+
(support_dir + 'server.log').read.should include "xid=\"#{xid}\""
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
354
409
|
describe 'error handling' do
|
355
410
|
it 'should raise Thumbnail::ThumbnailingError on realization of bad thumbnail sepc' do
|
356
411
|
state = Configuration::RequestState.new(
|
@@ -400,10 +455,10 @@ describe Configuration do
|
|
400
455
|
thumbnail "input1" "thumbnail1" if-image-name-on="#{list}"
|
401
456
|
thumbnail "input2" "thumbnail2" if-image-name-on="#{list}"
|
402
457
|
thumbnail "input3" if-image-name-on="#{list}" {
|
403
|
-
"thumbnail3"
|
458
|
+
"thumbnail3"
|
404
459
|
}
|
405
460
|
thumbnail "input4" if-image-name-on="#{list}" {
|
406
|
-
"thumbnail4"
|
461
|
+
"thumbnail4"
|
407
462
|
}
|
408
463
|
thumbnail "input5" if-image-name-on="#{list}" {
|
409
464
|
"thumbnail5" if-image-name-on="#{list}"
|
@@ -16,6 +16,10 @@ describe RubyStringTemplate do
|
|
16
16
|
}.to raise_error RubyStringTemplate::NoValueForTemplatePlaceholderError, %q{no value for '#{world}' in template '>#{hello}-#{world}#{test}<'}
|
17
17
|
end
|
18
18
|
|
19
|
+
it 'should not process nested placeholders' do
|
20
|
+
subject.render(hello: '#{nested}', world: 'world', test: 123, nested: 'xxx').should == '>#{nested}-world123<'
|
21
|
+
end
|
22
|
+
|
19
23
|
describe 'with custom resolver' do
|
20
24
|
subject do
|
21
25
|
RubyStringTemplate.new('>#{hello}-#{world}#{test}<') do |locals, name|
|