2020-01-01 13:55:28 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
require "spec_helper"
|
|
|
|
|
2023-07-09 08:55:56 +05:30
|
|
|
RSpec.describe Gitlab::Git::Commit, feature_category: :source_code_management do
|
2022-10-11 01:57:18 +05:30
|
|
|
let(:repository) { create(:project, :repository).repository.raw }
|
2018-12-05 23:21:45 +05:30
|
|
|
let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
describe "Commit info from gitaly commit" do
|
2020-01-01 13:55:28 +05:30
|
|
|
let(:subject) { (+"My commit").force_encoding('ASCII-8BIT') }
|
|
|
|
let(:body) { subject + (+"My body").force_encoding('ASCII-8BIT') }
|
2018-11-08 19:23:39 +05:30
|
|
|
let(:body_size) { body.length }
|
|
|
|
let(:gitaly_commit) { build(:gitaly_commit, subject: subject, body: body, body_size: body_size) }
|
2018-03-17 18:26:18 +05:30
|
|
|
let(:id) { gitaly_commit.id }
|
|
|
|
let(:committer) { gitaly_commit.committer }
|
|
|
|
let(:author) { gitaly_commit.author }
|
2017-09-10 17:25:29 +05:30
|
|
|
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) }
|
2020-01-01 13:55:28 +05:30
|
|
|
it { expect(commit.created_at).to eq(Time.at(committer.date.seconds).utc) }
|
2017-09-10 17:25:29 +05:30
|
|
|
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) }
|
2018-03-17 18:26:18 +05:30
|
|
|
it { expect(commit.parent_ids).to eq(gitaly_commit.parent_ids) }
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
context 'non-UTC dates' do
|
|
|
|
let(:seconds) { Time.now.to_i }
|
|
|
|
|
|
|
|
it 'sets timezones correctly' do
|
|
|
|
gitaly_commit.author.date.seconds = seconds
|
|
|
|
gitaly_commit.author.timezone = '-0800'
|
|
|
|
gitaly_commit.committer.date.seconds = seconds
|
|
|
|
gitaly_commit.committer.timezone = '+0800'
|
|
|
|
|
|
|
|
expect(commit.authored_date).to eq(Time.at(seconds, in: '-08:00'))
|
|
|
|
expect(commit.committed_date).to eq(Time.at(seconds, in: '+08:00'))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
context 'body_size != body.size' do
|
2020-01-01 13:55:28 +05:30
|
|
|
let(:body) { (+"").force_encoding('ASCII-8BIT') }
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
context 'zero body_size' do
|
|
|
|
it { expect(commit.safe_message).to eq(subject) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'body_size less than threshold' do
|
|
|
|
let(:body_size) { 123 }
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it 'fetches commit message separately' do
|
2018-11-08 19:23:39 +05:30
|
|
|
expect(described_class).to receive(:get_message).with(repository, id)
|
|
|
|
|
|
|
|
commit.safe_message
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'body_size greater than threshold' do
|
|
|
|
let(:body_size) { described_class::MAX_COMMIT_MESSAGE_DISPLAY_SIZE + 1 }
|
|
|
|
|
2023-07-09 08:55:56 +05:30
|
|
|
it 'returns the subject plus a notice about message size' do
|
2018-11-08 19:23:39 +05:30
|
|
|
expect(commit.safe_message).to eq("My commit\n\n--commit message is too big")
|
|
|
|
end
|
|
|
|
end
|
2023-07-09 08:55:56 +05:30
|
|
|
|
|
|
|
context "large commit message" do
|
|
|
|
let(:user) { create(:user) }
|
|
|
|
let(:sha) { create_commit_with_large_message }
|
|
|
|
let(:commit) { repository.commit(sha) }
|
|
|
|
|
|
|
|
def create_commit_with_large_message
|
|
|
|
repository.commit_files(
|
|
|
|
user,
|
|
|
|
branch_name: 'HEAD',
|
|
|
|
message: "Repeat " * 10 * 1024,
|
|
|
|
actions: []
|
|
|
|
).newrev
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns a String' do
|
|
|
|
# When #message is called, its encoding is forced from
|
|
|
|
# ASCII-8BIT to UTF-8, and the method returns a
|
|
|
|
# string. Calling #message again may cause BatchLoader to
|
|
|
|
# return since the encoding has been modified to UTF-8, and
|
|
|
|
# the encoding helper will return the original object unmodified.
|
|
|
|
#
|
|
|
|
# To ensure #fetch_body_from_gitaly returns a String, invoke
|
|
|
|
# #to_s. In the test below, do a strict type check to ensure
|
|
|
|
# that a String is always returned. Note that the Rspec
|
|
|
|
# matcher be_instance_of(String) appears to evaluate the
|
|
|
|
# BatchLoader result, so we have to do a strict comparison
|
|
|
|
# here.
|
|
|
|
2.times { expect(String === commit.message).to be true }
|
|
|
|
end
|
|
|
|
end
|
2017-09-10 17:25:29 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
context 'Class methods' do
|
2019-05-03 19:53:19 +05:30
|
|
|
shared_examples '.find' do
|
2019-07-07 11:18:12 +05:30
|
|
|
it "returns first head commit if without params" do
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(described_class.last(repository).id).to eq(
|
2022-08-13 15:12:31 +05:30
|
|
|
repository.commit.sha
|
2017-08-17 22:00:37 +05:30
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
it "returns valid commit" do
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(described_class.find(repository, SeedRepo::Commit::ID)).to be_valid_commit
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it "returns an array of parent ids" do
|
|
|
|
expect(described_class.find(repository, SeedRepo::Commit::ID).parent_ids).to be_an(Array)
|
|
|
|
end
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
it "returns valid commit for tag" do
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(described_class.find(repository, 'v1.0.0').id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
it "returns nil for non-commit ids" do
|
2017-08-17 22:00:37 +05:30
|
|
|
blob = Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb")
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(described_class.find(repository, blob.id)).to be_nil
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
it "returns nil for parent of non-commit object" do
|
2017-08-17 22:00:37 +05:30
|
|
|
blob = Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb")
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(described_class.find(repository, "#{blob.id}^")).to be_nil
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
it "returns nil for nonexisting ids" do
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(described_class.find(repository, "+123_4532530XYZ")).to be_nil
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
it "returns nil for id started with dash" do
|
|
|
|
expect(described_class.find(repository, "-HEAD")).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns nil for id containing colon" do
|
|
|
|
expect(described_class.find(repository, "HEAD:")).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns nil for id containing space" do
|
|
|
|
expect(described_class.find(repository, "HE AD")).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns nil for id containing tab" do
|
|
|
|
expect(described_class.find(repository, "HE\tAD")).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns nil for id containing NULL" do
|
|
|
|
expect(described_class.find(repository, "HE\x00AD")).to be_nil
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2019-05-03 19:53:19 +05:30
|
|
|
describe '.find with Gitaly enabled' do
|
2020-11-24 15:15:51 +05:30
|
|
|
it_behaves_like '.find'
|
2019-05-03 19:53:19 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
describe '.find with Rugged enabled', :enable_rugged do
|
|
|
|
it 'calls out to the Rugged implementation' do
|
2020-01-01 13:55:28 +05:30
|
|
|
allow_next_instance_of(Rugged) do |instance|
|
|
|
|
allow(instance).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
|
|
|
|
end
|
2019-05-03 19:53:19 +05:30
|
|
|
|
|
|
|
described_class.find(repository, SeedRepo::Commit::ID)
|
|
|
|
end
|
|
|
|
|
2020-11-24 15:15:51 +05:30
|
|
|
it_behaves_like '.find'
|
2019-05-03 19:53:19 +05:30
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
describe '.last_for_path' do
|
|
|
|
context 'no path' do
|
2017-09-10 17:25:29 +05:30
|
|
|
subject { described_class.last_for_path(repository, 'master') }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
describe '#id' do
|
|
|
|
subject { super().id }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
it { is_expected.to eq(TestEnv::BRANCH_SHA['master']) }
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'path' do
|
2017-09-10 17:25:29 +05:30
|
|
|
subject { described_class.last_for_path(repository, 'master', 'files/ruby') }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
describe '#id' do
|
|
|
|
subject { super().id }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
it { is_expected.to eq(SeedRepo::Commit::ID) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
context 'pathspec' do
|
|
|
|
let(:pathspec) { 'files/ruby/*' }
|
|
|
|
|
|
|
|
context 'with default literal_pathspec value' do
|
|
|
|
it 'finds the seed commit' do
|
|
|
|
commit = described_class.last_for_path(repository, 'master', pathspec)
|
|
|
|
|
|
|
|
expect(commit.id).to eq(SeedRepo::Commit::ID)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with literal_pathspec set to false' do
|
|
|
|
it 'finds the seed commit' do
|
|
|
|
commit = described_class.last_for_path(repository, 'master', pathspec, literal_pathspec: false)
|
|
|
|
|
|
|
|
expect(commit.id).to eq(SeedRepo::Commit::ID)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with literal_pathspec set to true' do
|
|
|
|
it 'does not find the seed commit' do
|
|
|
|
commit = described_class.last_for_path(repository, 'master', pathspec, literal_pathspec: true)
|
|
|
|
|
|
|
|
expect(commit).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
context 'ref + path' do
|
2017-09-10 17:25:29 +05:30
|
|
|
subject { described_class.last_for_path(repository, SeedRepo::Commit::ID, 'encoding') }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
describe '#id' do
|
|
|
|
subject { super().id }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
it { is_expected.to eq(SeedRepo::BigCommit::ID) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
context 'path is empty string' do
|
|
|
|
subject do
|
|
|
|
commits = described_class.where(
|
|
|
|
repo: repository,
|
|
|
|
ref: 'master',
|
|
|
|
path: '',
|
|
|
|
limit: 10
|
|
|
|
)
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
commits.map { |c| c.id }
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it 'has 10 elements' do
|
|
|
|
expect(subject.size).to eq(10)
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
2022-08-27 11:52:29 +05:30
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
it { is_expected.to include(TestEnv::BRANCH_SHA['master']) }
|
2019-02-15 15:39:39 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
context 'path is nil' do
|
|
|
|
subject do
|
|
|
|
commits = described_class.where(
|
|
|
|
repo: repository,
|
|
|
|
ref: 'master',
|
|
|
|
path: nil,
|
|
|
|
limit: 10
|
|
|
|
)
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
commits.map { |c| c.id }
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it 'has 10 elements' do
|
|
|
|
expect(subject.size).to eq(10)
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
2022-08-27 11:52:29 +05:30
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
it { is_expected.to include(TestEnv::BRANCH_SHA['master']) }
|
2019-02-15 15:39:39 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
context 'ref is commit id' do
|
|
|
|
subject do
|
|
|
|
commits = described_class.where(
|
|
|
|
repo: repository,
|
|
|
|
ref: "874797c3a73b60d2187ed6e2fcabd289ff75171e",
|
|
|
|
path: 'files',
|
|
|
|
limit: 3,
|
|
|
|
offset: 1
|
|
|
|
)
|
2018-12-23 12:14:25 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
commits.map { |c| c.id }
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it 'has 3 elements' do
|
|
|
|
expect(subject.size).to eq(3)
|
2018-12-23 12:14:25 +05:30
|
|
|
end
|
2022-08-27 11:52:29 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it { is_expected.to include("2f63565e7aac07bcdadb654e253078b727143ec4") }
|
|
|
|
it { is_expected.not_to include(SeedRepo::Commit::ID) }
|
2019-01-03 12:48:30 +05:30
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
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
|
2019-01-03 12:48:30 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it 'has 3 elements' do
|
|
|
|
expect(subject.size).to eq(3)
|
|
|
|
end
|
2022-08-27 11:52:29 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it { is_expected.to include("874797c3a73b60d2187ed6e2fcabd289ff75171e") }
|
|
|
|
it { is_expected.not_to include(SeedRepo::Commit::ID) }
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
describe '.between' do
|
2021-11-11 11:23:49 +05:30
|
|
|
let(:limit) { nil }
|
|
|
|
let(:commit_ids) { commits.map(&:id) }
|
|
|
|
|
|
|
|
subject(:commits) { described_class.between(repository, from, to, limit: limit) }
|
|
|
|
|
|
|
|
context 'requesting a single commit' do
|
|
|
|
let(:from) { SeedRepo::Commit::PARENT_ID }
|
|
|
|
let(:to) { SeedRepo::Commit::ID }
|
|
|
|
|
|
|
|
it { expect(commit_ids).to contain_exactly(to) }
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
context 'requesting a commit range' do
|
|
|
|
let(:from) { 'v1.0.0' }
|
2022-10-11 01:57:18 +05:30
|
|
|
let(:to) { 'v1.1.0' }
|
2021-10-27 15:23:28 +05:30
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
let(:commits_in_range) do
|
|
|
|
%w[
|
|
|
|
570e7b2abdd848b95f2f578043fc23bd6f6fd24d
|
|
|
|
5937ac0a7beb003549fc5fd26fc247adbce4a52e
|
|
|
|
]
|
2021-10-27 15:23:28 +05:30
|
|
|
end
|
|
|
|
|
2021-11-11 11:23:49 +05:30
|
|
|
context 'no limit' do
|
|
|
|
it { expect(commit_ids).to eq(commits_in_range) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'limited' do
|
2022-10-11 01:57:18 +05:30
|
|
|
let(:limit) { 1 }
|
2021-11-11 11:23:49 +05:30
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
it { expect(commit_ids).to eq(commits_in_range.last(1)) }
|
2021-11-11 11:23:49 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
describe '.shas_with_signatures' do
|
2018-03-17 18:26:18 +05:30
|
|
|
let(:signed_shas) { %w[5937ac0a7beb003549fc5fd26fc247adbce4a52e 570e7b2abdd848b95f2f578043fc23bd6f6fd24d] }
|
|
|
|
let(:unsigned_shas) { %w[19e2e9b4ef76b422ce1154af39a91323ccc57434 c642fe9b8b9f28f9225d7ea953fe14e74748d53b] }
|
|
|
|
let(:first_signed_shas) { %w[5937ac0a7beb003549fc5fd26fc247adbce4a52e c642fe9b8b9f28f9225d7ea953fe14e74748d53b] }
|
|
|
|
|
|
|
|
it 'has 2 signed shas' do
|
|
|
|
ret = described_class.shas_with_signatures(repository, signed_shas)
|
|
|
|
expect(ret).to eq(signed_shas)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'has 0 signed shas' do
|
|
|
|
ret = described_class.shas_with_signatures(repository, unsigned_shas)
|
|
|
|
expect(ret).to eq([])
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'has 1 signed sha' do
|
|
|
|
ret = described_class.shas_with_signatures(repository, first_signed_shas)
|
|
|
|
expect(ret).to contain_exactly(first_signed_shas.first)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
describe '.find_all' do
|
2019-07-07 11:18:12 +05:30
|
|
|
it 'returns a return a collection of commits' do
|
2018-11-08 19:23:39 +05:30
|
|
|
commits = described_class.find_all(repository)
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
expect(commits).to all( be_a_kind_of(described_class) )
|
|
|
|
end
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
context 'max_count' do
|
|
|
|
subject do
|
|
|
|
commits = described_class.find_all(
|
|
|
|
repository,
|
|
|
|
max_count: 50
|
|
|
|
)
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
commits.map(&:id)
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
it 'has maximum elements' do
|
|
|
|
expect(subject.size).to eq(50)
|
2018-11-08 19:23:39 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
context 'ref + max_count + skip' do
|
|
|
|
subject do
|
|
|
|
commits = described_class.find_all(
|
|
|
|
repository,
|
|
|
|
ref: 'master',
|
|
|
|
max_count: 50,
|
|
|
|
skip: 1
|
|
|
|
)
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
commits.map(&:id)
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
it 'has 36 elements' do
|
|
|
|
expect(subject.size).to eq(36)
|
2018-11-08 19:23:39 +05:30
|
|
|
end
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
it 'includes the expected commits' do
|
|
|
|
expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID)
|
2022-10-11 01:57:18 +05:30
|
|
|
expect(subject).not_to include(TestEnv::BRANCH_SHA['master'])
|
2018-11-08 19:23:39 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2019-05-03 19:53:19 +05:30
|
|
|
shared_examples '.batch_by_oid' do
|
|
|
|
context 'with multiple OIDs' do
|
|
|
|
let(:oids) { [SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID] }
|
|
|
|
|
|
|
|
it 'returns multiple commits' do
|
|
|
|
commits = described_class.batch_by_oid(repository, oids)
|
|
|
|
|
|
|
|
expect(commits.count).to eq(2)
|
|
|
|
expect(commits).to all( be_a(Gitlab::Git::Commit) )
|
|
|
|
expect(commits.first.sha).to eq(SeedRepo::Commit::ID)
|
|
|
|
expect(commits.second.sha).to eq(SeedRepo::FirstCommit::ID)
|
|
|
|
end
|
2022-01-26 12:08:38 +05:30
|
|
|
|
|
|
|
context 'when repo does not exist' do
|
|
|
|
let(:no_repository) { Gitlab::Git::Repository.new('default', '@does-not-exist/project', '', 'bogus/project') }
|
|
|
|
|
|
|
|
it 'returns empty commits' do
|
|
|
|
commits = described_class.batch_by_oid(no_repository, oids)
|
|
|
|
|
|
|
|
expect(commits.count).to eq(0)
|
|
|
|
end
|
|
|
|
end
|
2019-05-03 19:53:19 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when oids is empty' do
|
|
|
|
it 'returns empty commits' do
|
|
|
|
commits = described_class.batch_by_oid(repository, [])
|
|
|
|
|
|
|
|
expect(commits.count).to eq(0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.batch_by_oid with Gitaly enabled' do
|
2020-11-24 15:15:51 +05:30
|
|
|
it_behaves_like '.batch_by_oid'
|
2019-05-03 19:53:19 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
context 'when oids is empty' do
|
|
|
|
it 'makes no Gitaly request' do
|
2019-09-30 21:07:59 +05:30
|
|
|
expect(Gitlab::GitalyClient).not_to receive(:call).with(repository.storage, :commit_service, :list_commits_by_oid)
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
described_class.batch_by_oid(repository, [])
|
2017-09-10 17:25:29 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2019-05-03 19:53:19 +05:30
|
|
|
describe '.batch_by_oid with Rugged enabled', :enable_rugged do
|
2020-11-24 15:15:51 +05:30
|
|
|
it_behaves_like '.batch_by_oid'
|
2019-05-03 19:53:19 +05:30
|
|
|
|
|
|
|
it 'calls out to the Rugged implementation' do
|
2020-01-01 13:55:28 +05:30
|
|
|
allow_next_instance_of(Rugged) do |instance|
|
|
|
|
allow(instance).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
|
|
|
|
end
|
2019-05-03 19:53:19 +05:30
|
|
|
|
|
|
|
described_class.batch_by_oid(repository, [SeedRepo::Commit::ID])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-12-26 22:10:19 +05:30
|
|
|
describe '.extract_signature_lazily' do
|
|
|
|
subject { described_class.extract_signature_lazily(repository, commit_id).itself }
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
context 'when the commit is signed' do
|
|
|
|
let(:commit_id) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' }
|
|
|
|
|
|
|
|
it 'returns signature and signed text' do
|
|
|
|
signature, signed_text = subject
|
|
|
|
|
|
|
|
expected_signature = <<~SIGNATURE
|
|
|
|
-----BEGIN PGP SIGNATURE-----
|
|
|
|
Version: GnuPG/MacGPG2 v2.0.22 (Darwin)
|
|
|
|
Comment: GPGTools - https://gpgtools.org
|
|
|
|
|
|
|
|
iQEcBAABCgAGBQJTDvaZAAoJEGJ8X1ifRn8XfvYIAMuB0yrbTGo1BnOSoDfyrjb0
|
|
|
|
Kw2EyUzvXYL72B63HMdJ+/0tlSDC6zONF3fc+bBD8z+WjQMTbwFNMRbSSy2rKEh+
|
|
|
|
mdRybOP3xBIMGgEph0/kmWln39nmFQBsPRbZBWoU10VfI/ieJdEOgOphszgryRar
|
|
|
|
TyS73dLBGE9y9NIININVaNISet9D9QeXFqc761CGjh4YIghvPpi+YihMWapGka6v
|
|
|
|
hgKhX+hc5rj+7IEE0CXmlbYR8OYvAbAArc5vJD7UTxAY4Z7/l9d6Ydt9GQ25khfy
|
|
|
|
ANFgltYzlR6evLFmDjssiP/mx/ZMN91AL0ueJ9nNGv411Mu2CUW+tDCaQf35mdc=
|
|
|
|
=j51i
|
|
|
|
-----END PGP SIGNATURE-----
|
|
|
|
SIGNATURE
|
|
|
|
|
|
|
|
expect(signature).to eq(expected_signature.chomp)
|
|
|
|
expect(signature).to be_a_binary_string
|
|
|
|
|
|
|
|
expected_signed_text = <<~SIGNED_TEXT
|
|
|
|
tree 22bfa2fbd217df24731f43ff43a4a0f8db759dae
|
|
|
|
parent ae73cb07c9eeaf35924a10f713b364d32b2dd34f
|
|
|
|
author Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> 1393489561 +0200
|
|
|
|
committer Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> 1393489561 +0200
|
|
|
|
|
|
|
|
Feature added
|
|
|
|
|
|
|
|
Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
|
|
|
|
SIGNED_TEXT
|
|
|
|
|
|
|
|
expect(signed_text).to eq(expected_signed_text)
|
|
|
|
expect(signed_text).to be_a_binary_string
|
|
|
|
end
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
context 'when the commit has no signature' do
|
|
|
|
let(:commit_id) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
it 'returns nil' do
|
|
|
|
expect(subject).to be_nil
|
|
|
|
end
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
context 'when the commit cannot be found' do
|
|
|
|
let(:commit_id) { Gitlab::Git::BLANK_SHA }
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
it 'returns nil' do
|
|
|
|
expect(subject).to be_nil
|
|
|
|
end
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
context 'when the commit ID is invalid' do
|
|
|
|
let(:commit_id) { '4b4918a572fa86f9771e5ba40fbd48e' }
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
it 'raises ArgumentError' do
|
|
|
|
expect { subject }.to raise_error(ArgumentError)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2018-03-27 19:54:05 +05:30
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2019-12-26 22:10:19 +05:30
|
|
|
context 'when loading signatures in batch once' do
|
2018-03-27 19:54:05 +05:30
|
|
|
it 'fetches signatures in batch once' do
|
|
|
|
commit_ids = %w[0b4bc9a49b562e85de7cc9e834518ea6828729b9 4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6]
|
|
|
|
signatures = commit_ids.map do |commit_id|
|
|
|
|
described_class.extract_signature_lazily(repository, commit_id)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
other_repository = double(:repository)
|
|
|
|
described_class.extract_signature_lazily(other_repository, commit_ids.first)
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
expect(described_class).to receive(:batch_signature_extraction)
|
|
|
|
.with(repository, commit_ids)
|
|
|
|
.once
|
|
|
|
.and_return({})
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
expect(described_class).not_to receive(:batch_signature_extraction)
|
|
|
|
.with(other_repository, commit_ids.first)
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
2.times { signatures.each(&:itself) }
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2018-03-27 19:54:05 +05:30
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
describe '#init_from_hash' do
|
2017-09-10 17:25:29 +05:30
|
|
|
let(:commit) { described_class.new(repository, sample_commit_hash) }
|
2020-01-01 13:55:28 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
subject { commit }
|
|
|
|
|
|
|
|
describe '#id' do
|
|
|
|
subject { super().id }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2022-08-27 11:52:29 +05:30
|
|
|
it { is_expected.to eq(sample_commit_hash[:id]) }
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
describe '#message' do
|
|
|
|
subject { super().message }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2022-08-27 11:52:29 +05:30
|
|
|
it { is_expected.to eq(sample_commit_hash[:message]) }
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
describe '#stats' do
|
2017-08-17 22:00:37 +05:30
|
|
|
subject { commit.stats }
|
|
|
|
|
|
|
|
describe '#additions' do
|
|
|
|
subject { super().additions }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
it { is_expected.to eq(11) }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#deletions' do
|
|
|
|
subject { super().deletions }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
it { is_expected.to eq(6) }
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
describe '#total' do
|
|
|
|
subject { super().total }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it { is_expected.to eq(17) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
describe '#gitaly_commit?' do
|
|
|
|
context 'when the commit data comes from gitaly' do
|
|
|
|
it { expect(commit.gitaly_commit?).to eq(true) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the commit data comes from a Hash' do
|
|
|
|
let(:commit) { described_class.new(repository, sample_commit_hash) }
|
|
|
|
|
|
|
|
it { expect(commit.gitaly_commit?).to eq(false) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
describe '#has_zero_stats?' do
|
|
|
|
it { expect(commit.has_zero_stats?).to eq(false) }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#to_hash' do
|
|
|
|
let(:hash) { commit.to_hash }
|
2020-01-01 13:55:28 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
subject { hash }
|
|
|
|
|
|
|
|
it { is_expected.to be_kind_of Hash }
|
|
|
|
|
|
|
|
describe '#keys' do
|
|
|
|
subject { super().keys.sort }
|
2019-12-21 20:55:43 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
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
|
2017-09-10 17:25:29 +05:30
|
|
|
let(:commit) { described_class.find(repository, 'master') }
|
2020-01-01 13:55:28 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
subject { commit.ref_names(repository) }
|
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
it 'has 3 elements' do
|
|
|
|
expect(subject.size).to eq(3)
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
2022-08-27 11:52:29 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
it { is_expected.to include("master") }
|
|
|
|
it { is_expected.not_to include("feature") }
|
|
|
|
end
|
|
|
|
|
2021-12-11 22:18:48 +05:30
|
|
|
describe '#first_ref_by_oid' do
|
|
|
|
let(:commit) { described_class.find(repository, 'master') }
|
|
|
|
|
|
|
|
subject { commit.first_ref_by_oid(repository) }
|
|
|
|
|
|
|
|
it { is_expected.to eq("master") }
|
|
|
|
end
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
describe '.get_message' do
|
|
|
|
let(:commit_ids) { %w[6d394385cf567f80a8fd85055db1ab4c5295806f cfe32cf61b73a0d5e9f13e774abde7ff789b1660] }
|
|
|
|
|
|
|
|
subject do
|
|
|
|
commit_ids.map { |id| described_class.get_message(repository, id) }
|
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it 'gets commit messages' do
|
|
|
|
expect(subject).to contain_exactly(
|
|
|
|
"Added contributing guide\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n",
|
|
|
|
"Add submodule\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n"
|
|
|
|
)
|
2018-11-08 19:23:39 +05:30
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it 'gets messages in one batch', :request_store do
|
2022-10-11 01:57:18 +05:30
|
|
|
repository # preload repository so that the project factory does not pollute request counts
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
|
2018-11-08 19:23:39 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
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",
|
2021-03-11 19:13:27 +05:30
|
|
|
parent_ids: ["874797c3a73b60d2187ed6e2fcabd289ff75171e"],
|
2023-05-27 22:25:52 +05:30
|
|
|
trailers: {},
|
|
|
|
referenced_by: []
|
2017-08-17 22:00:37 +05:30
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|