diff options
Diffstat (limited to 'spec/lib/gitlab/git/repository_spec.rb')
-rw-r--r-- | spec/lib/gitlab/git/repository_spec.rb | 475 |
1 files changed, 135 insertions, 340 deletions
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 703b0c2c202..50736d353ad 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -3,6 +3,20 @@ require "spec_helper" describe Gitlab::Git::Repository, seed_helper: true do include Gitlab::EncodingHelper + shared_examples 'wrapping gRPC errors' do |gitaly_client_class, gitaly_client_method| + it 'wraps gRPC not found error' do + expect_any_instance_of(gitaly_client_class).to receive(gitaly_client_method) + .and_raise(GRPC::NotFound) + expect { subject }.to raise_error(Gitlab::Git::Repository::NoRepository) + end + + it 'wraps gRPC unknown error' do + expect_any_instance_of(gitaly_client_class).to receive(gitaly_client_method) + .and_raise(GRPC::Unknown) + expect { subject }.to raise_error(Gitlab::Git::CommandError) + end + end + let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) } describe "Respond to" do @@ -26,31 +40,17 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - context 'with gitaly enabled' do - before do - stub_gitaly - end - - after do - Gitlab::GitalyClient.clear_stubs! - end - - it 'gets the branch name from GitalyClient' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name) - repository.root_ref - end + it 'returns UTF-8' do + expect(repository.root_ref).to be_utf8 + end - it 'wraps GRPC not found' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name) - .and_raise(GRPC::NotFound) - expect { repository.root_ref }.to raise_error(Gitlab::Git::Repository::NoRepository) - end + it 'gets the branch name from GitalyClient' do + expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:default_branch_name) + repository.root_ref + end - it 'wraps GRPC exceptions' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name) - .and_raise(GRPC::Unknown) - expect { repository.root_ref }.to raise_error(Gitlab::Git::CommandError) - end + it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :default_branch_name do + subject { repository.root_ref } end end @@ -123,45 +123,35 @@ describe Gitlab::Git::Repository, seed_helper: true do it 'has SeedRepo::Repo::BRANCHES.size elements' do expect(subject.size).to eq(SeedRepo::Repo::BRANCHES.size) end - it { is_expected.to include("master") } - it { is_expected.not_to include("branch-from-space") } - context 'with gitaly enabled' do - before do - stub_gitaly - end - - after do - Gitlab::GitalyClient.clear_stubs! - end - - it 'gets the branch names from GitalyClient' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names) - subject - end + it 'returns UTF-8' do + expect(subject.first).to be_utf8 + end - it 'wraps GRPC not found' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names) - .and_raise(GRPC::NotFound) - expect { subject }.to raise_error(Gitlab::Git::Repository::NoRepository) - end + it { is_expected.to include("master") } + it { is_expected.not_to include("branch-from-space") } - it 'wraps GRPC other exceptions' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names) - .and_raise(GRPC::Unknown) - expect { subject }.to raise_error(Gitlab::Git::CommandError) - end + it 'gets the branch names from GitalyClient' do + expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:branch_names) + subject end + + it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :branch_names end describe '#tag_names' do subject { repository.tag_names } it { is_expected.to be_kind_of Array } + it 'has SeedRepo::Repo::TAGS.size elements' do expect(subject.size).to eq(SeedRepo::Repo::TAGS.size) end + it 'returns UTF-8' do + expect(subject.first).to be_utf8 + end + describe '#last' do subject { super().last } it { is_expected.to eq("v1.2.1") } @@ -169,32 +159,12 @@ describe Gitlab::Git::Repository, seed_helper: true do it { is_expected.to include("v1.0.0") } it { is_expected.not_to include("v5.0.0") } - context 'with gitaly enabled' do - before do - stub_gitaly - end - - after do - Gitlab::GitalyClient.clear_stubs! - end - - it 'gets the tag names from GitalyClient' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names) - subject - end - - it 'wraps GRPC not found' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names) - .and_raise(GRPC::NotFound) - expect { subject }.to raise_error(Gitlab::Git::Repository::NoRepository) - end - - it 'wraps GRPC exceptions' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names) - .and_raise(GRPC::Unknown) - expect { subject }.to raise_error(Gitlab::Git::CommandError) - end + it 'gets the tag names from GitalyClient' do + expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:tag_names) + subject end + + it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tag_names end shared_examples 'archive check' do |extenstion| @@ -264,33 +234,6 @@ describe Gitlab::Git::Repository, seed_helper: true do it { expect(repository.bare?).to be_truthy } end - describe '#heads' do - let(:heads) { repository.heads } - subject { heads } - - it { is_expected.to be_kind_of Array } - - describe '#size' do - subject { super().size } - it { is_expected.to eq(SeedRepo::Repo::BRANCHES.size) } - end - - context :head do - subject { heads.first } - - describe '#name' do - subject { super().name } - it { is_expected.to eq("feature") } - end - - context :commit do - subject { heads.first.dereferenced_target.sha } - - it { is_expected.to eq("0b4bc9a49b562e85de7cc9e834518ea6828729b9") } - end - end - end - describe '#ref_names' do let(:ref_names) { repository.ref_names } subject { ref_names } @@ -308,39 +251,35 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe '#search_files' do - let(:results) { repository.search_files('rails', 'master') } - subject { results } - - it { is_expected.to be_kind_of Array } + describe '#submodule_url_for' do + let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) } + let(:ref) { 'master' } - describe '#first' do - subject { super().first } - it { is_expected.to be_kind_of Gitlab::Git::BlobSnippet } + def submodule_url(path) + repository.submodule_url_for(ref, path) end - context 'blob result' do - subject { results.first } + it { expect(submodule_url('six')).to eq('git://github.com/randx/six.git') } + it { expect(submodule_url('nested/six')).to eq('git://github.com/randx/six.git') } + it { expect(submodule_url('deeper/nested/six')).to eq('git://github.com/randx/six.git') } + it { expect(submodule_url('invalid/path')).to eq(nil) } - describe '#ref' do - subject { super().ref } - it { is_expected.to eq('master') } - end + context 'uncommitted submodule dir' do + let(:ref) { 'fix-existing-submodule-dir' } - describe '#filename' do - subject { super().filename } - it { is_expected.to eq('CHANGELOG') } - end + it { expect(submodule_url('submodule-existing-dir')).to eq(nil) } + end - describe '#startline' do - subject { super().startline } - it { is_expected.to eq(35) } - end + context 'tags' do + let(:ref) { 'v1.2.1' } - describe '#data' do - subject { super().data } - it { is_expected.to include "Ability to filter by multiple labels" } - end + it { expect(submodule_url('six')).to eq('git://github.com/randx/six.git') } + end + + context 'no submodules at commit' do + let(:ref) { '6d39438' } + + it { expect(submodule_url('six')).to eq(nil) } end end @@ -348,7 +287,7 @@ describe Gitlab::Git::Repository, seed_helper: true do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) } context 'where repo has submodules' do - let(:submodules) { repository.submodules('master') } + let(:submodules) { repository.send(:submodules, 'master') } let(:submodule) { submodules.first } it { expect(submodules).to be_kind_of Hash } @@ -383,12 +322,12 @@ describe Gitlab::Git::Repository, seed_helper: true do end it 'should not have an entry for an uncommited submodule dir' do - submodules = repository.submodules('fix-existing-submodule-dir') + submodules = repository.send(:submodules, 'fix-existing-submodule-dir') expect(submodules).not_to have_key('submodule-existing-dir') end it 'should handle tags correctly' do - submodules = repository.submodules('v1.2.1') + submodules = repository.send(:submodules, 'v1.2.1') expect(submodules.first).to eq([ "six", { @@ -414,7 +353,7 @@ describe Gitlab::Git::Repository, seed_helper: true do end context 'where repo doesn\'t have submodules' do - let(:submodules) { repository.submodules('6d39438') } + let(:submodules) { repository.send(:submodules, '6d39438') } it 'should return an empty hash' do expect(submodules).to be_empty end @@ -422,145 +361,20 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#commit_count' do - it { expect(repository.commit_count("master")).to eq(25) } - it { expect(repository.commit_count("feature")).to eq(9) } - end - - describe "#reset" do - change_path = File.join(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH, "CHANGELOG") - untracked_path = File.join(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH, "UNTRACKED") - tracked_path = File.join(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH, "files", "ruby", "popen.rb") - - change_text = "New changelog text" - untracked_text = "This file is untracked" - - reset_commit = SeedRepo::LastCommit::ID - - context "--hard" do - before(:all) do - # Modify a tracked file - File.open(change_path, "w") do |f| - f.write(change_text) - end - - # Add an untracked file to the working directory - File.open(untracked_path, "w") do |f| - f.write(untracked_text) - end - - @normal_repo = Gitlab::Git::Repository.new('default', TEST_NORMAL_REPO_PATH) - @normal_repo.reset("HEAD", :hard) - end - - it "should replace the working directory with the content of the index" do - File.open(change_path, "r") do |f| - expect(f.each_line.first).not_to eq(change_text) - end - - File.open(tracked_path, "r") do |f| - expect(f.each_line.to_a[8]).to include('raise RuntimeError, "System commands') - end - end - - it "should not touch untracked files" do - expect(File.exist?(untracked_path)).to be_truthy - end - - it "should move the HEAD to the correct commit" do - new_head = @normal_repo.rugged.head.target.oid - expect(new_head).to eq(reset_commit) - end - - it "should move the tip of the master branch to the correct commit" do - new_tip = @normal_repo.rugged.references["refs/heads/master"] - .target.oid - - expect(new_tip).to eq(reset_commit) - end - - after(:all) do - # Fast-forward to the original HEAD - FileUtils.rm_rf(TEST_NORMAL_REPO_PATH) - ensure_seeds - end + shared_examples 'counting commits' do + it { expect(repository.commit_count("master")).to eq(25) } + it { expect(repository.commit_count("feature")).to eq(9) } end - end - - describe "#checkout" do - new_branch = "foo_branch" - - context "-b" do - before(:all) do - @normal_repo = Gitlab::Git::Repository.new('default', TEST_NORMAL_REPO_PATH) - @normal_repo.checkout(new_branch, { b: true }, "origin/feature") - end - it "should create a new branch" do - expect(@normal_repo.rugged.branches[new_branch]).not_to be_nil - end - - it "should move the HEAD to the correct commit" do - expect(@normal_repo.rugged.head.target.oid).to( - eq(@normal_repo.rugged.branches["origin/feature"].target.oid) - ) - end - - it "should refresh the repo's #heads collection" do - head_names = @normal_repo.heads.map { |h| h.name } - expect(head_names).to include(new_branch) - end - - after(:all) do - FileUtils.rm_rf(TEST_NORMAL_REPO_PATH) - ensure_seeds + context 'when Gitaly commit_count feature is enabled' do + it_behaves_like 'counting commits' + it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::CommitService, :commit_count do + subject { repository.commit_count('master') } end end - context "without -b" do - context "and specifying a nonexistent branch" do - it "should not do anything" do - normal_repo = Gitlab::Git::Repository.new('default', TEST_NORMAL_REPO_PATH) - - expect { normal_repo.checkout(new_branch) }.to raise_error(Rugged::ReferenceError) - expect(normal_repo.rugged.branches[new_branch]).to be_nil - expect(normal_repo.rugged.head.target.oid).to( - eq(normal_repo.rugged.branches["master"].target.oid) - ) - - head_names = normal_repo.heads.map { |h| h.name } - expect(head_names).not_to include(new_branch) - end - - after(:all) do - FileUtils.rm_rf(TEST_NORMAL_REPO_PATH) - ensure_seeds - end - end - - context "and with a valid branch" do - before(:all) do - @normal_repo = Gitlab::Git::Repository.new('default', TEST_NORMAL_REPO_PATH) - @normal_repo.rugged.branches.create("feature", "origin/feature") - @normal_repo.checkout("feature") - end - - it "should move the HEAD to the correct commit" do - expect(@normal_repo.rugged.head.target.oid).to( - eq(@normal_repo.rugged.branches["feature"].target.oid) - ) - end - - it "should update the working directory" do - File.open(File.join(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH, ".gitignore"), "r") do |f| - expect(f.read.each_line.to_a).not_to include(".DS_Store\n") - end - end - - after(:all) do - FileUtils.rm_rf(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH) - ensure_seeds - end - end + context 'when Gitaly commit_count feature is disabled', skip_gitaly_mock: true do + it_behaves_like 'counting commits' end end @@ -574,10 +388,6 @@ describe Gitlab::Git::Repository, seed_helper: true do expect(@repo.rugged.branches["feature"]).to be_nil end - it "should update the repo's #heads collection" do - expect(@repo.heads).not_to include("feature") - end - after(:all) do FileUtils.rm_rf(TEST_MUTABLE_REPO_PATH) ensure_seeds @@ -690,9 +500,9 @@ describe Gitlab::Git::Repository, seed_helper: true do # Add new commits so that there's a renamed file in the commit history repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH).rugged - commit_with_old_name = new_commit_edit_old_file(repo) - rename_commit = new_commit_move_file(repo) - commit_with_new_name = new_commit_edit_new_file(repo) + commit_with_old_name = Gitlab::Git::Commit.decorate(new_commit_edit_old_file(repo)) + rename_commit = Gitlab::Git::Commit.decorate(new_commit_move_file(repo)) + commit_with_new_name = Gitlab::Git::Commit.decorate(new_commit_edit_new_file(repo)) end after(:context) do @@ -865,8 +675,8 @@ describe Gitlab::Git::Repository, seed_helper: true do context "compare results between log_by_walk and log_by_shell" do let(:options) { { ref: "master" } } - let(:commits_by_walk) { repository.log(options).map(&:oid) } - let(:commits_by_shell) { repository.log(options.merge({ disable_walk: true })).map(&:oid) } + let(:commits_by_walk) { repository.log(options).map(&:id) } + let(:commits_by_shell) { repository.log(options.merge({ disable_walk: true })).map(&:id) } it { expect(commits_by_walk).to eq(commits_by_shell) } @@ -909,7 +719,7 @@ describe Gitlab::Git::Repository, seed_helper: true do expect(commits.size).to be > 0 expect(commits).to satisfy do |commits| - commits.all? { |commit| commit.time >= options[:after] } + commits.all? { |commit| commit.committed_date >= options[:after] } end end end @@ -922,7 +732,7 @@ describe Gitlab::Git::Repository, seed_helper: true do expect(commits.size).to be > 0 expect(commits).to satisfy do |commits| - commits.all? { |commit| commit.time <= options[:before] } + commits.all? { |commit| commit.committed_date <= options[:before] } end end end @@ -931,7 +741,7 @@ describe Gitlab::Git::Repository, seed_helper: true do let(:options) { { ref: 'master', path: ['PROCESS.md', 'README.md'] } } def commit_files(commit) - commit.diff(commit.parent_ids.first).deltas.flat_map do |delta| + commit.diff_from_parent.deltas.flat_map do |delta| [delta.old_file[:path], delta.new_file[:path]].uniq.compact end end @@ -1101,48 +911,49 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe '#find_commits' do - it 'should return a return a collection of commits' do - commits = repository.find_commits + describe '#branches' do + subject { repository.branches } - expect(commits).not_to be_empty - expect(commits).to all( be_a_kind_of(Gitlab::Git::Commit) ) - end + context 'with local and remote branches' do + let(:repository) do + Gitlab::Git::Repository.new('default', File.join(TEST_MUTABLE_REPO_PATH, '.git')) + end - context 'while applying a sort order based on the `order` option' do - it "allows ordering topologically (no parents shown before their children)" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO) + before do + create_remote_branch(repository, 'joe', 'remote_branch', 'master') + repository.create_branch('local_branch', 'master') + end - repository.find_commits(order: :topo) + after do + FileUtils.rm_rf(TEST_MUTABLE_REPO_PATH) + ensure_seeds end - it "allows ordering by date" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE | Rugged::SORT_TOPO) + it 'returns the local and remote branches' do + expect(subject.any? { |b| b.name == 'joe/remote_branch' }).to eq(true) + expect(subject.any? { |b| b.name == 'local_branch' }).to eq(true) + end + end - repository.find_commits(order: :date) + # With Gitaly enabled, Gitaly just doesn't return deleted branches. + context 'with deleted branch with Gitaly disabled' do + before do + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) end - it "applies no sorting by default" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE) + it 'returns no results' do + ref = double() + allow(ref).to receive(:name) { 'bad-branch' } + allow(ref).to receive(:target) { raise Rugged::ReferenceError } + branches = double() + allow(branches).to receive(:each) { [ref].each } + allow(repository.rugged).to receive(:branches) { branches } - repository.find_commits + expect(subject).to be_empty end end - end - - describe '#branches with deleted branch' do - before(:each) do - ref = double() - allow(ref).to receive(:name) { 'bad-branch' } - allow(ref).to receive(:target) { raise Rugged::ReferenceError } - branches = double() - allow(branches).to receive(:each) { [ref].each } - allow(repository.rugged).to receive(:branches) { branches } - end - it 'should return empty branches' do - expect(repository.branches).to eq([]) - end + it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :branches end describe '#branch_count' do @@ -1289,45 +1100,36 @@ describe Gitlab::Git::Repository, seed_helper: true do end it 'returns the local branches' do - create_remote_branch('joe', 'remote_branch', 'master') + create_remote_branch(@repo, 'joe', 'remote_branch', 'master') @repo.create_branch('local_branch', 'master') expect(@repo.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false) expect(@repo.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true) end - context 'with gitaly enabled' do - before do - stub_gitaly - end - - after do - Gitlab::GitalyClient.clear_stubs! - end - - it 'gets the branches from GitalyClient' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches) - .and_return([]) - @repo.local_branches + it 'returns a Branch with UTF-8 fields' do + branches = @repo.local_branches.to_a + expect(branches.size).to be > 0 + branches.each do |branch| + expect(branch.name).to be_utf8 + expect(branch.target).to be_utf8 unless branch.target.nil? end + end - it 'wraps GRPC not found' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches) - .and_raise(GRPC::NotFound) - expect { @repo.local_branches }.to raise_error(Gitlab::Git::Repository::NoRepository) - end + it 'gets the branches from GitalyClient' do + expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:local_branches) + .and_return([]) + @repo.local_branches + end - it 'wraps GRPC exceptions' do - expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches) - .and_raise(GRPC::Unknown) - expect { @repo.local_branches }.to raise_error(Gitlab::Git::CommandError) - end + it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :local_branches do + subject { @repo.local_branches } end end - def create_remote_branch(remote_name, branch_name, source_branch_name) - source_branch = @repo.branches.find { |branch| branch.name == source_branch_name } - rugged = @repo.rugged + def create_remote_branch(repository, remote_name, branch_name, source_branch_name) + source_branch = repository.branches.find { |branch| branch.name == source_branch_name } + rugged = repository.rugged rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha) end @@ -1400,11 +1202,4 @@ describe Gitlab::Git::Repository, seed_helper: true do sha = Rugged::Commit.create(repo, options) repo.lookup(sha) end - - def stub_gitaly - allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(true) - - stub = double(:stub) - allow(Gitaly::Ref::Stub).to receive(:new).and_return(stub) - end end |