2017-02-15 13:11:44 -05:00
|
|
|
require 'rails_helper'
|
|
|
|
|
|
|
|
describe RecordsUploads do
|
2017-05-29 03:54:35 -04:00
|
|
|
let!(:uploader) do
|
2017-02-28 13:34:43 -05:00
|
|
|
class RecordsUploadsExampleUploader < GitlabUploader
|
2018-01-29 12:57:34 -05:00
|
|
|
include RecordsUploads::Concern
|
2017-02-15 13:11:44 -05:00
|
|
|
|
|
|
|
storage :file
|
|
|
|
|
2018-01-29 12:57:34 -05:00
|
|
|
def dynamic_segment
|
|
|
|
'co/fe/ee'
|
2017-02-15 13:11:44 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-01-29 12:57:34 -05:00
|
|
|
RecordsUploadsExampleUploader.new(build_stubbed(:user))
|
2017-02-15 13:11:44 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def upload_fixture(filename)
|
2018-06-05 17:18:06 -04:00
|
|
|
fixture_file_upload(File.join('spec', 'fixtures', filename))
|
2017-02-15 13:11:44 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
describe 'callbacks' do
|
2018-01-29 12:57:34 -05:00
|
|
|
let(:upload) { create(:upload) }
|
|
|
|
|
|
|
|
before do
|
|
|
|
uploader.upload = upload
|
|
|
|
end
|
|
|
|
|
|
|
|
it '#record_upload after `store`' do
|
2017-02-15 13:11:44 -05:00
|
|
|
expect(uploader).to receive(:record_upload).once
|
|
|
|
|
|
|
|
uploader.store!(upload_fixture('doc_sample.txt'))
|
|
|
|
end
|
|
|
|
|
2018-01-29 12:57:34 -05:00
|
|
|
it '#destroy_upload after `remove`' do
|
2017-02-15 13:11:44 -05:00
|
|
|
uploader.store!(upload_fixture('doc_sample.txt'))
|
|
|
|
|
2018-01-29 12:57:34 -05:00
|
|
|
expect(uploader).to receive(:destroy_upload).once
|
2017-02-15 13:11:44 -05:00
|
|
|
uploader.remove!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#record_upload callback' do
|
2018-01-29 12:57:34 -05:00
|
|
|
it 'creates an Upload record after store' do
|
|
|
|
expect { uploader.store!(upload_fixture('rails_sample.jpg')) }.to change { Upload.count }.by(1)
|
2017-02-15 13:11:44 -05:00
|
|
|
end
|
|
|
|
|
2018-01-29 12:57:34 -05:00
|
|
|
it 'creates a new record and assigns size, path, model, and uploader' do
|
2017-02-15 13:11:44 -05:00
|
|
|
uploader.store!(upload_fixture('rails_sample.jpg'))
|
2018-01-29 12:57:34 -05:00
|
|
|
|
|
|
|
upload = uploader.upload
|
|
|
|
aggregate_failures do
|
|
|
|
expect(upload).to be_persisted
|
|
|
|
expect(upload.size).to eq uploader.file.size
|
|
|
|
expect(upload.path).to eq uploader.upload_path
|
|
|
|
expect(upload.model_id).to eq uploader.model.id
|
|
|
|
expect(upload.model_type).to eq uploader.model.class.to_s
|
|
|
|
expect(upload.uploader).to eq uploader.class.to_s
|
|
|
|
end
|
2017-02-15 13:11:44 -05:00
|
|
|
end
|
|
|
|
|
2018-01-29 12:57:34 -05:00
|
|
|
it "does not create an Upload record when the file doesn't exist" do
|
|
|
|
allow(uploader).to receive(:file).and_return(double(exists?: false))
|
2017-02-15 13:11:44 -05:00
|
|
|
|
2018-01-29 12:57:34 -05:00
|
|
|
expect { uploader.store!(upload_fixture('rails_sample.jpg')) }.not_to change { Upload.count }
|
2017-02-15 13:11:44 -05:00
|
|
|
end
|
|
|
|
|
2017-05-29 03:54:35 -04:00
|
|
|
it 'does not create an Upload record if model is missing' do
|
2018-01-29 12:57:34 -05:00
|
|
|
allow_any_instance_of(RecordsUploadsExampleUploader).to receive(:model).and_return(nil)
|
2017-05-29 03:54:35 -04:00
|
|
|
|
2018-01-29 12:57:34 -05:00
|
|
|
expect { uploader.store!(upload_fixture('rails_sample.jpg')) }.not_to change { Upload.count }
|
2017-05-29 03:54:35 -04:00
|
|
|
end
|
|
|
|
|
2019-04-05 04:43:27 -04:00
|
|
|
it 'destroys Upload records at the same path before recording' do
|
2017-02-15 13:11:44 -05:00
|
|
|
existing = Upload.create!(
|
2017-02-28 13:34:43 -05:00
|
|
|
path: File.join('uploads', 'rails_sample.jpg'),
|
2017-02-15 13:11:44 -05:00
|
|
|
size: 512.kilobytes,
|
|
|
|
model: build_stubbed(:user),
|
2017-02-28 13:34:43 -05:00
|
|
|
uploader: uploader.class.to_s
|
2017-02-15 13:11:44 -05:00
|
|
|
)
|
|
|
|
|
2018-01-29 12:57:34 -05:00
|
|
|
uploader.upload = existing
|
2017-02-15 13:11:44 -05:00
|
|
|
uploader.store!(upload_fixture('rails_sample.jpg'))
|
|
|
|
|
|
|
|
expect { existing.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
2018-01-29 12:57:34 -05:00
|
|
|
expect(Upload.count).to eq(1)
|
2017-02-15 13:11:44 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#destroy_upload callback' do
|
2019-04-05 04:43:27 -04:00
|
|
|
it 'destroys Upload records at the same path after removal' do
|
2017-02-15 13:11:44 -05:00
|
|
|
uploader.store!(upload_fixture('rails_sample.jpg'))
|
|
|
|
|
|
|
|
expect { uploader.remove! }.to change { Upload.count }.from(1).to(0)
|
|
|
|
end
|
|
|
|
end
|
Speed up avatar URLs with object storage
With object storage enabled, calling `#filename` on an upload does this:
1. Call the `#filename` method on the CarrierWave object.
2. Generate the URL for that object.
3. If the uploader isn't public, do so by generating an authenticated
URL, including signing that request.
That's all correct behaviour, but for the case where we use `#filename`,
it's typically to generate a GitLab URL. That URL doesn't need to be
signed because we do our own auth.
Signing the URLs can be very expensive, especially in batch (say, we
need to get the avatar URLs for 150 users in one request). It's all
unnecessary work. If we used the `RecordsUploads` concern, we have
already recorded a `path` in the database. That `path` is actually
generated from CarrierWave's `#filename` at upload time, so we don't
need to recompute it - we can just use it and strip off the prefix if
it's available.
On a sample users autocomplete URL, at least 10% of the time before this
change went to signing URLs. After this change, we spend no time in URL
signing, and still get the correct results.
2019-04-02 07:52:28 -04:00
|
|
|
|
|
|
|
describe '#filename' do
|
|
|
|
it 'gets the filename from the path recorded in the database, not CarrierWave' do
|
|
|
|
uploader.store!(upload_fixture('rails_sample.jpg'))
|
|
|
|
expect_any_instance_of(GitlabUploader).not_to receive(:filename)
|
|
|
|
|
|
|
|
expect(uploader.filename).to eq('rails_sample.jpg')
|
|
|
|
end
|
|
|
|
end
|
2017-02-15 13:11:44 -05:00
|
|
|
end
|