4f2cdb51df
This makes grouping on version number available. In general I'd like to group based on MAJOR.MINOR version numbers for Gitaly.
538 lines
15 KiB
Ruby
538 lines
15 KiB
Ruby
require 'spec_helper'
|
|
|
|
describe CommitStatus do
|
|
set(:project) { create(:project, :repository) }
|
|
|
|
set(:pipeline) do
|
|
create(:ci_pipeline, project: project, sha: project.commit.id)
|
|
end
|
|
|
|
let(:commit_status) { create_status(stage: 'test') }
|
|
|
|
def create_status(**opts)
|
|
create(:commit_status, pipeline: pipeline, **opts)
|
|
end
|
|
|
|
it { is_expected.to belong_to(:pipeline) }
|
|
it { is_expected.to belong_to(:user) }
|
|
it { is_expected.to belong_to(:project) }
|
|
it { is_expected.to belong_to(:auto_canceled_by) }
|
|
|
|
it { is_expected.to validate_presence_of(:name) }
|
|
it { is_expected.to validate_inclusion_of(:status).in_array(%w(pending running failed success canceled)) }
|
|
|
|
it { is_expected.to delegate_method(:sha).to(:pipeline) }
|
|
it { is_expected.to delegate_method(:short_sha).to(:pipeline) }
|
|
|
|
it { is_expected.to respond_to :success? }
|
|
it { is_expected.to respond_to :failed? }
|
|
it { is_expected.to respond_to :running? }
|
|
it { is_expected.to respond_to :pending? }
|
|
|
|
describe '#author' do
|
|
subject { commit_status.author }
|
|
|
|
before do
|
|
commit_status.author = User.new
|
|
end
|
|
|
|
it { is_expected.to eq(commit_status.user) }
|
|
end
|
|
|
|
describe 'status state machine' do
|
|
let!(:commit_status) { create(:commit_status, :running, project: project) }
|
|
|
|
it 'invalidates the cache after a transition' do
|
|
expect(ExpireJobCacheWorker).to receive(:perform_async).with(commit_status.id)
|
|
|
|
commit_status.success!
|
|
end
|
|
end
|
|
|
|
describe '#started?' do
|
|
subject { commit_status.started? }
|
|
|
|
context 'without started_at' do
|
|
before do
|
|
commit_status.started_at = nil
|
|
end
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
|
|
%w[running success failed].each do |status|
|
|
context "if commit status is #{status}" do
|
|
before do
|
|
commit_status.status = status
|
|
end
|
|
|
|
it { is_expected.to be_truthy }
|
|
end
|
|
end
|
|
|
|
%w[pending canceled].each do |status|
|
|
context "if commit status is #{status}" do
|
|
before do
|
|
commit_status.status = status
|
|
end
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#active?' do
|
|
subject { commit_status.active? }
|
|
|
|
%w[pending running].each do |state|
|
|
context "if commit_status.status is #{state}" do
|
|
before do
|
|
commit_status.status = state
|
|
end
|
|
|
|
it { is_expected.to be_truthy }
|
|
end
|
|
end
|
|
|
|
%w[success failed canceled].each do |state|
|
|
context "if commit_status.status is #{state}" do
|
|
before do
|
|
commit_status.status = state
|
|
end
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#complete?' do
|
|
subject { commit_status.complete? }
|
|
|
|
%w[success failed canceled].each do |state|
|
|
context "if commit_status.status is #{state}" do
|
|
before do
|
|
commit_status.status = state
|
|
end
|
|
|
|
it { is_expected.to be_truthy }
|
|
end
|
|
end
|
|
|
|
%w[pending running].each do |state|
|
|
context "if commit_status.status is #{state}" do
|
|
before do
|
|
commit_status.status = state
|
|
end
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#auto_canceled?' do
|
|
subject { commit_status.auto_canceled? }
|
|
|
|
context 'when it is canceled' do
|
|
before do
|
|
commit_status.update(status: 'canceled')
|
|
end
|
|
|
|
context 'when there is auto_canceled_by' do
|
|
before do
|
|
commit_status.update(auto_canceled_by: create(:ci_empty_pipeline))
|
|
end
|
|
|
|
it 'is auto canceled' do
|
|
is_expected.to be_truthy
|
|
end
|
|
end
|
|
|
|
context 'when there is no auto_canceled_by' do
|
|
it 'is not auto canceled' do
|
|
is_expected.to be_falsey
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#duration' do
|
|
subject { commit_status.duration }
|
|
|
|
it { is_expected.to eq(120.0) }
|
|
|
|
context 'if the building process has not started yet' do
|
|
before do
|
|
commit_status.started_at = nil
|
|
commit_status.finished_at = nil
|
|
end
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
|
|
context 'if the building process has started' do
|
|
before do
|
|
commit_status.started_at = Time.now - 1.minute
|
|
commit_status.finished_at = nil
|
|
end
|
|
|
|
it { is_expected.to be_a(Float) }
|
|
it { is_expected.to be > 0.0 }
|
|
end
|
|
end
|
|
|
|
describe '.latest' do
|
|
subject { described_class.latest.order(:id) }
|
|
|
|
let(:statuses) do
|
|
[create_status(name: 'aa', ref: 'bb', status: 'running', retried: true),
|
|
create_status(name: 'cc', ref: 'cc', status: 'pending', retried: true),
|
|
create_status(name: 'aa', ref: 'cc', status: 'success', retried: true),
|
|
create_status(name: 'cc', ref: 'bb', status: 'success'),
|
|
create_status(name: 'aa', ref: 'bb', status: 'success')]
|
|
end
|
|
|
|
it 'returns unique statuses' do
|
|
is_expected.to eq(statuses.values_at(3, 4))
|
|
end
|
|
end
|
|
|
|
describe '.retried' do
|
|
subject { described_class.retried.order(:id) }
|
|
|
|
let(:statuses) do
|
|
[create_status(name: 'aa', ref: 'bb', status: 'running', retried: true),
|
|
create_status(name: 'cc', ref: 'cc', status: 'pending', retried: true),
|
|
create_status(name: 'aa', ref: 'cc', status: 'success', retried: true),
|
|
create_status(name: 'cc', ref: 'bb', status: 'success'),
|
|
create_status(name: 'aa', ref: 'bb', status: 'success')]
|
|
end
|
|
|
|
it 'returns unique statuses' do
|
|
is_expected.to contain_exactly(*statuses.values_at(0, 1, 2))
|
|
end
|
|
end
|
|
|
|
describe '.running_or_pending' do
|
|
subject { described_class.running_or_pending.order(:id) }
|
|
|
|
let(:statuses) do
|
|
[create_status(name: 'aa', ref: 'bb', status: 'running'),
|
|
create_status(name: 'cc', ref: 'cc', status: 'pending'),
|
|
create_status(name: 'aa', ref: nil, status: 'success'),
|
|
create_status(name: 'dd', ref: nil, status: 'failed'),
|
|
create_status(name: 'ee', ref: nil, status: 'canceled')]
|
|
end
|
|
|
|
it 'returns statuses that are running or pending' do
|
|
is_expected.to contain_exactly(*statuses.values_at(0, 1))
|
|
end
|
|
end
|
|
|
|
describe '.after_stage' do
|
|
subject { described_class.after_stage(0) }
|
|
|
|
let(:statuses) do
|
|
[create_status(name: 'aa', stage_idx: 0),
|
|
create_status(name: 'cc', stage_idx: 1),
|
|
create_status(name: 'aa', stage_idx: 2)]
|
|
end
|
|
|
|
it 'returns statuses from second and third stage' do
|
|
is_expected.to eq(statuses.values_at(1, 2))
|
|
end
|
|
end
|
|
|
|
describe '.exclude_ignored' do
|
|
subject { described_class.exclude_ignored.order(:id) }
|
|
|
|
let(:statuses) do
|
|
[create_status(when: 'manual', status: 'skipped'),
|
|
create_status(when: 'manual', status: 'success'),
|
|
create_status(when: 'manual', status: 'failed'),
|
|
create_status(when: 'on_failure', status: 'skipped'),
|
|
create_status(when: 'on_failure', status: 'success'),
|
|
create_status(when: 'on_failure', status: 'failed'),
|
|
create_status(allow_failure: true, status: 'success'),
|
|
create_status(allow_failure: true, status: 'failed'),
|
|
create_status(allow_failure: false, status: 'success'),
|
|
create_status(allow_failure: false, status: 'failed'),
|
|
create_status(allow_failure: true, status: 'manual'),
|
|
create_status(allow_failure: false, status: 'manual')]
|
|
end
|
|
|
|
it 'returns statuses without what we want to ignore' do
|
|
is_expected.to eq(statuses.values_at(0, 1, 2, 3, 4, 5, 6, 8, 9, 11))
|
|
end
|
|
end
|
|
|
|
describe '.failed_but_allowed' do
|
|
subject { described_class.failed_but_allowed.order(:id) }
|
|
|
|
let(:statuses) do
|
|
[create_status(allow_failure: true, status: 'success'),
|
|
create_status(allow_failure: true, status: 'failed'),
|
|
create_status(allow_failure: false, status: 'success'),
|
|
create_status(allow_failure: false, status: 'failed'),
|
|
create_status(allow_failure: true, status: 'canceled'),
|
|
create_status(allow_failure: false, status: 'canceled'),
|
|
create_status(allow_failure: true, status: 'manual'),
|
|
create_status(allow_failure: false, status: 'manual')]
|
|
end
|
|
|
|
it 'returns statuses without what we want to ignore' do
|
|
is_expected.to eq(statuses.values_at(1, 4))
|
|
end
|
|
end
|
|
|
|
describe '.status' do
|
|
context 'when there are multiple statuses present' do
|
|
before do
|
|
create_status(status: 'running')
|
|
create_status(status: 'success')
|
|
create_status(allow_failure: true, status: 'failed')
|
|
end
|
|
|
|
it 'returns a correct compound status' do
|
|
expect(described_class.all.status).to eq 'running'
|
|
end
|
|
end
|
|
|
|
context 'when there are only allowed to fail commit statuses present' do
|
|
before do
|
|
create_status(allow_failure: true, status: 'failed')
|
|
end
|
|
|
|
it 'returns status that indicates success' do
|
|
expect(described_class.all.status).to eq 'success'
|
|
end
|
|
end
|
|
|
|
context 'when using a scope to select latest statuses' do
|
|
before do
|
|
create_status(name: 'test', retried: true, status: 'failed')
|
|
create_status(allow_failure: true, name: 'test', status: 'failed')
|
|
end
|
|
|
|
it 'returns status according to the scope' do
|
|
expect(described_class.latest.status).to eq 'success'
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#before_sha' do
|
|
subject { commit_status.before_sha }
|
|
|
|
context 'when no before_sha is set for pipeline' do
|
|
before do
|
|
pipeline.before_sha = nil
|
|
end
|
|
|
|
it 'returns blank sha' do
|
|
is_expected.to eq(Gitlab::Git::BLANK_SHA)
|
|
end
|
|
end
|
|
|
|
context 'for before_sha set for pipeline' do
|
|
let(:value) { '1234' }
|
|
|
|
before do
|
|
pipeline.before_sha = value
|
|
end
|
|
|
|
it 'returns the set value' do
|
|
is_expected.to eq(value)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#commit' do
|
|
it 'returns commit pipeline has been created for' do
|
|
expect(commit_status.commit).to eq project.commit
|
|
end
|
|
end
|
|
|
|
describe '#group_name' do
|
|
subject { commit_status.group_name }
|
|
|
|
tests = {
|
|
'rspec:windows' => 'rspec:windows',
|
|
'rspec:windows 0' => 'rspec:windows 0',
|
|
'rspec:windows 0 test' => 'rspec:windows 0 test',
|
|
'rspec:windows 0 1' => 'rspec:windows',
|
|
'rspec:windows 0 1 name' => 'rspec:windows name',
|
|
'rspec:windows 0/1' => 'rspec:windows',
|
|
'rspec:windows 0/1 name' => 'rspec:windows name',
|
|
'rspec:windows 0:1' => 'rspec:windows',
|
|
'rspec:windows 0:1 name' => 'rspec:windows name',
|
|
'rspec:windows 10000 20000' => 'rspec:windows',
|
|
'rspec:windows 0 : / 1' => 'rspec:windows',
|
|
'rspec:windows 0 : / 1 name' => 'rspec:windows name',
|
|
'0 1 name ruby' => 'name ruby',
|
|
'0 :/ 1 name ruby' => 'name ruby',
|
|
'golang test 1.8' => 'golang test',
|
|
'1.9 golang test' => 'golang test'
|
|
}
|
|
|
|
tests.each do |name, group_name|
|
|
it "'#{name}' puts in '#{group_name}'" do
|
|
commit_status.name = name
|
|
|
|
is_expected.to eq(group_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#detailed_status' do
|
|
let(:user) { create(:user) }
|
|
|
|
it 'returns a detailed status' do
|
|
expect(commit_status.detailed_status(user))
|
|
.to be_a Gitlab::Ci::Status::Success
|
|
end
|
|
end
|
|
|
|
describe '#sortable_name' do
|
|
tests = {
|
|
'karma' => ['karma'],
|
|
'karma 0 20' => ['karma ', 0, ' ', 20],
|
|
'karma 10 20' => ['karma ', 10, ' ', 20],
|
|
'karma 50:100' => ['karma ', 50, ':', 100],
|
|
'karma 1.10' => ['karma ', 1, '.', 10],
|
|
'karma 1.5.1' => ['karma ', 1, '.', 5, '.', 1],
|
|
'karma 1 a' => ['karma ', 1, ' a']
|
|
}
|
|
|
|
tests.each do |name, sortable_name|
|
|
it "'#{name}' sorts as '#{sortable_name}'" do
|
|
commit_status.name = name
|
|
expect(commit_status.sortable_name).to eq(sortable_name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#locking_enabled?' do
|
|
before do
|
|
commit_status.lock_version = 100
|
|
end
|
|
|
|
subject { commit_status.locking_enabled? }
|
|
|
|
context "when changing status" do
|
|
before do
|
|
commit_status.status = "running"
|
|
end
|
|
|
|
it "lock" do
|
|
is_expected.to be true
|
|
end
|
|
|
|
it "raise exception when trying to update" do
|
|
expect { commit_status.save }.to raise_error(ActiveRecord::StaleObjectError)
|
|
end
|
|
end
|
|
|
|
context "when changing description" do
|
|
before do
|
|
commit_status.description = "test"
|
|
end
|
|
|
|
it "do not lock" do
|
|
is_expected.to be false
|
|
end
|
|
|
|
it "save correctly" do
|
|
expect(commit_status.save).to be true
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'set failure_reason when drop' do
|
|
let(:commit_status) { create(:commit_status, :created) }
|
|
|
|
subject do
|
|
commit_status.drop!(reason)
|
|
commit_status
|
|
end
|
|
|
|
context 'when failure_reason is nil' do
|
|
let(:reason) { }
|
|
|
|
it { is_expected.to be_unknown_failure }
|
|
end
|
|
|
|
context 'when failure_reason is script_failure' do
|
|
let(:reason) { :script_failure }
|
|
|
|
it { is_expected.to be_script_failure }
|
|
end
|
|
end
|
|
|
|
describe 'ensure stage assignment' do
|
|
context 'when commit status has a stage_id assigned' do
|
|
let!(:stage) do
|
|
create(:ci_stage_entity, project: project, pipeline: pipeline)
|
|
end
|
|
|
|
let(:commit_status) do
|
|
create(:commit_status, stage_id: stage.id, name: 'rspec', stage: 'test')
|
|
end
|
|
|
|
it 'does not create a new stage' do
|
|
expect { commit_status }.not_to change { Ci::Stage.count }
|
|
expect(commit_status.stage_id).to eq stage.id
|
|
end
|
|
end
|
|
|
|
context 'when commit status does not have a stage_id assigned' do
|
|
let(:commit_status) do
|
|
create(:commit_status, name: 'rspec', stage: 'test', status: :success)
|
|
end
|
|
|
|
let(:stage) { Ci::Stage.first }
|
|
|
|
it 'creates a new stage' do
|
|
expect { commit_status }.to change { Ci::Stage.count }.by(1)
|
|
|
|
expect(stage.name).to eq 'test'
|
|
expect(stage.project).to eq commit_status.project
|
|
expect(stage.pipeline).to eq commit_status.pipeline
|
|
expect(stage.status).to eq commit_status.status
|
|
expect(commit_status.stage_id).to eq stage.id
|
|
end
|
|
end
|
|
|
|
context 'when commit status does not have stage but it exists' do
|
|
let!(:stage) do
|
|
create(:ci_stage_entity, project: project,
|
|
pipeline: pipeline,
|
|
name: 'test')
|
|
end
|
|
|
|
let(:commit_status) do
|
|
create(:commit_status, project: project,
|
|
pipeline: pipeline,
|
|
name: 'rspec',
|
|
stage: 'test',
|
|
status: :success)
|
|
end
|
|
|
|
it 'uses existing stage' do
|
|
expect { commit_status }.not_to change { Ci::Stage.count }
|
|
|
|
expect(commit_status.stage_id).to eq stage.id
|
|
expect(stage.reload.status).to eq commit_status.status
|
|
end
|
|
end
|
|
|
|
context 'when commit status is being imported' do
|
|
let(:commit_status) do
|
|
create(:commit_status, name: 'rspec', stage: 'test', importing: true)
|
|
end
|
|
|
|
it 'does not create a new stage' do
|
|
expect { commit_status }.not_to change { Ci::Stage.count }
|
|
expect(commit_status.stage_id).not_to be_present
|
|
end
|
|
end
|
|
end
|
|
end
|