require "spec_helper" describe Gitlab::Git::Commit, seed_helper: true do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) } let(:rugged_commit) do repository.rugged.lookup(SeedRepo::Commit::ID) end describe "Commit info" do before do repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged @committer = { email: 'mike@smith.com', name: "Mike Smith", time: Time.now } @author = { email: 'john@smith.com', name: "John Smith", time: Time.now } @parents = [repo.head.target] @gitlab_parents = @parents.map { |c| described_class.decorate(repository, c) } @tree = @parents.first.tree sha = Rugged::Commit.create( repo, author: @author, committer: @committer, tree: @tree, parents: @parents, message: "Refactoring specs", update_ref: "HEAD" ) @raw_commit = repo.lookup(sha) @commit = described_class.new(repository, @raw_commit) end it { expect(@commit.short_id).to eq(@raw_commit.oid[0..10]) } it { expect(@commit.id).to eq(@raw_commit.oid) } it { expect(@commit.sha).to eq(@raw_commit.oid) } it { expect(@commit.safe_message).to eq(@raw_commit.message) } it { expect(@commit.created_at).to eq(@raw_commit.author[:time]) } it { expect(@commit.date).to eq(@raw_commit.committer[:time]) } it { expect(@commit.author_email).to eq(@author[:email]) } it { expect(@commit.author_name).to eq(@author[:name]) } it { expect(@commit.committer_name).to eq(@committer[:name]) } it { expect(@commit.committer_email).to eq(@committer[:email]) } it { expect(@commit.different_committer?).to be_truthy } it { expect(@commit.parents).to eq(@gitlab_parents) } it { expect(@commit.parent_id).to eq(@parents.first.oid) } it { expect(@commit.no_commit_message).to eq("--no commit message") } it { expect(@commit.tree).to eq(@tree) } after do # Erase the new commit so other tests get the original repo repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) end end describe "Commit info from gitaly commit" do let(:id) { 'f00' } let(:parent_ids) { %w(b45 b46) } let(:subject) { "My commit".force_encoding('ASCII-8BIT') } let(:body) { subject + "My body".force_encoding('ASCII-8BIT') } let(:committer) do Gitaly::CommitAuthor.new( name: generate(:name), email: generate(:email), date: Google::Protobuf::Timestamp.new(seconds: 123) ) end let(:author) do Gitaly::CommitAuthor.new( name: generate(:name), email: generate(:email), date: Google::Protobuf::Timestamp.new(seconds: 456) ) end let(:gitaly_commit) do Gitaly::GitCommit.new( id: id, subject: subject, body: body, author: author, committer: committer, parent_ids: parent_ids ) end let(:commit) { described_class.new(repository, gitaly_commit) } it { expect(commit.short_id).to eq(id[0..10]) } it { expect(commit.id).to eq(id) } it { expect(commit.sha).to eq(id) } it { expect(commit.safe_message).to eq(body) } it { expect(commit.created_at).to eq(Time.at(committer.date.seconds)) } it { expect(commit.author_email).to eq(author.email) } it { expect(commit.author_name).to eq(author.name) } it { expect(commit.committer_name).to eq(committer.name) } it { expect(commit.committer_email).to eq(committer.email) } it { expect(commit.parent_ids).to eq(parent_ids) } context 'no body' do let(:body) { "".force_encoding('ASCII-8BIT') } it { expect(commit.safe_message).to eq(subject) } end end context 'Class methods' do describe '.find' do it "should return first head commit if without params" do expect(described_class.last(repository).id).to eq( repository.rugged.head.target.oid ) end it "should return valid commit" do expect(described_class.find(repository, SeedRepo::Commit::ID)).to be_valid_commit end it "should return valid commit for tag" do expect(described_class.find(repository, 'v1.0.0').id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') end it "should return nil for non-commit ids" do blob = Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") expect(described_class.find(repository, blob.id)).to be_nil end it "should return nil for parent of non-commit object" do blob = Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") expect(described_class.find(repository, "#{blob.id}^")).to be_nil end it "should return nil for nonexisting ids" do expect(described_class.find(repository, "+123_4532530XYZ")).to be_nil end context 'with broken repo' do let(:repository) { Gitlab::Git::Repository.new('default', TEST_BROKEN_REPO_PATH, '') } it 'returns nil' do expect(described_class.find(repository, SeedRepo::Commit::ID)).to be_nil end end end describe '.last_for_path' do context 'no path' do subject { described_class.last_for_path(repository, 'master') } describe '#id' do subject { super().id } it { is_expected.to eq(SeedRepo::LastCommit::ID) } end end context 'path' do subject { described_class.last_for_path(repository, 'master', 'files/ruby') } describe '#id' do subject { super().id } it { is_expected.to eq(SeedRepo::Commit::ID) } end end context 'ref + path' do subject { described_class.last_for_path(repository, SeedRepo::Commit::ID, 'encoding') } describe '#id' do subject { super().id } it { is_expected.to eq(SeedRepo::BigCommit::ID) } end end end describe '.where' do context 'path is empty string' do subject do commits = described_class.where( repo: repository, ref: 'master', path: '', limit: 10 ) commits.map { |c| c.id } end it 'has 10 elements' do expect(subject.size).to eq(10) end it { is_expected.to include(SeedRepo::EmptyCommit::ID) } end context 'path is nil' do subject do commits = described_class.where( repo: repository, ref: 'master', path: nil, limit: 10 ) commits.map { |c| c.id } end it 'has 10 elements' do expect(subject.size).to eq(10) end it { is_expected.to include(SeedRepo::EmptyCommit::ID) } end context 'ref is branch name' do subject do commits = described_class.where( repo: repository, ref: 'master', path: 'files', limit: 3, offset: 1 ) commits.map { |c| c.id } end it 'has 3 elements' do expect(subject.size).to eq(3) end it { is_expected.to include("d14d6c0abdd253381df51a723d58691b2ee1ab08") } it { is_expected.not_to include("eb49186cfa5c4338011f5f590fac11bd66c5c631") } end context 'ref is commit id' do subject do commits = described_class.where( repo: repository, ref: "874797c3a73b60d2187ed6e2fcabd289ff75171e", path: 'files', limit: 3, offset: 1 ) commits.map { |c| c.id } end it 'has 3 elements' do expect(subject.size).to eq(3) end it { is_expected.to include("2f63565e7aac07bcdadb654e253078b727143ec4") } it { is_expected.not_to include(SeedRepo::Commit::ID) } end context 'ref is tag' do subject do commits = described_class.where( repo: repository, ref: 'v1.0.0', path: 'files', limit: 3, offset: 1 ) commits.map { |c| c.id } end it 'has 3 elements' do expect(subject.size).to eq(3) end it { is_expected.to include("874797c3a73b60d2187ed6e2fcabd289ff75171e") } it { is_expected.not_to include(SeedRepo::Commit::ID) } end end describe '.between' do subject do commits = described_class.between(repository, SeedRepo::Commit::PARENT_ID, SeedRepo::Commit::ID) commits.map { |c| c.id } end it 'has 1 element' do expect(subject.size).to eq(1) end it { is_expected.to include(SeedRepo::Commit::ID) } it { is_expected.not_to include(SeedRepo::FirstCommit::ID) } end describe '.find_all' do shared_examples 'finding all commits' do it 'should return a return a collection of commits' do commits = described_class.find_all(repository) expect(commits).to all( be_a_kind_of(described_class) ) end context 'max_count' do subject do commits = described_class.find_all( repository, max_count: 50 ) commits.map(&:id) end it 'has 34 elements' do expect(subject.size).to eq(34) end it 'includes the expected commits' do expect(subject).to include( SeedRepo::Commit::ID, SeedRepo::Commit::PARENT_ID, SeedRepo::FirstCommit::ID ) end end context 'ref + max_count + skip' do subject do commits = described_class.find_all( repository, ref: 'master', max_count: 50, skip: 1 ) commits.map(&:id) end it 'has 24 elements' do expect(subject.size).to eq(24) end it 'includes the expected commits' do expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID) expect(subject).not_to include(SeedRepo::LastCommit::ID) end end end context 'when Gitaly find_all_commits feature is enabled' do it_behaves_like 'finding all commits' end context 'when Gitaly find_all_commits feature is disabled', skip_gitaly_mock: true do it_behaves_like 'finding all commits' 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) described_class.find_all(repository, order: :topo) end it "allows ordering by date" do expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE | Rugged::SORT_TOPO) described_class.find_all(repository, order: :date) end it "applies no sorting by default" do expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE) described_class.find_all(repository) end end end end end describe '#init_from_rugged' do let(:gitlab_commit) { described_class.new(repository, rugged_commit) } subject { gitlab_commit } describe '#id' do subject { super().id } it { is_expected.to eq(SeedRepo::Commit::ID) } end end describe '#init_from_hash' do let(:commit) { described_class.new(repository, sample_commit_hash) } subject { commit } describe '#id' do subject { super().id } it { is_expected.to eq(sample_commit_hash[:id])} end describe '#message' do subject { super().message } it { is_expected.to eq(sample_commit_hash[:message])} end end shared_examples '#stats' do subject { commit.stats } describe '#additions' do subject { super().additions } it { is_expected.to eq(11) } end describe '#deletions' do subject { super().deletions } it { is_expected.to eq(6) } end end describe '#stats with gitaly on' do it_should_behave_like '#stats' end describe '#stats with gitaly disabled', skip_gitaly_mock: true do it_should_behave_like '#stats' end describe '#to_diff' do subject { commit.to_diff } it { is_expected.not_to include "From #{SeedRepo::Commit::ID}" } it { is_expected.to include 'diff --git a/files/ruby/popen.rb b/files/ruby/popen.rb'} end describe '#has_zero_stats?' do it { expect(commit.has_zero_stats?).to eq(false) } end describe '#to_patch' do subject { commit.to_patch } it { is_expected.to include "From #{SeedRepo::Commit::ID}" } it { is_expected.to include 'diff --git a/files/ruby/popen.rb b/files/ruby/popen.rb'} end describe '#to_hash' do let(:hash) { commit.to_hash } subject { hash } it { is_expected.to be_kind_of Hash } describe '#keys' do subject { super().keys.sort } it { is_expected.to match(sample_commit_hash.keys.sort) } end end describe '#diffs' do subject { commit.diffs } it { is_expected.to be_kind_of Gitlab::Git::DiffCollection } it { expect(subject.count).to eq(2) } it { expect(subject.first).to be_kind_of Gitlab::Git::Diff } end describe '#ref_names' do let(:commit) { described_class.find(repository, 'master') } subject { commit.ref_names(repository) } it 'has 1 element' do expect(subject.size).to eq(1) end it { is_expected.to include("master") } it { is_expected.not_to include("feature") } end def sample_commit_hash { author_email: "dmitriy.zaporozhets@gmail.com", author_name: "Dmitriy Zaporozhets", authored_date: "2012-02-27 20:51:12 +0200", committed_date: "2012-02-27 20:51:12 +0200", committer_email: "dmitriy.zaporozhets@gmail.com", committer_name: "Dmitriy Zaporozhets", id: SeedRepo::Commit::ID, message: "tree css fixes", parent_ids: ["874797c3a73b60d2187ed6e2fcabd289ff75171e"] } end end