2019-07-07 11:18:12 +05:30
# frozen_string_literal: true
2019-12-04 20:38:33 +05:30
require 'spec_helper'
2017-08-17 22:00:37 +05:30
2020-07-28 23:09:34 +05:30
RSpec . describe Upload do
2019-12-21 20:55:43 +05:30
describe 'associations' do
2017-08-17 22:00:37 +05:30
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-12-11 22:18:48 +05:30
upload = described_class . create! (
2017-08-17 22:00:37 +05:30
path : __FILE__ ,
size : described_class :: CHECKSUM_THRESHOLD + 1 . kilobyte ,
model : build_stubbed ( :user ) ,
2018-12-13 13:39:08 +05:30
uploader : double ( 'ExampleUploader' ) ,
store : ObjectStorage :: Store :: LOCAL
2017-08-17 22:00:37 +05:30
)
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-12-13 13:39:08 +05:30
uploader : double ( 'ExampleUploader' ) ,
store : ObjectStorage :: Store :: LOCAL
2017-08-17 22:00:37 +05:30
)
2021-12-11 22:18:48 +05:30
expect { upload . save! }
2017-08-17 22:00:37 +05:30
. to change { upload . checksum } . from ( nil )
. to ( a_string_matching ( / \ A \ h{64} \ z / ) )
end
end
2018-03-17 18:26:18 +05:30
describe 'after_destroy' do
context 'uploader is FileUploader-based' do
subject { create ( :upload , :issuable_upload ) }
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
it 'calls delete_file!' do
is_expected . to receive ( :delete_file! )
2017-08-17 22:00:37 +05:30
2021-12-11 22:18:48 +05:30
subject . destroy!
2018-03-17 18:26:18 +05:30
end
2017-08-17 22:00:37 +05:30
end
end
end
describe '#absolute_path' do
it 'returns the path directly when already absolute' do
path = '/path/to/namespace/project/secret/file.jpg'
2018-12-13 13:39:08 +05:30
upload = described_class . new ( path : path , store : ObjectStorage :: Store :: LOCAL )
2017-08-17 22:00:37 +05:30
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-12-13 13:39:08 +05:30
upload = described_class . new ( path : 'secret/file.jpg' , store : ObjectStorage :: Store :: LOCAL )
2017-08-17 22:00:37 +05:30
expect ( upload ) . to receive ( :uploader_class ) . and_return ( uploader )
upload . absolute_path
expect ( uploader ) . to have_received ( :absolute_path ) . with ( upload )
end
end
2021-12-11 22:18:48 +05:30
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-03-17 18:26:18 +05:30
describe '#calculate_checksum!' do
let ( :upload ) do
2023-07-09 08:55:56 +05:30
described_class . new (
path : __FILE__ ,
size : described_class :: CHECKSUM_THRESHOLD - 1 . megabyte ,
store : ObjectStorage :: Store :: LOCAL
)
2018-03-17 18:26:18 +05:30
end
it 'sets `checksum` to SHA256 sum of the file' do
2017-08-17 22:00:37 +05:30
expected = Digest :: SHA256 . file ( __FILE__ ) . hexdigest
2018-03-17 18:26:18 +05:30
expect { upload . calculate_checksum! }
2017-08-17 22:00:37 +05:30
. to change { upload . checksum } . from ( nil ) . to ( expected )
end
2018-12-13 13:39:08 +05:30
it 'sets `checksum` to nil for a non-existent file' do
2017-08-17 22:00:37 +05:30
expect ( upload ) . to receive ( :exist? ) . and_return ( false )
2018-03-17 18:26:18 +05:30
checksum = Digest :: SHA256 . file ( __FILE__ ) . hexdigest
upload . checksum = checksum
expect { upload . calculate_checksum! }
. to change { upload . checksum } . from ( checksum ) . to ( nil )
2017-08-17 22:00:37 +05:30
end
2019-12-21 20:55:43 +05:30
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-04-08 14:13:33 +05:30
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-12-21 20:55:43 +05:30
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-08-17 22:00:37 +05:30
end
describe '#exist?' do
it 'returns true when the file exists' do
2018-12-13 13:39:08 +05:30
upload = described_class . new ( path : __FILE__ , store : ObjectStorage :: Store :: LOCAL )
2017-08-17 22:00:37 +05:30
expect ( upload ) . to exist
end
2018-12-13 13:39:08 +05:30
context 'when the file does not exist' do
it 'returns false' do
upload = described_class . new ( path : " #{ __FILE__ } -nope " , store : ObjectStorage :: Store :: LOCAL )
2017-08-17 22:00:37 +05:30
2018-12-13 13:39:08 +05:30
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 )
2020-01-01 13:55:28 +05:30
expect ( Gitlab :: ErrorTracking ) . to receive ( :track_exception ) . with ( instance_of ( RuntimeError ) , upload . attributes )
2018-12-13 13:39:08 +05:30
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-04-17 20:07:23 +05:30
expect ( Gitlab :: ErrorTracking ) . not_to receive ( :track_exception )
2018-12-13 13:39:08 +05:30
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-08-17 22:00:37 +05:30
end
end
2018-03-17 18:26:18 +05:30
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-11-18 22:05:49 +05:30
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-08-17 22:00:37 +05:30
end