image_vise 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.travis.yml +13 -0
  4. data/DEVELOPMENT.md +0 -11
  5. data/Gemfile +2 -20
  6. data/Rakefile +3 -26
  7. data/image_vise.gemspec +37 -132
  8. data/lib/image_vise/file_response.rb +2 -2
  9. data/lib/image_vise/image_request.rb +2 -0
  10. data/lib/image_vise/operators/background_fill.rb +18 -0
  11. data/lib/image_vise/operators/ellipse_stencil.rb +7 -5
  12. data/lib/image_vise/operators/force_jpg_out.rb +17 -0
  13. data/lib/image_vise/pipeline.rb +13 -3
  14. data/lib/image_vise/render_engine.rb +36 -64
  15. data/lib/image_vise/version.rb +3 -0
  16. data/lib/image_vise/writers/auto_writer.rb +23 -0
  17. data/lib/image_vise/writers/jpeg_writer.rb +9 -0
  18. data/lib/image_vise.rb +19 -19
  19. metadata +43 -135
  20. data/spec/image_vise/auto_orient_spec.rb +0 -10
  21. data/spec/image_vise/crop_spec.rb +0 -20
  22. data/spec/image_vise/ellipse_stencil_spec.rb +0 -19
  23. data/spec/image_vise/fetcher_file_spec.rb +0 -48
  24. data/spec/image_vise/fetcher_http_spec.rb +0 -44
  25. data/spec/image_vise/file_response_spec.rb +0 -45
  26. data/spec/image_vise/fit_crop_spec.rb +0 -20
  27. data/spec/image_vise/geom_spec.rb +0 -33
  28. data/spec/image_vise/image_request_spec.rb +0 -62
  29. data/spec/image_vise/pipeline_spec.rb +0 -72
  30. data/spec/image_vise/render_engine_spec.rb +0 -325
  31. data/spec/image_vise/sharpen_spec.rb +0 -17
  32. data/spec/image_vise/srgb_spec.rb +0 -28
  33. data/spec/image_vise/strip_metadata_spec.rb +0 -14
  34. data/spec/image_vise_spec.rb +0 -110
  35. data/spec/layers-with-blending.psd +0 -0
  36. data/spec/spec_helper.rb +0 -103
  37. data/spec/test_server.rb +0 -61
  38. data/spec/waterside_magic_hour.jpg +0 -0
  39. data/spec/waterside_magic_hour.psd +0 -0
  40. data/spec/waterside_magic_hour_adobergb.jpg +0 -0
  41. data/spec/waterside_magic_hour_gray.tif +0 -0
  42. data/spec/waterside_magic_hour_transp.png +0 -0
