gitlab-org--gitlab-foss/spec/models/ci/pipeline_spec.rb
Lin Jen-Shin 245103e888 Merge remote-tracking branch 'upstream/master' into smart-pipeline-duration
* upstream/master: (289 commits)
  Fix a typo
  Change minimum Unicorns required to two
  Update memory requirements
  Change the inline code to codeblocks for the new features doc guideline
  Update CHANGELOG with 8.11.4 entries.
  removed null return - renamed 'placeTop' to 'placeProfileAvatarsToTop'
  Change widths of content in MR pipeline tab
  Add curve to generic commit status pipeline
  Rubocop syntax 2.3
  Some minor updates for upgrade guides for 8.12.
  Remove inconsistent font weight for sidebar's labels
  Replace play icon font with svg
  Project tools visibility level
  Added todo filter tests
  Fixed project filtering
  Review changes, simplified dropdown init
  Removed select2 from todos feature spec
  Removed inline JS and improved dropdown labels
  Added type and action dropdowns, need to finalize by removing all inline and polishing off the selected dropdown states
  Completed project filter dropdown, still need to move it from inline to ProjectSelect.js (or different)
  ...
2016-09-02 19:33:19 +08:00

453 lines
12 KiB
Ruby

require 'spec_helper'
describe Ci::Pipeline, models: true do
let(:project) { FactoryGirl.create :empty_project }
let(:pipeline) { FactoryGirl.create :ci_empty_pipeline, status: 'created', project: project }
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:statuses) }
it { is_expected.to have_many(:trigger_requests) }
it { is_expected.to have_many(:builds) }
it { is_expected.to validate_presence_of :sha }
it { is_expected.to validate_presence_of :status }
it { is_expected.to respond_to :git_author_name }
it { is_expected.to respond_to :git_author_email }
it { is_expected.to respond_to :short_sha }
it { is_expected.to delegate_method(:stages).to(:statuses) }
describe '#valid_commit_sha' do
context 'commit.sha can not start with 00000000' do
before do
pipeline.sha = '0' * 40
pipeline.valid_commit_sha
end
it('commit errors should not be empty') { expect(pipeline.errors).not_to be_empty }
end
end
describe '#short_sha' do
subject { pipeline.short_sha }
it 'has 8 items' do
expect(subject.size).to eq(8)
end
it { expect(pipeline.sha).to start_with(subject) }
end
describe '#retried' do
subject { pipeline.retried }
before do
@build1 = FactoryGirl.create :ci_build, pipeline: pipeline, name: 'deploy'
@build2 = FactoryGirl.create :ci_build, pipeline: pipeline, name: 'deploy'
end
it 'returns old builds' do
is_expected.to contain_exactly(@build1)
end
end
describe "coverage" do
let(:project) { FactoryGirl.create :empty_project, build_coverage_regex: "/.*/" }
let(:pipeline) { FactoryGirl.create :ci_empty_pipeline, project: project }
it "calculates average when there are two builds with coverage" do
FactoryGirl.create :ci_build, name: "rspec", coverage: 30, pipeline: pipeline
FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, pipeline: pipeline
expect(pipeline.coverage).to eq("35.00")
end
it "calculates average when there are two builds with coverage and one with nil" do
FactoryGirl.create :ci_build, name: "rspec", coverage: 30, pipeline: pipeline
FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, pipeline: pipeline
FactoryGirl.create :ci_build, pipeline: pipeline
expect(pipeline.coverage).to eq("35.00")
end
it "calculates average when there are two builds with coverage and one is retried" do
FactoryGirl.create :ci_build, name: "rspec", coverage: 30, pipeline: pipeline
FactoryGirl.create :ci_build, name: "rubocop", coverage: 30, pipeline: pipeline
FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, pipeline: pipeline
expect(pipeline.coverage).to eq("35.00")
end
it "calculates average when there is one build without coverage" do
FactoryGirl.create :ci_build, pipeline: pipeline
expect(pipeline.coverage).to be_nil
end
end
describe '#retryable?' do
subject { pipeline.retryable? }
context 'no failed builds' do
before do
FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'success'
end
it 'be not retryable' do
is_expected.to be_falsey
end
end
context 'with failed builds' do
before do
FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'running'
FactoryGirl.create :ci_build, name: "rubocop", pipeline: pipeline, status: 'failed'
end
it 'be retryable' do
is_expected.to be_truthy
end
end
end
describe '#stages' do
let(:pipeline2) { FactoryGirl.create :ci_pipeline, project: project }
subject { CommitStatus.where(pipeline: [pipeline, pipeline2]).stages }
before do
FactoryGirl.create :ci_build, pipeline: pipeline2, stage: 'test', stage_idx: 1
FactoryGirl.create :ci_build, pipeline: pipeline, stage: 'build', stage_idx: 0
end
it 'return all stages' do
is_expected.to eq(%w(build test))
end
end
describe 'state machine' do
let(:current) { Time.now.change(usec: 0) }
let(:build) { create_build('build1', current, 10) }
let(:another_build) { create_build('build2', current + 80, 5) }
describe '#duration and #pending_duration' do
before do
pipeline.update(created_at: current)
travel_to(current + 5) do
pipeline.run
pipeline.save
end
travel_to(current + 70) do
build.success
end
travel_to(current + 145) do
another_build.drop
end
pipeline.drop
end
it 'matches sum of builds duration' do
pipeline.reload
expect(pipeline.duration).to eq(70 - 10 + 145 - 85)
expect(pipeline.pending_duration).to eq(5 + 10 + 5)
end
end
describe '#started_at' do
it 'updates on transitioning to running' do
build.run
expect(pipeline.reload.started_at).not_to be_nil
end
it 'does not update on transitioning to success' do
build.success
expect(pipeline.reload.started_at).to be_nil
end
end
describe '#finished_at' do
it 'updates on transitioning to success' do
build.success
expect(pipeline.reload.finished_at).not_to be_nil
end
it 'does not update on transitioning to running' do
build.run
expect(pipeline.reload.finished_at).to be_nil
end
end
def create_build(name, queued_at = current, started_from = 0)
create(:ci_build,
name: name,
pipeline: pipeline,
queued_at: queued_at,
started_at: queued_at + started_from)
end
end
describe '#branch?' do
subject { pipeline.branch? }
context 'is not a tag' do
before do
pipeline.tag = false
end
it 'return true when tag is set to false' do
is_expected.to be_truthy
end
end
context 'is not a tag' do
before do
pipeline.tag = true
end
it 'return false when tag is set to true' do
is_expected.to be_falsey
end
end
end
context 'with non-empty project' do
let(:project) { create(:project) }
let(:pipeline) do
create(:ci_pipeline,
project: project,
ref: project.default_branch,
sha: project.commit.sha)
end
describe '#latest?' do
context 'with latest sha' do
it 'returns true' do
expect(pipeline).to be_latest
end
end
context 'with not latest sha' do
before do
pipeline.update(
sha: project.commit("#{project.default_branch}~1").sha)
end
it 'returns false' do
expect(pipeline).not_to be_latest
end
end
end
end
describe '#manual_actions' do
subject { pipeline.manual_actions }
it 'when none defined' do
is_expected.to be_empty
end
context 'when action defined' do
let!(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy') }
it 'returns one action' do
is_expected.to contain_exactly(manual)
end
context 'there are multiple of the same name' do
let!(:manual2) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy') }
it 'returns latest one' do
is_expected.to contain_exactly(manual2)
end
end
end
end
describe '#has_warnings?' do
subject { pipeline.has_warnings? }
context 'build which is allowed to fail fails' do
before do
create :ci_build, :success, pipeline: pipeline, name: 'rspec'
create :ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop'
end
it 'returns true' do
is_expected.to be_truthy
end
end
context 'build which is allowed to fail succeeds' do
before do
create :ci_build, :success, pipeline: pipeline, name: 'rspec'
create :ci_build, :allowed_to_fail, :success, pipeline: pipeline, name: 'rubocop'
end
it 'returns false' do
is_expected.to be_falsey
end
end
context 'build is retried and succeeds' do
before do
create :ci_build, :success, pipeline: pipeline, name: 'rubocop'
create :ci_build, :failed, pipeline: pipeline, name: 'rspec'
create :ci_build, :success, pipeline: pipeline, name: 'rspec'
end
it 'returns false' do
is_expected.to be_falsey
end
end
end
describe '#status' do
let!(:build) { create(:ci_build, :created, pipeline: pipeline, name: 'test') }
subject { pipeline.reload.status }
context 'on queuing' do
before do
build.enqueue
end
it { is_expected.to eq('pending') }
end
context 'on run' do
before do
build.enqueue
build.run
end
it { is_expected.to eq('running') }
end
context 'on drop' do
before do
build.drop
end
it { is_expected.to eq('failed') }
end
context 'on success' do
before do
build.success
end
it { is_expected.to eq('success') }
end
context 'on cancel' do
before do
build.cancel
end
it { is_expected.to eq('canceled') }
end
context 'on failure and build retry' do
before do
build.drop
Ci::Build.retry(build)
end
# We are changing a state: created > failed > running
# Instead of: created > failed > pending
# Since the pipeline already run, so it should not be pending anymore
it { is_expected.to eq('running') }
end
end
describe '#execute_hooks' do
let!(:build_a) { create_build('a') }
let!(:build_b) { create_build('b') }
let!(:hook) do
create(:project_hook, project: project, pipeline_events: enabled)
end
before do
ProjectWebHookWorker.drain
end
context 'with pipeline hooks enabled' do
let(:enabled) { true }
before do
WebMock.stub_request(:post, hook.url)
end
context 'with multiple builds' do
context 'when build is queued' do
before do
build_a.enqueue
build_b.enqueue
end
it 'receive a pending event once' do
expect(WebMock).to have_requested_pipeline_hook('pending').once
end
end
context 'when build is run' do
before do
build_a.enqueue
build_a.run
build_b.enqueue
build_b.run
end
it 'receive a running event once' do
expect(WebMock).to have_requested_pipeline_hook('running').once
end
end
context 'when all builds succeed' do
before do
build_a.success
build_b.success
end
it 'receive a success event once' do
expect(WebMock).to have_requested_pipeline_hook('success').once
end
end
def have_requested_pipeline_hook(status)
have_requested(:post, hook.url).with do |req|
json_body = JSON.parse(req.body)
json_body['object_attributes']['status'] == status &&
json_body['builds'].length == 2
end
end
end
end
context 'with pipeline hooks disabled' do
let(:enabled) { false }
before do
build_a.enqueue
build_b.enqueue
end
it 'did not execute pipeline_hook after touched' do
expect(WebMock).not_to have_requested(:post, hook.url)
end
end
def create_build(name)
create(:ci_build, :created, pipeline: pipeline, name: name)
end
end
end