2017-02-22 19:25:06 +00:00
|
|
|
require 'rails_helper'
|
|
|
|
|
|
|
|
describe Upload, type: :model do
|
|
|
|
describe 'assocations' do
|
|
|
|
it { is_expected.to belong_to(:model) }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'validations' do
|
|
|
|
it { is_expected.to validate_presence_of(:size) }
|
|
|
|
it { is_expected.to validate_presence_of(:path) }
|
|
|
|
it { is_expected.to validate_presence_of(:model) }
|
|
|
|
it { is_expected.to validate_presence_of(:uploader) }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'callbacks' do
|
|
|
|
context 'for a file above the checksum threshold' do
|
|
|
|
it 'schedules checksum calculation' do
|
|
|
|
stub_const('UploadChecksumWorker', spy)
|
|
|
|
|
|
|
|
upload = described_class.create(
|
|
|
|
path: __FILE__,
|
|
|
|
size: described_class::CHECKSUM_THRESHOLD + 1.kilobyte,
|
|
|
|
model: build_stubbed(:user),
|
|
|
|
uploader: double('ExampleUploader')
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(UploadChecksumWorker)
|
|
|
|
.to have_received(:perform_async).with(upload.id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'for a file at or below the checksum threshold' do
|
|
|
|
it 'calculates checksum immediately before save' do
|
|
|
|
upload = described_class.new(
|
|
|
|
path: __FILE__,
|
|
|
|
size: described_class::CHECKSUM_THRESHOLD,
|
|
|
|
model: build_stubbed(:user),
|
|
|
|
uploader: double('ExampleUploader')
|
|
|
|
)
|
|
|
|
|
|
|
|
expect { upload.save }
|
|
|
|
.to change { upload.checksum }.from(nil)
|
|
|
|
.to(a_string_matching(/\A\h{64}\z/))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.remove_path' do
|
|
|
|
it 'removes all records at the given path' do
|
|
|
|
described_class.create!(
|
|
|
|
size: File.size(__FILE__),
|
|
|
|
path: __FILE__,
|
|
|
|
model: build_stubbed(:user),
|
|
|
|
uploader: 'AvatarUploader'
|
|
|
|
)
|
|
|
|
|
2017-06-21 13:48:12 +00:00
|
|
|
expect { described_class.remove_path(__FILE__) }
|
|
|
|
.to change { described_class.count }.from(1).to(0)
|
2017-02-22 19:25:06 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.record' do
|
|
|
|
let(:fake_uploader) do
|
|
|
|
double(
|
|
|
|
file: double(size: 12_345),
|
|
|
|
relative_path: 'foo/bar.jpg',
|
|
|
|
model: build_stubbed(:user),
|
|
|
|
class: 'AvatarUploader'
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'removes existing paths before creation' do
|
|
|
|
expect(described_class).to receive(:remove_path)
|
|
|
|
.with(fake_uploader.relative_path)
|
|
|
|
|
|
|
|
described_class.record(fake_uploader)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'creates a new record and assigns size, path, model, and uploader' do
|
|
|
|
upload = described_class.record(fake_uploader)
|
|
|
|
|
|
|
|
aggregate_failures do
|
|
|
|
expect(upload).to be_persisted
|
|
|
|
expect(upload.size).to eq fake_uploader.file.size
|
|
|
|
expect(upload.path).to eq fake_uploader.relative_path
|
|
|
|
expect(upload.model_id).to eq fake_uploader.model.id
|
|
|
|
expect(upload.model_type).to eq fake_uploader.model.class.to_s
|
|
|
|
expect(upload.uploader).to eq fake_uploader.class
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#absolute_path' do
|
|
|
|
it 'returns the path directly when already absolute' do
|
|
|
|
path = '/path/to/namespace/project/secret/file.jpg'
|
|
|
|
upload = described_class.new(path: path)
|
|
|
|
|
|
|
|
expect(upload).not_to receive(:uploader_class)
|
|
|
|
|
|
|
|
expect(upload.absolute_path).to eq path
|
|
|
|
end
|
|
|
|
|
|
|
|
it "delegates to the uploader's absolute_path method" do
|
|
|
|
uploader = spy('FakeUploader')
|
|
|
|
upload = described_class.new(path: 'secret/file.jpg')
|
|
|
|
expect(upload).to receive(:uploader_class).and_return(uploader)
|
|
|
|
|
|
|
|
upload.absolute_path
|
|
|
|
|
|
|
|
expect(uploader).to have_received(:absolute_path).with(upload)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#calculate_checksum' do
|
|
|
|
it 'calculates the SHA256 sum' do
|
|
|
|
upload = described_class.new(
|
|
|
|
path: __FILE__,
|
|
|
|
size: described_class::CHECKSUM_THRESHOLD - 1.megabyte
|
|
|
|
)
|
|
|
|
expected = Digest::SHA256.file(__FILE__).hexdigest
|
|
|
|
|
|
|
|
expect { upload.calculate_checksum }
|
|
|
|
.to change { upload.checksum }.from(nil).to(expected)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns nil for a non-existant file' do
|
|
|
|
upload = described_class.new(
|
|
|
|
path: __FILE__,
|
|
|
|
size: described_class::CHECKSUM_THRESHOLD - 1.megabyte
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(upload).to receive(:exist?).and_return(false)
|
|
|
|
|
|
|
|
expect(upload.calculate_checksum).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#exist?' do
|
|
|
|
it 'returns true when the file exists' do
|
|
|
|
upload = described_class.new(path: __FILE__)
|
|
|
|
|
|
|
|
expect(upload).to exist
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false when the file does not exist' do
|
|
|
|
upload = described_class.new(path: "#{__FILE__}-nope")
|
|
|
|
|
|
|
|
expect(upload).not_to exist
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|