@@ -1,33 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ImageVise::Geom do
4
- it 'refuses invalid parameters' do
5
- expect { described_class.new(geometry_string: nil) }.to raise_error(ArgumentError)
6
- end
7
-
8
- it 'fits to height' do
9
- image = Magick::Image.read(test_image_path)[0]
10
- crop = described_class.new(geometry_string: 'x200')
11
- crop.apply!(image)
12
- expect(image.rows).to eq(200)
13
- examine_image(image, 'fit-height-200')
14
- end
15
-
16
- it 'fits to width' do
17
- image = Magick::Image.read(test_image_path)[0]
18
- crop = described_class.new(geometry_string: '100x')
19
- crop.apply!(image)
20
- expect(image.columns).to eq(100)
21
- examine_image(image, 'fit-width-100')
22
- end
23
-
24
- it 'applies various geometry strings' do
25
- %w( ^220x110 !20x20 !10x100 ).each do |geom_string|
26
- image = Magick::Image.read(test_image_path)[0]
27
- crop = described_class.new(geometry_string: geom_string)
28
-
29
- crop.apply!(image)
30
- examine_image(image, 'geom-%s' % geom_string)
31
- end
32
- end
33
- end
@@ -1,62 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ImageVise::ImageRequest do
4
- it 'accepts a set of params and secrets, and returns a Pipeline' do
5
- img_params = {src_url: 'http://bucket.s3.aws.com/image.jpg', pipeline: [[:crop, {width: 10, height: 10, gravity: 's'}]]}
6
- img_params_json = JSON.dump(img_params)
7
-
8
- q = Base64.encode64(img_params_json)
9
- signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, 'this is a secret', q)
10
- params = {q: q, sig: signature}
11
-
12
- image_request = described_class.from_params(qs_params: params, secrets: ['this is a secret'])
13
- request_qs_params = image_request.to_query_string_params('this is a secret')
14
- expect(request_qs_params).to be_kind_of(Hash)
15
-
16
- image_request_roundtrip = described_class.from_params(qs_params: request_qs_params, secrets: ['this is a secret'])
17
- end
18
-
19
- it 'converts a file:// URL into a URI objectlist' do
20
- img_params = {src_url: 'file:///etc/passwd', pipeline: [[:auto_orient, {}]]}
21
- img_params_json = JSON.dump(img_params)
22
- q = Base64.encode64(img_params_json)
23
- signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, 'this is a secret', q)
24
- params = {q: q, sig: signature}
25
- image_request = described_class.from_params(qs_params: params, secrets: ['this is a secret'])
26
- expect(image_request.src_url).to be_kind_of(URI)
27
- end
28
-
29
- it 'composes path parameters' do
30
- parametrized = double(to_params: {foo: 'bar'})
31
- uri = URI('http://example.com/image.psd')
32
- image_request = described_class.new(src_url: uri, pipeline: parametrized)
33
- path = image_request.to_path_params('password')
34
- expect(path).to start_with('/eyJwaXB')
35
- expect(path).to end_with('f207b')
36
- end
37
-
38
- it 'never apppends "="-padding to the Base64-encoded "q"' do
39
- parametrized = double(to_params: {foo: 'bar'})
40
- (1..12).each do |num_chars_in_url|
41
- uri = URI('http://ex.com/%s' % ('i' * num_chars_in_url))
42
- image_request = described_class.new(src_url: uri, pipeline: parametrized)
43
- q = image_request.to_query_string_params('password').fetch(:q)
44
- expect(q).not_to include('=')
45
- end
46
- end
47
-
48
- describe 'fails with an invalid signature' do
49
- it 'when the sig is invalid' do
50
- img_params = {src_url: 'http://bucket.s3.aws.com/image.jpg',
51
- pipeline: [[:crop, {width: 10, height: 10, gravity: 's'}]]}
52
- img_params_json = JSON.dump(img_params)
53
- enc = Base64.encode64(img_params_json)
54
- signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, 'a', enc)
55
- params = {q: enc, sig: signature}
56
-
57
- expect {
58
- described_class.from_params(qs_params: params, secrets: ['b'])
59
- }.to raise_error(/Invalid or missing signature/)
60
- end
61
- end
62
- end
@@ -1,72 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ImageVise::Pipeline do
4
- it 'is empty by default' do
5
- expect(subject).to be_empty
6
- end
7
-
8
- it 'reinstates the pipeline from the operator list parameters' do
9
- params = [
10
- ["geom", {:geometry_string=>"10x10"}],
11
- ["crop", {:width=>5, :height=>5, :gravity=>"se"}],
12
- ["auto_orient", {}],
13
- ["fit_crop", {:width=>10, :height=>32, :gravity=>"c"}]
14
- ]
15
- pipeline = described_class.from_param(params)
16
- expect(pipeline).not_to be_empty
17
- end
18
-
19
- it 'produces a usable operator parameter list that can be roundtripped' do
20
- operator_list = subject.geom(geometry_string: '10x10').
21
- crop(width: 5, height: 5, gravity: 'se').
22
- auto_orient.
23
- fit_crop(width: 10, height: 32, gravity: 'c').to_params
24
-
25
- expect(operator_list).to eq([
26
- ["geom", {:geometry_string=>"10x10"}],
27
- ["crop", {:width=>5, :height=>5, :gravity=>"se"}],
28
- ["auto_orient", {}],
29
- ["fit_crop", {:width=>10, :height=>32, :gravity=>"c"}]
30
- ])
31
-
32
- pipeline = described_class.from_param(operator_list)
33
- expect(pipeline).not_to be_empty
34
- end
35
-
36
- it 'applies itself to the image' do
37
- pipeline = subject.
38
- auto_orient.
39
- fit_crop(width: 48, height: 48, gravity: 'c').
40
- srgb.
41
- sharpen(radius: 2, sigma: 0.5).
42
- ellipse_stencil.
43
- strip_metadata
44
-
45
- image = Magick::Image.read(test_image_path)[0]
46
- pipeline.apply! image
47
- examine_image(image, "stenciled")
48
- end
49
-
50
- it 'raises an exception when an attempt is made to serialize an unknown operator' do
51
- unknown_op_class = Class.new
52
- subject << unknown_op_class.new
53
- expect {
54
- subject.to_params
55
- }.to raise_error(/not registered/)
56
- end
57
-
58
- it 'composes parameters even if one of the operators does not support to_h' do
59
- class AnonOp
60
- end
61
- class ParametricOp
62
- def to_h; {a: 133}; end
63
- end
64
-
65
- ImageVise.add_operator('t_anon', AnonOp)
66
- ImageVise.add_operator('t_parametric', ParametricOp)
67
-
68
- subject << AnonOp.new
69
- subject << ParametricOp.new
70
- expect(subject.to_params).to eq([["t_anon", {}], ["t_parametric", {:a=>133}]])
71
- end
72
- end
@@ -1,325 +0,0 @@
1
- require_relative '../spec_helper'
2
- require 'rack/test'
3
-
4
- describe ImageVise::RenderEngine do
5
- include Rack::Test::Methods
6
-
7
- let(:app) { ImageVise::RenderEngine.new }
8
-
9
- context 'when the subclass is configured to raise exceptions' do
10
- after :each do
11
- ImageVise.reset_allowed_hosts!
12
- ImageVise.reset_secret_keys!
13
- end
14
-
15
- it 'raises an exception instead of returning an error response' do
16
- class << app
17
- def raise_exceptions?
18
- true
19
- end
20
- end
21
-
22
- p = ImageVise::Pipeline.new.crop(width: 10, height: 10, gravity: 'c')
23
- image_request = ImageVise::ImageRequest.new(src_url: 'http://unknown.com/image.jpg', pipeline: p)
24
- expect(app).to receive(:handle_generic_error).and_call_original
25
- expect {
26
- get image_request.to_path_params('l33tness')
27
- }.to raise_error(/No keys set/)
28
- end
29
- end
30
-
31
- context 'when requesting an image' do
32
- before :each do
33
- parsed_url = Addressable::URI.parse(public_url)
34
- ImageVise.add_allowed_host!(parsed_url.host)
35
- end
36
-
37
- after :each do
38
- ImageVise.reset_allowed_hosts!
39
- ImageVise.reset_secret_keys!
40
- end
41
-
42
- it 'halts with 400 when the requested image cannot be opened by ImageMagick' do
43
- uri = Addressable::URI.parse(public_url)
44
- ImageVise.add_allowed_host!(uri.host)
45
- ImageVise.add_secret_key!('l33tness')
46
- uri.path = '/___nonexistent_image.jpg'
47
-
48
- p = ImageVise::Pipeline.new.crop(width: 10, height: 10, gravity: 'c')
49
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
50
-
51
- expect_any_instance_of(Patron::Session).to receive(:get_file) {|_self, url, path|
52
- File.open(path, 'wb') {|f| f << 'totally not an image' }
53
- double(status: 200)
54
- }
55
- expect(app).to receive(:handle_request_error).and_call_original
56
-
57
- get image_request.to_path_params('l33tness')
58
- expect(last_response.status).to eq(400)
59
- expect(last_response['Cache-Control']).to match(/public/)
60
- expect(last_response.body).to include('Unsupported/unknown')
61
- end
62
-
63
- it 'halts with 400 when a file:// URL is given and filesystem access is not enabled' do
64
- uri = 'file://' + test_image_path
65
- ImageVise.deny_filesystem_sources!
66
- ImageVise.add_secret_key!('l33tness')
67
-
68
- p = ImageVise::Pipeline.new.fit_crop(width: 10, height: 10, gravity: 'c')
69
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
70
-
71
- get image_request.to_path_params('l33tness')
72
- expect(last_response.status).to eq(403)
73
- expect(last_response.body).to include('filesystem access is disabled')
74
- end
75
-
76
- it 'responds with 403 when upstream returns it, and includes the URL in the error message' do
77
- uri = Addressable::URI.parse(public_url)
78
- ImageVise.add_allowed_host!(uri.host)
79
- ImageVise.add_secret_key!('l33tness')
80
- uri.path = '/forbidden'
81
-
82
- p = ImageVise::Pipeline.new.crop(width: 10, height: 10, gravity: 'c')
83
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
84
-
85
- get image_request.to_path_params('l33tness')
86
- expect(last_response.status).to eq(403)
87
- expect(last_response.headers['Content-Type']).to eq('application/json')
88
- parsed = JSON.load(last_response.body)
89
- expect(parsed['errors'].to_s).to include("Unfortunate upstream response")
90
- expect(parsed['errors'].to_s).to include(uri.to_s)
91
- end
92
-
93
- it 'replays upstream error response codes that are selected to be replayed to the requester' do
94
- uri = Addressable::URI.parse(public_url)
95
- ImageVise.add_allowed_host!(uri.host)
96
- ImageVise.add_secret_key!('l33tness')
97
-
98
- [404, 403, 503, 504, 500].each do | error_code |
99
- allow_any_instance_of(Patron::Session).to receive(:get_file).and_return(double(status: error_code))
100
-
101
- p = ImageVise::Pipeline.new.crop(width: 10, height: 10, gravity: 'c')
102
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
103
-
104
- get image_request.to_path_params('l33tness')
105
-
106
- expect(last_response.status).to eq(error_code)
107
- expect(last_response.headers).to have_key('Cache-Control')
108
- expect(last_response.headers['Cache-Control']).to match(/public/)
109
-
110
- expect(last_response.headers['Content-Type']).to eq('application/json')
111
- parsed = JSON.load(last_response.body)
112
- expect(parsed['errors'].to_s).to include("Unfortunate upstream response")
113
- end
114
- end
115
-
116
- it 'sets very far caching headers and an ETag, and returns a 304 if any ETag is set' do
117
- uri = Addressable::URI.parse(public_url)
118
- ImageVise.add_allowed_host!(uri.host)
119
- ImageVise.add_secret_key!('l33tness')
120
-
121
- p = ImageVise::Pipeline.new.fit_crop(width: 10, height: 35, gravity: 'c')
122
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
123
-
124
- req_path = image_request.to_path_params('l33tness')
125
-
126
- get req_path, {}
127
- expect(last_response).to be_ok
128
- expect(last_response['ETag']).not_to be_nil
129
- expect(last_response['Cache-Control']).to match(/public/)
130
-
131
- get req_path, {}, {'HTTP_IF_NONE_MATCH' => last_response['ETag']}
132
- expect(last_response.status).to eq(304)
133
-
134
- # Should consider _any_ ETag a request to rerender something
135
- # that already exists in an upstream cache
136
- get req_path, {}, {'HTTP_IF_NONE_MATCH' => SecureRandom.hex(4)}
137
- expect(last_response.status).to eq(304)
138
- end
139
-
140
- it 'responds with an image that passes through all the processing steps' do
141
- uri = Addressable::URI.parse(public_url)
142
- ImageVise.add_allowed_host!(uri.host)
143
- ImageVise.add_secret_key!('l33tness')
144
-
145
- p = ImageVise::Pipeline.new.geom(geometry_string: '512x335').fit_crop(width: 10, height: 10, gravity: 'c')
146
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
147
-
148
- get image_request.to_path_params('l33tness')
149
- expect(last_response.status).to eq(200)
150
-
151
- expect(last_response.headers['Content-Type']).to eq('image/jpeg')
152
- expect(last_response.headers).to have_key('Content-Length')
153
- parsed_image = Magick::Image.from_blob(last_response.body)[0]
154
- expect(parsed_image.columns).to eq(10)
155
- end
156
-
157
- it 'properly decodes the image request if its Base64 representation contains masked slashes and plus characters' do
158
- ImageVise.add_secret_key!("this is fab")
159
- sig = '64759d9ea610d75d9138bfa3ea01595d343ca8994261ae06fca8e6490222f140'
160
- q = 'eyJwaXBlbGluZSI6W1sic2hhcnBlbiIseyJyYWRpdXMiO' +
161
- 'jAuNSwic2lnbWEiOjAuNX1dXSwic3JjX3VybCI6InNoYWRl' +
162
- 'cmljb246L0NQR1BfRmlyZWJhbGw-Yz1kOWM4ZTMzO'+
163
- 'TZmNjMwYzM1MjM0MTYwMmM2YzJhYmQyZjAzNTcxMTF'+
164
- 'jIn0'
165
- params = {q: q, sig: sig}
166
- req = ImageVise::ImageRequest.from_params(qs_params: params, secrets: ['this is fab'])
167
-
168
- # We do a check based on the raised exception - the request will fail
169
- # at the fetcher lookup stage. That stage however takes place _after_ the
170
- # signature has been validated, which means that the slash within the
171
- # Base64 payload has been taken into account
172
- expect(app).to receive(:raise_exceptions?).and_return(true)
173
- expect {
174
- get req.to_path_params('this is fab')
175
- }.to raise_error(/No fetcher registered for shadericon/)
176
- end
177
-
178
- it 'calls all of the internal methods during execution' do
179
- uri = Addressable::URI.parse(public_url)
180
- ImageVise.add_allowed_host!(uri.host)
181
- ImageVise.add_secret_key!('l33tness')
182
-
183
- p = ImageVise::Pipeline.new.geom(geometry_string: '512x335').fit_crop(width: 10, height: 10, gravity: 'c')
184
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
185
- params = image_request.to_query_string_params('l33tness')
186
-
187
- expect(app).to receive(:parse_env_into_request).and_call_original
188
- expect(app).to receive(:process_image_request).and_call_original
189
- expect(app).to receive(:extract_params_from_request).and_call_original
190
- expect(app).to receive(:image_rack_response).and_call_original
191
- expect(app).to receive(:source_file_type_permitted?).and_call_original
192
- expect(app).to receive(:output_file_type_permitted?).and_call_original
193
- expect(app).to receive(:enable_forking?).and_call_original
194
-
195
- get image_request.to_path_params('l33tness')
196
- expect(last_response.status).to eq(200)
197
- end
198
-
199
- it 'picks the image from the filesystem if that is permitted' do
200
- uri = 'file://' + test_image_path
201
- ImageVise.allow_filesystem_source!(File.dirname(test_image_path) + '/*.*')
202
- ImageVise.add_secret_key!('l33tness')
203
-
204
- p = ImageVise::Pipeline.new.fit_crop(width: 10, height: 10, gravity: 'c')
205
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
206
-
207
- get image_request.to_path_params('l33tness')
208
- expect(last_response.status).to eq(200)
209
- expect(last_response.headers['Content-Type']).to eq('image/jpeg')
210
- end
211
-
212
- it 'URI-decodes the path in a file:// URL for a file with a Unicode path' do
213
- utf8_file_path = File.dirname(test_image_path) + '/картинка.jpg'
214
- FileUtils.cp_r(test_image_path, utf8_file_path)
215
- uri = 'file://' + URI.encode(utf8_file_path)
216
-
217
- ImageVise.allow_filesystem_source!(File.dirname(test_image_path) + '/*.*')
218
- ImageVise.add_secret_key!('l33tness')
219
-
220
- p = ImageVise::Pipeline.new.fit_crop(width: 10, height: 10, gravity: 'c')
221
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
222
-
223
- get image_request.to_path_params('l33tness')
224
- File.unlink(utf8_file_path)
225
- expect(last_response.status).to eq(200)
226
- expect(last_response.headers['Content-Type']).to eq('image/jpeg')
227
- end
228
-
229
- it 'forbids a request with an extra GET param' do
230
- uri = 'file://' + URI.encode(test_image_path)
231
-
232
- p = ImageVise::Pipeline.new.fit_crop(width: 10, height: 10, gravity: 'c')
233
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
234
-
235
- get image_request.to_path_params('l33tness'), {'extra' => '123'}
236
-
237
- expect(last_response.status).to eq(400)
238
- end
239
-
240
- it 'returns the processed JPEG image as a PNG if it had to get an alpha channel during processing' do
241
- uri = Addressable::URI.parse(public_url)
242
- ImageVise.add_allowed_host!(uri.host)
243
- ImageVise.add_secret_key!('l33tness')
244
-
245
- p = ImageVise::Pipeline.new.geom(geometry_string: '220x220').ellipse_stencil
246
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
247
-
248
- get image_request.to_path_params('l33tness')
249
- expect(last_response.status).to eq(200)
250
-
251
- expect(last_response.headers['Content-Type']).to eq('image/png')
252
- expect(last_response.headers).to have_key('Content-Length')
253
-
254
- examine_image_from_string(last_response.body)
255
- end
256
-
257
- it 'permits a PSD file by default' do
258
- uri = Addressable::URI.parse(public_url_psd)
259
- ImageVise.add_allowed_host!(uri.host)
260
- ImageVise.add_secret_key!('l33tness')
261
-
262
- p = ImageVise::Pipeline.new.geom(geometry_string: '220x220').ellipse_stencil
263
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
264
-
265
- get image_request.to_path_params('l33tness')
266
- expect(last_response.status).to eq(200)
267
- end
268
-
269
- it 'destroys all the loaded PSD layers' do
270
- uri = Addressable::URI.parse(public_url_psd_multilayer)
271
- ImageVise.add_allowed_host!(uri.host)
272
- ImageVise.add_secret_key!('l33tness')
273
-
274
- p = ImageVise::Pipeline.new.geom(geometry_string: '220x220')
275
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
276
-
277
- class << app
278
- def raise_exceptions?; true; end
279
- end
280
-
281
- # For each layer loaded into the ImageList
282
- expect(ImageVise).to receive(:destroy).and_call_original.exactly(5).times
283
-
284
- get image_request.to_path_params('l33tness')
285
-
286
- expect(last_response.status).to eq(200)
287
- end
288
-
289
- it 'outputs a converted TIFF file as a PNG' do
290
- uri = Addressable::URI.parse(public_url_tif)
291
- ImageVise.add_allowed_host!(uri.host)
292
- ImageVise.add_secret_key!('l33tness')
293
-
294
- p = ImageVise::Pipeline.new.geom(geometry_string: '220x220')
295
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
296
-
297
- class << app
298
- def source_file_type_permitted?(type); true; end
299
- end
300
-
301
- get image_request.to_path_params('l33tness')
302
- expect(last_response.status).to eq(200)
303
- expect(last_response.headers['Content-Type']).to eq('image/png')
304
- end
305
-
306
- it 'outputs a converted TIFF file in the TIFF format if it is on the permitted list' do
307
- uri = Addressable::URI.parse(public_url_tif)
308
- ImageVise.add_allowed_host!(uri.host)
309
- ImageVise.add_secret_key!('l33tness')
310
-
311
- p = ImageVise::Pipeline.new.geom(geometry_string: '220x220')
312
- image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
313
-
314
-
315
- class << app
316
- def source_file_type_permitted?(type); true; end
317
- def output_file_type_permitted?(type); true; end
318
- end
319
-
320
- get image_request.to_path_params('l33tness')
321
- expect(last_response.status).to eq(200)
322
- expect(last_response.headers['Content-Type']).to eq('image/tiff')
323
- end
324
- end
325
- end
@@ -1,17 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ImageVise::Sharpen do
4
- it 'refuses invalid parameters' do
5
- expect { described_class.new(sigma: 0, radius: -1) }.to raise_error(ArgumentError)
6
- end
7
-
8
- it 'applies the crop with different gravities' do
9
- [[1, 1], [4, 2], [0.75, 0.5]].each do |(r, s)|
10
- image = Magick::Image.read(test_image_path)[0]
11
- expect(ImageVise).to receive(:destroy).with(instance_of(Magick::Image)).and_call_original
12
- sharpen = described_class.new(radius: r, sigma: s)
13
- sharpen.apply!(image)
14
- examine_image(image, "sharpen-rad_%02f-sigma_%02f" % [r, s])
15
- end
16
- end
17
- end
@@ -1,28 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ImageVise::SRGB do
4
- it 'applies the profile, creating a perceptible difference with the original' do
5
- opset = ImageVise::Pipeline.new([
6
- ImageVise::FitCrop.new(width: 512, height: 512, gravity: 'c'),
7
- described_class.new,
8
- ])
9
-
10
- # This test will function only if you have RMagick with LCMS2 support
11
- # built-in. If you do, the two images will look _very_ much like one
12
- # another.
13
- #
14
- # If you don't, the images will look remarkably different
15
- # (the AdobeRGB version has color values that match AdobeRGB
16
- # primaries, and will render diffrently in pretty much any
17
- # viewer).
18
- image = Magick::Image.read(test_image_adobergb_path).first
19
- opset.apply!(image)
20
- image.strip!
21
- examine_image(image, "from-adobergb")
22
-
23
- image = Magick::Image.read(test_image_path).first
24
- opset.apply!(image)
25
- image.strip!
26
- examine_image(image, "from-srgb")
27
- end
28
- end
@@ -1,14 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ImageVise::StripMetadata do
4
- it 'applies the strip! method to the image' do
5
- image = Magick::Image.read(test_image_path).first
6
- expect(image).to receive(:strip!).and_call_original
7
- described_class.new.apply!(image)
8
- end
9
-
10
- it 'is registered with the operator registry' do
11
- op = ImageVise.operator_from('strip_metadata')
12
- expect(op).to eq(described_class)
13
- end
14
- end
@@ -1,110 +0,0 @@
1
- require_relative 'spec_helper'
2
- require 'rack/test'
3
-
4
- describe ImageVise do
5
- include Rack::Test::Methods
6
-
7
- def app
8
- described_class.new
9
- end
10
-
11
- context 'ImageVise.allowed_hosts' do
12
- it 'returns the allowed hosts and is empty by default' do
13
- expect(described_class.allowed_hosts).to be_empty
14
- end
15
-
16
- it 'allows add_allowed_host! and reset_allowed_hosts!' do
17
- described_class.add_allowed_host!('www.imageboard.im')
18
- expect(described_class.allowed_hosts).to include('www.imageboard.im')
19
- described_class.reset_allowed_hosts!
20
- expect(described_class.allowed_hosts).not_to include('www.imageboard.im')
21
- end
22
- end
23
-
24
- context 'ImageVise.secret_keys' do
25
- it 'raises when asked for a key and no keys has been set' do
26
- expect {
27
- described_class.secret_keys
28
- }.to raise_error("No keys set, add a key using `ImageVise.add_secret_key!(key)'")
29
- end
30
-
31
- it 'allows add_secret_key!(key) and reset_secret_keys!' do
32
- described_class.add_secret_key!('l33t')
33
- expect(described_class.secret_keys).to include('l33t')
34
- described_class.reset_secret_keys!
35
- expect {
36
- expect(described_class.secret_keys)
37
- }.to raise_error
38
- end
39
- end
40
-
41
- describe 'ImageVise.new.call' do
42
- it 'instantiates a new app and performs call() on it' do
43
- expect_any_instance_of(ImageVise::RenderEngine).to receive(:call).with(:mock_env) { :yes }
44
- ImageVise.new.call(:mock_env)
45
- end
46
- end
47
-
48
- describe 'ImageVise.call' do
49
- it 'instantiates a new app and performs call() on it' do
50
- expect_any_instance_of(ImageVise::RenderEngine).to receive(:call).with(:mock_env) { :yes }
51
- ImageVise.call(:mock_env)
52
- end
53
- end
54
-
55
- describe '.image_params' do
56
- it 'generates a Hash with paremeters for processing the resized image' do
57
- params = ImageVise.image_params(src_url: 'http://host.com/image.jpg', secret: 'l33t') do |pipe|
58
- pipe.fit_crop width: 128, height: 256, gravity: 'c'
59
- end
60
- expect(params).to be_kind_of(Hash)
61
- expect(params[:q]).not_to be_empty
62
- expect(params[:sig]).not_to be_empty
63
- end
64
- end
65
-
66
- describe 'methods dealing with fetchers' do
67
- it 'returns the fetchers for the default schemes' do
68
- http = ImageVise.fetcher_for('http')
69
- expect(http).to respond_to(:fetch_uri_to_tempfile)
70
- file = ImageVise.fetcher_for('file')
71
- expect(http).to respond_to(:fetch_uri_to_tempfile)
72
-
73
- expect {
74
- ImageVise.fetcher_for('undernet')
75
- }.to raise_error(/No fetcher registered/)
76
- end
77
- end
78
-
79
- describe '.image_path' do
80
- it 'returns the path to the image within the application' do
81
- path = ImageVise.image_path(src_url: 'file://tmp/img.jpg', secret: 'a') do |p|
82
- p.ellipse_stencil
83
- end
84
- expect(path).to start_with('/')
85
- end
86
- end
87
-
88
- describe 'methods dealing with the operator list' do
89
- it 'have the basic operators already set up' do
90
- oplist = ImageVise.defined_operator_names
91
- expect(oplist).to include('sharpen')
92
- expect(oplist).to include('crop')
93
- end
94
-
95
- it 'allows an operator to be added and retrieved' do
96
- class CustomOp; end
97
- ImageVise.add_operator 'custom_op', CustomOp
98
- expect(ImageVise.operator_from(:custom_op)).to eq(CustomOp)
99
- expect(ImageVise.operator_name_for(CustomOp.new)).to eq('custom_op')
100
- expect(ImageVise.defined_operator_names).to include('custom_op')
101
- end
102
-
103
- it 'raises an exception when an operator key is requested that does not exist' do
104
- class UnknownOp; end
105
- expect {
106
- ImageVise.operator_name_for(UnknownOp.new)
107
- }.to raise_error(/not registered using ImageVise/)
108
- end
109
- end
110
- end
Binary file