2015-10-11 05:41:23 -04:00
|
|
|
require 'rails_helper'
|
2013-06-16 04:39:48 -04:00
|
|
|
|
|
|
|
describe OptimizedImage do
|
2013-11-05 13:04:47 -05:00
|
|
|
let(:upload) { build(:upload) }
|
|
|
|
before { upload.id = 42 }
|
2013-06-16 20:46:42 -04:00
|
|
|
|
2018-07-17 05:11:05 -04:00
|
|
|
unless ENV["TRAVIS"]
|
|
|
|
describe '.crop' do
|
|
|
|
it 'should work correctly' do
|
|
|
|
tmp_path = "/tmp/cropped.png"
|
2018-07-17 03:48:59 -04:00
|
|
|
|
2018-07-17 05:11:05 -04:00
|
|
|
begin
|
|
|
|
OptimizedImage.crop(
|
|
|
|
"#{Rails.root}/spec/fixtures/images/logo.png",
|
|
|
|
tmp_path,
|
|
|
|
5,
|
|
|
|
5
|
|
|
|
)
|
2018-07-17 03:48:59 -04:00
|
|
|
|
2018-07-17 05:11:05 -04:00
|
|
|
expect(File.read(tmp_path)).to eq(
|
|
|
|
File.read("#{Rails.root}/spec/fixtures/images/cropped.png")
|
|
|
|
)
|
|
|
|
ensure
|
|
|
|
File.delete(tmp_path) if File.exists?(tmp_path)
|
|
|
|
end
|
2018-07-17 03:48:59 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-07-17 05:11:05 -04:00
|
|
|
describe '.resize' do
|
|
|
|
it 'should work correctly' do
|
|
|
|
tmp_path = "/tmp/resized.png"
|
2018-07-17 03:48:59 -04:00
|
|
|
|
2018-07-17 05:11:05 -04:00
|
|
|
begin
|
|
|
|
OptimizedImage.resize(
|
|
|
|
"#{Rails.root}/spec/fixtures/images/logo.png",
|
|
|
|
tmp_path,
|
|
|
|
5,
|
|
|
|
5
|
|
|
|
)
|
2018-07-17 03:48:59 -04:00
|
|
|
|
2018-07-17 05:11:05 -04:00
|
|
|
expect(File.read(tmp_path)).to eq(
|
|
|
|
File.read("#{Rails.root}/spec/fixtures/images/resized.png")
|
|
|
|
)
|
|
|
|
ensure
|
|
|
|
File.delete(tmp_path) if File.exists?(tmp_path)
|
|
|
|
end
|
2018-07-17 03:48:59 -04:00
|
|
|
end
|
2018-07-25 21:16:14 -04:00
|
|
|
|
|
|
|
describe 'when an svg with a href is masked as a png' do
|
|
|
|
it 'should not trigger the external request' do
|
|
|
|
tmp_path = "/tmp/resized.png"
|
|
|
|
|
|
|
|
begin
|
|
|
|
expect do
|
|
|
|
OptimizedImage.resize(
|
|
|
|
"#{Rails.root}/spec/fixtures/images/svg.png",
|
|
|
|
tmp_path,
|
|
|
|
5,
|
|
|
|
5
|
|
|
|
)
|
|
|
|
end.to raise_error(RuntimeError, /improper image header/)
|
|
|
|
ensure
|
|
|
|
File.delete(tmp_path) if File.exists?(tmp_path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-07-17 03:48:59 -04:00
|
|
|
end
|
|
|
|
|
2018-07-17 05:11:05 -04:00
|
|
|
describe '.downsize' do
|
|
|
|
it 'should work correctly' do
|
|
|
|
tmp_path = "/tmp/downsized.png"
|
2018-07-17 03:48:59 -04:00
|
|
|
|
2018-07-17 05:11:05 -04:00
|
|
|
begin
|
|
|
|
OptimizedImage.downsize(
|
|
|
|
"#{Rails.root}/spec/fixtures/images/logo.png",
|
|
|
|
tmp_path,
|
|
|
|
"100x100\>"
|
|
|
|
)
|
2018-07-17 03:48:59 -04:00
|
|
|
|
2018-07-17 05:11:05 -04:00
|
|
|
expect(File.read(tmp_path)).to eq(
|
|
|
|
File.read("#{Rails.root}/spec/fixtures/images/downsized.png")
|
|
|
|
)
|
|
|
|
ensure
|
|
|
|
File.delete(tmp_path) if File.exists?(tmp_path)
|
|
|
|
end
|
2018-07-17 03:48:59 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-12-18 18:16:18 -05:00
|
|
|
describe ".safe_path?" do
|
|
|
|
|
|
|
|
it "correctly detects unsafe paths" do
|
|
|
|
expect(OptimizedImage.safe_path?("/path/A-AA/22_00.TIFF")).to eq(true)
|
|
|
|
expect(OptimizedImage.safe_path?("/path/AAA/2200.TIFF")).to eq(true)
|
|
|
|
expect(OptimizedImage.safe_path?("/tmp/a.png")).to eq(true)
|
|
|
|
expect(OptimizedImage.safe_path?("../a.png")).to eq(false)
|
|
|
|
expect(OptimizedImage.safe_path?("/tmp/a.png\\test")).to eq(false)
|
|
|
|
expect(OptimizedImage.safe_path?("/tmp/a.png\\test")).to eq(false)
|
|
|
|
expect(OptimizedImage.safe_path?("/path/\u1000.png")).to eq(false)
|
|
|
|
expect(OptimizedImage.safe_path?("/path/x.png\n")).to eq(false)
|
|
|
|
expect(OptimizedImage.safe_path?("/path/x.png\ny.png")).to eq(false)
|
|
|
|
expect(OptimizedImage.safe_path?("/path/x.png y.png")).to eq(false)
|
|
|
|
expect(OptimizedImage.safe_path?(nil)).to eq(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "ensure_safe_paths!" do
|
|
|
|
it "raises nothing on safe paths" do
|
|
|
|
expect {
|
|
|
|
OptimizedImage.ensure_safe_paths!("/a.png", "/b.png")
|
|
|
|
}.not_to raise_error
|
|
|
|
end
|
|
|
|
|
2018-07-25 16:00:04 -04:00
|
|
|
it "raises InvalidAccess error on paths" do
|
2016-12-18 18:16:18 -05:00
|
|
|
expect {
|
|
|
|
OptimizedImage.ensure_safe_paths!("/a.png", "/b.png", "c.png")
|
|
|
|
}.to raise_error(Discourse::InvalidAccess)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-05-25 22:32:52 -04:00
|
|
|
describe ".local?" do
|
|
|
|
|
|
|
|
def local(url)
|
|
|
|
OptimizedImage.new(url: url).local?
|
|
|
|
end
|
|
|
|
|
|
|
|
it "correctly detects local vs remote" do
|
|
|
|
expect(local("//hello")).to eq(false)
|
|
|
|
expect(local("http://hello")).to eq(false)
|
|
|
|
expect(local("https://hello")).to eq(false)
|
|
|
|
expect(local("https://hello")).to eq(false)
|
|
|
|
expect(local("/hello")).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-16 20:46:42 -04:00
|
|
|
describe ".create_for" do
|
|
|
|
|
2013-11-05 13:04:47 -05:00
|
|
|
context "when using an internal store" do
|
|
|
|
|
|
|
|
let(:store) { FakeInternalStore.new }
|
|
|
|
before { Discourse.stubs(:store).returns(store) }
|
|
|
|
|
2014-05-26 05:46:43 -04:00
|
|
|
context "when an error happened while generating the thumbnail" do
|
2013-11-05 13:04:47 -05:00
|
|
|
|
|
|
|
it "returns nil" do
|
2014-05-26 05:46:43 -04:00
|
|
|
OptimizedImage.expects(:resize).returns(false)
|
2015-01-05 11:04:23 -05:00
|
|
|
expect(OptimizedImage.create_for(upload, 100, 200)).to eq(nil)
|
2013-11-05 13:04:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when the thumbnail is properly generated" do
|
|
|
|
|
2014-05-26 05:46:43 -04:00
|
|
|
before do
|
|
|
|
OptimizedImage.expects(:resize).returns(true)
|
|
|
|
end
|
2013-11-05 13:04:47 -05:00
|
|
|
|
|
|
|
it "does not download a copy of the original image" do
|
|
|
|
store.expects(:download).never
|
|
|
|
OptimizedImage.create_for(upload, 100, 200)
|
|
|
|
end
|
2013-07-31 17:26:34 -04:00
|
|
|
|
2013-11-05 13:04:47 -05:00
|
|
|
it "closes and removes the tempfile" do
|
|
|
|
Tempfile.any_instance.expects(:close!)
|
|
|
|
OptimizedImage.create_for(upload, 100, 200)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works" do
|
|
|
|
oi = OptimizedImage.create_for(upload, 100, 200)
|
2015-01-05 11:04:23 -05:00
|
|
|
expect(oi.sha1).to eq("da39a3ee5e6b4b0d3255bfef95601890afd80709")
|
|
|
|
expect(oi.extension).to eq(".png")
|
|
|
|
expect(oi.width).to eq(100)
|
|
|
|
expect(oi.height).to eq(200)
|
|
|
|
expect(oi.url).to eq("/internally/stored/optimized/image.png")
|
2013-11-05 13:04:47 -05:00
|
|
|
end
|
2013-07-31 17:26:34 -04:00
|
|
|
|
|
|
|
end
|
|
|
|
|
2013-06-16 20:46:42 -04:00
|
|
|
end
|
|
|
|
|
2013-07-31 17:26:34 -04:00
|
|
|
describe "external store" do
|
|
|
|
|
2013-11-05 13:04:47 -05:00
|
|
|
let(:store) { FakeExternalStore.new }
|
|
|
|
before { Discourse.stubs(:store).returns(store) }
|
|
|
|
|
|
|
|
context "when an error happened while generatign the thumbnail" do
|
|
|
|
|
|
|
|
it "returns nil" do
|
2014-05-26 05:46:43 -04:00
|
|
|
OptimizedImage.expects(:resize).returns(false)
|
2015-01-05 11:04:23 -05:00
|
|
|
expect(OptimizedImage.create_for(upload, 100, 200)).to eq(nil)
|
2013-11-05 13:04:47 -05:00
|
|
|
end
|
2013-07-31 17:26:34 -04:00
|
|
|
|
|
|
|
end
|
|
|
|
|
2013-11-05 13:04:47 -05:00
|
|
|
context "when the thumbnail is properly generated" do
|
|
|
|
|
2014-05-26 05:46:43 -04:00
|
|
|
before do
|
|
|
|
OptimizedImage.expects(:resize).returns(true)
|
|
|
|
end
|
2013-11-05 13:04:47 -05:00
|
|
|
|
|
|
|
it "downloads a copy of the original image" do
|
2015-02-09 11:13:22 -05:00
|
|
|
Tempfile.any_instance.expects(:close!)
|
2014-04-14 16:55:57 -04:00
|
|
|
store.expects(:download).with(upload).returns(Tempfile.new(["discourse-external", ".png"]))
|
2013-11-05 13:04:47 -05:00
|
|
|
OptimizedImage.create_for(upload, 100, 200)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works" do
|
|
|
|
oi = OptimizedImage.create_for(upload, 100, 200)
|
2015-01-05 11:04:23 -05:00
|
|
|
expect(oi.sha1).to eq("da39a3ee5e6b4b0d3255bfef95601890afd80709")
|
|
|
|
expect(oi.extension).to eq(".png")
|
|
|
|
expect(oi.width).to eq(100)
|
|
|
|
expect(oi.height).to eq(200)
|
|
|
|
expect(oi.url).to eq("/externally/stored/optimized/image.png")
|
2013-11-05 13:04:47 -05:00
|
|
|
end
|
|
|
|
|
2013-07-31 17:26:34 -04:00
|
|
|
end
|
|
|
|
|
2013-06-16 20:46:42 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2013-06-16 04:39:48 -04:00
|
|
|
end
|
2013-11-05 13:04:47 -05:00
|
|
|
|
|
|
|
class FakeInternalStore
|
|
|
|
|
|
|
|
def external?
|
2015-05-29 12:39:47 -04:00
|
|
|
false
|
2013-11-05 13:04:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def path_for(upload)
|
|
|
|
upload.url
|
|
|
|
end
|
|
|
|
|
|
|
|
def store_optimized_image(file, optimized_image)
|
|
|
|
"/internally/stored/optimized/image#{optimized_image.extension}"
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
class FakeExternalStore
|
|
|
|
|
2015-05-31 21:17:42 -04:00
|
|
|
def path_for(upload)
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2013-11-05 13:04:47 -05:00
|
|
|
def external?
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def store_optimized_image(file, optimized_image)
|
|
|
|
"/externally/stored/optimized/image#{optimized_image.extension}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def download(upload)
|
|
|
|
extension = File.extname(upload.original_filename)
|
|
|
|
Tempfile.new(["discourse-s3", extension])
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|