2017-01-04 13:43:06 -05:00
|
|
|
require "spec_helper"
|
|
|
|
|
|
|
|
describe Gitlab::Git::Diff, seed_helper: true do
|
2017-02-15 21:08:30 -05:00
|
|
|
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) }
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2017-05-12 12:44:03 -04:00
|
|
|
describe 'size limit feature toggles' do
|
|
|
|
context 'when the feature gitlab_git_diff_size_limit_increase is enabled' do
|
|
|
|
before do
|
2017-07-13 03:58:05 -04:00
|
|
|
stub_feature_flags(gitlab_git_diff_size_limit_increase: true)
|
2017-05-12 12:44:03 -04:00
|
|
|
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
|
2017-07-13 03:58:05 -04:00
|
|
|
stub_feature_flags(gitlab_git_diff_size_limit_increase: false)
|
2017-05-12 12:44:03 -04:00
|
|
|
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
|
|
|
|
|
2017-01-04 13:43:06 -05:00
|
|
|
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
|
2017-05-12 12:44:03 -04:00
|
|
|
diff = described_class.new(diff: 'a' * (described_class.size_limit + 1))
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
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
|
2017-06-09 07:48:25 -04:00
|
|
|
expect(diff.to_hash).to eq(@raw_diff_hash)
|
2017-01-04 13:43:06 -05:00
|
|
|
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
|
2017-06-21 09:48:12 -04:00
|
|
|
expect_any_instance_of(String).to receive(:bytesize)
|
|
|
|
.and_return(1024 * 1024 * 1024)
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
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.
|
2017-05-12 12:44:03 -04:00
|
|
|
allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(150)
|
|
|
|
allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(100)
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'prunes the diff as a large diff instead of as a collapsed diff' do
|
2017-05-30 22:48:30 -04:00
|
|
|
diff = described_class.new(@rugged_diff, expanded: false)
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
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
|
2017-06-21 09:48:12 -04:00
|
|
|
expect_any_instance_of(Rugged::Diff::Delta).to receive(:binary?)
|
|
|
|
.and_return(true)
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
diff = described_class.new(@rugged_diff)
|
|
|
|
|
|
|
|
expect(diff.diff).not_to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-02-24 10:53:44 -05:00
|
|
|
|
2017-05-30 15:30:05 -04:00
|
|
|
context 'using a GitalyClient::Diff' do
|
2017-02-24 10:53:44 -05:00
|
|
|
let(:diff) do
|
|
|
|
described_class.new(
|
2017-05-30 15:30:05 -04:00
|
|
|
Gitlab::GitalyClient::Diff.new(
|
2017-02-24 10:53:44 -05:00
|
|
|
to_path: ".gitmodules",
|
|
|
|
from_path: ".gitmodules",
|
|
|
|
old_mode: 0100644,
|
|
|
|
new_mode: 0100644,
|
|
|
|
from_id: '357406f3075a57708d0163752905cc1576fceacc',
|
|
|
|
to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0',
|
2017-05-30 15:30:05 -04:00
|
|
|
patch: raw_patch
|
2017-02-24 10:53:44 -05:00
|
|
|
)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with a small diff' do
|
2017-05-30 15:30:05 -04:00
|
|
|
let(:raw_patch) { @raw_diff_hash[:diff] }
|
2017-02-24 10:53:44 -05:00
|
|
|
|
|
|
|
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
|
2017-05-30 15:30:05 -04:00
|
|
|
let(:raw_patch) { 'a' * 204800 }
|
2017-02-24 10:53:44 -05:00
|
|
|
|
|
|
|
it 'prunes the diff' do
|
|
|
|
expect(diff.diff).to be_empty
|
|
|
|
expect(diff).to be_too_large
|
|
|
|
end
|
|
|
|
end
|
2017-06-21 19:51:46 -04:00
|
|
|
|
|
|
|
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
|
2017-06-28 11:44:37 -04:00
|
|
|
expect(diff.diff).to be_utf8
|
2017-06-21 19:51:46 -04:00
|
|
|
end
|
|
|
|
end
|
2017-02-24 10:53:44 -05:00
|
|
|
end
|
2017-01-04 13:43:06 -05:00
|
|
|
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
|
2017-07-12 11:06:35 -04:00
|
|
|
let(:options) { { max_files: 100, invalid_opt: true } }
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
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
|
2017-07-12 11:06:35 -04:00
|
|
|
default_options = { max_files: 5, bad_opt: 1, ignore_whitespace_change: true }
|
2017-01-04 13:43:06 -05:00
|
|
|
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
|
2017-07-12 11:06:35 -04:00
|
|
|
expect(filtered_options).to have_key(:ignore_whitespace_change)
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it "should override default options" do
|
2017-07-12 11:06:35 -04:00
|
|
|
expect(filtered_options).to have_key(:max_files)
|
|
|
|
expect(filtered_options[:max_files]).to eq(100)
|
2017-01-04 13:43:06 -05:00
|
|
|
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')
|
|
|
|
|
2017-05-26 19:27:30 -04:00
|
|
|
diff.too_large!
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
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')
|
|
|
|
|
2017-05-26 19:27:30 -04:00
|
|
|
diff.collapse!
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
expect(diff).to be_collapsed
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-05-26 19:27:30 -04:00
|
|
|
describe '#collapsed?' do
|
2017-01-04 13:43:06 -05:00
|
|
|
it 'returns true for a diff that is quite large' do
|
2017-05-12 12:44:03 -04:00
|
|
|
diff = described_class.new({ diff: 'a' * (described_class.collapse_limit + 1) }, expanded: false)
|
2017-01-04 13:43:06 -05:00
|
|
|
|
2017-05-30 22:48:30 -04:00
|
|
|
expect(diff).to be_collapsed
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false for a diff that is small enough' do
|
2017-05-30 22:48:30 -04:00
|
|
|
diff = described_class.new({ diff: 'a' }, expanded: false)
|
2017-01-04 13:43:06 -05:00
|
|
|
|
2017-05-30 22:48:30 -04:00
|
|
|
expect(diff).not_to be_collapsed
|
2017-01-04 13:43:06 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-05-26 19:27:30 -04:00
|
|
|
describe '#collapse!' do
|
2017-01-04 13:43:06 -05:00
|
|
|
it 'prunes the diff' do
|
|
|
|
diff = described_class.new(diff: "foo\nbar")
|
|
|
|
|
2017-05-26 19:27:30 -04:00
|
|
|
diff.collapse!
|
2017-01-04 13:43:06 -05:00
|
|
|
|
|
|
|
expect(diff.diff).to eq('')
|
|
|
|
expect(diff.line_count).to eq(0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|