2019-03-30 03:23:56 -04:00
# frozen_string_literal: true
2019-08-29 03:56:52 -04:00
require 'spec_helper'
2017-02-22 14:25:06 -05:00
2020-06-24 02:09:01 -04:00
RSpec . describe Upload do
2019-10-02 20:05:59 -04:00
describe 'associations' do
2017-02-22 14:25:06 -05:00
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 )
2021-11-03 17:10:35 -04:00
upload = described_class . create! (
2017-02-22 14:25:06 -05:00
path : __FILE__ ,
size : described_class :: CHECKSUM_THRESHOLD + 1 . kilobyte ,
model : build_stubbed ( :user ) ,
2018-10-05 09:59:58 -04:00
uploader : double ( 'ExampleUploader' ) ,
store : ObjectStorage :: Store :: LOCAL
2017-02-22 14:25:06 -05:00
)
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 ) ,
2018-10-05 09:59:58 -04:00
uploader : double ( 'ExampleUploader' ) ,
store : ObjectStorage :: Store :: LOCAL
2017-02-22 14:25:06 -05:00
)
2021-11-03 17:10:35 -04:00
expect { upload . save! }
2017-02-22 14:25:06 -05:00
. to change { upload . checksum } . from ( nil )
. to ( a_string_matching ( / \ A \ h{64} \ z / ) )
end
end
2018-01-31 10:59:35 -05:00
describe 'after_destroy' do
context 'uploader is FileUploader-based' do
subject { create ( :upload , :issuable_upload ) }
it 'calls delete_file!' do
is_expected . to receive ( :delete_file! )
2021-11-03 17:10:35 -04:00
subject . destroy!
2018-01-31 10:59:35 -05:00
end
end
end
2017-02-22 14:25:06 -05:00
end
describe '#absolute_path' do
it 'returns the path directly when already absolute' do
path = '/path/to/namespace/project/secret/file.jpg'
2018-10-05 09:59:58 -04:00
upload = described_class . new ( path : path , store : ObjectStorage :: Store :: LOCAL )
2017-02-22 14:25:06 -05:00
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' )
2018-10-05 09:59:58 -04:00
upload = described_class . new ( path : 'secret/file.jpg' , store : ObjectStorage :: Store :: LOCAL )
2017-02-22 14:25:06 -05:00
expect ( upload ) . to receive ( :uploader_class ) . and_return ( uploader )
upload . absolute_path
expect ( uploader ) . to have_received ( :absolute_path ) . with ( upload )
end
end
2021-11-01 17:10:32 -04:00
describe '#relative_path' do
it " delegates to the uploader's relative_path method " do
uploader = spy ( 'FakeUploader' )
upload = described_class . new ( path : '/tmp/secret/file.jpg' , store : ObjectStorage :: Store :: LOCAL )
expect ( upload ) . to receive ( :uploader_class ) . and_return ( uploader )
upload . relative_path
expect ( uploader ) . to have_received ( :relative_path ) . with ( upload )
end
end
2018-01-29 12:57:34 -05:00
describe '#calculate_checksum!' do
let ( :upload ) do
described_class . new ( path : __FILE__ ,
2018-10-05 09:59:58 -04:00
size : described_class :: CHECKSUM_THRESHOLD - 1 . megabyte ,
store : ObjectStorage :: Store :: LOCAL )
2018-01-29 12:57:34 -05:00
end
it 'sets `checksum` to SHA256 sum of the file' do
2017-02-22 14:25:06 -05:00
expected = Digest :: SHA256 . file ( __FILE__ ) . hexdigest
2018-01-29 12:57:34 -05:00
expect { upload . calculate_checksum! }
2017-02-22 14:25:06 -05:00
. to change { upload . checksum } . from ( nil ) . to ( expected )
end
2018-10-30 06:53:01 -04:00
it 'sets `checksum` to nil for a non-existent file' do
2017-02-22 14:25:06 -05:00
expect ( upload ) . to receive ( :exist? ) . and_return ( false )
2018-01-29 12:57:34 -05:00
checksum = Digest :: SHA256 . file ( __FILE__ ) . hexdigest
upload . checksum = checksum
expect { upload . calculate_checksum! }
. to change { upload . checksum } . from ( checksum ) . to ( nil )
2017-02-22 14:25:06 -05:00
end
2019-10-02 20:05:59 -04:00
end
describe '#build_uploader' do
it 'returns a uploader object with current upload associated with it' do
subject = build ( :upload )
uploader = subject . build_uploader
expect ( uploader . upload ) . to eq ( subject )
expect ( uploader . mounted_as ) . to eq ( subject . send ( :mount_point ) )
expect ( uploader . file ) . to be_nil
end
end
describe '#retrieve_uploader' do
it 'returns a uploader object with current uploader associated with and cache retrieved' do
subject = build ( :upload )
uploader = subject . retrieve_uploader
expect ( uploader . upload ) . to eq ( subject )
expect ( uploader . mounted_as ) . to eq ( subject . send ( :mount_point ) )
expect ( uploader . file ) . not_to be_nil
end
2020-03-12 20:09:34 -04:00
context 'when upload has mount_point nil' do
context 'when an upload belongs to a note' do
it 'mounts it as attachment' do
project = create ( :project , :legacy_storage )
merge_request = create ( :merge_request , source_project : project )
note = create ( :legacy_diff_note_on_merge_request , note : 'some note' , project : project , noteable : merge_request )
subject = build ( :upload , :with_file , :attachment_upload , model : note , mount_point : nil )
uploader = subject . retrieve_uploader
expect ( uploader . upload ) . to eq ( subject )
expect ( uploader . path ) . to include ( 'attachment' )
expect ( uploader . file ) . not_to be_nil
end
end
context 'when an upload does not belong to a note' do
it 'does not mount it as attachment' do
appearance = create ( :appearance )
subject = build ( :upload , :with_file , :attachment_upload , model : appearance , mount_point : nil )
uploader = subject . retrieve_uploader
expect ( uploader . upload ) . to eq ( subject )
expect ( uploader . path ) . not_to include ( 'attachment' )
expect ( uploader . file ) . not_to be_nil
end
end
end
2019-10-02 20:05:59 -04:00
end
describe '#needs_checksum?' do
context 'with local storage' do
it 'returns true when no checksum exists' do
subject = create ( :upload , :with_file , checksum : nil )
expect ( subject . needs_checksum? ) . to be_truthy
end
it 'returns false when checksum is already present' do
subject = create ( :upload , :with_file , checksum : 'something' )
expect ( subject . needs_checksum? ) . to be_falsey
end
end
context 'with remote storage' do
subject { build ( :upload , :object_storage ) }
it 'returns false' do
expect ( subject . needs_checksum? ) . to be_falsey
end
end
2017-02-22 14:25:06 -05:00
end
describe '#exist?' do
it 'returns true when the file exists' do
2018-10-05 09:59:58 -04:00
upload = described_class . new ( path : __FILE__ , store : ObjectStorage :: Store :: LOCAL )
2017-02-22 14:25:06 -05:00
expect ( upload ) . to exist
end
2018-10-30 06:56:47 -04:00
context 'when the file does not exist' do
it 'returns false' do
upload = described_class . new ( path : " #{ __FILE__ } -nope " , store : ObjectStorage :: Store :: LOCAL )
2017-02-22 14:25:06 -05:00
2018-10-30 06:56:47 -04:00
expect ( upload ) . not_to exist
end
context 'when the record is persisted' do
it 'sends a message to Sentry' do
upload = create ( :upload , :issuable_upload )
2019-12-16 07:07:43 -05:00
expect ( Gitlab :: ErrorTracking ) . to receive ( :track_exception ) . with ( instance_of ( RuntimeError ) , upload . attributes )
2018-10-30 06:56:47 -04:00
upload . exist?
end
it 'increments a metric counter to signal a problem' do
upload = create ( :upload , :issuable_upload )
counter = double ( :counter )
expect ( counter ) . to receive ( :increment )
expect ( Gitlab :: Metrics ) . to receive ( :counter ) . with ( :upload_file_does_not_exist_total , 'The number of times an upload record could not find its file' ) . and_return ( counter )
upload . exist?
end
end
context 'when the record is not persisted' do
it 'does not send a message to Sentry' do
upload = described_class . new ( path : " #{ __FILE__ } -nope " , store : ObjectStorage :: Store :: LOCAL )
2021-03-03 07:11:16 -05:00
expect ( Gitlab :: ErrorTracking ) . not_to receive ( :track_exception )
2018-10-30 06:56:47 -04:00
upload . exist?
end
it 'does not increment a metric counter' do
upload = described_class . new ( path : " #{ __FILE__ } -nope " , store : ObjectStorage :: Store :: LOCAL )
expect ( Gitlab :: Metrics ) . not_to receive ( :counter )
upload . exist?
end
end
2017-02-22 14:25:06 -05:00
end
end
2018-01-29 16:06:17 -05:00
describe " # uploader_context " do
subject { create ( :upload , :issuable_upload , secret : 'secret' , filename : 'file.txt' ) }
it { expect ( subject . uploader_context ) . to match ( a_hash_including ( secret : 'secret' , identifier : 'file.txt' ) ) }
end
2021-10-04 14:12:46 -04:00
describe '#update_project_statistics' do
let_it_be ( :project ) { create ( :project ) }
subject do
create ( :upload , model : project )
end
it 'updates project statistics when upload is added' do
expect ( ProjectCacheWorker ) . to receive ( :perform_async )
. with ( project . id , [ ] , [ :uploads_size ] )
subject . save!
end
it 'updates project statistics when upload is removed' do
subject . save!
expect ( ProjectCacheWorker ) . to receive ( :perform_async )
. with ( project . id , [ ] , [ :uploads_size ] )
subject . destroy!
end
end
2017-02-22 14:25:06 -05:00
end