httpimagestore 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/Gemfile +4 -3
  2. data/Gemfile.lock +12 -16
  3. data/README.md +73 -0
  4. data/VERSION +1 -1
  5. data/bin/httpimagestore +7 -7
  6. data/features/data-uri.feature +55 -0
  7. data/features/error-reporting.feature +21 -3
  8. data/features/source-failover.feature +71 -0
  9. data/features/step_definitions/httpimagestore_steps.rb +27 -12
  10. data/features/storage.feature +26 -25
  11. data/features/support/tiny.png +0 -0
  12. data/features/xid-forwarding.feature +49 -0
  13. data/httpimagestore.gemspec +19 -23
  14. data/lib/httpimagestore/configuration/file.rb +6 -0
  15. data/lib/httpimagestore/configuration/handler.rb +4 -1
  16. data/lib/httpimagestore/configuration/identify.rb +2 -2
  17. data/lib/httpimagestore/configuration/output.rb +20 -1
  18. data/lib/httpimagestore/configuration/s3.rb +15 -9
  19. data/lib/httpimagestore/configuration/source_failover.rb +51 -0
  20. data/lib/httpimagestore/configuration/thumbnailer.rb +7 -7
  21. data/lib/httpimagestore/error_reporter.rb +9 -1
  22. data/lib/httpimagestore/ruby_string_template.rb +12 -7
  23. data/load_test/load_test.jmx +50 -77
  24. data/load_test/thumbnail_specs_v2.csv +10 -0
  25. data/spec/configuration_file_spec.rb +27 -2
  26. data/spec/configuration_identify_spec.rb +25 -2
  27. data/spec/configuration_s3_spec.rb +29 -3
  28. data/spec/configuration_source_failover_spec.rb +101 -0
  29. data/spec/configuration_thumbnailer_spec.rb +63 -8
  30. data/spec/ruby_string_template_spec.rb +4 -0
  31. data/spec/support/full.cfg +167 -33
  32. metadata +19 -23
  33. data/.idea/.name +0 -1
  34. data/.idea/.rakeTasks +0 -7
  35. data/.idea/codeStyleSettings.xml +0 -13
  36. data/.idea/dictionaries/wcc.xml +0 -8
  37. data/.idea/encodings.xml +0 -5
  38. data/.idea/httpimagestore.iml +0 -69
  39. data/.idea/jenkinsSettings.xml +0 -9
  40. data/.idea/misc.xml +0 -5
  41. data/.idea/modules.xml +0 -9
  42. data/.idea/scopes/scope_settings.xml +0 -5
  43. data/.idea/vcs.xml +0 -7
@@ -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">test.iss.whatclinic.com</stringProp>
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)}/Documents/test_data</stringProp>
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">4</stringProp>
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">11</stringProp>
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">thumbnail_specs.csv</stringProp>
130
+ <stringProp name="filename">thumbnail_specs_v2.csv</stringProp>
197
131
  <stringProp name="fileEncoding"></stringProp>
198
- <stringProp name="variableNames">type,method,width,height,options</stringProp>
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 fit" enabled="true">
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/pictures/${__BeanShell(vars.get(&quot;store_path&quot;).replace(&quot;/:&quot;\, &quot;%2f&quot;))}?type=${type}</stringProp>
150
+ <stringProp name="HTTPSampler.path">/iss/v2/thumbnails/${__BeanShell(vars.get(&quot;store_path&quot;).replace(&quot;/:&quot;\, &quot;%2f&quot;))}?operation=${operation}&amp;width=${width}&amp;height=${height}&amp;options=${options}</stringProp>
217
151
  <stringProp name="HTTPSampler.method">GET</stringProp>
218
- <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
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="Response Assertion" enabled="true">
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(&quot;store_path&quot;).replace(&quot;/:&quot;\, &quot;%2f&quot;))}?operation=${operation}&amp;width=${width}&amp;height=${height}&amp;options=${options}&amp;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 'thumbnailer' do
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: 'bogous.jpg'})
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|