summaryrefslogtreecommitdiff
path: root/spec/models/repository_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/repository_spec.rb')
-rw-r--r--spec/models/repository_spec.rb253
1 files changed, 217 insertions, 36 deletions
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index f7dbfd712cc..db29f4d353b 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -16,6 +16,21 @@ describe Repository, models: true do
merge_commit_id = repository.merge(user, merge_request, commit_options)
repository.commit(merge_commit_id)
end
+ let(:author_email) { FFaker::Internet.email }
+
+ # I have to remove periods from the end of the name
+ # This happened when the user's name had a suffix (i.e. "Sr.")
+ # This seems to be what git does under the hood. For example, this commit:
+ #
+ # $ git commit --author='Foo Sr. <foo@example.com>' -m 'Where's my trailing period?'
+ #
+ # results in this:
+ #
+ # $ git show --pretty
+ # ...
+ # Author: Foo Sr <foo@example.com>
+ # ...
+ let(:author_name) { FFaker::Name.name.chomp("\.") }
describe '#branch_names_contains' do
subject { repository.branch_names_contains(sample_commit.id) }
@@ -132,7 +147,31 @@ describe Repository, models: true do
end
end
- describe :commit_file do
+ describe "#commit_dir" do
+ it "commits a change that creates a new directory" do
+ expect do
+ repository.commit_dir(user, 'newdir', 'Create newdir', 'master')
+ end.to change { repository.commits('master').count }.by(1)
+
+ newdir = repository.tree('master', 'newdir')
+ expect(newdir.path).to eq('newdir')
+ end
+
+ context "when an author is specified" do
+ it "uses the given email/name to set the commit's author" do
+ expect do
+ repository.commit_dir(user, "newdir", "Add newdir", '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 "#commit_file" do
it 'commits change to a file successfully' do
expect do
repository.commit_file(user, 'CHANGELOG', 'Changelog!',
@@ -144,9 +183,23 @@ describe Repository, models: true do
expect(blob.data).to eq('Changelog!')
end
+
+ context "when an author is specified" do
+ it "uses the given email/name to set the commit's author" do
+ expect do
+ repository.commit_file(user, "README", 'README!', 'Add README',
+ 'master', true, 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
+ describe "#update_file" do
it 'updates filename successfully' do
expect do
repository.update_file(user, 'NEWLICENSE', 'Copyright!',
@@ -160,6 +213,85 @@ describe Repository, models: true do
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
+ repository.commit_file(user, "README", 'README!', 'Add README', 'master', true)
+
+ expect do
+ repository.update_file(user, 'README', "Updated README!",
+ branch: '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 "#remove_file" do
+ it 'removes file successfully' do
+ repository.commit_file(user, "README", 'README!', 'Add README', 'master', true)
+
+ expect do
+ repository.remove_file(user, "README", "Remove README", '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
+ repository.commit_file(user, "README", 'README!', 'Add README', 'master', true)
+
+ expect do
+ repository.remove_file(user, "README", "Remove README", '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" do
@@ -186,32 +318,6 @@ describe Repository, models: true do
it { is_expected.to be_an String }
it { expect(subject.lines[2]).to eq("master:CHANGELOG:188: - Feature: Replace teams with group membership\n") }
end
-
- describe 'parsing result' do
- subject { repository.parse_search_result(search_result) }
- let(:search_result) { results.first }
-
- it { is_expected.to be_an OpenStruct }
- it { expect(subject.filename).to eq('CHANGELOG') }
- it { expect(subject.basename).to eq('CHANGELOG') }
- it { expect(subject.ref).to eq('master') }
- it { expect(subject.startline).to eq(186) }
- it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") }
-
- context "when filename has extension" do
- let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" }
-
- it { expect(subject.filename).to eq('CONTRIBUTE.md') }
- it { expect(subject.basename).to eq('CONTRIBUTE') }
- end
-
- context "when file under directory" do
- let(:search_result) { "master:a/b/c.md:5:a b c\n" }
-
- it { expect(subject.filename).to eq('a/b/c.md') }
- it { expect(subject.basename).to eq('a/b/c') }
- end
- end
end
describe "#changelog" do
@@ -382,6 +488,24 @@ describe Repository, models: true do
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' }
@@ -423,43 +547,77 @@ describe Repository, models: true do
end
end
- describe '#commit_with_hooks' do
+ 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
expect_any_instance_of(GitHooksService).to receive(:execute).
- with(user, repository.path_to_repo, old_rev, sample_commit.id, 'refs/heads/feature').
+ with(user, repository.path_to_repo, old_rev, new_rev, 'refs/heads/feature').
and_yield.and_return(true)
end
it 'runs without errors' do
expect do
- repository.commit_with_hooks(user, 'feature') { sample_commit.id }
+ repository.update_branch_with_hooks(user, 'feature') { new_rev }
end.not_to raise_error
end
it 'ensures the autocrlf Git option is set to :input' do
expect(repository).to receive(:update_autocrlf_option)
- repository.commit_with_hooks(user, 'feature') { sample_commit.id }
+ repository.update_branch_with_hooks(user, 'feature') { new_rev }
end
context "when the branch wasn't empty" do
it 'updates the head' do
expect(repository.find_branch('feature').target.id).to eq(old_rev)
- repository.commit_with_hooks(user, 'feature') { sample_commit.id }
- expect(repository.find_branch('feature').target.id).to eq(sample_commit.id)
+ repository.update_branch_with_hooks(user, 'feature') { new_rev }
+ expect(repository.find_branch('feature').target.id).to eq(new_rev)
end
end
end
+ context 'when the update adds more than one commit' do
+ it 'runs without errors' do
+ old_rev = '33f3729a45c02fc67d00adb1b8bca394b0e761d9'
+
+ # 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 { repository.update_branch_with_hooks(user, branch) { new_rev } }.not_to raise_error
+ end
+ end
+
+ context 'when the update would remove commits from the target branch' do
+ it 'raises an exception' do
+ branch = 'master'
+ old_rev = repository.find_branch(branch).target.sha
+
+ # 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
+ repository.update_branch_with_hooks(user, branch) { new_rev }
+ 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
- repository.commit_with_hooks(user, 'feature') { sample_commit.id }
+ repository.update_branch_with_hooks(user, 'feature') { new_rev }
end.to raise_error(GitHooksService::PreReceiveError)
end
end
@@ -467,6 +625,7 @@ describe Repository, models: true do
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, ''])
+ allow(repository).to receive(:update_ref!)
end
it 'expires branch cache' do
@@ -477,7 +636,7 @@ describe Repository, models: true do
expect(repository).to receive(:expire_has_visible_content_cache)
expect(repository).to receive(:expire_branch_count_cache)
- repository.commit_with_hooks(user, 'new-feature') { sample_commit.id }
+ repository.update_branch_with_hooks(user, 'new-feature') { new_rev }
end
end
@@ -719,6 +878,14 @@ describe Repository, models: true 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_request = create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project)
+ merge_commit_id = repository.merge(user, merge_request, commit_options)
+ repository.commit(merge_commit_id)
+
+ expect(merge_request.in_progress_merge_commit_sha).to eq(merge_commit_id)
+ end
end
describe '#revert' do
@@ -1242,4 +1409,18 @@ describe Repository, models: true do
File.delete(path)
end
end
+
+ describe '#update_ref!' do
+ it 'can create a ref' do
+ repository.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
+ repository.update_ref!('refs/heads/master', 'refs/heads/master', Gitlab::Git::BLANK_SHA)
+ end.to raise_error(Repository::CommitError)
+ end
+ end
end