httpimagestore 1.6.0 → 1.7.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 +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|
|