3ee48e422d
Otherwise some features would go untested in non-specific contexts I did need to disable the `gitlab_git_diff_size_limit_increase`-feature in some specs since we depend on diffs being expandable while the file we are testing on is smaller than the increased limit.
362 lines
9.9 KiB
Ruby
362 lines
9.9 KiB
Ruby
require "spec_helper"
|
|
|
|
describe Gitlab::Git::Diff, seed_helper: true do
|
|
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) }
|
|
|
|
before do
|
|
@raw_diff_hash = {
|
|
diff: <<EOT.gsub(/^ {8}/, "").sub(/\n$/, ""),
|
|
--- a/.gitmodules
|
|
+++ b/.gitmodules
|
|
@@ -4,3 +4,6 @@
|
|
[submodule "gitlab-shell"]
|
|
\tpath = gitlab-shell
|
|
\turl = https://github.com/gitlabhq/gitlab-shell.git
|
|
+[submodule "gitlab-grack"]
|
|
+ path = gitlab-grack
|
|
+ url = https://gitlab.com/gitlab-org/gitlab-grack.git
|
|
|
|
EOT
|
|
new_path: ".gitmodules",
|
|
old_path: ".gitmodules",
|
|
a_mode: '100644',
|
|
b_mode: '100644',
|
|
new_file: false,
|
|
renamed_file: false,
|
|
deleted_file: false,
|
|
too_large: false
|
|
}
|
|
|
|
@rugged_diff = repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths:
|
|
[".gitmodules"]).patches.first
|
|
end
|
|
|
|
describe 'size limit feature toggles' do
|
|
context 'when the feature gitlab_git_diff_size_limit_increase is enabled' do
|
|
before do
|
|
stub_feature_flags(gitlab_git_diff_size_limit_increase: true)
|
|
end
|
|
|
|
it 'returns 200 KB for size_limit' do
|
|
expect(described_class.size_limit).to eq(200.kilobytes)
|
|
end
|
|
|
|
it 'returns 100 KB for collapse_limit' do
|
|
expect(described_class.collapse_limit).to eq(100.kilobytes)
|
|
end
|
|
end
|
|
|
|
context 'when the feature gitlab_git_diff_size_limit_increase is disabled' do
|
|
before do
|
|
stub_feature_flags(gitlab_git_diff_size_limit_increase: false)
|
|
end
|
|
|
|
it 'returns 100 KB for size_limit' do
|
|
expect(described_class.size_limit).to eq(100.kilobytes)
|
|
end
|
|
|
|
it 'returns 10 KB for collapse_limit' do
|
|
expect(described_class.collapse_limit).to eq(10.kilobytes)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.new' do
|
|
context 'using a Hash' do
|
|
context 'with a small diff' do
|
|
let(:diff) { described_class.new(@raw_diff_hash) }
|
|
|
|
it 'initializes the diff' do
|
|
expect(diff.to_hash).to eq(@raw_diff_hash)
|
|
end
|
|
|
|
it 'does not prune the diff' do
|
|
expect(diff).not_to be_too_large
|
|
end
|
|
end
|
|
|
|
context 'using a diff that is too large' do
|
|
it 'prunes the diff' do
|
|
diff = described_class.new(diff: 'a' * (described_class.size_limit + 1))
|
|
|
|
expect(diff.diff).to be_empty
|
|
expect(diff).to be_too_large
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'using a Rugged::Patch' do
|
|
context 'with a small diff' do
|
|
let(:diff) { described_class.new(@rugged_diff) }
|
|
|
|
it 'initializes the diff' do
|
|
expect(diff.to_hash).to eq(@raw_diff_hash)
|
|
end
|
|
|
|
it 'does not prune the diff' do
|
|
expect(diff).not_to be_too_large
|
|
end
|
|
end
|
|
|
|
context 'using a diff that is too large' do
|
|
it 'prunes the diff' do
|
|
expect_any_instance_of(String).to receive(:bytesize)
|
|
.and_return(1024 * 1024 * 1024)
|
|
|
|
diff = described_class.new(@rugged_diff)
|
|
|
|
expect(diff.diff).to be_empty
|
|
expect(diff).to be_too_large
|
|
end
|
|
end
|
|
|
|
context 'using a collapsable diff that is too large' do
|
|
before do
|
|
# The patch total size is 200, with lines between 21 and 54.
|
|
# This is a quick-and-dirty way to test this. Ideally, a new patch is
|
|
# added to the test repo with a size that falls between the real limits.
|
|
allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(150)
|
|
allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(100)
|
|
end
|
|
|
|
it 'prunes the diff as a large diff instead of as a collapsed diff' do
|
|
diff = described_class.new(@rugged_diff, expanded: false)
|
|
|
|
expect(diff.diff).to be_empty
|
|
expect(diff).to be_too_large
|
|
expect(diff).not_to be_collapsed
|
|
end
|
|
end
|
|
|
|
context 'using a large binary diff' do
|
|
it 'does not prune the diff' do
|
|
expect_any_instance_of(Rugged::Diff::Delta).to receive(:binary?)
|
|
.and_return(true)
|
|
|
|
diff = described_class.new(@rugged_diff)
|
|
|
|
expect(diff.diff).not_to be_empty
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'using a GitalyClient::Diff' do
|
|
let(:diff) do
|
|
described_class.new(
|
|
Gitlab::GitalyClient::Diff.new(
|
|
to_path: ".gitmodules",
|
|
from_path: ".gitmodules",
|
|
old_mode: 0100644,
|
|
new_mode: 0100644,
|
|
from_id: '357406f3075a57708d0163752905cc1576fceacc',
|
|
to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0',
|
|
patch: raw_patch
|
|
)
|
|
)
|
|
end
|
|
|
|
context 'with a small diff' do
|
|
let(:raw_patch) { @raw_diff_hash[:diff] }
|
|
|
|
it 'initializes the diff' do
|
|
expect(diff.to_hash).to eq(@raw_diff_hash)
|
|
end
|
|
|
|
it 'does not prune the diff' do
|
|
expect(diff).not_to be_too_large
|
|
end
|
|
end
|
|
|
|
context 'using a diff that is too large' do
|
|
let(:raw_patch) { 'a' * 204800 }
|
|
|
|
it 'prunes the diff' do
|
|
expect(diff.diff).to be_empty
|
|
expect(diff).to be_too_large
|
|
end
|
|
end
|
|
|
|
context 'when the patch passed is not UTF-8-encoded' do
|
|
let(:raw_patch) { @raw_diff_hash[:diff].encode(Encoding::ASCII_8BIT) }
|
|
|
|
it 'encodes diff patch to UTF-8' do
|
|
expect(diff.diff).to be_utf8
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'straight diffs' do
|
|
let(:options) { { straight: true } }
|
|
let(:diffs) { described_class.between(repository, 'feature', 'master', options) }
|
|
|
|
it 'has the correct size' do
|
|
expect(diffs.size).to eq(24)
|
|
end
|
|
|
|
context 'diff' do
|
|
it 'is an instance of Diff' do
|
|
expect(diffs.first).to be_kind_of(described_class)
|
|
end
|
|
|
|
it 'has the correct new_path' do
|
|
expect(diffs.first.new_path).to eq('.DS_Store')
|
|
end
|
|
|
|
it 'has the correct diff' do
|
|
expect(diffs.first.diff).to include('Binary files /dev/null and b/.DS_Store differ')
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.between' do
|
|
let(:diffs) { described_class.between(repository, 'feature', 'master') }
|
|
subject { diffs }
|
|
|
|
it { is_expected.to be_kind_of Gitlab::Git::DiffCollection }
|
|
|
|
describe '#size' do
|
|
subject { super().size }
|
|
|
|
it { is_expected.to eq(1) }
|
|
end
|
|
|
|
context 'diff' do
|
|
subject { diffs.first }
|
|
|
|
it { is_expected.to be_kind_of described_class }
|
|
|
|
describe '#new_path' do
|
|
subject { super().new_path }
|
|
|
|
it { is_expected.to eq('files/ruby/feature.rb') }
|
|
end
|
|
|
|
describe '#diff' do
|
|
subject { super().diff }
|
|
|
|
it { is_expected.to include '+class Feature' }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.filter_diff_options' do
|
|
let(:options) { { max_size: 100, invalid_opt: true } }
|
|
|
|
context "without default options" do
|
|
let(:filtered_options) { described_class.filter_diff_options(options) }
|
|
|
|
it "should filter invalid options" do
|
|
expect(filtered_options).not_to have_key(:invalid_opt)
|
|
end
|
|
end
|
|
|
|
context "with default options" do
|
|
let(:filtered_options) do
|
|
default_options = { max_size: 5, bad_opt: 1, ignore_whitespace: true }
|
|
described_class.filter_diff_options(options, default_options)
|
|
end
|
|
|
|
it "should filter invalid options" do
|
|
expect(filtered_options).not_to have_key(:invalid_opt)
|
|
expect(filtered_options).not_to have_key(:bad_opt)
|
|
end
|
|
|
|
it "should merge with default options" do
|
|
expect(filtered_options).to have_key(:ignore_whitespace)
|
|
end
|
|
|
|
it "should override default options" do
|
|
expect(filtered_options).to have_key(:max_size)
|
|
expect(filtered_options[:max_size]).to eq(100)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#submodule?' do
|
|
before do
|
|
commit = repository.lookup('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
|
|
@diffs = commit.parents[0].diff(commit).patches
|
|
end
|
|
|
|
it { expect(described_class.new(@diffs[0]).submodule?).to eq(false) }
|
|
it { expect(described_class.new(@diffs[1]).submodule?).to eq(true) }
|
|
end
|
|
|
|
describe '#line_count' do
|
|
it 'returns the correct number of lines' do
|
|
diff = described_class.new(@rugged_diff)
|
|
|
|
expect(diff.line_count).to eq(9)
|
|
end
|
|
end
|
|
|
|
describe '#too_large?' do
|
|
it 'returns true for a diff that is too large' do
|
|
diff = described_class.new(diff: 'a' * 204800)
|
|
|
|
expect(diff.too_large?).to eq(true)
|
|
end
|
|
|
|
it 'returns false for a diff that is small enough' do
|
|
diff = described_class.new(diff: 'a')
|
|
|
|
expect(diff.too_large?).to eq(false)
|
|
end
|
|
|
|
it 'returns true for a diff that was explicitly marked as being too large' do
|
|
diff = described_class.new(diff: 'a')
|
|
|
|
diff.too_large!
|
|
|
|
expect(diff.too_large?).to eq(true)
|
|
end
|
|
end
|
|
|
|
describe '#collapsed?' do
|
|
it 'returns false by default even on quite big diff' do
|
|
diff = described_class.new(diff: 'a' * 20480)
|
|
|
|
expect(diff).not_to be_collapsed
|
|
end
|
|
|
|
it 'returns false by default for a diff that is small enough' do
|
|
diff = described_class.new(diff: 'a')
|
|
|
|
expect(diff).not_to be_collapsed
|
|
end
|
|
|
|
it 'returns true for a diff that was explicitly marked as being collapsed' do
|
|
diff = described_class.new(diff: 'a')
|
|
|
|
diff.collapse!
|
|
|
|
expect(diff).to be_collapsed
|
|
end
|
|
end
|
|
|
|
describe '#collapsed?' do
|
|
it 'returns true for a diff that is quite large' do
|
|
diff = described_class.new({ diff: 'a' * (described_class.collapse_limit + 1) }, expanded: false)
|
|
|
|
expect(diff).to be_collapsed
|
|
end
|
|
|
|
it 'returns false for a diff that is small enough' do
|
|
diff = described_class.new({ diff: 'a' }, expanded: false)
|
|
|
|
expect(diff).not_to be_collapsed
|
|
end
|
|
end
|
|
|
|
describe '#collapse!' do
|
|
it 'prunes the diff' do
|
|
diff = described_class.new(diff: "foo\nbar")
|
|
|
|
diff.collapse!
|
|
|
|
expect(diff.diff).to eq('')
|
|
expect(diff.line_count).to eq(0)
|
|
end
|
|
end
|
|
end
|