e04c1ae7c7
* upstream/master: (62 commits) Update gitlab.po: Missing 'r' in "Fouché" that comes from "Fourcher" verb. Docs: update user docs index Fix minor typos in views Fix Layout/SpaceBeforeBlockBraces violation in bin/changelog_spec Merge branch 'rs-alphanumeric-ssh-params' into 'security-9-4' Merge branch 'import-symlinks-9-3' into 'security-9-3' Fix wrong method call on prometheus histogram Document new all-in-one Helm chart - docs Fix 404 on link path Fix line numbers not matching up to code in code viewer. Hide overflow-x on collapsed sidebar removed global use of breakpoint checker Increase performance of the breakpoint size checker Filter sensitive query string parameters from NGINX access logs Added a template for database changes Render new issue link in failed job as a regular link instead of a UJS one Include RE2 in the upgrade docs Remove affix plugin from issuable sidebar with new navigation Fix linter error alternative route for download archive ...
2079 lines
63 KiB
Ruby
2079 lines
63 KiB
Ruby
require 'spec_helper'
|
|
|
|
describe Repository, models: true do
|
|
include RepoHelpers
|
|
TestBlob = Struct.new(:path)
|
|
|
|
let(:project) { create(:project, :repository) }
|
|
let(:repository) { project.repository }
|
|
let(:broken_repository) { create(:project, :broken_storage).repository }
|
|
let(:user) { create(:user) }
|
|
|
|
let(:commit_options) do
|
|
author = repository.user_to_committer(user)
|
|
{ message: 'Test message', committer: author, author: author }
|
|
end
|
|
|
|
let(:merge_commit) do
|
|
merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project)
|
|
|
|
merge_commit_id = repository.merge(user,
|
|
merge_request.diff_head_sha,
|
|
merge_request,
|
|
commit_options)
|
|
|
|
repository.commit(merge_commit_id)
|
|
end
|
|
|
|
let(:author_email) { 'user@example.org' }
|
|
let(:author_name) { 'John Doe' }
|
|
|
|
def expect_to_raise_storage_error
|
|
expect { yield }.to raise_error do |exception|
|
|
storage_exceptions = [Gitlab::Git::Storage::Inaccessible, Gitlab::Git::CommandError, GRPC::Unavailable]
|
|
expect(exception.class).to be_in(storage_exceptions)
|
|
end
|
|
end
|
|
|
|
describe '#branch_names_contains' do
|
|
subject { repository.branch_names_contains(sample_commit.id) }
|
|
|
|
it { is_expected.to include('master') }
|
|
it { is_expected.not_to include('feature') }
|
|
it { is_expected.not_to include('fix') }
|
|
|
|
describe 'when storage is broken', broken_storage: true do
|
|
it 'should raise a storage error' do
|
|
expect_to_raise_storage_error do
|
|
broken_repository.branch_names_contains(sample_commit.id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#tag_names_contains' do
|
|
subject { repository.tag_names_contains(sample_commit.id) }
|
|
|
|
it { is_expected.to include('v1.1.0') }
|
|
it { is_expected.not_to include('v1.0.0') }
|
|
end
|
|
|
|
describe 'tags_sorted_by' do
|
|
context 'name' do
|
|
subject { repository.tags_sorted_by('name').map(&:name) }
|
|
|
|
it { is_expected.to eq(['v1.1.0', 'v1.0.0']) }
|
|
end
|
|
|
|
context 'updated' do
|
|
let(:tag_a) { repository.find_tag('v1.0.0') }
|
|
let(:tag_b) { repository.find_tag('v1.1.0') }
|
|
|
|
context 'desc' do
|
|
subject { repository.tags_sorted_by('updated_desc').map(&:name) }
|
|
|
|
before do
|
|
double_first = double(committed_date: Time.now)
|
|
double_last = double(committed_date: Time.now - 1.second)
|
|
|
|
allow(tag_a).to receive(:dereferenced_target).and_return(double_first)
|
|
allow(tag_b).to receive(:dereferenced_target).and_return(double_last)
|
|
allow(repository).to receive(:tags).and_return([tag_a, tag_b])
|
|
end
|
|
|
|
it { is_expected.to eq(['v1.0.0', 'v1.1.0']) }
|
|
end
|
|
|
|
context 'asc' do
|
|
subject { repository.tags_sorted_by('updated_asc').map(&:name) }
|
|
|
|
before do
|
|
double_first = double(committed_date: Time.now - 1.second)
|
|
double_last = double(committed_date: Time.now)
|
|
|
|
allow(tag_a).to receive(:dereferenced_target).and_return(double_last)
|
|
allow(tag_b).to receive(:dereferenced_target).and_return(double_first)
|
|
allow(repository).to receive(:tags).and_return([tag_a, tag_b])
|
|
end
|
|
|
|
it { is_expected.to eq(['v1.1.0', 'v1.0.0']) }
|
|
end
|
|
|
|
context 'annotated tag pointing to a blob' do
|
|
let(:annotated_tag_name) { 'annotated-tag' }
|
|
|
|
subject { repository.tags_sorted_by('updated_asc').map(&:name) }
|
|
|
|
before do
|
|
options = { message: 'test tag message\n',
|
|
tagger: { name: 'John Smith', email: 'john@gmail.com' } }
|
|
repository.rugged.tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options)
|
|
|
|
double_first = double(committed_date: Time.now - 1.second)
|
|
double_last = double(committed_date: Time.now)
|
|
|
|
allow(tag_a).to receive(:dereferenced_target).and_return(double_last)
|
|
allow(tag_b).to receive(:dereferenced_target).and_return(double_first)
|
|
end
|
|
|
|
it { is_expected.to eq(['v1.1.0', 'v1.0.0', annotated_tag_name]) }
|
|
|
|
after do
|
|
repository.rugged.tags.delete(annotated_tag_name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#ref_name_for_sha' do
|
|
it 'returns the ref' do
|
|
allow(repository.raw_repository).to receive(:ref_name_for_sha)
|
|
.and_return('refs/environments/production/77')
|
|
|
|
expect(repository.ref_name_for_sha('bla', '0' * 40)).to eq 'refs/environments/production/77'
|
|
end
|
|
end
|
|
|
|
describe '#ref_exists?' do
|
|
context 'when ref exists' do
|
|
it 'returns true' do
|
|
expect(repository.ref_exists?('refs/heads/master')).to be true
|
|
end
|
|
end
|
|
|
|
context 'when ref does not exist' do
|
|
it 'returns false' do
|
|
expect(repository.ref_exists?('refs/heads/non-existent')).to be false
|
|
end
|
|
end
|
|
|
|
context 'when ref format is incorrect' do
|
|
it 'returns false' do
|
|
expect(repository.ref_exists?('refs/heads/invalid:master')).to be false
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#last_commit_for_path' do
|
|
shared_examples 'getting last commit for path' do
|
|
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
|
|
|
|
it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
|
|
|
|
describe 'when storage is broken', broken_storage: true do
|
|
it 'should raise a storage error' do
|
|
expect_to_raise_storage_error do
|
|
broken_repository.last_commit_id_for_path(sample_commit.id, '.gitignore')
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when Gitaly feature last_commit_for_path is enabled' do
|
|
it_behaves_like 'getting last commit for path'
|
|
end
|
|
|
|
context 'when Gitaly feature last_commit_for_path is disabled', skip_gitaly_mock: true do
|
|
it_behaves_like 'getting last commit for path'
|
|
end
|
|
end
|
|
|
|
describe '#last_commit_id_for_path' do
|
|
shared_examples 'getting last commit ID for path' do
|
|
subject { repository.last_commit_id_for_path(sample_commit.id, '.gitignore') }
|
|
|
|
it "returns last commit id for a given path" do
|
|
is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8')
|
|
end
|
|
|
|
it "caches last commit id for a given path" do
|
|
cache = repository.send(:cache)
|
|
key = "last_commit_id_for_path:#{sample_commit.id}:#{Digest::SHA1.hexdigest('.gitignore')}"
|
|
|
|
expect(cache).to receive(:fetch).with(key).and_return('c1acaa5')
|
|
is_expected.to eq('c1acaa5')
|
|
end
|
|
|
|
describe 'when storage is broken', broken_storage: true do
|
|
it 'should raise a storage error' do
|
|
expect_to_raise_storage_error do
|
|
broken_repository.last_commit_for_path(sample_commit.id, '.gitignore').id
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when Gitaly feature last_commit_for_path is enabled' do
|
|
it_behaves_like 'getting last commit ID for path'
|
|
end
|
|
|
|
context 'when Gitaly feature last_commit_for_path is disabled', skip_gitaly_mock: true do
|
|
it_behaves_like 'getting last commit ID for path'
|
|
end
|
|
end
|
|
|
|
describe '#commits' do
|
|
it 'sets follow when path is a single path' do
|
|
expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: true)).and_call_original.twice
|
|
|
|
repository.commits('master', path: 'README.md')
|
|
repository.commits('master', path: ['README.md'])
|
|
end
|
|
|
|
it 'does not set follow when path is multiple paths' do
|
|
expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: false)).and_call_original
|
|
|
|
repository.commits('master', path: ['README.md', 'CHANGELOG'])
|
|
end
|
|
|
|
it 'does not set follow when there are no paths' do
|
|
expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: false)).and_call_original
|
|
|
|
repository.commits('master')
|
|
end
|
|
end
|
|
|
|
describe '#find_commits_by_message' do
|
|
shared_examples 'finding commits by message' do
|
|
it 'returns commits with messages containing a given string' do
|
|
commit_ids = repository.find_commits_by_message('submodule').map(&:id)
|
|
|
|
expect(commit_ids).to include(
|
|
'5937ac0a7beb003549fc5fd26fc247adbce4a52e',
|
|
'6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9',
|
|
'cfe32cf61b73a0d5e9f13e774abde7ff789b1660'
|
|
)
|
|
expect(commit_ids).not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e')
|
|
end
|
|
|
|
it 'is case insensitive' do
|
|
commit_ids = repository.find_commits_by_message('SUBMODULE').map(&:id)
|
|
|
|
expect(commit_ids).to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
|
|
end
|
|
end
|
|
|
|
context 'when Gitaly commits_by_message feature is enabled' do
|
|
it_behaves_like 'finding commits by message'
|
|
end
|
|
|
|
context 'when Gitaly commits_by_message feature is disabled', skip_gitaly_mock: true do
|
|
it_behaves_like 'finding commits by message'
|
|
end
|
|
|
|
describe 'when storage is broken', broken_storage: true do
|
|
it 'should raise a storage error' do
|
|
expect_to_raise_storage_error { broken_repository.find_commits_by_message('s') }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#blob_at' do
|
|
context 'blank sha' do
|
|
subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') }
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
end
|
|
|
|
describe '#merged_to_root_ref?' do
|
|
context 'merged branch without ff' do
|
|
subject { repository.merged_to_root_ref?('branch-merged') }
|
|
|
|
it { is_expected.to be_truthy }
|
|
end
|
|
|
|
# If the HEAD was ff then it will be false
|
|
context 'merged with ff' do
|
|
subject { repository.merged_to_root_ref?('improve/awesome') }
|
|
|
|
it { is_expected.to be_truthy }
|
|
end
|
|
|
|
context 'not merged branch' do
|
|
subject { repository.merged_to_root_ref?('not-merged-branch') }
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
|
|
context 'default branch' do
|
|
subject { repository.merged_to_root_ref?('master') }
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
end
|
|
|
|
describe '#can_be_merged?' do
|
|
context 'mergeable branches' do
|
|
subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') }
|
|
|
|
it { is_expected.to be_truthy }
|
|
end
|
|
|
|
context 'non-mergeable branches' do
|
|
subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') }
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
|
|
context 'non merged branch' do
|
|
subject { repository.merged_to_root_ref?('fix') }
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
|
|
context 'non existent branch' do
|
|
subject { repository.merged_to_root_ref?('non_existent_branch') }
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
end
|
|
|
|
describe '#commit' do
|
|
context 'when ref exists' do
|
|
it 'returns commit object' do
|
|
expect(repository.commit('master'))
|
|
.to be_an_instance_of Commit
|
|
end
|
|
end
|
|
|
|
context 'when ref does not exist' do
|
|
it 'returns nil' do
|
|
expect(repository.commit('non-existent-ref')).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'when ref is not valid' do
|
|
context 'when preceding tree element exists' do
|
|
it 'returns nil' do
|
|
expect(repository.commit('master:ref')).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'when preceding tree element does not exist' do
|
|
it 'returns nil' do
|
|
expect(repository.commit('non-existent:ref')).to be_nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#create_dir" do
|
|
it "commits a change that creates a new directory" do
|
|
expect do
|
|
repository.create_dir(user, 'newdir',
|
|
message: 'Create newdir', branch_name: 'master')
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
newdir = repository.tree('master', 'newdir')
|
|
expect(newdir.path).to eq('newdir')
|
|
end
|
|
|
|
context "when committing to another project" do
|
|
let(:forked_project) { create(:project, :repository) }
|
|
|
|
it "creates a fork and commit to the forked project" do
|
|
expect do
|
|
repository.create_dir(user, 'newdir',
|
|
message: 'Create newdir', branch_name: 'patch',
|
|
start_branch_name: 'master', start_project: forked_project)
|
|
end.to change { repository.commits('master').count }.by(0)
|
|
|
|
expect(repository.branch_exists?('patch')).to be_truthy
|
|
expect(forked_project.repository.branch_exists?('patch')).to be_falsy
|
|
|
|
newdir = repository.tree('patch', 'newdir')
|
|
expect(newdir.path).to eq('newdir')
|
|
end
|
|
end
|
|
|
|
context "when an author is specified" do
|
|
it "uses the given email/name to set the commit's author" do
|
|
expect do
|
|
repository.create_dir(user, 'newdir',
|
|
message: 'Add newdir',
|
|
branch_name: 'master',
|
|
author_email: author_email, author_name: author_name)
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
last_commit = repository.commit
|
|
|
|
expect(last_commit.author_email).to eq(author_email)
|
|
expect(last_commit.author_name).to eq(author_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#create_file" do
|
|
it 'commits new file successfully' do
|
|
expect do
|
|
repository.create_file(user, 'NEWCHANGELOG', 'Changelog!',
|
|
message: 'Create changelog',
|
|
branch_name: 'master')
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
blob = repository.blob_at('master', 'NEWCHANGELOG')
|
|
|
|
expect(blob.data).to eq('Changelog!')
|
|
end
|
|
|
|
it 'creates new file and dir when file_path has a forward slash' do
|
|
expect do
|
|
repository.create_file(user, 'new_dir/new_file.txt', 'File!',
|
|
message: 'Create new_file with new_dir',
|
|
branch_name: 'master')
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
expect(repository.tree('master', 'new_dir').path).to eq('new_dir')
|
|
expect(repository.blob_at('master', 'new_dir/new_file.txt').data).to eq('File!')
|
|
end
|
|
|
|
it 'respects the autocrlf setting' do
|
|
repository.create_file(user, 'hello.txt', "Hello,\r\nWorld",
|
|
message: 'Add hello world',
|
|
branch_name: 'master')
|
|
|
|
blob = repository.blob_at('master', 'hello.txt')
|
|
|
|
expect(blob.data).to eq("Hello,\nWorld")
|
|
end
|
|
|
|
context "when an author is specified" do
|
|
it "uses the given email/name to set the commit's author" do
|
|
expect do
|
|
repository.create_file(user, 'NEWREADME', 'README!',
|
|
message: 'Add README',
|
|
branch_name: 'master',
|
|
author_email: author_email,
|
|
author_name: author_name)
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
last_commit = repository.commit
|
|
|
|
expect(last_commit.author_email).to eq(author_email)
|
|
expect(last_commit.author_name).to eq(author_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#update_file" do
|
|
it 'updates file successfully' do
|
|
expect do
|
|
repository.update_file(user, 'CHANGELOG', 'Changelog!',
|
|
message: 'Update changelog',
|
|
branch_name: 'master')
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
blob = repository.blob_at('master', 'CHANGELOG')
|
|
|
|
expect(blob.data).to eq('Changelog!')
|
|
end
|
|
|
|
it 'updates filename successfully' do
|
|
expect do
|
|
repository.update_file(user, 'NEWLICENSE', 'Copyright!',
|
|
branch_name: 'master',
|
|
previous_path: 'LICENSE',
|
|
message: 'Changes filename')
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
files = repository.ls_files('master')
|
|
|
|
expect(files).not_to include('LICENSE')
|
|
expect(files).to include('NEWLICENSE')
|
|
end
|
|
|
|
context "when an author is specified" do
|
|
it "uses the given email/name to set the commit's author" do
|
|
expect do
|
|
repository.update_file(user, 'README', 'Updated README!',
|
|
branch_name: 'master',
|
|
previous_path: 'README',
|
|
message: 'Update README',
|
|
author_email: author_email,
|
|
author_name: author_name)
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
last_commit = repository.commit
|
|
|
|
expect(last_commit.author_email).to eq(author_email)
|
|
expect(last_commit.author_name).to eq(author_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#delete_file" do
|
|
it 'removes file successfully' do
|
|
expect do
|
|
repository.delete_file(user, 'README',
|
|
message: 'Remove README', branch_name: 'master')
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
expect(repository.blob_at('master', 'README')).to be_nil
|
|
end
|
|
|
|
context "when an author is specified" do
|
|
it "uses the given email/name to set the commit's author" do
|
|
expect do
|
|
repository.delete_file(user, 'README',
|
|
message: 'Remove README', branch_name: 'master',
|
|
author_email: author_email, author_name: author_name)
|
|
end.to change { repository.commits('master').count }.by(1)
|
|
|
|
last_commit = repository.commit
|
|
|
|
expect(last_commit.author_email).to eq(author_email)
|
|
expect(last_commit.author_name).to eq(author_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#get_committer_and_author' do
|
|
it 'returns the committer and author data' do
|
|
options = repository.get_committer_and_author(user)
|
|
expect(options[:committer][:email]).to eq(user.email)
|
|
expect(options[:author][:email]).to eq(user.email)
|
|
end
|
|
|
|
context 'when the email/name are given' do
|
|
it 'returns an object containing the email/name' do
|
|
options = repository.get_committer_and_author(user, email: author_email, name: author_name)
|
|
expect(options[:author][:email]).to eq(author_email)
|
|
expect(options[:author][:name]).to eq(author_name)
|
|
end
|
|
end
|
|
|
|
context 'when the email is given but the name is not' do
|
|
it 'returns the committer as the author' do
|
|
options = repository.get_committer_and_author(user, email: author_email)
|
|
expect(options[:author][:email]).to eq(user.email)
|
|
expect(options[:author][:name]).to eq(user.name)
|
|
end
|
|
end
|
|
|
|
context 'when the name is given but the email is not' do
|
|
it 'returns nil' do
|
|
options = repository.get_committer_and_author(user, name: author_name)
|
|
expect(options[:author][:email]).to eq(user.email)
|
|
expect(options[:author][:name]).to eq(user.name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "search_files_by_content" do
|
|
let(:results) { repository.search_files_by_content('feature', 'master') }
|
|
subject { results }
|
|
|
|
it { is_expected.to be_an Array }
|
|
|
|
it 'regex-escapes the query string' do
|
|
results = repository.search_files_by_content("test\\", 'master')
|
|
|
|
expect(results.first).not_to start_with('fatal:')
|
|
end
|
|
|
|
it 'properly handles an unmatched parenthesis' do
|
|
results = repository.search_files_by_content("test(", 'master')
|
|
|
|
expect(results.first).not_to start_with('fatal:')
|
|
end
|
|
|
|
it 'properly handles when query is not present' do
|
|
results = repository.search_files_by_content('', 'master')
|
|
|
|
expect(results).to match_array([])
|
|
end
|
|
|
|
it 'properly handles query when repo is empty' do
|
|
repository = create(:project).repository
|
|
results = repository.search_files_by_content('test', 'master')
|
|
|
|
expect(results).to match_array([])
|
|
end
|
|
|
|
describe 'when storage is broken', broken_storage: true do
|
|
it 'should raise a storage error' do
|
|
expect_to_raise_storage_error do
|
|
broken_repository.search_files_by_content('feature', 'master')
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'result' do
|
|
subject { results.first }
|
|
|
|
it { is_expected.to be_an String }
|
|
it { expect(subject.lines[2]).to eq("master:CHANGELOG:190: - Feature: Replace teams with group membership\n") }
|
|
end
|
|
end
|
|
|
|
describe "search_files_by_name" do
|
|
let(:results) { repository.search_files_by_name('files', 'master') }
|
|
|
|
it 'returns result' do
|
|
expect(results.first).to eq('files/html/500.html')
|
|
end
|
|
|
|
it 'properly handles when query is not present' do
|
|
results = repository.search_files_by_name('', 'master')
|
|
|
|
expect(results).to match_array([])
|
|
end
|
|
|
|
it 'properly handles query when repo is empty' do
|
|
repository = create(:project).repository
|
|
|
|
results = repository.search_files_by_name('test', 'master')
|
|
|
|
expect(results).to match_array([])
|
|
end
|
|
|
|
describe 'when storage is broken', broken_storage: true do
|
|
it 'should raise a storage error' do
|
|
expect_to_raise_storage_error { broken_repository.search_files_by_name('files', 'master') }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#fetch_ref' do
|
|
describe 'when storage is broken', broken_storage: true do
|
|
it 'should raise a storage error' do
|
|
path = broken_repository.path_to_repo
|
|
|
|
expect_to_raise_storage_error { broken_repository.fetch_ref(path, '1', '2') }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#create_ref' do
|
|
it 'redirects the call to fetch_ref' do
|
|
ref, ref_path = '1', '2'
|
|
|
|
expect(repository).to receive(:fetch_ref).with(repository.path_to_repo, ref, ref_path)
|
|
|
|
repository.create_ref(ref, ref_path)
|
|
end
|
|
end
|
|
|
|
describe "#changelog", :use_clean_rails_memory_store_caching do
|
|
it 'accepts changelog' do
|
|
expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('changelog')])
|
|
|
|
expect(repository.changelog.path).to eq('changelog')
|
|
end
|
|
|
|
it 'accepts news instead of changelog' do
|
|
expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('news')])
|
|
|
|
expect(repository.changelog.path).to eq('news')
|
|
end
|
|
|
|
it 'accepts history instead of changelog' do
|
|
expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('history')])
|
|
|
|
expect(repository.changelog.path).to eq('history')
|
|
end
|
|
|
|
it 'accepts changes instead of changelog' do
|
|
expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('changes')])
|
|
|
|
expect(repository.changelog.path).to eq('changes')
|
|
end
|
|
|
|
it 'is case-insensitive' do
|
|
expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('CHANGELOG')])
|
|
|
|
expect(repository.changelog.path).to eq('CHANGELOG')
|
|
end
|
|
end
|
|
|
|
describe "#license_blob", :use_clean_rails_memory_store_caching do
|
|
before do
|
|
repository.delete_file(
|
|
user, 'LICENSE', message: 'Remove LICENSE', branch_name: 'master')
|
|
end
|
|
|
|
it 'handles when HEAD points to non-existent ref' do
|
|
repository.create_file(
|
|
user, 'LICENSE', 'Copyright!',
|
|
message: 'Add LICENSE', branch_name: 'master')
|
|
|
|
allow(repository).to receive(:file_on_head)
|
|
.and_raise(Rugged::ReferenceError)
|
|
|
|
expect(repository.license_blob).to be_nil
|
|
end
|
|
|
|
it 'looks in the root_ref only' do
|
|
repository.delete_file(user, 'LICENSE',
|
|
message: 'Remove LICENSE', branch_name: 'markdown')
|
|
repository.create_file(user, 'LICENSE',
|
|
Licensee::License.new('mit').content,
|
|
message: 'Add LICENSE', branch_name: 'markdown')
|
|
|
|
expect(repository.license_blob).to be_nil
|
|
end
|
|
|
|
it 'detects license file with no recognizable open-source license content' do
|
|
repository.create_file(user, 'LICENSE', 'Copyright!',
|
|
message: 'Add LICENSE', branch_name: 'master')
|
|
|
|
expect(repository.license_blob.path).to eq('LICENSE')
|
|
end
|
|
|
|
%w[LICENSE LICENCE LiCensE LICENSE.md LICENSE.foo COPYING COPYING.md].each do |filename|
|
|
it "detects '#{filename}'" do
|
|
repository.create_file(user, filename,
|
|
Licensee::License.new('mit').content,
|
|
message: "Add #{filename}", branch_name: 'master')
|
|
|
|
expect(repository.license_blob.name).to eq(filename)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#license_key', :use_clean_rails_memory_store_caching do
|
|
before do
|
|
repository.delete_file(user, 'LICENSE',
|
|
message: 'Remove LICENSE', branch_name: 'master')
|
|
end
|
|
|
|
it 'returns nil when no license is detected' do
|
|
expect(repository.license_key).to be_nil
|
|
end
|
|
|
|
it 'returns nil when the repository does not exist' do
|
|
expect(repository).to receive(:exists?).and_return(false)
|
|
|
|
expect(repository.license_key).to be_nil
|
|
end
|
|
|
|
it 'returns nil when the content is not recognizable' do
|
|
repository.create_file(user, 'LICENSE', 'Copyright!',
|
|
message: 'Add LICENSE', branch_name: 'master')
|
|
|
|
expect(repository.license_key).to be_nil
|
|
end
|
|
|
|
it 'returns the license key' do
|
|
repository.create_file(user, 'LICENSE',
|
|
Licensee::License.new('mit').content,
|
|
message: 'Add LICENSE', branch_name: 'master')
|
|
|
|
expect(repository.license_key).to eq('mit')
|
|
end
|
|
end
|
|
|
|
describe '#license' do
|
|
before do
|
|
repository.delete_file(user, 'LICENSE',
|
|
message: 'Remove LICENSE', branch_name: 'master')
|
|
end
|
|
|
|
it 'returns nil when no license is detected' do
|
|
expect(repository.license).to be_nil
|
|
end
|
|
|
|
it 'returns nil when the repository does not exist' do
|
|
expect(repository).to receive(:exists?).and_return(false)
|
|
|
|
expect(repository.license).to be_nil
|
|
end
|
|
|
|
it 'returns nil when the content is not recognizable' do
|
|
repository.create_file(user, 'LICENSE', 'Copyright!',
|
|
message: 'Add LICENSE', branch_name: 'master')
|
|
|
|
expect(repository.license).to be_nil
|
|
end
|
|
|
|
it 'returns the license' do
|
|
license = Licensee::License.new('mit')
|
|
repository.create_file(user, 'LICENSE',
|
|
license.content,
|
|
message: 'Add LICENSE', branch_name: 'master')
|
|
|
|
expect(repository.license).to eq(license)
|
|
end
|
|
end
|
|
|
|
describe "#gitlab_ci_yml", :use_clean_rails_memory_store_caching do
|
|
it 'returns valid file' do
|
|
files = [TestBlob.new('file'), TestBlob.new('.gitlab-ci.yml'), TestBlob.new('copying')]
|
|
expect(repository.tree).to receive(:blobs).and_return(files)
|
|
|
|
expect(repository.gitlab_ci_yml.path).to eq('.gitlab-ci.yml')
|
|
end
|
|
|
|
it 'returns nil if not exists' do
|
|
expect(repository.tree).to receive(:blobs).and_return([])
|
|
expect(repository.gitlab_ci_yml).to be_nil
|
|
end
|
|
|
|
it 'returns nil for empty repository' do
|
|
allow(repository).to receive(:file_on_head).and_raise(Rugged::ReferenceError)
|
|
expect(repository.gitlab_ci_yml).to be_nil
|
|
end
|
|
end
|
|
|
|
describe '#add_branch' do
|
|
context 'when pre hooks were successful' do
|
|
it 'runs without errors' do
|
|
hook = double(trigger: [true, nil])
|
|
expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook)
|
|
|
|
expect { repository.add_branch(user, 'new_feature', 'master') }.not_to raise_error
|
|
end
|
|
|
|
it 'creates the branch' do
|
|
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
|
|
|
|
branch = repository.add_branch(user, 'new_feature', 'master')
|
|
|
|
expect(branch.name).to eq('new_feature')
|
|
end
|
|
|
|
it 'calls the after_create_branch hook' do
|
|
expect(repository).to receive(:after_create_branch)
|
|
|
|
repository.add_branch(user, 'new_feature', 'master')
|
|
end
|
|
end
|
|
|
|
context 'when pre hooks failed' do
|
|
it 'gets an error' do
|
|
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
|
|
|
|
expect do
|
|
repository.add_branch(user, 'new_feature', 'master')
|
|
end.to raise_error(GitHooksService::PreReceiveError)
|
|
end
|
|
|
|
it 'does not create the branch' do
|
|
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
|
|
|
|
expect do
|
|
repository.add_branch(user, 'new_feature', 'master')
|
|
end.to raise_error(GitHooksService::PreReceiveError)
|
|
expect(repository.find_branch('new_feature')).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_branch' do
|
|
it 'loads a branch with a fresh repo' do
|
|
expect(Gitlab::Git::Repository).to receive(:new).twice.and_call_original
|
|
|
|
2.times do
|
|
expect(repository.find_branch('feature')).not_to be_nil
|
|
end
|
|
end
|
|
|
|
it 'loads a branch with a cached repo' do
|
|
expect(Gitlab::Git::Repository).to receive(:new).once.and_call_original
|
|
|
|
2.times do
|
|
expect(repository.find_branch('feature', fresh_repo: false)).not_to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#rm_branch' do
|
|
let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature
|
|
let(:blank_sha) { '0000000000000000000000000000000000000000' }
|
|
|
|
context 'when pre hooks were successful' do
|
|
it 'runs without errors' do
|
|
expect_any_instance_of(GitHooksService).to receive(:execute)
|
|
.with(user, project, old_rev, blank_sha, 'refs/heads/feature')
|
|
|
|
expect { repository.rm_branch(user, 'feature') }.not_to raise_error
|
|
end
|
|
|
|
it 'deletes the branch' do
|
|
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
|
|
|
|
expect { repository.rm_branch(user, 'feature') }.not_to raise_error
|
|
|
|
expect(repository.find_branch('feature')).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'when pre hooks failed' do
|
|
it 'gets an error' do
|
|
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
|
|
|
|
expect do
|
|
repository.rm_branch(user, 'feature')
|
|
end.to raise_error(GitHooksService::PreReceiveError)
|
|
end
|
|
|
|
it 'does not delete the branch' do
|
|
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
|
|
|
|
expect do
|
|
repository.rm_branch(user, 'feature')
|
|
end.to raise_error(GitHooksService::PreReceiveError)
|
|
expect(repository.find_branch('feature')).not_to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#update_branch_with_hooks' do
|
|
let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature
|
|
let(:new_rev) { 'a74ae73c1ccde9b974a70e82b901588071dc142a' } # commit whose parent is old_rev
|
|
|
|
context 'when pre hooks were successful' do
|
|
before do
|
|
service = GitHooksService.new
|
|
expect(GitHooksService).to receive(:new).and_return(service)
|
|
expect(service).to receive(:execute)
|
|
.with(user, project, old_rev, new_rev, 'refs/heads/feature')
|
|
.and_yield(service).and_return(true)
|
|
end
|
|
|
|
it 'runs without errors' do
|
|
expect do
|
|
GitOperationService.new(user, repository).with_branch('feature') do
|
|
new_rev
|
|
end
|
|
end.not_to raise_error
|
|
end
|
|
|
|
it 'ensures the autocrlf Git option is set to :input' do
|
|
service = GitOperationService.new(user, repository)
|
|
|
|
expect(service).to receive(:update_autocrlf_option)
|
|
|
|
service.with_branch('feature') { new_rev }
|
|
end
|
|
|
|
context "when the branch wasn't empty" do
|
|
it 'updates the head' do
|
|
expect(repository.find_branch('feature').dereferenced_target.id).to eq(old_rev)
|
|
|
|
GitOperationService.new(user, repository).with_branch('feature') do
|
|
new_rev
|
|
end
|
|
|
|
expect(repository.find_branch('feature').dereferenced_target.id).to eq(new_rev)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when temporary ref failed to be created from other project' do
|
|
let(:target_project) { create(:project, :empty_repo) }
|
|
|
|
before do
|
|
expect(target_project.repository).to receive(:run_git)
|
|
end
|
|
|
|
it 'raises Rugged::ReferenceError' do
|
|
raise_reference_error = raise_error(Rugged::ReferenceError) do |err|
|
|
expect(err.cause).to be_nil
|
|
end
|
|
|
|
expect do
|
|
GitOperationService.new(user, target_project.repository)
|
|
.with_branch('feature',
|
|
start_project: project,
|
|
&:itself)
|
|
end.to raise_reference_error
|
|
end
|
|
end
|
|
|
|
context 'when the update adds more than one commit' do
|
|
let(:old_rev) { '33f3729a45c02fc67d00adb1b8bca394b0e761d9' }
|
|
|
|
it 'runs without errors' do
|
|
# old_rev is an ancestor of new_rev
|
|
expect(repository.rugged.merge_base(old_rev, new_rev)).to eq(old_rev)
|
|
|
|
# old_rev is not a direct ancestor (parent) of new_rev
|
|
expect(repository.rugged.lookup(new_rev).parent_ids).not_to include(old_rev)
|
|
|
|
branch = 'feature-ff-target'
|
|
repository.add_branch(user, branch, old_rev)
|
|
|
|
expect do
|
|
GitOperationService.new(user, repository).with_branch(branch) do
|
|
new_rev
|
|
end
|
|
end.not_to raise_error
|
|
end
|
|
end
|
|
|
|
context 'when the update would remove commits from the target branch' do
|
|
let(:branch) { 'master' }
|
|
let(:old_rev) { repository.find_branch(branch).dereferenced_target.sha }
|
|
|
|
it 'raises an exception' do
|
|
# The 'master' branch is NOT an ancestor of new_rev.
|
|
expect(repository.rugged.merge_base(old_rev, new_rev)).not_to eq(old_rev)
|
|
|
|
# Updating 'master' to new_rev would lose the commits on 'master' that
|
|
# are not contained in new_rev. This should not be allowed.
|
|
expect do
|
|
GitOperationService.new(user, repository).with_branch(branch) do
|
|
new_rev
|
|
end
|
|
end.to raise_error(Repository::CommitError)
|
|
end
|
|
end
|
|
|
|
context 'when pre hooks failed' do
|
|
it 'gets an error' do
|
|
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
|
|
|
|
expect do
|
|
GitOperationService.new(user, repository).with_branch('feature') do
|
|
new_rev
|
|
end
|
|
end.to raise_error(GitHooksService::PreReceiveError)
|
|
end
|
|
end
|
|
|
|
context 'when target branch is different from source branch' do
|
|
before do
|
|
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, ''])
|
|
end
|
|
|
|
it 'expires branch cache' do
|
|
expect(repository).not_to receive(:expire_exists_cache)
|
|
expect(repository).not_to receive(:expire_root_ref_cache)
|
|
expect(repository).not_to receive(:expire_emptiness_caches)
|
|
expect(repository).to receive(:expire_branches_cache)
|
|
|
|
GitOperationService.new(user, repository)
|
|
.with_branch('new-feature') do
|
|
new_rev
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when repository is empty' do
|
|
before do
|
|
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, ''])
|
|
end
|
|
|
|
it 'expires creation and branch cache' do
|
|
empty_repository = create(:project, :empty_repo).repository
|
|
|
|
expect(empty_repository).to receive(:expire_exists_cache)
|
|
expect(empty_repository).to receive(:expire_root_ref_cache)
|
|
expect(empty_repository).to receive(:expire_emptiness_caches)
|
|
expect(empty_repository).to receive(:expire_branches_cache)
|
|
|
|
empty_repository.create_file(user, 'CHANGELOG', 'Changelog!',
|
|
message: 'Updates file content',
|
|
branch_name: 'master')
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples 'repo exists check' do
|
|
it 'returns true when a repository exists' do
|
|
expect(repository.exists?).to eq(true)
|
|
end
|
|
|
|
it 'returns false if no full path can be constructed' do
|
|
allow(repository).to receive(:full_path).and_return(nil)
|
|
|
|
expect(repository.exists?).to eq(false)
|
|
end
|
|
|
|
context 'with broken storage', broken_storage: true do
|
|
it 'should raise a storage error' do
|
|
expect_to_raise_storage_error { broken_repository.exists? }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#exists?' do
|
|
context 'when repository_exists is disabled' do
|
|
it_behaves_like 'repo exists check'
|
|
end
|
|
|
|
context 'when repository_exists is enabled', skip_gitaly_mock: true do
|
|
it_behaves_like 'repo exists check'
|
|
end
|
|
end
|
|
|
|
describe '#has_visible_content?' do
|
|
subject { repository.has_visible_content? }
|
|
|
|
describe 'when there are no branches' do
|
|
before do
|
|
allow(repository).to receive(:branch_count).and_return(0)
|
|
end
|
|
|
|
it { is_expected.to eq(false) }
|
|
end
|
|
|
|
describe 'when there are branches' do
|
|
it 'returns true' do
|
|
expect(repository).to receive(:branch_count).and_return(3)
|
|
|
|
expect(subject).to eq(true)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#update_autocrlf_option' do
|
|
describe 'when autocrlf is not already set to :input' do
|
|
before do
|
|
repository.raw_repository.autocrlf = true
|
|
end
|
|
|
|
it 'sets autocrlf to :input' do
|
|
GitOperationService.new(nil, repository).send(:update_autocrlf_option)
|
|
|
|
expect(repository.raw_repository.autocrlf).to eq(:input)
|
|
end
|
|
end
|
|
|
|
describe 'when autocrlf is already set to :input' do
|
|
before do
|
|
repository.raw_repository.autocrlf = :input
|
|
end
|
|
|
|
it 'does nothing' do
|
|
expect(repository.raw_repository).not_to receive(:autocrlf=)
|
|
.with(:input)
|
|
|
|
GitOperationService.new(nil, repository).send(:update_autocrlf_option)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#empty?' do
|
|
let(:empty_repository) { create(:project_empty_repo).repository }
|
|
|
|
it 'returns true for an empty repository' do
|
|
expect(empty_repository.empty?).to eq(true)
|
|
end
|
|
|
|
it 'returns false for a non-empty repository' do
|
|
expect(repository.empty?).to eq(false)
|
|
end
|
|
|
|
it 'caches the output' do
|
|
expect(repository.raw_repository).to receive(:empty?)
|
|
.once
|
|
.and_return(false)
|
|
|
|
repository.empty?
|
|
repository.empty?
|
|
end
|
|
end
|
|
|
|
describe '#root_ref' do
|
|
it 'returns a branch name' do
|
|
expect(repository.root_ref).to be_an_instance_of(String)
|
|
end
|
|
|
|
it 'caches the output' do
|
|
expect(repository.raw_repository).to receive(:root_ref)
|
|
.once
|
|
.and_return('master')
|
|
|
|
repository.root_ref
|
|
repository.root_ref
|
|
end
|
|
end
|
|
|
|
describe '#expire_root_ref_cache' do
|
|
it 'expires the root reference cache' do
|
|
repository.root_ref
|
|
|
|
expect(repository.raw_repository).to receive(:root_ref)
|
|
.once
|
|
.and_return('foo')
|
|
|
|
repository.expire_root_ref_cache
|
|
|
|
expect(repository.root_ref).to eq('foo')
|
|
end
|
|
end
|
|
|
|
describe '#expire_branch_cache' do
|
|
# This method is private but we need it for testing purposes. Sadly there's
|
|
# no other proper way of testing caching operations.
|
|
let(:cache) { repository.send(:cache) }
|
|
|
|
it 'expires the cache for all branches' do
|
|
expect(cache).to receive(:expire)
|
|
.at_least(repository.branches.length * 2)
|
|
.times
|
|
|
|
repository.expire_branch_cache
|
|
end
|
|
|
|
it 'expires the cache for all branches when the root branch is given' do
|
|
expect(cache).to receive(:expire)
|
|
.at_least(repository.branches.length * 2)
|
|
.times
|
|
|
|
repository.expire_branch_cache(repository.root_ref)
|
|
end
|
|
|
|
it 'expires the cache for a specific branch' do
|
|
expect(cache).to receive(:expire).twice
|
|
|
|
repository.expire_branch_cache('foo')
|
|
end
|
|
end
|
|
|
|
describe '#expire_emptiness_caches' do
|
|
let(:cache) { repository.send(:cache) }
|
|
|
|
it 'expires the caches for an empty repository' do
|
|
allow(repository).to receive(:empty?).and_return(true)
|
|
|
|
expect(cache).to receive(:expire).with(:empty?)
|
|
|
|
repository.expire_emptiness_caches
|
|
end
|
|
|
|
it 'does not expire the cache for a non-empty repository' do
|
|
allow(repository).to receive(:empty?).and_return(false)
|
|
|
|
expect(cache).not_to receive(:expire).with(:empty?)
|
|
|
|
repository.expire_emptiness_caches
|
|
end
|
|
end
|
|
|
|
describe 'skip_merges option' do
|
|
subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", limit: 100, skip_merges: true).map { |k| k.id } }
|
|
|
|
it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') }
|
|
end
|
|
|
|
describe '#merge' do
|
|
let(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project) }
|
|
|
|
let(:commit_options) do
|
|
author = repository.user_to_committer(user)
|
|
{ message: 'Test \r\n\r\n message', committer: author, author: author }
|
|
end
|
|
|
|
it 'merges the code and returns the commit id' do
|
|
expect(merge_commit).to be_present
|
|
expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present
|
|
end
|
|
|
|
it 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do
|
|
merge_commit_id = merge(repository, user, merge_request, commit_options)
|
|
|
|
expect(merge_request.in_progress_merge_commit_sha).to eq(merge_commit_id)
|
|
end
|
|
|
|
it 'removes carriage returns from commit message' do
|
|
merge_commit_id = merge(repository, user, merge_request, commit_options)
|
|
|
|
expect(repository.commit(merge_commit_id).message).to eq(commit_options[:message].delete("\r"))
|
|
end
|
|
|
|
def merge(repository, user, merge_request, options = {})
|
|
repository.merge(user, merge_request.diff_head_sha, merge_request, options)
|
|
end
|
|
end
|
|
|
|
describe '#revert' do
|
|
let(:new_image_commit) { repository.commit('33f3729a45c02fc67d00adb1b8bca394b0e761d9') }
|
|
let(:update_image_commit) { repository.commit('2f63565e7aac07bcdadb654e253078b727143ec4') }
|
|
|
|
context 'when there is a conflict' do
|
|
it 'raises an error' do
|
|
expect { repository.revert(user, new_image_commit, 'master') }.to raise_error(/Failed to/)
|
|
end
|
|
end
|
|
|
|
context 'when commit was already reverted' do
|
|
it 'raises an error' do
|
|
repository.revert(user, update_image_commit, 'master')
|
|
|
|
expect { repository.revert(user, update_image_commit, 'master') }.to raise_error(/Failed to/)
|
|
end
|
|
end
|
|
|
|
context 'when commit can be reverted' do
|
|
it 'reverts the changes' do
|
|
expect(repository.revert(user, update_image_commit, 'master')).to be_truthy
|
|
end
|
|
end
|
|
|
|
context 'reverting a merge commit' do
|
|
it 'reverts the changes' do
|
|
merge_commit
|
|
expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).to be_present
|
|
|
|
repository.revert(user, merge_commit, 'master')
|
|
expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).not_to be_present
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#cherry_pick' do
|
|
let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') }
|
|
let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
|
|
let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') }
|
|
|
|
context 'when there is a conflict' do
|
|
it 'raises an error' do
|
|
expect { repository.cherry_pick(user, conflict_commit, 'master') }.to raise_error(/Failed to/)
|
|
end
|
|
end
|
|
|
|
context 'when commit was already cherry-picked' do
|
|
it 'raises an error' do
|
|
repository.cherry_pick(user, pickable_commit, 'master')
|
|
|
|
expect { repository.cherry_pick(user, pickable_commit, 'master') }.to raise_error(/Failed to/)
|
|
end
|
|
end
|
|
|
|
context 'when commit can be cherry-picked' do
|
|
it 'cherry-picks the changes' do
|
|
expect(repository.cherry_pick(user, pickable_commit, 'master')).to be_truthy
|
|
end
|
|
end
|
|
|
|
context 'cherry-picking a merge commit' do
|
|
it 'cherry-picks the changes' do
|
|
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil
|
|
|
|
repository.cherry_pick(user, pickable_merge, 'improve/awesome')
|
|
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#before_delete' do
|
|
describe 'when a repository does not exist' do
|
|
before do
|
|
allow(repository).to receive(:exists?).and_return(false)
|
|
end
|
|
|
|
it 'does not flush caches that depend on repository data' do
|
|
expect(repository).not_to receive(:expire_cache)
|
|
|
|
repository.before_delete
|
|
end
|
|
|
|
it 'flushes the tags cache' do
|
|
expect(repository).to receive(:expire_tags_cache)
|
|
|
|
repository.before_delete
|
|
end
|
|
|
|
it 'flushes the branches cache' do
|
|
expect(repository).to receive(:expire_branches_cache)
|
|
|
|
repository.before_delete
|
|
end
|
|
|
|
it 'flushes the root ref cache' do
|
|
expect(repository).to receive(:expire_root_ref_cache)
|
|
|
|
repository.before_delete
|
|
end
|
|
|
|
it 'flushes the emptiness caches' do
|
|
expect(repository).to receive(:expire_emptiness_caches)
|
|
|
|
repository.before_delete
|
|
end
|
|
|
|
it 'flushes the exists cache' do
|
|
expect(repository).to receive(:expire_exists_cache).twice
|
|
|
|
repository.before_delete
|
|
end
|
|
end
|
|
|
|
describe 'when a repository exists' do
|
|
before do
|
|
allow(repository).to receive(:exists?).and_return(true)
|
|
end
|
|
|
|
it 'flushes the tags cache' do
|
|
expect(repository).to receive(:expire_tags_cache)
|
|
|
|
repository.before_delete
|
|
end
|
|
|
|
it 'flushes the branches cache' do
|
|
expect(repository).to receive(:expire_branches_cache)
|
|
|
|
repository.before_delete
|
|
end
|
|
|
|
it 'flushes the root ref cache' do
|
|
expect(repository).to receive(:expire_root_ref_cache)
|
|
|
|
repository.before_delete
|
|
end
|
|
|
|
it 'flushes the emptiness caches' do
|
|
expect(repository).to receive(:expire_emptiness_caches)
|
|
|
|
repository.before_delete
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#before_change_head' do
|
|
it 'flushes the branch cache' do
|
|
expect(repository).to receive(:expire_branch_cache)
|
|
|
|
repository.before_change_head
|
|
end
|
|
|
|
it 'flushes the root ref cache' do
|
|
expect(repository).to receive(:expire_root_ref_cache)
|
|
|
|
repository.before_change_head
|
|
end
|
|
end
|
|
|
|
describe '#after_change_head' do
|
|
it 'flushes the readme cache' do
|
|
expect(repository).to receive(:expire_method_caches).with([
|
|
:readme,
|
|
:changelog,
|
|
:license,
|
|
:contributing,
|
|
:gitignore,
|
|
:koding,
|
|
:gitlab_ci,
|
|
:avatar
|
|
])
|
|
|
|
repository.after_change_head
|
|
end
|
|
end
|
|
|
|
describe '#before_push_tag' do
|
|
it 'flushes the cache' do
|
|
expect(repository).to receive(:expire_statistics_caches)
|
|
expect(repository).to receive(:expire_emptiness_caches)
|
|
expect(repository).to receive(:expire_tags_cache)
|
|
|
|
repository.before_push_tag
|
|
end
|
|
end
|
|
|
|
describe '#after_import' do
|
|
it 'flushes and builds the cache' do
|
|
expect(repository).to receive(:expire_content_cache)
|
|
|
|
repository.after_import
|
|
end
|
|
end
|
|
|
|
describe '#after_push_commit' do
|
|
it 'expires statistics caches' do
|
|
expect(repository).to receive(:expire_statistics_caches)
|
|
.and_call_original
|
|
|
|
expect(repository).to receive(:expire_branch_cache)
|
|
.with('master')
|
|
.and_call_original
|
|
|
|
repository.after_push_commit('master')
|
|
end
|
|
end
|
|
|
|
describe '#after_create_branch' do
|
|
it 'expires the branch caches' do
|
|
expect(repository).to receive(:expire_branches_cache)
|
|
|
|
repository.after_create_branch
|
|
end
|
|
end
|
|
|
|
describe '#after_remove_branch' do
|
|
it 'expires the branch caches' do
|
|
expect(repository).to receive(:expire_branches_cache)
|
|
|
|
repository.after_remove_branch
|
|
end
|
|
end
|
|
|
|
describe '#after_create' do
|
|
it 'flushes the exists cache' do
|
|
expect(repository).to receive(:expire_exists_cache)
|
|
|
|
repository.after_create
|
|
end
|
|
|
|
it 'flushes the root ref cache' do
|
|
expect(repository).to receive(:expire_root_ref_cache)
|
|
|
|
repository.after_create
|
|
end
|
|
|
|
it 'flushes the emptiness caches' do
|
|
expect(repository).to receive(:expire_emptiness_caches)
|
|
|
|
repository.after_create
|
|
end
|
|
end
|
|
|
|
describe "#copy_gitattributes" do
|
|
it 'returns true with a valid ref' do
|
|
expect(repository.copy_gitattributes('master')).to be_truthy
|
|
end
|
|
|
|
it 'returns false with an invalid ref' do
|
|
expect(repository.copy_gitattributes('invalid')).to be_falsey
|
|
end
|
|
end
|
|
|
|
describe '#before_remove_tag' do
|
|
it 'flushes the tag cache' do
|
|
expect(repository).to receive(:expire_tags_cache).and_call_original
|
|
expect(repository).to receive(:expire_statistics_caches).and_call_original
|
|
|
|
repository.before_remove_tag
|
|
end
|
|
end
|
|
|
|
describe '#branch_count' do
|
|
it 'returns the number of branches' do
|
|
expect(repository.branch_count).to be_an(Integer)
|
|
|
|
# NOTE: Until rugged goes away, make sure rugged and gitaly are in sync
|
|
rugged_count = repository.raw_repository.rugged.branches.count
|
|
|
|
expect(repository.branch_count).to eq(rugged_count)
|
|
end
|
|
end
|
|
|
|
describe '#tag_count' do
|
|
it 'returns the number of tags' do
|
|
expect(repository.tag_count).to be_an(Integer)
|
|
|
|
# NOTE: Until rugged goes away, make sure rugged and gitaly are in sync
|
|
rugged_count = repository.raw_repository.rugged.tags.count
|
|
|
|
expect(repository.tag_count).to eq(rugged_count)
|
|
end
|
|
end
|
|
|
|
describe '#expire_branches_cache' do
|
|
it 'expires the cache' do
|
|
expect(repository).to receive(:expire_method_caches)
|
|
.with(%i(branch_names branch_count))
|
|
.and_call_original
|
|
|
|
repository.expire_branches_cache
|
|
end
|
|
end
|
|
|
|
describe '#expire_tags_cache' do
|
|
it 'expires the cache' do
|
|
expect(repository).to receive(:expire_method_caches)
|
|
.with(%i(tag_names tag_count))
|
|
.and_call_original
|
|
|
|
repository.expire_tags_cache
|
|
end
|
|
end
|
|
|
|
describe '#add_tag' do
|
|
context 'with a valid target' do
|
|
let(:user) { build_stubbed(:user) }
|
|
|
|
it 'creates the tag using rugged' do
|
|
expect(repository.rugged.tags).to receive(:create)
|
|
.with('8.5', repository.commit('master').id,
|
|
hash_including(message: 'foo',
|
|
tagger: hash_including(name: user.name, email: user.email)))
|
|
.and_call_original
|
|
|
|
repository.add_tag(user, '8.5', 'master', 'foo')
|
|
end
|
|
|
|
it 'returns a Gitlab::Git::Tag object' do
|
|
tag = repository.add_tag(user, '8.5', 'master', 'foo')
|
|
|
|
expect(tag).to be_a(Gitlab::Git::Tag)
|
|
end
|
|
|
|
it 'passes commit SHA to pre-receive and update hooks,\
|
|
and tag SHA to post-receive hook' do
|
|
pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', project)
|
|
update_hook = Gitlab::Git::Hook.new('update', project)
|
|
post_receive_hook = Gitlab::Git::Hook.new('post-receive', project)
|
|
|
|
allow(Gitlab::Git::Hook).to receive(:new)
|
|
.and_return(pre_receive_hook, update_hook, post_receive_hook)
|
|
|
|
allow(pre_receive_hook).to receive(:trigger).and_call_original
|
|
allow(update_hook).to receive(:trigger).and_call_original
|
|
allow(post_receive_hook).to receive(:trigger).and_call_original
|
|
|
|
tag = repository.add_tag(user, '8.5', 'master', 'foo')
|
|
|
|
commit_sha = repository.commit('master').id
|
|
tag_sha = tag.target
|
|
|
|
expect(pre_receive_hook).to have_received(:trigger)
|
|
.with(anything, anything, commit_sha, anything)
|
|
expect(update_hook).to have_received(:trigger)
|
|
.with(anything, anything, commit_sha, anything)
|
|
expect(post_receive_hook).to have_received(:trigger)
|
|
.with(anything, anything, tag_sha, anything)
|
|
end
|
|
end
|
|
|
|
context 'with an invalid target' do
|
|
it 'returns false' do
|
|
expect(repository.add_tag(user, '8.5', 'bar', 'foo')).to be false
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#rm_branch' do
|
|
let(:user) { create(:user) }
|
|
|
|
it 'removes a branch' do
|
|
expect(repository).to receive(:before_remove_branch)
|
|
expect(repository).to receive(:after_remove_branch)
|
|
|
|
repository.rm_branch(user, 'feature')
|
|
end
|
|
end
|
|
|
|
describe '#rm_tag' do
|
|
it 'removes a tag' do
|
|
expect(repository).to receive(:before_remove_tag)
|
|
|
|
repository.rm_tag(create(:user), 'v1.1.0')
|
|
|
|
expect(repository.find_tag('v1.1.0')).to be_nil
|
|
end
|
|
end
|
|
|
|
describe '#avatar' do
|
|
it 'returns nil if repo does not exist' do
|
|
expect(repository).to receive(:file_on_head)
|
|
.and_raise(Rugged::ReferenceError)
|
|
|
|
expect(repository.avatar).to eq(nil)
|
|
end
|
|
|
|
it 'returns the first avatar file found in the repository' do
|
|
expect(repository).to receive(:file_on_head)
|
|
.with(:avatar)
|
|
.and_return(double(:tree, path: 'logo.png'))
|
|
|
|
expect(repository.avatar).to eq('logo.png')
|
|
end
|
|
|
|
it 'caches the output' do
|
|
expect(repository).to receive(:file_on_head)
|
|
.with(:avatar)
|
|
.once
|
|
.and_return(double(:tree, path: 'logo.png'))
|
|
|
|
2.times { expect(repository.avatar).to eq('logo.png') }
|
|
end
|
|
end
|
|
|
|
describe '#expire_exists_cache' do
|
|
let(:cache) { repository.send(:cache) }
|
|
|
|
it 'expires the cache' do
|
|
expect(cache).to receive(:expire).with(:exists?)
|
|
|
|
repository.expire_exists_cache
|
|
end
|
|
end
|
|
|
|
describe "#keep_around" do
|
|
it "does not fail if we attempt to reference bad commit" do
|
|
expect(repository.kept_around?('abc1234')).to be_falsey
|
|
end
|
|
|
|
it "stores a reference to the specified commit sha so it isn't garbage collected" do
|
|
repository.keep_around(sample_commit.id)
|
|
|
|
expect(repository.kept_around?(sample_commit.id)).to be_truthy
|
|
end
|
|
|
|
it "attempting to call keep_around on truncated ref does not fail" do
|
|
repository.keep_around(sample_commit.id)
|
|
ref = repository.send(:keep_around_ref_name, sample_commit.id)
|
|
path = File.join(repository.path, ref)
|
|
# Corrupt the reference
|
|
File.truncate(path, 0)
|
|
|
|
expect(repository.kept_around?(sample_commit.id)).to be_falsey
|
|
|
|
repository.keep_around(sample_commit.id)
|
|
|
|
expect(repository.kept_around?(sample_commit.id)).to be_falsey
|
|
|
|
File.delete(path)
|
|
end
|
|
end
|
|
|
|
describe '#update_ref' do
|
|
it 'can create a ref' do
|
|
GitOperationService.new(nil, repository).send(:update_ref, 'refs/heads/foobar', 'refs/heads/master', Gitlab::Git::BLANK_SHA)
|
|
|
|
expect(repository.find_branch('foobar')).not_to be_nil
|
|
end
|
|
|
|
it 'raises CommitError when the ref update fails' do
|
|
expect do
|
|
GitOperationService.new(nil, repository).send(:update_ref, 'refs/heads/master', 'refs/heads/master', Gitlab::Git::BLANK_SHA)
|
|
end.to raise_error(Repository::CommitError)
|
|
end
|
|
end
|
|
|
|
describe '#contribution_guide', :use_clean_rails_memory_store_caching do
|
|
it 'returns and caches the output' do
|
|
expect(repository).to receive(:file_on_head)
|
|
.with(:contributing)
|
|
.and_return(Gitlab::Git::Tree.new(path: 'CONTRIBUTING.md'))
|
|
.once
|
|
|
|
2.times do
|
|
expect(repository.contribution_guide)
|
|
.to be_an_instance_of(Gitlab::Git::Tree)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#gitignore', :use_clean_rails_memory_store_caching do
|
|
it 'returns and caches the output' do
|
|
expect(repository).to receive(:file_on_head)
|
|
.with(:gitignore)
|
|
.and_return(Gitlab::Git::Tree.new(path: '.gitignore'))
|
|
.once
|
|
|
|
2.times do
|
|
expect(repository.gitignore).to be_an_instance_of(Gitlab::Git::Tree)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#koding_yml', :use_clean_rails_memory_store_caching do
|
|
it 'returns and caches the output' do
|
|
expect(repository).to receive(:file_on_head)
|
|
.with(:koding)
|
|
.and_return(Gitlab::Git::Tree.new(path: '.koding.yml'))
|
|
.once
|
|
|
|
2.times do
|
|
expect(repository.koding_yml).to be_an_instance_of(Gitlab::Git::Tree)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#readme', :use_clean_rails_memory_store_caching do
|
|
context 'with a non-existing repository' do
|
|
it 'returns nil' do
|
|
allow(repository).to receive(:tree).with(:head).and_return(nil)
|
|
|
|
expect(repository.readme).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'with an existing repository' do
|
|
context 'when no README exists' do
|
|
it 'returns nil' do
|
|
allow_any_instance_of(Tree).to receive(:readme).and_return(nil)
|
|
|
|
expect(repository.readme).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'when a README exists' do
|
|
it 'returns the README' do
|
|
expect(repository.readme).to be_an_instance_of(ReadmeBlob)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#expire_statistics_caches' do
|
|
it 'expires the caches' do
|
|
expect(repository).to receive(:expire_method_caches)
|
|
.with(%i(size commit_count))
|
|
|
|
repository.expire_statistics_caches
|
|
end
|
|
end
|
|
|
|
describe '#expire_method_caches' do
|
|
it 'expires the caches of the given methods' do
|
|
expect_any_instance_of(RepositoryCache).to receive(:expire).with(:readme)
|
|
expect_any_instance_of(RepositoryCache).to receive(:expire).with(:gitignore)
|
|
|
|
repository.expire_method_caches(%i(readme gitignore))
|
|
end
|
|
end
|
|
|
|
describe '#expire_all_method_caches' do
|
|
it 'expires the caches of all methods' do
|
|
expect(repository).to receive(:expire_method_caches)
|
|
.with(Repository::CACHED_METHODS)
|
|
|
|
repository.expire_all_method_caches
|
|
end
|
|
end
|
|
|
|
describe '#file_on_head' do
|
|
context 'with a non-existing repository' do
|
|
it 'returns nil' do
|
|
expect(repository).to receive(:tree).with(:head).and_return(nil)
|
|
|
|
expect(repository.file_on_head(:readme)).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'with a repository that has no blobs' do
|
|
it 'returns nil' do
|
|
expect_any_instance_of(Tree).to receive(:blobs).and_return([])
|
|
|
|
expect(repository.file_on_head(:readme)).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'with an existing repository' do
|
|
it 'returns a Gitlab::Git::Tree' do
|
|
expect(repository.file_on_head(:readme))
|
|
.to be_an_instance_of(Gitlab::Git::Tree)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#head_tree' do
|
|
context 'with an existing repository' do
|
|
it 'returns a Tree' do
|
|
expect(repository.head_tree).to be_an_instance_of(Tree)
|
|
end
|
|
end
|
|
|
|
context 'with a non-existing repository' do
|
|
it 'returns nil' do
|
|
expect(repository).to receive(:head_commit).and_return(nil)
|
|
|
|
expect(repository.head_tree).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#tree' do
|
|
context 'using a non-existing repository' do
|
|
before do
|
|
allow(repository).to receive(:head_commit).and_return(nil)
|
|
end
|
|
|
|
it 'returns nil' do
|
|
expect(repository.tree(:head)).to be_nil
|
|
end
|
|
|
|
it 'returns nil when using a path' do
|
|
expect(repository.tree(:head, 'README.md')).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'using an existing repository' do
|
|
it 'returns a Tree' do
|
|
expect(repository.tree(:head)).to be_an_instance_of(Tree)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#size' do
|
|
context 'with a non-existing repository' do
|
|
it 'returns 0' do
|
|
expect(repository).to receive(:exists?).and_return(false)
|
|
|
|
expect(repository.size).to eq(0.0)
|
|
end
|
|
end
|
|
|
|
context 'with an existing repository' do
|
|
it 'returns the repository size as a Float' do
|
|
expect(repository.size).to be_an_instance_of(Float)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#commit_count' do
|
|
context 'with a non-existing repository' do
|
|
it 'returns 0' do
|
|
expect(repository).to receive(:root_ref).and_return(nil)
|
|
|
|
expect(repository.commit_count).to eq(0)
|
|
end
|
|
end
|
|
|
|
context 'with an existing repository' do
|
|
it 'returns the commit count' do
|
|
expect(repository.commit_count).to be_an(Integer)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#commit_count_for_ref' do
|
|
let(:project) { create :project }
|
|
|
|
context 'with a non-existing repository' do
|
|
it 'returns 0' do
|
|
expect(project.repository.commit_count_for_ref('master')).to eq(0)
|
|
end
|
|
end
|
|
|
|
context 'with empty repository' do
|
|
it 'returns 0' do
|
|
project.create_repository
|
|
expect(project.repository.commit_count_for_ref('master')).to eq(0)
|
|
end
|
|
end
|
|
|
|
context 'when searching for the root ref' do
|
|
it 'returns the same count as #commit_count' do
|
|
expect(repository.commit_count_for_ref(repository.root_ref)).to eq(repository.commit_count)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#cache_method_output', :use_clean_rails_memory_store_caching do
|
|
context 'with a non-existing repository' do
|
|
let(:value) do
|
|
repository.cache_method_output(:cats, fallback: 10) do
|
|
raise Rugged::ReferenceError
|
|
end
|
|
end
|
|
|
|
it 'returns a fallback value' do
|
|
expect(value).to eq(10)
|
|
end
|
|
|
|
it 'does not cache the data' do
|
|
value
|
|
|
|
expect(repository.instance_variable_defined?(:@cats)).to eq(false)
|
|
expect(repository.send(:cache).exist?(:cats)).to eq(false)
|
|
end
|
|
end
|
|
|
|
context 'with an existing repository' do
|
|
it 'caches the output' do
|
|
object = double
|
|
|
|
expect(object).to receive(:number).once.and_return(10)
|
|
|
|
2.times do
|
|
val = repository.cache_method_output(:cats) { object.number }
|
|
|
|
expect(val).to eq(10)
|
|
end
|
|
|
|
expect(repository.send(:cache).exist?(:cats)).to eq(true)
|
|
expect(repository.instance_variable_get(:@cats)).to eq(10)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#refresh_method_caches' do
|
|
it 'refreshes the caches of the given types' do
|
|
expect(repository).to receive(:expire_method_caches)
|
|
.with(%i(rendered_readme license_blob license_key license))
|
|
|
|
expect(repository).to receive(:rendered_readme)
|
|
expect(repository).to receive(:license_blob)
|
|
expect(repository).to receive(:license_key)
|
|
expect(repository).to receive(:license)
|
|
|
|
repository.refresh_method_caches(%i(readme license))
|
|
end
|
|
end
|
|
|
|
describe '#gitlab_ci_yml_for' do
|
|
before do
|
|
repository.create_file(User.last, '.gitlab-ci.yml', 'CONTENT', message: 'Add .gitlab-ci.yml', branch_name: 'master')
|
|
end
|
|
|
|
context 'when there is a .gitlab-ci.yml at the commit' do
|
|
it 'returns the content' do
|
|
expect(repository.gitlab_ci_yml_for(repository.commit.sha)).to eq('CONTENT')
|
|
end
|
|
end
|
|
|
|
context 'when there is no .gitlab-ci.yml at the commit' do
|
|
it 'returns nil' do
|
|
expect(repository.gitlab_ci_yml_for(repository.commit.parent.sha)).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#route_map_for' do
|
|
before do
|
|
repository.create_file(User.last, '.gitlab/route-map.yml', 'CONTENT', message: 'Add .gitlab/route-map.yml', branch_name: 'master')
|
|
end
|
|
|
|
context 'when there is a .gitlab/route-map.yml at the commit' do
|
|
it 'returns the content' do
|
|
expect(repository.route_map_for(repository.commit.sha)).to eq('CONTENT')
|
|
end
|
|
end
|
|
|
|
context 'when there is no .gitlab/route-map.yml at the commit' do
|
|
it 'returns nil' do
|
|
expect(repository.route_map_for(repository.commit.parent.sha)).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#is_ancestor?' do
|
|
let(:commit) { repository.commit }
|
|
let(:ancestor) { commit.parents.first }
|
|
|
|
context 'with Gitaly enabled' do
|
|
it 'it is an ancestor' do
|
|
expect(repository.is_ancestor?(ancestor.id, commit.id)).to eq(true)
|
|
end
|
|
|
|
it 'it is not an ancestor' do
|
|
expect(repository.is_ancestor?(commit.id, ancestor.id)).to eq(false)
|
|
end
|
|
|
|
it 'returns false on nil-values' do
|
|
expect(repository.is_ancestor?(nil, commit.id)).to eq(false)
|
|
expect(repository.is_ancestor?(ancestor.id, nil)).to eq(false)
|
|
expect(repository.is_ancestor?(nil, nil)).to eq(false)
|
|
end
|
|
end
|
|
|
|
context 'with Gitaly disabled' do
|
|
before do
|
|
allow(Gitlab::GitalyClient).to receive(:enabled?).and_return(false)
|
|
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(false)
|
|
end
|
|
|
|
it 'it is an ancestor' do
|
|
expect(repository.is_ancestor?(ancestor.id, commit.id)).to eq(true)
|
|
end
|
|
|
|
it 'it is not an ancestor' do
|
|
expect(repository.is_ancestor?(commit.id, ancestor.id)).to eq(false)
|
|
end
|
|
|
|
it 'returns false on nil-values' do
|
|
expect(repository.is_ancestor?(nil, commit.id)).to eq(false)
|
|
expect(repository.is_ancestor?(ancestor.id, nil)).to eq(false)
|
|
expect(repository.is_ancestor?(nil, nil)).to eq(false)
|
|
end
|
|
end
|
|
end
|
|
end
|