image_vise 0.1.6 → 0.2.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 (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