5a3e6fdff9
This commit, introduced in https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23812, fixes a problem creating a displaying image diff notes when the image is stored in LFS. The main problem was that `Gitlab::Diff::File` was returning an invalid valid in `text?` for this kind of files. It also fixes a rendering problem with other LFS files, like text ones. They LFS pointer shouldn't be shown when LFS is enabled for the project, but they were.
520 lines
16 KiB
Ruby
520 lines
16 KiB
Ruby
# encoding: utf-8
|
|
|
|
require "spec_helper"
|
|
|
|
describe Gitlab::Git::Blob, :seed_helper do
|
|
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
|
|
let(:rugged) do
|
|
Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH))
|
|
end
|
|
|
|
describe 'initialize' do
|
|
let(:blob) { Gitlab::Git::Blob.new(name: 'test') }
|
|
|
|
it 'handles nil data' do
|
|
expect(blob.name).to eq('test')
|
|
expect(blob.size).to eq(nil)
|
|
expect(blob.loaded_size).to eq(nil)
|
|
end
|
|
end
|
|
|
|
describe '.find' do
|
|
context 'nil path' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, nil) }
|
|
|
|
it { expect(blob).to eq(nil) }
|
|
end
|
|
|
|
context 'utf-8 branch' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, 'Ääh-test-utf-8', "files/ruby/popen.rb")}
|
|
|
|
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
|
|
end
|
|
|
|
context 'blank path' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, '') }
|
|
|
|
it { expect(blob).to eq(nil) }
|
|
end
|
|
|
|
context 'file in subdir' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
|
|
|
|
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
|
|
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
|
|
it { expect(blob.path).to eq("files/ruby/popen.rb") }
|
|
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
|
|
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
|
|
it { expect(blob.size).to eq(669) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
end
|
|
|
|
context 'file in root' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, ".gitignore") }
|
|
|
|
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
|
|
it { expect(blob.name).to eq(".gitignore") }
|
|
it { expect(blob.path).to eq(".gitignore") }
|
|
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
|
|
it { expect(blob.data[0..10]).to eq("*.rbc\n*.sas") }
|
|
it { expect(blob.size).to eq(241) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
it { expect(blob).not_to be_binary_in_repo }
|
|
end
|
|
|
|
context 'file in root with leading slash' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "/.gitignore") }
|
|
|
|
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
|
|
it { expect(blob.name).to eq(".gitignore") }
|
|
it { expect(blob.path).to eq(".gitignore") }
|
|
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
|
|
it { expect(blob.data[0..10]).to eq("*.rbc\n*.sas") }
|
|
it { expect(blob.size).to eq(241) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
end
|
|
|
|
context 'non-exist file' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "missing.rb") }
|
|
|
|
it { expect(blob).to be_nil }
|
|
end
|
|
|
|
context 'six submodule' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'six') }
|
|
|
|
it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
|
|
it { expect(blob.data).to eq('') }
|
|
|
|
it 'does not get messed up by load_all_data!' do
|
|
blob.load_all_data!(repository)
|
|
expect(blob.data).to eq('')
|
|
end
|
|
|
|
it 'does not mark the blob as binary' do
|
|
expect(blob).not_to be_binary_in_repo
|
|
end
|
|
end
|
|
|
|
context 'large file' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg') }
|
|
let(:blob_size) { 111803 }
|
|
let(:stub_limit) { 1000 }
|
|
|
|
before do
|
|
stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', stub_limit)
|
|
end
|
|
|
|
it { expect(blob.size).to eq(blob_size) }
|
|
it { expect(blob.data.length).to eq(stub_limit) }
|
|
|
|
it 'check that this test is sane' do
|
|
# It only makes sense to test limiting if the blob is larger than the limit.
|
|
expect(blob.size).to be > Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE
|
|
end
|
|
|
|
it 'can load all data' do
|
|
blob.load_all_data!(repository)
|
|
expect(blob.data.length).to eq(blob_size)
|
|
end
|
|
|
|
it 'marks the blob as binary' do
|
|
expect(Gitlab::Git::Blob).to receive(:new)
|
|
.with(hash_including(binary: true))
|
|
.and_call_original
|
|
|
|
expect(blob).to be_binary_in_repo
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.raw' do
|
|
let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) }
|
|
let(:bad_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::BigCommit::ID) }
|
|
|
|
it { expect(raw_blob.id).to eq(SeedRepo::RubyBlob::ID) }
|
|
it { expect(raw_blob.data[0..10]).to eq("require \'fi") }
|
|
it { expect(raw_blob.size).to eq(669) }
|
|
it { expect(raw_blob.truncated?).to be_falsey }
|
|
it { expect(bad_blob).to be_nil }
|
|
|
|
context 'large file' do
|
|
it 'limits the size of a large file' do
|
|
blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1
|
|
buffer = Array.new(blob_size, 0)
|
|
rugged_blob = Rugged::Blob.from_buffer(rugged, buffer.join(''))
|
|
blob = Gitlab::Git::Blob.raw(repository, rugged_blob)
|
|
|
|
expect(blob.size).to eq(blob_size)
|
|
expect(blob.loaded_size).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
|
|
expect(blob.data.length).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
|
|
expect(blob.truncated?).to be_truthy
|
|
|
|
blob.load_all_data!(repository)
|
|
expect(blob.loaded_size).to eq(blob_size)
|
|
end
|
|
end
|
|
|
|
context 'when sha references a tree' do
|
|
it 'returns nil' do
|
|
tree = rugged.rev_parse('master^{tree}')
|
|
|
|
blob = Gitlab::Git::Blob.raw(repository, tree.oid)
|
|
|
|
expect(blob).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.batch' do
|
|
let(:blob_references) do
|
|
[
|
|
[SeedRepo::Commit::ID, "files/ruby/popen.rb"],
|
|
[SeedRepo::Commit::ID, 'six']
|
|
]
|
|
end
|
|
|
|
subject { described_class.batch(repository, blob_references) }
|
|
|
|
it { expect(subject.size).to eq(blob_references.size) }
|
|
|
|
context 'first blob' do
|
|
let(:blob) { subject[0] }
|
|
|
|
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
|
|
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
|
|
it { expect(blob.path).to eq("files/ruby/popen.rb") }
|
|
it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
|
|
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
|
|
it { expect(blob.size).to eq(669) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
end
|
|
|
|
context 'second blob' do
|
|
let(:blob) { subject[1] }
|
|
|
|
it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
|
|
it { expect(blob.data).to eq('') }
|
|
it 'does not mark the blob as binary' do
|
|
expect(blob).not_to be_binary_in_repo
|
|
end
|
|
end
|
|
|
|
context 'limiting' do
|
|
subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
|
|
|
|
context 'positive' do
|
|
let(:blob_size_limit) { 10 }
|
|
|
|
it { expect(subject.first.data.size).to eq(10) }
|
|
end
|
|
|
|
context 'zero' do
|
|
let(:blob_size_limit) { 0 }
|
|
|
|
it 'only loads the metadata' do
|
|
expect(subject.first.size).not_to be(0)
|
|
expect(subject.first.data).to eq('')
|
|
end
|
|
end
|
|
|
|
context 'negative' do
|
|
let(:blob_size_limit) { -1 }
|
|
|
|
it 'ignores MAX_DATA_DISPLAY_SIZE' do
|
|
stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
|
|
|
|
expect(subject.first.data.size).to eq(669)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.batch_metadata' do
|
|
let(:blob_references) do
|
|
[
|
|
[SeedRepo::Commit::ID, "files/ruby/popen.rb"],
|
|
[SeedRepo::Commit::ID, 'six']
|
|
]
|
|
end
|
|
|
|
subject { described_class.batch_metadata(repository, blob_references) }
|
|
|
|
it 'returns an empty data attribute' do
|
|
first_blob, last_blob = subject
|
|
|
|
expect(first_blob.data).to be_blank
|
|
expect(first_blob.path).to eq("files/ruby/popen.rb")
|
|
expect(last_blob.data).to be_blank
|
|
expect(last_blob.path).to eq("six")
|
|
end
|
|
end
|
|
|
|
describe '.batch_lfs_pointers' do
|
|
let(:tree_object) { rugged.rev_parse('master^{tree}') }
|
|
|
|
let(:non_lfs_blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'master',
|
|
'README.md'
|
|
)
|
|
end
|
|
|
|
let(:lfs_blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
|
'files/lfs/image.jpg'
|
|
)
|
|
end
|
|
|
|
it 'returns a list of Gitlab::Git::Blob' do
|
|
blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
|
|
|
|
expect(blobs.count).to eq(1)
|
|
expect(blobs).to all( be_a(Gitlab::Git::Blob) )
|
|
expect(blobs).to be_an(Array)
|
|
end
|
|
|
|
it 'accepts blob IDs as a lazy enumerator' do
|
|
blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
|
|
|
|
expect(blobs.count).to eq(1)
|
|
expect(blobs).to all( be_a(Gitlab::Git::Blob) )
|
|
end
|
|
|
|
it 'handles empty list of IDs gracefully' do
|
|
blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
|
|
blobs_2 = described_class.batch_lfs_pointers(repository, [])
|
|
|
|
expect(blobs_1).to eq([])
|
|
expect(blobs_2).to eq([])
|
|
end
|
|
|
|
it 'silently ignores tree objects' do
|
|
blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
|
|
|
|
expect(blobs).to eq([])
|
|
end
|
|
|
|
it 'silently ignores non lfs objects' do
|
|
blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
|
|
|
|
expect(blobs).to eq([])
|
|
end
|
|
|
|
it 'avoids loading large blobs into memory' do
|
|
# This line could call `lookup` on `repository`, so do here before mocking.
|
|
non_lfs_blob_id = non_lfs_blob.id
|
|
|
|
expect(repository).not_to receive(:lookup)
|
|
|
|
described_class.batch_lfs_pointers(repository, [non_lfs_blob_id])
|
|
end
|
|
end
|
|
|
|
describe 'encoding' do
|
|
context 'file with russian text' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/russian.rb") }
|
|
|
|
it { expect(blob.name).to eq("russian.rb") }
|
|
it { expect(blob.data.lines.first).to eq("Хороший файл") }
|
|
it { expect(blob.size).to eq(23) }
|
|
it { expect(blob.truncated?).to be_falsey }
|
|
# Run it twice since data is encoded after the first run
|
|
it { expect(blob.truncated?).to be_falsey }
|
|
it { expect(blob.mode).to eq("100755") }
|
|
end
|
|
|
|
context 'file with Chinese text' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/テスト.txt") }
|
|
|
|
it { expect(blob.name).to eq("テスト.txt") }
|
|
it { expect(blob.data).to include("これはテスト") }
|
|
it { expect(blob.size).to eq(340) }
|
|
it { expect(blob.mode).to eq("100755") }
|
|
it { expect(blob.truncated?).to be_falsey }
|
|
end
|
|
|
|
context 'file with ISO-8859 text' do
|
|
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::LastCommit::ID, "encoding/iso8859.txt") }
|
|
|
|
it { expect(blob.name).to eq("iso8859.txt") }
|
|
it { expect(blob.loaded_size).to eq(4) }
|
|
it { expect(blob.size).to eq(4) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
it { expect(blob.truncated?).to be_falsey }
|
|
end
|
|
end
|
|
|
|
describe 'mode' do
|
|
context 'file regular' do
|
|
let(:blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
|
|
'files/ruby/regex.rb'
|
|
)
|
|
end
|
|
|
|
it { expect(blob.name).to eq('regex.rb') }
|
|
it { expect(blob.path).to eq('files/ruby/regex.rb') }
|
|
it { expect(blob.size).to eq(1200) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
end
|
|
|
|
context 'file binary' do
|
|
let(:blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
|
|
'files/executables/ls'
|
|
)
|
|
end
|
|
|
|
it { expect(blob.name).to eq('ls') }
|
|
it { expect(blob.path).to eq('files/executables/ls') }
|
|
it { expect(blob.size).to eq(110080) }
|
|
it { expect(blob.mode).to eq("100755") }
|
|
end
|
|
|
|
context 'file symlink to regular' do
|
|
let(:blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
|
|
'files/links/ruby-style-guide.md'
|
|
)
|
|
end
|
|
|
|
it { expect(blob.name).to eq('ruby-style-guide.md') }
|
|
it { expect(blob.path).to eq('files/links/ruby-style-guide.md') }
|
|
it { expect(blob.size).to eq(31) }
|
|
it { expect(blob.mode).to eq("120000") }
|
|
end
|
|
|
|
context 'file symlink to binary' do
|
|
let(:blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
|
|
'files/links/touch'
|
|
)
|
|
end
|
|
|
|
it { expect(blob.name).to eq('touch') }
|
|
it { expect(blob.path).to eq('files/links/touch') }
|
|
it { expect(blob.size).to eq(20) }
|
|
it { expect(blob.mode).to eq("120000") }
|
|
end
|
|
end
|
|
|
|
describe 'lfs_pointers' do
|
|
context 'file a valid lfs pointer' do
|
|
let(:blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
|
'files/lfs/image.jpg'
|
|
)
|
|
end
|
|
|
|
it { expect(blob.lfs_pointer?).to eq(true) }
|
|
it { expect(blob.lfs_oid).to eq("4206f951d2691c78aac4c0ce9f2b23580b2c92cdcc4336e1028742c0274938e0") }
|
|
it { expect(blob.lfs_size).to eq(19548) }
|
|
it { expect(blob.id).to eq("f4d76af13003d1106be7ac8c5a2a3d37ddf32c2a") }
|
|
it { expect(blob.name).to eq("image.jpg") }
|
|
it { expect(blob.path).to eq("files/lfs/image.jpg") }
|
|
it { expect(blob.size).to eq(130) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
end
|
|
|
|
describe 'file an invalid lfs pointer' do
|
|
context 'with correct version header but incorrect size and oid' do
|
|
let(:blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
|
'files/lfs/archive-invalid.tar'
|
|
)
|
|
end
|
|
|
|
it { expect(blob.lfs_pointer?).to eq(false) }
|
|
it { expect(blob.lfs_oid).to eq(nil) }
|
|
it { expect(blob.lfs_size).to eq(nil) }
|
|
it { expect(blob.id).to eq("f8a898db217a5a85ed8b3d25b34c1df1d1094c46") }
|
|
it { expect(blob.name).to eq("archive-invalid.tar") }
|
|
it { expect(blob.path).to eq("files/lfs/archive-invalid.tar") }
|
|
it { expect(blob.size).to eq(43) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
end
|
|
|
|
context 'with correct version header and size but incorrect size and oid' do
|
|
let(:blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
|
'files/lfs/picture-invalid.png'
|
|
)
|
|
end
|
|
|
|
it { expect(blob.lfs_pointer?).to eq(false) }
|
|
it { expect(blob.lfs_oid).to eq(nil) }
|
|
it { expect(blob.lfs_size).to eq(1575078) }
|
|
it { expect(blob.id).to eq("5ae35296e1f95c1ef9feda1241477ed29a448572") }
|
|
it { expect(blob.name).to eq("picture-invalid.png") }
|
|
it { expect(blob.path).to eq("files/lfs/picture-invalid.png") }
|
|
it { expect(blob.size).to eq(57) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
end
|
|
|
|
context 'with correct version header and size but invalid size and oid' do
|
|
let(:blob) do
|
|
Gitlab::Git::Blob.find(
|
|
repository,
|
|
'33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
|
|
'files/lfs/file-invalid.zip'
|
|
)
|
|
end
|
|
|
|
it { expect(blob.lfs_pointer?).to eq(false) }
|
|
it { expect(blob.lfs_oid).to eq(nil) }
|
|
it { expect(blob.lfs_size).to eq(nil) }
|
|
it { expect(blob.id).to eq("d831981bd876732b85a1bcc6cc01210c9f36248f") }
|
|
it { expect(blob.name).to eq("file-invalid.zip") }
|
|
it { expect(blob.path).to eq("files/lfs/file-invalid.zip") }
|
|
it { expect(blob.size).to eq(60) }
|
|
it { expect(blob.mode).to eq("100644") }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#load_all_data!' do
|
|
let(:full_data) { 'abcd' }
|
|
let(:blob) { Gitlab::Git::Blob.new(name: 'test', size: 4, data: 'abc') }
|
|
|
|
subject { blob.load_all_data!(repository) }
|
|
|
|
it 'loads missing data' do
|
|
expect(repository.gitaly_blob_client).to receive(:get_blob)
|
|
.and_return(double(:response, data: full_data))
|
|
|
|
subject
|
|
|
|
expect(blob.data).to eq(full_data)
|
|
end
|
|
|
|
context 'with a fully loaded blob' do
|
|
let(:blob) { Gitlab::Git::Blob.new(name: 'test', size: 4, data: full_data) }
|
|
|
|
it "doesn't perform any loading" do
|
|
expect(repository.gitaly_blob_client).not_to receive(:get_blob)
|
|
|
|
subject
|
|
|
|
expect(blob.data).to eq(full_data)
|
|
end
|
|
end
|
|
end
|
|
end
|