httpimagestore 1.8.1 → 1.9.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.
Files changed (64) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +7 -7
  3. data/Gemfile.lock +20 -20
  4. data/README.md +165 -37
  5. data/Rakefile +7 -2
  6. data/VERSION +1 -1
  7. data/bin/httpimagestore +74 -41
  8. data/lib/httpimagestore/configuration/file.rb +20 -11
  9. data/lib/httpimagestore/configuration/handler.rb +96 -257
  10. data/lib/httpimagestore/configuration/handler/source_store_base.rb +37 -0
  11. data/lib/httpimagestore/configuration/handler/statement.rb +114 -0
  12. data/lib/httpimagestore/configuration/identify.rb +17 -9
  13. data/lib/httpimagestore/configuration/output.rb +33 -61
  14. data/lib/httpimagestore/configuration/path.rb +2 -2
  15. data/lib/httpimagestore/configuration/request_state.rb +131 -0
  16. data/lib/httpimagestore/configuration/s3.rb +41 -29
  17. data/lib/httpimagestore/configuration/thumbnailer.rb +189 -96
  18. data/lib/httpimagestore/configuration/validate_hmac.rb +170 -0
  19. data/lib/httpimagestore/error_reporter.rb +6 -1
  20. data/lib/httpimagestore/ruby_string_template.rb +10 -19
  21. metadata +40 -102
  22. data/.rspec +0 -1
  23. data/features/cache-control.feature +0 -41
  24. data/features/compatibility.feature +0 -165
  25. data/features/data-uri.feature +0 -55
  26. data/features/encoding.feature +0 -103
  27. data/features/error-reporting.feature +0 -281
  28. data/features/flexi.feature +0 -259
  29. data/features/health-check.feature +0 -29
  30. data/features/request-matching.feature +0 -211
  31. data/features/rewrite.feature +0 -122
  32. data/features/s3-store-and-thumbnail.feature +0 -82
  33. data/features/source-failover.feature +0 -71
  34. data/features/step_definitions/httpimagestore_steps.rb +0 -203
  35. data/features/storage.feature +0 -198
  36. data/features/support/env.rb +0 -116
  37. data/features/support/test-large.jpg +0 -0
  38. data/features/support/test.empty +0 -0
  39. data/features/support/test.jpg +0 -0
  40. data/features/support/test.png +0 -0
  41. data/features/support/test.txt +0 -1
  42. data/features/support/tiny.png +0 -0
  43. data/features/xid-forwarding.feature +0 -49
  44. data/httpimagestore.gemspec +0 -145
  45. data/load_test/load_test.1k.23a022f6e.m1.small-comp.csv +0 -3
  46. data/load_test/load_test.1k.ec9bde794.m1.small.csv +0 -4
  47. data/load_test/load_test.jmx +0 -317
  48. data/load_test/thumbnail_specs.csv +0 -11
  49. data/load_test/thumbnail_specs_v2.csv +0 -10
  50. data/spec/configuration_file_spec.rb +0 -333
  51. data/spec/configuration_handler_spec.rb +0 -255
  52. data/spec/configuration_identify_spec.rb +0 -67
  53. data/spec/configuration_output_spec.rb +0 -821
  54. data/spec/configuration_path_spec.rb +0 -138
  55. data/spec/configuration_s3_spec.rb +0 -911
  56. data/spec/configuration_source_failover_spec.rb +0 -101
  57. data/spec/configuration_spec.rb +0 -90
  58. data/spec/configuration_thumbnailer_spec.rb +0 -483
  59. data/spec/ruby_string_template_spec.rb +0 -61
  60. data/spec/spec_helper.rb +0 -89
  61. data/spec/support/compute.jpg +0 -0
  62. data/spec/support/cuba_response_env.rb +0 -40
  63. data/spec/support/full.cfg +0 -183
  64. data/spec/support/utf_string.txt +0 -1
