httpimagestore 0.5.0 → 1.0.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 +10 -12
- data/Gemfile.lock +57 -55
- data/README.md +829 -0
- data/VERSION +1 -1
- data/bin/httpimagestore +114 -180
- data/features/cache-control.feature +26 -90
- data/features/compatibility.feature +129 -0
- data/features/error-reporting.feature +207 -0
- data/features/health-check.feature +30 -0
- data/features/s3-store-and-thumbnail.feature +65 -0
- data/features/step_definitions/httpimagestore_steps.rb +66 -26
- data/features/support/env.rb +32 -5
- data/features/support/test.empty +0 -0
- data/httpimagestore.gemspec +60 -47
- data/lib/httpimagestore/aws_sdk_regions_hack.rb +23 -0
- data/lib/httpimagestore/configuration/file.rb +120 -0
- data/lib/httpimagestore/configuration/handler.rb +239 -0
- data/lib/httpimagestore/configuration/output.rb +119 -0
- data/lib/httpimagestore/configuration/path.rb +77 -0
- data/lib/httpimagestore/configuration/s3.rb +194 -0
- data/lib/httpimagestore/configuration/thumbnailer.rb +244 -0
- data/lib/httpimagestore/configuration.rb +126 -29
- data/lib/httpimagestore/error_reporter.rb +36 -0
- data/lib/httpimagestore/ruby_string_template.rb +26 -0
- data/load_test/load_test.1k.23a022f6e.m1.small-comp.csv +3 -0
- data/load_test/load_test.1k.ec9bde794.m1.small.csv +4 -0
- data/load_test/load_test.jmx +344 -0
- data/load_test/thumbnail_specs.csv +11 -0
- data/spec/configuration_file_spec.rb +309 -0
- data/spec/configuration_handler_spec.rb +124 -0
- data/spec/configuration_output_spec.rb +338 -0
- data/spec/configuration_path_spec.rb +92 -0
- data/spec/configuration_s3_spec.rb +571 -0
- data/spec/configuration_spec.rb +80 -105
- data/spec/configuration_thumbnailer_spec.rb +417 -0
- data/spec/ruby_string_template_spec.rb +43 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/support/compute.jpg +0 -0
- data/spec/support/cuba_response_env.rb +40 -0
- data/spec/support/full.cfg +49 -0
- metadata +138 -84
- data/README.rdoc +0 -23
- data/features/httpimagestore.feature +0 -167
- data/lib/httpimagestore/image_path.rb +0 -54
- data/lib/httpimagestore/s3_service.rb +0 -37
- data/lib/httpimagestore/thumbnail_class.rb +0 -13
- data/spec/image_path_spec.rb +0 -72
- data/spec/test.cfg +0 -8
data/spec/configuration_spec.rb
CHANGED
@@ -1,116 +1,91 @@
|
|
1
|
-
|
1
|
+
require_relative 'spec_helper'
|
2
2
|
require 'httpimagestore/configuration'
|
3
|
-
require 'sinatra/base'
|
4
3
|
|
5
|
-
|
6
|
-
it "should provide thumbnail classes" do
|
7
|
-
c = Configuration.new do
|
8
|
-
thumbnail_class 'small', 'crop', 128, 128, 'JPEG', :magick => 'option', :number => 42
|
9
|
-
thumbnail_class 'tiny', 'pad', 32, 48, 'PNG'
|
10
|
-
thumbnail_class 'test', 'pad', 32, 48
|
11
|
-
end.get
|
12
|
-
|
13
|
-
tc = c.thumbnail_classes['small']
|
14
|
-
tc.name.should == 'small'
|
15
|
-
tc.method.should == 'crop'
|
16
|
-
tc.width.should == 128
|
17
|
-
tc.height.should == 128
|
18
|
-
tc.format.should == 'JPEG'
|
19
|
-
tc.options.should == { :magick => 'option', :number => 42}
|
20
|
-
|
21
|
-
tc = c.thumbnail_classes['tiny']
|
22
|
-
tc.name.should == 'tiny'
|
23
|
-
tc.method.should == 'pad'
|
24
|
-
tc.width.should == 32
|
25
|
-
tc.height.should == 48
|
26
|
-
tc.format.should == 'PNG'
|
27
|
-
tc.options.should == {}
|
28
|
-
|
29
|
-
tc = c.thumbnail_classes['test']
|
30
|
-
tc.name.should == 'test'
|
31
|
-
tc.method.should == 'pad'
|
32
|
-
tc.width.should == 32
|
33
|
-
tc.height.should == 48
|
34
|
-
tc.format.should == 'JPEG'
|
35
|
-
tc.options.should == {}
|
36
|
-
end
|
37
|
-
|
38
|
-
it "should provide S3 key id and secret" do
|
39
|
-
c = Configuration.new do
|
40
|
-
s3_key 'abc', 'xyz'
|
41
|
-
end.get
|
42
|
-
|
43
|
-
c.s3_key_id.should == 'abc'
|
44
|
-
c.s3_key_secret.should == 'xyz'
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should provide S3 bucket" do
|
48
|
-
c = Configuration.new do
|
49
|
-
s3_bucket 'test'
|
50
|
-
end.get
|
4
|
+
Configuration::Scope.logger = Logger.new('/dev/null')
|
51
5
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
c = Configuration.new do
|
57
|
-
end.get
|
58
|
-
|
59
|
-
c.thumbnailer_url.should == 'http://localhost:3100'
|
60
|
-
|
61
|
-
c = Configuration.new do
|
62
|
-
thumbnailer_url 'http://test'
|
63
|
-
end.get
|
6
|
+
require 'pathname'
|
7
|
+
Pathname.glob('lib/httpimagestore/configuration/*.rb').each do |conf|
|
8
|
+
require conf.relative_path_from(Pathname.new 'lib').to_s
|
9
|
+
end
|
64
10
|
|
65
|
-
|
11
|
+
describe Configuration do
|
12
|
+
it 'should parse configuration file' do
|
13
|
+
Configuration.from_file(support_dir + 'full.cfg')
|
66
14
|
end
|
67
15
|
|
68
|
-
it
|
69
|
-
|
70
|
-
c = Configuration.new do
|
71
|
-
s3_key 'abc', 'xyz'
|
72
|
-
s3_bucket 'test'
|
73
|
-
thumbnailer_url 'http://test'
|
74
|
-
end.put(sinatra)
|
75
|
-
|
76
|
-
sinatra.settings.s3_key_id.should == 'abc'
|
77
|
-
sinatra.settings.s3_key_secret.should == 'xyz'
|
78
|
-
sinatra.settings.s3_bucket.should == 'test'
|
79
|
-
sinatra.settings.thumbnailer_url.should == 'http://test'
|
16
|
+
it 'should read configuration from string' do
|
17
|
+
Configuration.read (support_dir + 'full.cfg').read
|
80
18
|
end
|
81
19
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
20
|
+
describe Configuration::SDL4RTagExtensions do
|
21
|
+
let :configuration do
|
22
|
+
SDL4R::read <<-'EOF'
|
23
|
+
test "hello" "world" abc=123 xyz=321 qwe=true asd="fd"
|
24
|
+
EOF
|
25
|
+
end
|
26
|
+
|
27
|
+
subject do
|
28
|
+
configuration.children.first
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#required_attributes' do
|
32
|
+
it 'should raise NoAttributeError if there is missing required attribute' do
|
33
|
+
subject.required_attributes('abc', 'xyz')
|
34
|
+
|
35
|
+
expect {
|
36
|
+
subject.required_attributes('abc', 'xyz', 'blah')
|
37
|
+
}.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': expected 'blah' attribute to be set}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#grab_attributes' do
|
42
|
+
it 'should grab list of attributes form node' do
|
43
|
+
list = subject.grab_attributes('abc', 'xyz', 'qwe', 'opt', 'asd')
|
44
|
+
list.should == [123, 321, true, nil, 'fd']
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should raise UnexpectedAttributesError if unlisted attribut is on closed attribute list' do
|
48
|
+
expect {
|
49
|
+
subject.grab_attributes('abc', 'qwe')
|
50
|
+
}.to raise_error Configuration::UnexpectedAttributesError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': unexpected attributes: 'xyz', 'asd'}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#grab_attributes_with_remaining' do
|
55
|
+
it 'should grab list of attributes form node and all other remaining attributes' do
|
56
|
+
*list, remaining = *subject.grab_attributes_with_remaining('abc', 'xyz')
|
57
|
+
list.should == [123, 321]
|
58
|
+
remaining.should == {'qwe' => true, 'asd' => 'fd'}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#valid_attribute_values' do
|
63
|
+
it 'should rise BadAttributeValueError if attribute value is not on defined value list' do
|
64
|
+
subject.valid_attribute_values('qwe', true, false)
|
65
|
+
|
66
|
+
expect {
|
67
|
+
subject.valid_attribute_values('qwe', 'hello', 'world')
|
68
|
+
}.to raise_error Configuration::BadAttributeValueError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': expected 'qwe' attribute value to be "hello" or "world"; got: true}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#grab_values' do
|
73
|
+
it 'should grab list of values' do
|
74
|
+
subject.grab_values('value 1', 'value 2').should == ['hello', 'world']
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should raise NoValueError if there is not enought values' do
|
78
|
+
expect {
|
79
|
+
subject.grab_values('value 1', 'value 2', 'value 3')
|
80
|
+
}.to raise_error Configuration::NoValueError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': expected value 3}
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should raise UnexpectedValueError if there is too many values' do
|
84
|
+
expect {
|
85
|
+
subject.grab_values('value 1')
|
86
|
+
}.to raise_error Configuration::UnexpectedValueError, %{syntax error while parsing 'test "hello" "world" abc=123 asd="fd" qwe=true xyz=321': unexpected values: "world"}
|
87
|
+
end
|
88
|
+
end
|
114
89
|
end
|
115
90
|
end
|
116
91
|
|
@@ -0,0 +1,417 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'httpimagestore/configuration'
|
3
|
+
Configuration::Scope.logger = Logger.new('/dev/null')
|
4
|
+
|
5
|
+
require 'httpimagestore/configuration/thumbnailer'
|
6
|
+
MemoryLimit.logger = Logger.new('/dev/null')
|
7
|
+
|
8
|
+
describe Configuration do
|
9
|
+
describe 'thumbnailer' do
|
10
|
+
subject do
|
11
|
+
Configuration.read('')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should provide HTTPThumbnailerClient' do
|
15
|
+
subject.thumbnailer.should be_a HTTPThumbnailerClient
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should use default URL' do
|
19
|
+
subject.thumbnailer.server_url.should == 'http://localhost:3100'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should allow to override default URL' do
|
23
|
+
subject = Configuration.read(<<-EOF, thumbnailer_url: 'http://1.1.1.1:8080')
|
24
|
+
EOF
|
25
|
+
|
26
|
+
subject.thumbnailer.server_url.should == 'http://1.1.1.1:8080'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should get thumbnailer URL from configuration' do
|
30
|
+
subject = Configuration.read(<<-EOF, thumbnailer_url: 'http://1.1.1.1:8080')
|
31
|
+
thumbnailer url="http://2.2.2.2:1000"
|
32
|
+
EOF
|
33
|
+
|
34
|
+
subject.thumbnailer.server_url.should == 'http://2.2.2.2:1000'
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'error handling' do
|
38
|
+
it 'should raise StatementCollisionError on duplicate thumbnailer statement' do
|
39
|
+
expect {
|
40
|
+
Configuration.read(<<-EOF)
|
41
|
+
thumbnailer url="http://2.2.2.2:1000"
|
42
|
+
thumbnailer url="http://2.2.2.2:1000"
|
43
|
+
EOF
|
44
|
+
}.to raise_error Configuration::StatementCollisionError, %{syntax error while parsing 'thumbnailer url="http://2.2.2.2:1000"': only one thumbnailer type statement can be specified within context}
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should raise NoAttributeError on missing url attribute' do
|
48
|
+
expect {
|
49
|
+
Configuration.read(<<-EOF)
|
50
|
+
thumbnailer
|
51
|
+
EOF
|
52
|
+
}.to raise_error Configuration::NoAttributeError, %{syntax error while parsing 'thumbnailer': expected 'url' attribute to be set}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe Configuration::Thumbnail::ThumbnailSpec do
|
58
|
+
describe 'should provide thumbnail spec array based on given locals' do
|
59
|
+
it 'where image name is hash key pointing to array where all values are string and options is hash' do
|
60
|
+
Configuration::Thumbnail::ThumbnailSpec.new('small', 'pad', 100, 100, 'jpeg', 'background-color' => 'red').render.should == {
|
61
|
+
'small' =>
|
62
|
+
['pad', '100', '100', 'jpeg', {'background-color' => 'red'}]
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'that are templated' do
|
67
|
+
it 'with local values for operation, width, height and values of options' do
|
68
|
+
locals = {
|
69
|
+
operation: 'fit',
|
70
|
+
width: 99,
|
71
|
+
height: 66,
|
72
|
+
format: 'png',
|
73
|
+
bg: 'white'
|
74
|
+
}
|
75
|
+
|
76
|
+
Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}', 'background-color' => '#{bg}').render(locals).should == {
|
77
|
+
'small' =>
|
78
|
+
['fit', '99', '66', 'png', {'background-color' => 'white'}]
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'with nested options provided by options key in <key>:<value>[,<key>:<value>]* format but not overwriting predefined options' do
|
83
|
+
locals = {
|
84
|
+
operation: 'fit',
|
85
|
+
width: 99,
|
86
|
+
height: 66,
|
87
|
+
format: 'png',
|
88
|
+
opts: 'background-color:blue,quality:100'
|
89
|
+
}
|
90
|
+
|
91
|
+
Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}', 'options' => '#{opts}', 'background-color' => 'red').render(locals).should == {
|
92
|
+
'small' =>
|
93
|
+
['fit', '99', '66', 'png', {'background-color' => 'red', 'quality' => '100'}]
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'error handling' do
|
99
|
+
it 'should raise NoValueForSpecTemplatePlaceholerError on missing spec template value' do
|
100
|
+
locals = {
|
101
|
+
width: 99,
|
102
|
+
height: 66,
|
103
|
+
format: 'png'
|
104
|
+
}
|
105
|
+
|
106
|
+
expect {
|
107
|
+
Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}').render(locals)
|
108
|
+
}.to raise_error Configuration::NoValueForSpecTemplatePlaceholerError, %q{cannot generate specification for thumbnail 'small': cannot generate value for attribute 'method' from template '#{operation}': no value for #{operation}}
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should raise NoValueForSpecTemplatePlaceholerError on missing option template value' do
|
112
|
+
locals = {
|
113
|
+
width: 99,
|
114
|
+
height: 66,
|
115
|
+
format: 'png',
|
116
|
+
}
|
117
|
+
|
118
|
+
expect {
|
119
|
+
Configuration::Thumbnail::ThumbnailSpec.new('small', '#{operation}', '#{width}', '#{height}', '#{format}', 'background-color' => '#{bg}').render(locals)
|
120
|
+
}.to raise_error Configuration::NoValueForSpecTemplatePlaceholerError, %q{cannot generate specification for thumbnail 'small': cannot generate value for attribute 'background-color' from template '#{bg}': no value for #{bg}}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe 'thumbnail source image' do
|
126
|
+
before :all do
|
127
|
+
log = support_dir + 'server.log'
|
128
|
+
start_server(
|
129
|
+
"httpthumbnailer -f -d -l #{log}",
|
130
|
+
'/tmp/httpthumbnailer.pid',
|
131
|
+
log,
|
132
|
+
'http://localhost:3100/'
|
133
|
+
)
|
134
|
+
end
|
135
|
+
|
136
|
+
let :state do
|
137
|
+
Configuration::RequestState.new(
|
138
|
+
(support_dir + 'compute.jpg').read,
|
139
|
+
operation: 'pad',
|
140
|
+
width: '10',
|
141
|
+
height: '10',
|
142
|
+
options: 'background-color:green',
|
143
|
+
path: nil
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
before :each do
|
148
|
+
subject.handlers[0].image_sources[0].realize(state)
|
149
|
+
end
|
150
|
+
|
151
|
+
describe 'thumbnailing to single spec' do
|
152
|
+
subject do
|
153
|
+
Configuration.read(<<-'EOF')
|
154
|
+
put ":operation" ":width" ":height" ":options" {
|
155
|
+
thumbnail "input" "original" operation="#{operation}" width="#{width}" height="#{height}" options="#{options}" quality=84 format="jpeg"
|
156
|
+
}
|
157
|
+
EOF
|
158
|
+
end
|
159
|
+
|
160
|
+
before :each do
|
161
|
+
state.images['input'].source_path = 'test.in'
|
162
|
+
state.images['input'].source_url = 'file://test.in'
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should provide thumbnail data' do
|
166
|
+
subject.handlers[0].image_sources[1].realize(state)
|
167
|
+
state.images['original'].data.should_not be_nil
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'should set thumbnail mime type' do
|
171
|
+
subject.handlers[0].image_sources[1].realize(state)
|
172
|
+
state.images['original'].mime_type.should == 'image/jpeg'
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should use input image source path and url' do
|
176
|
+
subject.handlers[0].image_sources[1].realize(state)
|
177
|
+
state.images['original'].source_path.should == 'test.in'
|
178
|
+
state.images['original'].source_url.should == 'file://test.in'
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should set input image mime type' do
|
182
|
+
subject.handlers[0].image_sources[1].realize(state)
|
183
|
+
state.images['input'].mime_type.should == 'image/jpeg'
|
184
|
+
end
|
185
|
+
|
186
|
+
describe 'memory limit' do
|
187
|
+
let :state do
|
188
|
+
Configuration::RequestState.new(
|
189
|
+
(support_dir + 'compute.jpg').read,
|
190
|
+
{operation: 'pad',
|
191
|
+
width: '10',
|
192
|
+
height: '10',
|
193
|
+
options: 'background-color:green',
|
194
|
+
path: nil},
|
195
|
+
MemoryLimit.new(10)
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should raise MemoryLimit::MemoryLimitedExceededError when limit is exceeded' do
|
200
|
+
expect {
|
201
|
+
subject.handlers[0].image_sources[1].realize(state)
|
202
|
+
}.to raise_error MemoryLimit::MemoryLimitedExceededError
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe 'error handling' do
|
207
|
+
it 'should raise Thumbnail::ThumbnailingError on realization of bad thumbnail sepc' do
|
208
|
+
state = Configuration::RequestState.new(
|
209
|
+
(support_dir + 'compute.jpg').read,
|
210
|
+
operation: 'pad',
|
211
|
+
width: '0',
|
212
|
+
height: '10',
|
213
|
+
options: 'background-color:green',
|
214
|
+
path: nil
|
215
|
+
)
|
216
|
+
|
217
|
+
expect {
|
218
|
+
subject.handlers[0].image_sources[0].realize(state)
|
219
|
+
subject.handlers[0].image_sources[1].realize(state)
|
220
|
+
}.to raise_error Configuration::Thumbnail::ThumbnailingError # WTF?, "thumbnailing of 'input' into 'original' failed: at least one image dimension is zero: 0x10"
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should raise NoValueError on missing source image name' do
|
224
|
+
expect {
|
225
|
+
Configuration.read(<<-EOF)
|
226
|
+
put {
|
227
|
+
thumbnail
|
228
|
+
}
|
229
|
+
EOF
|
230
|
+
}.to raise_error Configuration::NoValueError, %{syntax error while parsing 'thumbnail': expected source image name}
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should raise NoValueError on missing source image name' do
|
234
|
+
expect {
|
235
|
+
Configuration.read(<<-EOF)
|
236
|
+
put {
|
237
|
+
thumbnail "input"
|
238
|
+
}
|
239
|
+
EOF
|
240
|
+
}.to raise_error Configuration::NoValueError, %{syntax error while parsing 'thumbnail "input"': expected thumbnail image name}
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe 'thumbnailing to multiple specs' do
|
246
|
+
subject do
|
247
|
+
Configuration.read(<<-'EOF')
|
248
|
+
put ":operation" ":width" ":height" ":options" {
|
249
|
+
thumbnail "input" {
|
250
|
+
"original" operation="#{operation}" width="#{width}" height="#{height}" options="#{options}" quality=84 format="jpeg"
|
251
|
+
"small" operation="crop" width=128 height=128 format="jpeg"
|
252
|
+
"padded" operation="pad" width=128 height=128 format="png" background-color="gray"
|
253
|
+
}
|
254
|
+
}
|
255
|
+
EOF
|
256
|
+
end
|
257
|
+
|
258
|
+
before :each do
|
259
|
+
state.images['input'].source_path = 'test.in'
|
260
|
+
state.images['input'].source_url = 'file://test.in'
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'should provide thumbnail data' do
|
264
|
+
subject.handlers[0].image_sources[1].realize(state)
|
265
|
+
state.images['original'].data.should_not be_nil
|
266
|
+
state.images['small'].data.should_not be_nil
|
267
|
+
state.images['padded'].data.should_not be_nil
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'should set thumbnail mime type' do
|
271
|
+
subject.handlers[0].image_sources[1].realize(state)
|
272
|
+
state.images['original'].mime_type.should == 'image/jpeg'
|
273
|
+
state.images['small'].mime_type.should == 'image/jpeg'
|
274
|
+
state.images['padded'].mime_type.should == 'image/png'
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'should set input image mime type' do
|
278
|
+
subject.handlers[0].image_sources[1].realize(state)
|
279
|
+
state.images['input'].mime_type.should == 'image/jpeg'
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'should use input image source path and url' do
|
283
|
+
subject.handlers[0].image_sources[1].realize(state)
|
284
|
+
state.images['original'].source_path.should == 'test.in'
|
285
|
+
state.images['original'].source_url.should == 'file://test.in'
|
286
|
+
state.images['small'].source_path.should == 'test.in'
|
287
|
+
state.images['small'].source_url.should == 'file://test.in'
|
288
|
+
state.images['padded'].source_path.should == 'test.in'
|
289
|
+
state.images['padded'].source_url.should == 'file://test.in'
|
290
|
+
end
|
291
|
+
|
292
|
+
describe 'memory limit' do
|
293
|
+
let :state do
|
294
|
+
Configuration::RequestState.new(
|
295
|
+
(support_dir + 'compute.jpg').read,
|
296
|
+
{operation: 'pad',
|
297
|
+
width: '10',
|
298
|
+
height: '10',
|
299
|
+
options: 'background-color:green',
|
300
|
+
path: nil},
|
301
|
+
MemoryLimit.new(10)
|
302
|
+
)
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'should raise MemoryLimit::MemoryLimitedExceededError when limit is exceeded' do
|
306
|
+
expect {
|
307
|
+
subject.handlers[0].image_sources[1].realize(state)
|
308
|
+
}.to raise_error MemoryLimit::MemoryLimitedExceededError
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
describe 'conditional inclusion support' do
|
313
|
+
subject do
|
314
|
+
Configuration.read(<<-'EOF')
|
315
|
+
put {
|
316
|
+
thumbnail "input" {
|
317
|
+
"original" if-image-name-on="#{list}"
|
318
|
+
"small" if-image-name-on="#{list}"
|
319
|
+
"padded" if-image-name-on="#{list}"
|
320
|
+
}
|
321
|
+
}
|
322
|
+
EOF
|
323
|
+
end
|
324
|
+
|
325
|
+
let :state do
|
326
|
+
Configuration::RequestState.new(
|
327
|
+
(support_dir + 'compute.jpg').read,
|
328
|
+
operation: 'pad',
|
329
|
+
width: '10',
|
330
|
+
height: '10',
|
331
|
+
options: 'background-color:green',
|
332
|
+
path: nil,
|
333
|
+
list: 'small,padded'
|
334
|
+
)
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'should provide thumbnails that name match if-image-name-on list' do
|
338
|
+
subject.handlers[0].image_sources[1].realize(state)
|
339
|
+
state.images.should_not include 'original'
|
340
|
+
state.images['small'].data.should_not be_nil
|
341
|
+
state.images['padded'].data.should_not be_nil
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe 'error handling' do
|
346
|
+
it 'should raise Thumbnail::ThumbnailingError on realization of bad thumbnail sepc' do
|
347
|
+
state = Configuration::RequestState.new(
|
348
|
+
(support_dir + 'compute.jpg').read,
|
349
|
+
operation: 'pad',
|
350
|
+
width: '0',
|
351
|
+
height: '10',
|
352
|
+
options: 'background-color:green',
|
353
|
+
path: nil
|
354
|
+
)
|
355
|
+
|
356
|
+
subject.handlers[0].image_sources[0].realize(state)
|
357
|
+
|
358
|
+
expect {
|
359
|
+
subject.handlers[0].image_sources[1].realize(state)
|
360
|
+
}.to raise_error Configuration::Thumbnail::ThumbnailingError, "thumbnailing of 'input' into 'original' failed: at least one image dimension is zero: 0x10"
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'should raise NoValueError on missing source image name' do
|
364
|
+
expect {
|
365
|
+
Configuration.read(<<-EOF)
|
366
|
+
put {
|
367
|
+
thumbnail {
|
368
|
+
}
|
369
|
+
}
|
370
|
+
EOF
|
371
|
+
}.to raise_error Configuration::NoValueError, %{syntax error while parsing 'thumbnail': expected source image name}
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
describe 'conditional inclusion support' do
|
378
|
+
let :state do
|
379
|
+
Configuration::RequestState.new(
|
380
|
+
(support_dir + 'compute.jpg').read,
|
381
|
+
list: 'thumbnail1,input4,thumbnail5,input6'
|
382
|
+
)
|
383
|
+
end
|
384
|
+
|
385
|
+
subject do
|
386
|
+
Configuration.read(<<-'EOF')
|
387
|
+
put {
|
388
|
+
thumbnail "input1" "thumbnail1" if-image-name-on="#{list}"
|
389
|
+
thumbnail "input2" "thumbnail2" if-image-name-on="#{list}"
|
390
|
+
thumbnail "input3" if-image-name-on="#{list}" {
|
391
|
+
"thumbnail3"
|
392
|
+
}
|
393
|
+
thumbnail "input4" if-image-name-on="#{list}" {
|
394
|
+
"thumbnail4"
|
395
|
+
}
|
396
|
+
thumbnail "input5" if-image-name-on="#{list}" {
|
397
|
+
"thumbnail5" if-image-name-on="#{list}"
|
398
|
+
}
|
399
|
+
thumbnail "input6" if-image-name-on="#{list}" {
|
400
|
+
"thumbnail6" if-image-name-on="#{list}"
|
401
|
+
}
|
402
|
+
}
|
403
|
+
EOF
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'should mark source to be included when output image name in oneline and destination image name in multiline statement match if-image-name-on list' do
|
407
|
+
subject.handlers[0].image_sources[1].excluded?(state).should be_false
|
408
|
+
subject.handlers[0].image_sources[2].excluded?(state).should be_true
|
409
|
+
subject.handlers[0].image_sources[3].excluded?(state).should be_true
|
410
|
+
subject.handlers[0].image_sources[4].excluded?(state).should be_false
|
411
|
+
subject.handlers[0].image_sources[5].excluded?(state).should be_true
|
412
|
+
subject.handlers[0].image_sources[6].excluded?(state).should be_false
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'httpimagestore/ruby_string_template'
|
3
|
+
|
4
|
+
describe RubyStringTemplate do
|
5
|
+
subject do
|
6
|
+
RubyStringTemplate.new('>#{hello}-#{world}#{test}<')
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should replace holders with given values' do
|
10
|
+
subject.render(hello: 'hello', world: 'world', test: 123).should == '>hello-world123<'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should raise NoValueForTemplatePlaceholderError if template value was not provided' do
|
14
|
+
expect {
|
15
|
+
subject.render(hello: 'hello', test: 123)
|
16
|
+
}.to raise_error RubyStringTemplate::NoValueForTemplatePlaceholderError, %q{no value for '#{world}' in template '>#{hello}-#{world}#{test}<'}
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'with custom resolver' do
|
20
|
+
subject do
|
21
|
+
RubyStringTemplate.new('>#{hello}-#{world}#{test}<') do |locals, name|
|
22
|
+
case name
|
23
|
+
when :test
|
24
|
+
321
|
25
|
+
else
|
26
|
+
locals[name]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should ask for values using provided resolver' do
|
32
|
+
subject.render(hello: 'hello', world: 'world').should == '>hello-world321<'
|
33
|
+
subject.render(hello: 'hello', world: 'world', test: 123).should == '>hello-world321<'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should raise NoValueForTemplatePlaceholderError if template value was not provided' do
|
37
|
+
expect {
|
38
|
+
subject.render(hello: 'hello', test: 123)
|
39
|
+
}.to raise_error RubyStringTemplate::NoValueForTemplatePlaceholderError, %q{no value for '#{world}' in template '>#{hello}-#{world}#{test}<'}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|