diff options
Diffstat (limited to 'spec/models/merge_request_diff_file_spec.rb')
-rw-r--r-- | spec/models/merge_request_diff_file_spec.rb | 153 |
1 files changed, 140 insertions, 13 deletions
diff --git a/spec/models/merge_request_diff_file_spec.rb b/spec/models/merge_request_diff_file_spec.rb index c9bcb900eca..7dc550a6c93 100644 --- a/spec/models/merge_request_diff_file_spec.rb +++ b/spec/models/merge_request_diff_file_spec.rb @@ -13,50 +13,53 @@ RSpec.describe MergeRequestDiffFile do let(:invalid_items_for_bulk_insertion) { [] } # class does not have any validations defined end + let(:unpacked) { 'unpacked' } + let(:packed) { [unpacked].pack('m0') } + let(:file) { create(:merge_request).merge_request_diff.merge_request_diff_files.first } + describe '#diff' do + let(:file) { build(:merge_request_diff_file) } + context 'when diff is not stored' do let(:unpacked) { 'unpacked' } let(:packed) { [unpacked].pack('m0') } before do - subject.diff = packed + file.diff = packed end context 'when the diff is marked as binary' do before do - subject.binary = true + file.binary = true end it 'unpacks from base 64' do - expect(subject.diff).to eq(unpacked) + expect(file.diff).to eq(unpacked) end context 'invalid base64' do let(:packed) { '---/dev/null' } it 'returns the raw diff' do - expect(subject.diff).to eq(packed) + expect(file.diff).to eq(packed) end end end context 'when the diff is not marked as binary' do it 'returns the raw diff' do - expect(subject.diff).to eq(packed) + expect(file.diff).to eq(packed) end end end context 'when diff is stored in DB' do - let(:file) { create(:merge_request).merge_request_diff.merge_request_diff_files.first } - it 'returns UTF-8 string' do expect(file.diff.encoding).to eq Encoding::UTF_8 end end context 'when diff is stored in external storage' do - let(:file) { create(:merge_request).merge_request_diff.merge_request_diff_files.first } let(:test_dir) { 'tmp/tests/external-diffs' } around do |example| @@ -81,17 +84,141 @@ RSpec.describe MergeRequestDiffFile do describe '#utf8_diff' do it 'does not raise error when the diff is binary' do - subject.diff = "\x05\x00\x68\x65\x6c\x6c\x6f" + file = build(:merge_request_diff_file) + file.diff = "\x05\x00\x68\x65\x6c\x6c\x6f" - expect { subject.utf8_diff }.not_to raise_error + expect { file.utf8_diff }.not_to raise_error end it 'calls #diff once' do - allow(subject).to receive(:diff).and_return('test') + allow(file).to receive(:diff).and_return('test') + + expect(file).to receive(:diff).once + + file.utf8_diff + end + + context 'externally stored diff caching' do + let(:test_dir) { 'tmp/tests/external-diffs' } + + around do |example| + FileUtils.mkdir_p(test_dir) + + begin + example.run + ensure + FileUtils.rm_rf(test_dir) + end + end + + before do + stub_external_diffs_setting(enabled: true, storage_path: test_dir) + end + + context 'when external diff is not cached' do + it 'caches external diffs' do + expect(file.merge_request_diff).to receive(:cache_external_diff).and_call_original + + expect(file.utf8_diff).to eq(file.diff) + end + end + + context 'when external diff is already cached' do + it 'reads diff from cached external diff' do + file_stub = double + + allow(file.merge_request_diff).to receive(:cached_external_diff).and_yield(file_stub) + expect(file_stub).to receive(:seek).with(file.external_diff_offset) + expect(file_stub).to receive(:read).with(file.external_diff_size) + + file.utf8_diff + end + end + + context 'when the diff is marked as binary' do + let(:file) { build(:merge_request_diff_file) } - expect(subject).to receive(:diff).once + before do + allow(file.merge_request_diff).to receive(:stored_externally?).and_return(true) + allow(file.merge_request_diff).to receive(:cached_external_diff).and_return(packed) + end - subject.utf8_diff + context 'when the diff is marked as binary' do + before do + file.binary = true + end + + it 'unpacks from base 64' do + expect(file.utf8_diff).to eq(unpacked) + end + + context 'invalid base64' do + let(:packed) { '---/dev/null' } + + it 'returns the raw diff' do + expect(file.utf8_diff).to eq(packed) + end + end + end + + context 'when the diff is not marked as binary' do + it 'returns the raw diff' do + expect(file.utf8_diff).to eq(packed) + end + end + end + + context 'when content responds to #encoding' do + it 'encodes content to utf8 encoding' do + expect(file.utf8_diff.encoding).to eq(Encoding::UTF_8) + end + end + + context 'when content is blank' do + it 'returns an empty string' do + allow(file.merge_request_diff).to receive(:cached_external_diff).and_return(nil) + + expect(file.utf8_diff).to eq('') + end + end + + context 'when exception is raised' do + it 'falls back to #diff' do + allow(file).to receive(:binary?).and_raise(StandardError, 'Error!') + expect(file).to receive(:diff) + expect(Gitlab::AppLogger) + .to receive(:warn) + .with( + a_hash_including( + :message => 'Cached external diff export failed', + :merge_request_diff_file_id => file.id, + :merge_request_diff_id => file.merge_request_diff.id, + 'exception.class' => 'StandardError', + 'exception.message' => 'Error!' + ) + ) + + file.utf8_diff + end + end + end + + context 'when externally_stored_diffs_caching_export feature flag is disabled' do + it 'calls #diff' do + stub_feature_flags(externally_stored_diffs_caching_export: false) + + expect(file).to receive(:diff) + + file.utf8_diff + end + end + + context 'when diff is not stored externally' do + it 'calls #diff' do + expect(file).to receive(:diff) + + file.utf8_diff + end end end end |