@@ -1,255 +0,0 @@
1
- require_relative 'spec_helper'
2
- require_relative 'support/cuba_response_env'
3
-
4
- require 'httpimagestore/configuration'
5
- MemoryLimit.logger = Configuration::Scope.logger = RootLogger.new('/dev/null')
6
-
7
- require 'httpimagestore/configuration/handler'
8
- require 'httpimagestore/configuration/output'
9
-
10
- describe Configuration do
11
- describe Configuration::Handler do
12
- subject do
13
- Configuration.read(<<-EOF)
14
- get "thumbnail" "v1" ":operation" ":width" ":height" ":options" {
15
- }
16
-
17
- put "thumbnail" "v1" ":test/.+/" {
18
- }
19
-
20
- post {
21
- }
22
- EOF
23
- end
24
-
25
- describe 'http method and matchers' do
26
- it 'should provide request http_method and uri_matchers' do
27
- subject.handlers.length.should == 3
28
-
29
- subject.handlers[0].http_method.should == 'get'
30
- subject.handlers[0].uri_matchers.map{|m| m.names}.flatten.should == [:operation, :width, :height, :options]
31
-
32
- subject.handlers[1].http_method.should == 'put'
33
- subject.handlers[1].uri_matchers.map{|m| m.names}.flatten.should == [:test]
34
-
35
- subject.handlers[2].http_method.should == 'post'
36
- subject.handlers[2].uri_matchers.should be_empty
37
- end
38
- end
39
-
40
- describe Configuration::RequestState do
41
- subject do
42
- Configuration::RequestState.new(
43
- 'test',
44
- {operation: 'pad'},
45
- '/hello/world.jpg',
46
- {width: '123', height: '321'}
47
- )
48
- end
49
-
50
- it 'should behave like hash' do
51
- subject['a'] = 'b'
52
- subject['a'].should == 'b'
53
- end
54
-
55
- it 'should provide body' do
56
- subject.body.should == 'test'
57
- end
58
-
59
- describe 'variables' do
60
- it 'should provide path' do
61
- subject[:path].should == '/hello/world.jpg'
62
- end
63
-
64
- it 'should provide matches' do
65
- subject[:operation].should == 'pad'
66
- end
67
-
68
- it 'should provide query_string_options' do
69
- subject[:query_string_options].should == 'height:321,width:123'
70
- end
71
-
72
- it 'should provide request body digest' do
73
- subject[:digest].should == '9f86d081884c7d65' # deprecated
74
- subject[:input_digest].should == '9f86d081884c7d65'
75
- end
76
-
77
- it 'should provide request body full sha256 checsum' do
78
- subject[:input_sha256].should == '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'
79
- end
80
-
81
- it 'should provide input image mime extension' do
82
- subject.images['input'] = Struct.new(:data, :mime_type).new('image body', 'image/jpeg')
83
- subject.images['input'].extend Configuration::ImageMetaData
84
- subject[:input_image_mime_extension].should == 'jpg'
85
- end
86
-
87
- it 'should provide input image width and height' do
88
- subject.images['input'] = Struct.new(:data, :width, :height).new('image body', 128, 256)
89
- subject[:input_image_width].should == 128
90
- subject[:input_image_height].should == 256
91
- end
92
-
93
- it 'should provide image body digest' do
94
- subject.images['abc'] = Struct.new(:data).new('image body')
95
- subject.with_locals(image_name: 'abc')[:image_digest].should == 'f5288dd892bb007b'
96
- end
97
-
98
- it 'should provide image body full sha256 checsum' do
99
- subject.images['abc'] = Struct.new(:data).new('image body')
100
- subject.with_locals(image_name: 'abc')[:image_sha256].should == 'f5288dd892bb007b607304a8fb20c91ea769dcd04d82cc8ddf3239602867eb4d'
101
- end
102
-
103
- it 'should provide uuid' do
104
- subject[:uuid].should_not be_empty
105
- subject[:uuid].should == subject[:uuid]
106
- end
107
-
108
- it 'should provide extension' do
109
- subject[:extension].should == 'jpg'
110
- end
111
-
112
- it 'should provide dirname' do
113
- subject[:dirname].should == '/hello'
114
- end
115
-
116
- it 'should provide basename' do
117
- subject[:basename].should == 'world'
118
- end
119
-
120
- it 'should provide image mime extension' do
121
- subject.images['abc'] = Struct.new(:data, :mime_type).new('image body', 'image/jpeg')
122
- subject.images['abc'].extend Configuration::ImageMetaData
123
- subject.with_locals(image_name: 'abc')[:mimeextension].should == 'jpg' # deprecated
124
- subject.with_locals(image_name: 'abc')[:image_mime_extension].should == 'jpg'
125
- end
126
-
127
- it 'should provide image width and height' do
128
- subject.images['abc'] = Struct.new(:data, :width, :height).new('image body', 128, 256)
129
- subject.with_locals(image_name: 'abc')[:image_width].should == 128
130
- subject.with_locals(image_name: 'abc')[:image_height].should == 256
131
- end
132
-
133
- describe "error handling" do
134
- it 'should raise NoRequestBodyToGenerateMetaVariableError when empty body was provided and body needed for variable calculation' do
135
- subject = Configuration::RequestState.new(
136
- '',
137
- {operation: 'pad'},
138
- '/hello/world.jpg',
139
- {width: '123', height: '321'}
140
- )
141
-
142
- expect {
143
- subject[:input_digest]
144
- }.to raise_error Configuration::NoRequestBodyToGenerateMetaVariableError, %q{need not empty request body to generate value for 'input_digest'}
145
- end
146
-
147
- it 'should raise ImageNotLoadedError when asking for image related variable of not loaded image' do
148
- expect {
149
- subject.with_locals(image_name: 'abc')[:image_mime_extension]
150
- }.to raise_error Configuration::ImageNotLoadedError, %q{image 'abc' not loaded}
151
- end
152
-
153
- it 'should raise NoVariableToGenerateMetaVariableError when no image_name was defined' do
154
- expect {
155
- subject.with_locals({})[:image_mime_extension]
156
- }.to raise_error Configuration::NoVariableToGenerateMetaVariableError, %q{need 'image_name' variable to generate value for 'image_mime_extension'}
157
- end
158
-
159
- it 'should raise NoVariableToGenerateMetaVariableError when no path was defined' do
160
- expect {
161
- subject.delete(:path)
162
- subject[:basename]
163
- }.to raise_error Configuration::NoVariableToGenerateMetaVariableError, %q{need 'path' variable to generate value for 'basename'}
164
- end
165
-
166
- it 'should raise NoImageDataForVariableError when image has no mime type' do
167
- subject.images['abc'] = Struct.new(:data, :mime_type).new('image body', nil)
168
- subject.images['abc'].extend Configuration::ImageMetaData
169
- expect {
170
- subject.with_locals(image_name: 'abc')[:image_mime_extension]
171
- }.to raise_error Configuration::NoImageDataForVariableError, %q{image 'abc' does not have data for variable 'image_mime_extension'}
172
- end
173
-
174
- it 'should raise NoImageDataForVariableError when image has no known width or height' do
175
- subject.images['abc'] = Struct.new(:data, :width, :height).new('image body', nil, nil)
176
- expect {
177
- subject.with_locals(image_name: 'abc')[:image_width]
178
- }.to raise_error Configuration::NoImageDataForVariableError, %q{image 'abc' does not have data for variable 'image_width'}
179
- expect {
180
- subject.with_locals(image_name: 'abc')[:image_height]
181
- }.to raise_error Configuration::NoImageDataForVariableError, %q{image 'abc' does not have data for variable 'image_height'}
182
- end
183
- end
184
- end
185
-
186
- it 'should raise ImageNotLoadedError if image lookup fails' do
187
- expect {
188
- Configuration::RequestState.new.images['test']
189
- }.to raise_error Configuration::ImageNotLoadedError, "image 'test' not loaded"
190
- end
191
-
192
- it 'should free memory limit if overwritting image' do
193
- limit = MemoryLimit.new(2)
194
- request_state = Configuration::RequestState.new('abc', {}, '', {}, limit)
195
-
196
- limit.borrow 1
197
- request_state.images['test'] = Configuration::Image.new('x')
198
- limit.limit.should == 1
199
-
200
- limit.borrow 1
201
- limit.limit.should == 0
202
- request_state.images['test'] = Configuration::Image.new('x')
203
- limit.limit.should == 1
204
-
205
- limit.borrow 1
206
- limit.limit.should == 0
207
- request_state.images['test'] = Configuration::Image.new('x')
208
- limit.limit.should == 1
209
-
210
- limit.borrow 1
211
- limit.limit.should == 0
212
- request_state.images['test2'] = Configuration::Image.new('x')
213
- limit.limit.should == 0
214
- end
215
- end
216
-
217
- describe 'sources' do
218
- it 'should have implicit InputSource on non get handlers' do
219
- subject.handlers[0].sources.first.should_not be_a Configuration::InputSource
220
- subject.handlers[1].sources.first.should be_a Configuration::InputSource
221
- subject.handlers[2].sources.first.should be_a Configuration::InputSource
222
- end
223
-
224
- describe Configuration::InputSource do
225
- it 'should copy input data to "input" image when realized' do
226
- state = Configuration::RequestState.new('abc')
227
- input_source = subject.handlers[1].sources[0].realize(state)
228
- state.images['input'].data.should == 'abc'
229
- end
230
-
231
- it 'should have nil mime type' do
232
- state = Configuration::RequestState.new('abc')
233
- input_source = subject.handlers[1].sources[0].realize(state)
234
- state.images['input'].mime_type.should be_nil
235
- end
236
-
237
- it 'should have nil source path and url' do
238
- state = Configuration::RequestState.new('abc')
239
- input_source = subject.handlers[1].sources[0].realize(state)
240
- state.images['input'].source_path.should be_nil
241
- state.images['input'].source_url.should be_nil
242
- end
243
- end
244
- end
245
-
246
- describe 'output' do
247
- it 'should default to OutputOK' do
248
- subject.handlers[0].output.should be_a Configuration::OutputOK
249
- subject.handlers[1].output.should be_a Configuration::OutputOK
250
- subject.handlers[2].output.should be_a Configuration::OutputOK
251
- end
252
- end
253
- end
254
- end
255
-
@@ -1,67 +0,0 @@
1
- require_relative 'spec_helper'
2
- require 'httpimagestore/configuration'
3
- MemoryLimit.logger = Configuration::Scope.logger = RootLogger.new('/dev/null')
4
-
5
- require 'httpimagestore/configuration/output'
6
- require 'httpimagestore/configuration/thumbnailer'
7
- require 'httpimagestore/configuration/identify'
8
-
9
- describe Configuration do
10
- describe 'identify' do
11
- before :all do
12
- log = support_dir + 'server.log'
13
- start_server(
14
- "httpthumbnailer -f -d -x XID -l #{log}",
15
- '/tmp/httpthumbnailer.pid',
16
- log,
17
- 'http://localhost:3100/'
18
- )
19
- end
20
-
21
- let :state do
22
- Configuration::RequestState.new(
23
- (support_dir + 'compute.jpg').read
24
- )
25
- end
26
-
27
- subject do
28
- Configuration.read(<<-'EOF')
29
- put {
30
- identify "input"
31
- }
32
- EOF
33
- end
34
-
35
- it 'should provide input image mime type' do
36
- subject.handlers[0].sources[0].realize(state)
37
- state.images['input'].mime_type.should be_nil
38
-
39
- subject.handlers[0].processors[0].realize(state)
40
- state.images['input'].mime_type.should == 'image/jpeg'
41
- end
42
-
43
- describe 'passing HTTP headers to thumbnailer' do
44
- let :xid do
45
- rand(0..1000)
46
- end
47
-
48
- let :state do
49
- Configuration::RequestState.new(
50
- (support_dir + 'compute.jpg').read,
51
- {}, '', {}, MemoryLimit.new,
52
- {'XID' => xid}
53
- )
54
- end
55
-
56
- it 'should pass headers provided with request state' do
57
- subject.handlers[0].sources[0].realize(state)
58
- subject.handlers[0].processors[0].realize(state)
59
- state.images['input'].mime_type.should == 'image/jpeg'
60
-
61
- (support_dir + 'server.log').read.should include "xid=\"#{xid}\""
62
- end
63
- end
64
- end
65
- end
66
-
67
-
@@ -1,821 +0,0 @@
1
- require_relative 'spec_helper'
2
- require_relative 'support/cuba_response_env'
3
-
4
- require 'httpimagestore/configuration'
5
- MemoryLimit.logger = Configuration::Scope.logger = RootLogger.new('/dev/null')
6
-
7
- require 'httpimagestore/configuration/output'
8
- require 'httpimagestore/configuration/file'
9
-
10
- describe Configuration do
11
- let :state do
12
- Configuration::RequestState.new('abc')
13
- end
14
-
15
- let :env do
16
- CubaResponseEnv.new
17
- end
18
-
19
- describe Configuration::OutputText do
20
- subject do
21
- Configuration.read(<<-'EOF')
22
- get "test" {
23
- output_text "hello world"
24
- }
25
- get "test" {
26
- output_text "bad stuff" status=500
27
- }
28
- get "test" {
29
- output_text "welcome" cache-control="public"
30
- }
31
- get "test" {
32
- output_text "test1: #{test1} test2: #{test2}"
33
- }
34
- EOF
35
- end
36
-
37
- it 'should output hello world with default 200 status' do
38
- subject.handlers[0].output.realize(state)
39
- env = CubaResponseEnv.new
40
- env.instance_eval &state.output_callback
41
- env.res.status.should == 200
42
- env.res.data.should == "hello world\r\n"
43
- env.res['Content-Type'].should == 'text/plain'
44
- env.res['Cache-Control'].should be_nil
45
- end
46
-
47
- it 'should output bad stuff with 500 status' do
48
- subject.handlers[1].output.realize(state)
49
- env = CubaResponseEnv.new
50
- env.instance_eval &state.output_callback
51
- env.res.status.should == 500
52
- env.res.data.should == "bad stuff\r\n"
53
- env.res['Content-Type'].should == 'text/plain'
54
- env.res['Cache-Control'].should be_nil
55
- end
56
-
57
- it 'should output welcome with public cache control' do
58
- subject.handlers[2].output.realize(state)
59
- env = CubaResponseEnv.new
60
- env.instance_eval &state.output_callback
61
- env.res.status.should == 200
62
- env.res.data.should == "welcome\r\n"
63
- env.res['Content-Type'].should == 'text/plain'
64
- env.res['Cache-Control'].should == 'public'
65
- end
66
-
67
- it 'should output text interpolated with variable values' do
68
- state = Configuration::RequestState.new
69
- state[:test1] = 'abc'
70
- state[:test2] = 'xyz'
71
-
72
- subject.handlers[3].output.realize(state)
73
- env = CubaResponseEnv.new
74
- env.instance_eval &state.output_callback
75
- env.res.data.should == "test1: abc test2: xyz\r\n"
76
- end
77
- end
78
-
79
- describe Configuration::OutputOK do
80
- subject do
81
- Configuration.read(<<-EOF)
82
- put "test" {
83
- output_ok
84
- }
85
- EOF
86
- end
87
-
88
- before :each do
89
- subject.handlers[0].sources[0].realize(state)
90
- end
91
-
92
- it 'should output 200 with OK text/plain message when realized' do
93
- state = Configuration::RequestState.new('abc')
94
- subject.handlers[0].output.realize(state)
95
-
96
- env = CubaResponseEnv.new
97
- env.instance_eval &state.output_callback
98
- env.res.status.should == 200
99
- env.res.data.should == "OK\r\n"
100
- env.res['Content-Type'].should == 'text/plain'
101
- end
102
- end
103
-
104
- describe Configuration::OutputImage do
105
- subject do
106
- Configuration.read(<<-EOF)
107
- put "test" {
108
- output_image "input"
109
- }
110
- EOF
111
- end
112
-
113
- before :each do
114
- subject.handlers[0].sources[0].realize(state)
115
- end
116
-
117
- it 'should provide given image' do
118
- subject.handlers[0].output.should be_a Configuration::OutputImage
119
- subject.handlers[0].output.realize(state)
120
-
121
- env.instance_eval &state.output_callback
122
- env.res.status.should == 200
123
- env.res.data.should == 'abc'
124
- end
125
-
126
- it 'should use default content type if not defined on image' do
127
- subject.handlers[0].output.realize(state)
128
-
129
- env.instance_eval &state.output_callback
130
- env.res['Content-Type'].should == 'application/octet-stream'
131
- end
132
-
133
- it 'should use image mime type if available' do
134
- state.images['input'].mime_type = 'image/jpeg'
135
-
136
- subject.handlers[0].output.realize(state)
137
-
138
- env.instance_eval &state.output_callback
139
- env.res['Content-Type'].should == 'image/jpeg'
140
- end
141
-
142
- describe 'Cache-Control header support' do
143
- subject do
144
- Configuration.read(<<-EOF)
145
- put "test" {
146
- output_image "input" cache-control="public, max-age=999, s-maxage=666"
147
- }
148
- EOF
149
- end
150
-
151
- it 'should allow setting Cache-Control header' do
152
- subject.handlers[0].output.realize(state)
153
-
154
- env.instance_eval &state.output_callback
155
- env.res['Cache-Control'].should == 'public, max-age=999, s-maxage=666'
156
- end
157
- end
158
- end
159
-
160
- describe 'output store paths and URLs' do
161
- let :utf_string do
162
- (support_dir + 'utf_string.txt').read.strip
163
- end
164
-
165
- let :in_file do
166
- Pathname.new("/tmp/test.in")
167
- end
168
-
169
- let :out_file do
170
- Pathname.new("/tmp/test.out")
171
- end
172
-
173
- let :out2_file do
174
- Pathname.new("/tmp/test.out2")
175
- end
176
-
177
- let :test_file do
178
- Pathname.new('/tmp/abc/test.out')
179
- end
180
-
181
- let :space_test_file do
182
- Pathname.new('/tmp/abc/t e s t.out')
183
- end
184
-
185
- let :utf_test_file do
186
- Pathname.new("/tmp/abc/#{utf_string}.out")
187
- end
188
-
189
- before :each do
190
- test_file.dirname.mkdir unless test_file.dirname.directory?
191
- test_file.open('w'){|io| io.write('abc')}
192
- space_test_file.open('w'){|io| io.write('abc')}
193
- in_file.open('w'){|io| io.write('abc')}
194
- out_file.unlink if out_file.exist?
195
- out2_file.unlink if out2_file.exist?
196
- end
197
-
198
- after :each do
199
- test_file.exist? and test_file.unlink
200
- space_test_file.exist? and space_test_file.unlink
201
- utf_test_file.exist? and utf_test_file.unlink
202
- test_file.dirname.exist? and test_file.dirname.rmdir
203
- out_file.unlink if out_file.exist?
204
- out2_file.unlink if out2_file.exist?
205
- in_file.unlink
206
- end
207
-
208
- describe Configuration::OutputStorePath do
209
- it 'should provide file store path' do
210
- subject = Configuration.read(<<-EOF)
211
- path {
212
- "out" "test.out"
213
- }
214
-
215
- post "single" {
216
- store_file "input" root="/tmp" path="out"
217
-
218
- output_store_path "input"
219
- }
220
- EOF
221
-
222
- subject.handlers[0].sources[0].realize(state)
223
- subject.handlers[0].stores[0].realize(state)
224
- subject.handlers[0].output.realize(state)
225
-
226
- env.instance_eval &state.output_callback
227
- env.res['Content-Type'].should == 'text/plain'
228
- env.res.data.should == "test.out\r\n"
229
- end
230
-
231
- it 'should provide multiple file store paths' do
232
- subject = Configuration.read(<<-EOF)
233
- path {
234
- "in" "test.in"
235
- "out" "test.out"
236
- "out2" "test.out2"
237
- }
238
-
239
- post "multi" {
240
- source_file "original" root="/tmp" path="in"
241
-
242
- store_file "input" root="/tmp" path="out"
243
- store_file "original" root="/tmp" path="out2"
244
-
245
- output_store_path {
246
- "input"
247
- "original"
248
- }
249
- }
250
- EOF
251
-
252
- subject.handlers[0].sources[0].realize(state)
253
- subject.handlers[0].sources[1].realize(state)
254
- subject.handlers[0].stores[0].realize(state)
255
- subject.handlers[0].stores[1].realize(state)
256
- subject.handlers[0].output.realize(state)
257
-
258
- env.instance_eval &state.output_callback
259
- env.res['Content-Type'].should == 'text/plain'
260
- env.res.data.should == "test.out\r\ntest.out2\r\n"
261
- end
262
-
263
- describe 'conditional inclusion support' do
264
- let :state do
265
- Configuration::RequestState.new('abc', {list: 'input,image2'})
266
- end
267
-
268
- subject do
269
- Configuration.read(<<-'EOF')
270
- path {
271
- "in" "test.in"
272
- "out1" "test.out1"
273
- "out2" "test.out2"
274
- "out3" "test.out3"
275
- }
276
-
277
- post "multi" {
278
- source_file "image1" root="/tmp" path="in"
279
- source_file "image2" root="/tmp" path="in"
280
-
281
- store_file "input" root="/tmp" path="out1"
282
- store_file "image1" root="/tmp" path="out2"
283
- store_file "image2" root="/tmp" path="out3"
284
-
285
- output_store_path {
286
- "input" if-image-name-on="#{list}"
287
- "image1" if-image-name-on="#{list}"
288
- "image2" if-image-name-on="#{list}"
289
- }
290
- }
291
- EOF
292
- end
293
-
294
- it 'should output store path only for images that names match if-image-name-on list' do
295
- subject.handlers[0].sources[0].realize(state)
296
- subject.handlers[0].sources[1].realize(state)
297
- subject.handlers[0].sources[2].realize(state)
298
- subject.handlers[0].stores[0].realize(state)
299
- subject.handlers[0].stores[1].realize(state)
300
- subject.handlers[0].stores[2].realize(state)
301
- subject.handlers[0].output.realize(state)
302
-
303
- env.instance_eval &state.output_callback
304
- env.res['Content-Type'].should == 'text/plain'
305
- env.res.data.should == "test.out1\r\ntest.out3\r\n"
306
- end
307
- end
308
-
309
- describe 'custom formatting' do
310
- it 'should provide formatted file store path' do
311
- subject = Configuration.read(<<-'EOF')
312
- path "out" "abc/test.out"
313
- path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
314
-
315
- post "single" {
316
- store_file "input" root="/tmp" path="out"
317
-
318
- output_store_path "input" path="formatted"
319
- }
320
- EOF
321
-
322
- subject.handlers[0].sources[0].realize(state)
323
- subject.handlers[0].stores[0].realize(state)
324
- subject.handlers[0].output.realize(state)
325
-
326
- env.instance_eval &state.output_callback
327
- env.res['Content-Type'].should == 'text/plain'
328
- env.res.data.should == "hello/abc/world/test-xyz.out\r\n"
329
- end
330
-
331
- it 'should provide formatted file store path for each path' do
332
- subject = Configuration.read(<<-'EOF')
333
- path "in" "test.in"
334
- path "out" "abc/test.out"
335
- path "out2" "test.out2"
336
-
337
- path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
338
- path "formatted2" "#{image_digest}.#{extension}"
339
-
340
- post "single" {
341
- source_file "original" root="/tmp" path="in"
342
-
343
- store_file "input" root="/tmp" path="out"
344
- store_file "original" root="/tmp" path="out2"
345
-
346
- output_store_path {
347
- "input" path="formatted"
348
- "original" path="formatted2"
349
- }
350
- }
351
- EOF
352
-
353
- subject.handlers[0].sources[0].realize(state)
354
- subject.handlers[0].sources[1].realize(state)
355
- subject.handlers[0].stores[0].realize(state)
356
- subject.handlers[0].stores[1].realize(state)
357
- subject.handlers[0].output.realize(state)
358
-
359
- env.instance_eval &state.output_callback
360
- env.res['Content-Type'].should == 'text/plain'
361
- env.res.data.should == "hello/abc/world/test-xyz.out\r\nba7816bf8f01cfea.out2\r\n"
362
- end
363
- end
364
-
365
- describe 'error handling' do
366
- it 'should raise StorePathNotSetForImage for output of not stored image' do
367
- subject = Configuration.read(<<-EOF)
368
- post "single" {
369
- output_store_path "input"
370
- }
371
- EOF
372
-
373
- subject.handlers[0].sources[0].realize(state)
374
-
375
- expect {
376
- subject.handlers[0].output.realize(state)
377
- }.to raise_error Configuration::StorePathNotSetForImage, %{store path not set for image 'input'}
378
- end
379
- end
380
- end
381
-
382
- describe Configuration::OutputStoreURL do
383
- it 'should provide file store URL' do
384
- subject = Configuration.read(<<-EOF)
385
- path {
386
- "out" "test.out"
387
- }
388
-
389
- post "single" {
390
- store_file "input" root="/tmp" path="out"
391
-
392
- output_store_url "input"
393
- }
394
- EOF
395
-
396
- subject.handlers[0].sources[0].realize(state)
397
- subject.handlers[0].stores[0].realize(state)
398
- subject.handlers[0].output.realize(state)
399
-
400
- env.instance_eval &state.output_callback
401
- env.res['Content-Type'].should == 'text/uri-list'
402
- env.res.data.should == "file:/test.out\r\n"
403
- end
404
-
405
- it 'should provide multiple file store URLs' do
406
- subject = Configuration.read(<<-EOF)
407
- path {
408
- "in" "test.in"
409
- "out" "test.out"
410
- "out2" "test.out2"
411
- }
412
-
413
- post "multi" {
414
- source_file "original" root="/tmp" path="in"
415
-
416
- store_file "input" root="/tmp" path="out"
417
- store_file "original" root="/tmp" path="out2"
418
-
419
- output_store_url {
420
- "input"
421
- "original"
422
- }
423
- }
424
- EOF
425
-
426
- subject.handlers[0].sources[0].realize(state)
427
- subject.handlers[0].sources[1].realize(state)
428
- subject.handlers[0].stores[0].realize(state)
429
- subject.handlers[0].stores[1].realize(state)
430
- subject.handlers[0].output.realize(state)
431
-
432
- env.instance_eval &state.output_callback
433
- env.res['Content-Type'].should == 'text/uri-list'
434
- env.res.data.should == "file:/test.out\r\nfile:/test.out2\r\n"
435
- end
436
-
437
- describe 'conditional inclusion support' do
438
- let :state do
439
- Configuration::RequestState.new('abc', list: 'input,image2')
440
- end
441
-
442
- subject do
443
- Configuration.read(<<-'EOF')
444
- path {
445
- "in" "test.in"
446
- "out1" "test.out1"
447
- "out2" "test.out2"
448
- "out3" "test.out3"
449
- }
450
-
451
- post "multi" {
452
- source_file "image1" root="/tmp" path="in"
453
- source_file "image2" root="/tmp" path="in"
454
-
455
- store_file "input" root="/tmp" path="out1"
456
- store_file "image1" root="/tmp" path="out2"
457
- store_file "image2" root="/tmp" path="out3"
458
-
459
- output_store_url {
460
- "input" if-image-name-on="#{list}"
461
- "image1" if-image-name-on="#{list}"
462
- "image2" if-image-name-on="#{list}"
463
- }
464
- }
465
- EOF
466
- end
467
-
468
- it 'should output store url only for images that names match if-image-name-on list' do
469
- subject.handlers[0].sources[0].realize(state)
470
- subject.handlers[0].sources[1].realize(state)
471
- subject.handlers[0].sources[2].realize(state)
472
- subject.handlers[0].stores[0].realize(state)
473
- subject.handlers[0].stores[1].realize(state)
474
- subject.handlers[0].stores[2].realize(state)
475
- subject.handlers[0].output.realize(state)
476
-
477
- env.instance_eval &state.output_callback
478
- env.res['Content-Type'].should == 'text/uri-list'
479
- env.res.data.should == "file:/test.out1\r\nfile:/test.out3\r\n"
480
- end
481
- end
482
-
483
- describe 'URL rewrites' do
484
- it 'should allow using path spec to rewrite URL path component' do
485
- subject = Configuration.read(<<-'EOF')
486
- path "out" "abc/test.out"
487
-
488
- path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
489
-
490
- post "single" {
491
- store_file "input" root="/tmp" path="out"
492
-
493
- output_store_url "input" path="formatted"
494
- }
495
- EOF
496
-
497
- subject.handlers[0].sources[0].realize(state)
498
- subject.handlers[0].stores[0].realize(state)
499
- subject.handlers[0].output.realize(state)
500
-
501
- env.instance_eval &state.output_callback
502
- env.res['Content-Type'].should == 'text/uri-list'
503
- env.res.data.should == "file:/hello/abc/world/test-xyz.out\r\n"
504
- end
505
-
506
- it 'should allow rewriting scheme component' do
507
- subject = Configuration.read(<<-'EOF')
508
- path "out" "abc/test.out"
509
-
510
- post "single" {
511
- store_file "input" root="/tmp" path="out"
512
-
513
- output_store_url "input" scheme="ftp"
514
- }
515
- EOF
516
-
517
- subject.handlers[0].sources[0].realize(state)
518
- subject.handlers[0].stores[0].realize(state)
519
- subject.handlers[0].output.realize(state)
520
-
521
- env.instance_eval &state.output_callback
522
- env.res['Content-Type'].should == 'text/uri-list'
523
- env.res.data.should == "ftp:/abc/test.out\r\n"
524
- end
525
-
526
- it 'should allow rewriting host component' do
527
- subject = Configuration.read(<<-'EOF')
528
- path "out" "abc/test.out"
529
-
530
- post "single" {
531
- store_file "input" root="/tmp" path="out"
532
-
533
- output_store_url "input" host="localhost"
534
- }
535
- EOF
536
-
537
- subject.handlers[0].sources[0].realize(state)
538
- subject.handlers[0].stores[0].realize(state)
539
- subject.handlers[0].output.realize(state)
540
-
541
- env.instance_eval &state.output_callback
542
- env.res['Content-Type'].should == 'text/uri-list'
543
- env.res.data.should == "file://localhost/abc/test.out\r\n"
544
- end
545
-
546
- it 'should allow rewriting port component (defaults host to localhost)' do
547
- subject = Configuration.read(<<-'EOF')
548
- path "out" "abc/test.out"
549
-
550
- post "single" {
551
- store_file "input" root="/tmp" path="out"
552
-
553
- output_store_url "input" port="21"
554
- }
555
- EOF
556
-
557
- subject.handlers[0].sources[0].realize(state)
558
- subject.handlers[0].stores[0].realize(state)
559
- subject.handlers[0].output.realize(state)
560
-
561
- env.instance_eval &state.output_callback
562
- env.res['Content-Type'].should == 'text/uri-list'
563
- env.res.data.should == "file://localhost:21/abc/test.out\r\n"
564
- end
565
-
566
- it 'should allow using variables for all supported rewrites' do
567
- state = Configuration::RequestState.new('abc',
568
- remote: 'example.com',
569
- remote_port: 421,
570
- proto: 'ftp'
571
- )
572
- subject = Configuration.read(<<-'EOF')
573
- path "out" "abc/test.out"
574
- path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
575
-
576
- post "single" {
577
- store_file "input" root="/tmp" path="out"
578
-
579
- output_store_url "input" scheme="#{proto}" host="#{remote}" port="#{remote_port}" path="formatted"
580
- }
581
- EOF
582
-
583
- subject.handlers[0].sources[0].realize(state)
584
- subject.handlers[0].stores[0].realize(state)
585
- subject.handlers[0].output.realize(state)
586
-
587
- env.instance_eval &state.output_callback
588
- env.res['Content-Type'].should == 'text/uri-list'
589
- env.res.data.should == "ftp://example.com:421/hello/abc/world/test-xyz.out\r\n"
590
- end
591
- end
592
-
593
- describe 'URL encoding' do
594
- it 'should provide properly encoded file store URL' do
595
- subject = Configuration.read(<<-'EOF')
596
- path "out" "abc/t e s t.out"
597
- path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
598
-
599
- post "single" {
600
- store_file "input" root="/tmp" path="out"
601
-
602
- output_store_url {
603
- "input"
604
- "input" path="formatted"
605
- }
606
- }
607
- EOF
608
-
609
- subject.handlers[0].sources[0].realize(state)
610
- subject.handlers[0].stores[0].realize(state)
611
- subject.handlers[0].output.realize(state)
612
-
613
- env.instance_eval &state.output_callback
614
- env.res['Content-Type'].should == 'text/uri-list'
615
- env.res.data.should == "file:/abc/t%20e%20s%20t.out\r\nfile:/hello/abc/world/t%20e%20s%20t-xyz.out\r\n"
616
- end
617
- end
618
-
619
- describe 'error handling' do
620
- it 'should raise StoreURLNotSetForImage for output of not stored image' do
621
- subject = Configuration.read(<<-EOF)
622
- post "single" {
623
- output_store_url "input"
624
- }
625
- EOF
626
-
627
- subject.handlers[0].sources[0].realize(state)
628
-
629
- expect {
630
- subject.handlers[0].output.realize(state)
631
- }.to raise_error Configuration::StoreURLNotSetForImage, %{store URL not set for image 'input'}
632
- end
633
- end
634
- end
635
-
636
- describe Configuration::OutputStoreURI do
637
- it 'should provide file store URI' do
638
- subject = Configuration.read(<<-EOF)
639
- path {
640
- "out" "test.out"
641
- }
642
-
643
- post "single" {
644
- store_file "input" root="/tmp" path="out"
645
-
646
- output_store_uri "input"
647
- }
648
- EOF
649
-
650
- subject.handlers[0].sources[0].realize(state)
651
- subject.handlers[0].stores[0].realize(state)
652
- subject.handlers[0].output.realize(state)
653
-
654
- env.instance_eval &state.output_callback
655
- env.res['Content-Type'].should == 'text/uri-list'
656
- env.res.data.should == "/test.out\r\n"
657
- end
658
-
659
- it 'should provide multiple file store URIs' do
660
- subject = Configuration.read(<<-EOF)
661
- path {
662
- "in" "test.in"
663
- "out" "test.out"
664
- "out2" "test.out2"
665
- }
666
-
667
- post "multi" {
668
- source_file "original" root="/tmp" path="in"
669
-
670
- store_file "input" root="/tmp" path="out"
671
- store_file "original" root="/tmp" path="out2"
672
-
673
- output_store_uri {
674
- "input"
675
- "original"
676
- }
677
- }
678
- EOF
679
-
680
- subject.handlers[0].sources[0].realize(state)
681
- subject.handlers[0].sources[1].realize(state)
682
- subject.handlers[0].stores[0].realize(state)
683
- subject.handlers[0].stores[1].realize(state)
684
- subject.handlers[0].output.realize(state)
685
-
686
- env.instance_eval &state.output_callback
687
- env.res['Content-Type'].should == 'text/uri-list'
688
- env.res.data.should == "/test.out\r\n/test.out2\r\n"
689
- end
690
-
691
- describe 'conditional inclusion support' do
692
- let :state do
693
- Configuration::RequestState.new('abc', list: 'input,image2')
694
- end
695
-
696
- subject do
697
- Configuration.read(<<-'EOF')
698
- path {
699
- "in" "test.in"
700
- "out1" "test.out1"
701
- "out2" "test.out2"
702
- "out3" "test.out3"
703
- }
704
-
705
- post "multi" {
706
- source_file "image1" root="/tmp" path="in"
707
- source_file "image2" root="/tmp" path="in"
708
-
709
- store_file "input" root="/tmp" path="out1"
710
- store_file "image1" root="/tmp" path="out2"
711
- store_file "image2" root="/tmp" path="out3"
712
-
713
- output_store_uri {
714
- "input" if-image-name-on="#{list}"
715
- "image1" if-image-name-on="#{list}"
716
- "image2" if-image-name-on="#{list}"
717
- }
718
- }
719
- EOF
720
- end
721
-
722
- it 'should output store url only for images that names match if-image-name-on list' do
723
- subject.handlers[0].sources[0].realize(state)
724
- subject.handlers[0].sources[1].realize(state)
725
- subject.handlers[0].sources[2].realize(state)
726
- subject.handlers[0].stores[0].realize(state)
727
- subject.handlers[0].stores[1].realize(state)
728
- subject.handlers[0].stores[2].realize(state)
729
- subject.handlers[0].output.realize(state)
730
-
731
- env.instance_eval &state.output_callback
732
- env.res['Content-Type'].should == 'text/uri-list'
733
- env.res.data.should == "/test.out1\r\n/test.out3\r\n"
734
- end
735
- end
736
-
737
- describe 'URI rewrites' do
738
- it 'should allow using path spec to rewrite URI path' do
739
- subject = Configuration.read(<<-'EOF')
740
- path "out" "abc/test.out"
741
-
742
- path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
743
-
744
- post "single" {
745
- store_file "input" root="/tmp" path="out"
746
-
747
- output_store_uri "input" path="formatted"
748
- }
749
- EOF
750
-
751
- subject.handlers[0].sources[0].realize(state)
752
- subject.handlers[0].stores[0].realize(state)
753
- subject.handlers[0].output.realize(state)
754
-
755
- env.instance_eval &state.output_callback
756
- env.res['Content-Type'].should == 'text/uri-list'
757
- env.res.data.should == "/hello/abc/world/test-xyz.out\r\n"
758
- end
759
- end
760
-
761
- describe 'URI encoding' do
762
- let :subject do
763
- Configuration.read(<<-'EOF')
764
- path "out" "abc/#{name}.out"
765
- path "formatted" "hello/#{dirname}/world/#{basename}-xyz.#{extension}"
766
-
767
- post "single" {
768
- store_file "input" root="/tmp" path="out"
769
-
770
- output_store_uri {
771
- "input"
772
- "input" path="formatted"
773
- }
774
- }
775
- EOF
776
- end
777
-
778
- it 'should provide properly encoded file store URI' do
779
- state = Configuration::RequestState.new('abc', name: 't e s t')
780
-
781
- subject.handlers[0].sources[0].realize(state)
782
- subject.handlers[0].stores[0].realize(state)
783
- subject.handlers[0].output.realize(state)
784
-
785
- env.instance_eval &state.output_callback
786
- env.res['Content-Type'].should == 'text/uri-list'
787
- env.res.data.should == "/abc/t%20e%20s%20t.out\r\n/hello/abc/world/t%20e%20s%20t-xyz.out\r\n"
788
- end
789
-
790
- it 'should handle UTF-8 characters' do
791
- state = Configuration::RequestState.new('abc', name: utf_string)
792
- subject.handlers[0].sources[0].realize(state)
793
- subject.handlers[0].stores[0].realize(state)
794
- subject.handlers[0].output.realize(state)
795
-
796
- env.instance_eval &state.output_callback
797
- env.res['Content-Type'].should == 'text/uri-list'
798
- l1, l2 = *env.res.data.split("\r\n")
799
- URI.utf_decode(l1).should == "/abc/#{utf_string}.out"
800
- URI.utf_decode(l2).should == "/hello/abc/world/#{utf_string}-xyz.out"
801
- end
802
- end
803
-
804
- describe 'error handling' do
805
- it 'should raise StoreURLNotSetForImage for output of not stored image' do
806
- subject = Configuration.read(<<-EOF)
807
- post "single" {
808
- output_store_uri "input"
809
- }
810
- EOF
811
-
812
- subject.handlers[0].sources[0].realize(state)
813
-
814
- expect {
815
- subject.handlers[0].output.realize(state)
816
- }.to raise_error Configuration::StoreURLNotSetForImage, %{store URL not set for image 'input'}
817
- end
818
- end
819
- end
820
- end
821
- end