From 914cfbd2f154ed3154a7dc3cee3309713eea786f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 6 Oct 2015 12:01:16 +0200 Subject: Implement Commit Status API --- spec/factories/ci/builds.rb | 1 + spec/factories/commit_statuses.rb | 15 + spec/features/commits_spec.rb | 1 + spec/models/build_spec.rb | 276 +++++++++++++++ spec/models/ci/build_spec.rb | 387 --------------------- spec/models/ci/commit_spec.rb | 57 ++- .../ci/project_services/mail_service_spec.rb | 8 +- spec/models/commit_status_spec.rb | 157 +++++++++ spec/models/generic_commit_status_spec.rb | 39 +++ spec/requests/api/commit_status_spec.rb | 135 +++++++ spec/requests/api/commits_spec.rb | 13 + 11 files changed, 685 insertions(+), 404 deletions(-) create mode 100644 spec/factories/commit_statuses.rb create mode 100644 spec/models/build_spec.rb delete mode 100644 spec/models/ci/build_spec.rb create mode 100644 spec/models/commit_status_spec.rb create mode 100644 spec/models/generic_commit_status_spec.rb create mode 100644 spec/requests/api/commit_status_spec.rb (limited to 'spec') diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 21b582afba4..2fcd70182b9 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -27,6 +27,7 @@ FactoryGirl.define do factory :ci_build, class: Ci::Build do + name 'test' ref 'master' tag false started_at 'Di 29. Okt 09:51:28 CET 2013' diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb new file mode 100644 index 00000000000..52de437052d --- /dev/null +++ b/spec/factories/commit_statuses.rb @@ -0,0 +1,15 @@ +FactoryGirl.define do + factory :commit_status, class: CommitStatus do + started_at 'Di 29. Okt 09:51:28 CET 2013' + finished_at 'Di 29. Okt 09:53:28 CET 2013' + name 'default' + status 'success' + description 'commit status' + commit factory: :ci_commit + + factory :generic_commit_status, class: GenericCommitStatus do + name 'generic' + description 'external commit status' + end + end +end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 5da220859e3..cbb6360069b 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -12,6 +12,7 @@ describe "Commits" do @ci_project = project.ensure_gitlab_ci_project @commit = FactoryGirl.create :ci_commit, gl_project: project, sha: project.commit.sha @build = FactoryGirl.create :ci_build, commit: @commit + @generic_status = FactoryGirl.create :generic_commit_status, commit: @commit end before do diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb new file mode 100644 index 00000000000..d875015b991 --- /dev/null +++ b/spec/models/build_spec.rb @@ -0,0 +1,276 @@ +# == Schema Information +# +# Table name: builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# commit_id :integer +# coverage :float +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# + +require 'spec_helper' + +describe Ci::Build do + let(:project) { FactoryGirl.create :ci_project } + let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let(:build) { FactoryGirl.create :ci_build, commit: commit } + + it { is_expected.to validate_presence_of :ref } + + it { is_expected.to respond_to :trace_html } + + describe :first_pending do + let(:first) { FactoryGirl.create :ci_build, commit: commit, status: 'pending', created_at: Date.yesterday } + let(:second) { FactoryGirl.create :ci_build, commit: commit, status: 'pending' } + before { first; second } + subject { Ci::Build.first_pending } + + it { is_expected.to be_a(Ci::Build) } + it('returns with the first pending build') { is_expected.to eq(first) } + end + + describe :create_from do + before do + build.status = 'success' + build.save + end + let(:create_from_build) { Ci::Build.create_from build } + + it 'there should be a pending task' do + expect(Ci::Build.pending.count(:all)).to eq 0 + create_from_build + expect(Ci::Build.pending.count(:all)).to be > 0 + end + end + + describe :ignored? do + subject { build.ignored? } + + context 'if build is not allowed to fail' do + before { build.allow_failure = false } + + context 'and build.status is success' do + before { build.status = 'success' } + + it { is_expected.to be_falsey } + end + + context 'and build.status is failed' do + before { build.status = 'failed' } + + it { is_expected.to be_falsey } + end + end + + context 'if build is allowed to fail' do + before { build.allow_failure = true } + + context 'and build.status is success' do + before { build.status = 'success' } + + it { is_expected.to be_falsey } + end + + context 'and build.status is failed' do + before { build.status = 'failed' } + + it { is_expected.to be_truthy } + end + end + end + + describe :trace do + subject { build.trace_html } + + it { is_expected.to be_empty } + + context 'if build.trace contains text' do + let(:text) { 'example output' } + before { build.trace = text } + + it { is_expected.to include(text) } + it { expect(subject.length).to be >= text.length } + end + + context 'if build.trace hides token' do + let(:token) { 'my_secret_token' } + + before do + build.project.update_attributes(token: token) + build.update_attributes(trace: token) + end + + it { is_expected.to_not include(token) } + end + end + + describe :timeout do + subject { build.timeout } + + it { is_expected.to eq(commit.project.timeout) } + end + + describe :options do + let(:options) do + { + image: "ruby:2.1", + services: [ + "postgres" + ] + } + end + + subject { build.options } + it { is_expected.to eq(options) } + end + + describe :allow_git_fetch do + subject { build.allow_git_fetch } + + it { is_expected.to eq(project.allow_git_fetch) } + end + + describe :project do + subject { build.project } + + it { is_expected.to eq(commit.project) } + end + + describe :project_id do + subject { build.project_id } + + it { is_expected.to eq(commit.project_id) } + end + + describe :project_name do + subject { build.project_name } + + it { is_expected.to eq(project.name) } + end + + describe :repo_url do + subject { build.repo_url } + + it { is_expected.to eq(project.repo_url_with_auth) } + end + + describe :extract_coverage do + context 'valid content & regex' do + subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') } + + it { is_expected.to eq(98.29) } + end + + context 'valid content & bad regex' do + subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') } + + it { is_expected.to be_nil } + end + + context 'no coverage content & regex' do + subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') } + + it { is_expected.to be_nil } + end + + context 'multiple results in content & regex' do + subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') } + + it { is_expected.to eq(98.29) } + end + end + + describe :variables do + context 'returns variables' do + subject { build.variables } + + let(:variables) do + [ + { key: :DB_NAME, value: 'postgres', public: true } + ] + end + + it { is_expected.to eq(variables) } + + context 'and secure variables' do + let(:secure_variables) do + [ + { key: 'SECRET_KEY', value: 'secret_value', public: false } + ] + end + + before do + build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') + end + + it { is_expected.to eq(variables + secure_variables) } + + context 'and trigger variables' do + let(:trigger) { FactoryGirl.create :ci_trigger, project: project } + let(:trigger_request) { FactoryGirl.create :ci_trigger_request_with_variables, commit: commit, trigger: trigger } + let(:trigger_variables) do + [ + { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false } + ] + end + + before do + build.trigger_request = trigger_request + end + + it { is_expected.to eq(variables + secure_variables + trigger_variables) } + end + end + end + end + + describe :project_recipients do + let(:pusher_email) { 'pusher@gitlab.test' } + let(:user) { User.new(notification_email: pusher_email) } + subject { build.project_recipients } + + before do + build.update_attributes(user: user) + end + + it 'should return pusher_email as only recipient when no additional recipients are given' do + project.update_attributes(email_add_pusher: true, + email_recipients: '') + is_expected.to eq([pusher_email]) + end + + it 'should return pusher_email and additional recipients' do + project.update_attributes(email_add_pusher: true, + email_recipients: 'rec1 rec2') + is_expected.to eq(['rec1', 'rec2', pusher_email]) + end + + it 'should return recipients' do + project.update_attributes(email_add_pusher: false, + email_recipients: 'rec1 rec2') + is_expected.to eq(['rec1', 'rec2']) + end + + it 'should return unique recipients only' do + project.update_attributes(email_add_pusher: true, + email_recipients: "rec1 rec1 #{pusher_email}") + is_expected.to eq(['rec1', pusher_email]) + end + end +end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb deleted file mode 100644 index da56f6e31ae..00000000000 --- a/spec/models/ci/build_spec.rb +++ /dev/null @@ -1,387 +0,0 @@ -# == Schema Information -# -# Table name: builds -# -# id :integer not null, primary key -# project_id :integer -# status :string(255) -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# commit_id :integer -# coverage :float -# commands :text -# job_id :integer -# name :string(255) -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string(255) -# trigger_request_id :integer -# - -require 'spec_helper' - -describe Ci::Build do - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } - let(:build) { FactoryGirl.create :ci_build, commit: commit } - subject { build } - - it { is_expected.to belong_to(:commit) } - it { is_expected.to belong_to(:user) } - it { is_expected.to validate_presence_of :status } - it { is_expected.to validate_presence_of :ref } - - 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? } - it { is_expected.to respond_to :trace_html } - - describe :first_pending do - let(:first) { FactoryGirl.create :ci_build, commit: commit, status: 'pending', created_at: Date.yesterday } - let(:second) { FactoryGirl.create :ci_build, commit: commit, status: 'pending' } - before { first; second } - subject { Ci::Build.first_pending } - - it { is_expected.to be_a(Ci::Build) } - it('returns with the first pending build') { is_expected.to eq(first) } - end - - describe :create_from do - before do - build.status = 'success' - build.save - end - let(:create_from_build) { Ci::Build.create_from build } - - it 'there should be a pending task' do - expect(Ci::Build.pending.count(:all)).to eq 0 - create_from_build - expect(Ci::Build.pending.count(:all)).to be > 0 - end - end - - describe :started? do - subject { build.started? } - - context 'without started_at' do - before { build.started_at = nil } - - it { is_expected.to be_falsey } - end - - %w(running success failed).each do |status| - context "if build status is #{status}" do - before { build.status = status } - - it { is_expected.to be_truthy } - end - end - - %w(pending canceled).each do |status| - context "if build status is #{status}" do - before { build.status = status } - - it { is_expected.to be_falsey } - end - end - end - - describe :active? do - subject { build.active? } - - %w(pending running).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_truthy } - end - end - - %w(success failed canceled).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_falsey } - end - end - end - - describe :complete? do - subject { build.complete? } - - %w(success failed canceled).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_truthy } - end - end - - %w(pending running).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_falsey } - end - end - end - - describe :ignored? do - subject { build.ignored? } - - context 'if build is not allowed to fail' do - before { build.allow_failure = false } - - context 'and build.status is success' do - before { build.status = 'success' } - - it { is_expected.to be_falsey } - end - - context 'and build.status is failed' do - before { build.status = 'failed' } - - it { is_expected.to be_falsey } - end - end - - context 'if build is allowed to fail' do - before { build.allow_failure = true } - - context 'and build.status is success' do - before { build.status = 'success' } - - it { is_expected.to be_falsey } - end - - context 'and build.status is failed' do - before { build.status = 'failed' } - - it { is_expected.to be_truthy } - end - end - end - - describe :trace do - subject { build.trace_html } - - it { is_expected.to be_empty } - - context 'if build.trace contains text' do - let(:text) { 'example output' } - before { build.trace = text } - - it { is_expected.to include(text) } - it { expect(subject.length).to be >= text.length } - end - - context 'if build.trace hides token' do - let(:token) { 'my_secret_token' } - - before do - build.project.update_attributes(token: token) - build.update_attributes(trace: token) - end - - it { is_expected.to_not include(token) } - end - end - - describe :timeout do - subject { build.timeout } - - it { is_expected.to eq(commit.project.timeout) } - end - - describe :duration do - subject { build.duration } - - it { is_expected.to eq(120.0) } - - context 'if the building process has not started yet' do - before do - build.started_at = nil - build.finished_at = nil - end - - it { is_expected.to be_nil } - end - - context 'if the building process has started' do - before do - build.started_at = Time.now - 1.minute - build.finished_at = nil - end - - it { is_expected.to be_a(Float) } - it { is_expected.to be > 0.0 } - end - end - - describe :options do - let(:options) do - { - image: "ruby:2.1", - services: [ - "postgres" - ] - } - end - - subject { build.options } - it { is_expected.to eq(options) } - end - - describe :sha do - subject { build.sha } - - it { is_expected.to eq(commit.sha) } - end - - describe :short_sha do - subject { build.short_sha } - - it { is_expected.to eq(commit.short_sha) } - end - - describe :allow_git_fetch do - subject { build.allow_git_fetch } - - it { is_expected.to eq(project.allow_git_fetch) } - end - - describe :project do - subject { build.project } - - it { is_expected.to eq(commit.project) } - end - - describe :project_id do - subject { build.project_id } - - it { is_expected.to eq(commit.project_id) } - end - - describe :project_name do - subject { build.project_name } - - it { is_expected.to eq(project.name) } - end - - describe :repo_url do - subject { build.repo_url } - - it { is_expected.to eq(project.repo_url_with_auth) } - end - - describe :extract_coverage do - context 'valid content & regex' do - subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') } - - it { is_expected.to eq(98.29) } - end - - context 'valid content & bad regex' do - subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') } - - it { is_expected.to be_nil } - end - - context 'no coverage content & regex' do - subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') } - - it { is_expected.to be_nil } - end - - context 'multiple results in content & regex' do - subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') } - - it { is_expected.to eq(98.29) } - end - end - - describe :variables do - context 'returns variables' do - subject { build.variables } - - let(:variables) do - [ - { key: :DB_NAME, value: 'postgres', public: true } - ] - end - - it { is_expected.to eq(variables) } - - context 'and secure variables' do - let(:secure_variables) do - [ - { key: 'SECRET_KEY', value: 'secret_value', public: false } - ] - end - - before do - build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') - end - - it { is_expected.to eq(variables + secure_variables) } - - context 'and trigger variables' do - let(:trigger) { FactoryGirl.create :ci_trigger, project: project } - let(:trigger_request) { FactoryGirl.create :ci_trigger_request_with_variables, commit: commit, trigger: trigger } - let(:trigger_variables) do - [ - { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false } - ] - end - - before do - build.trigger_request = trigger_request - end - - it { is_expected.to eq(variables + secure_variables + trigger_variables) } - end - end - end - end - - describe :project_recipients do - let(:pusher_email) { 'pusher@gitlab.test' } - let(:user) { User.new(notification_email: pusher_email) } - subject { build.project_recipients } - - before do - build.update_attributes(user: user) - end - - it 'should return pusher_email as only recipient when no additional recipients are given' do - project.update_attributes(email_add_pusher: true, - email_recipients: '') - is_expected.to eq([pusher_email]) - end - - it 'should return pusher_email and additional recipients' do - project.update_attributes(email_add_pusher: true, - email_recipients: 'rec1 rec2') - is_expected.to eq(['rec1', 'rec2', pusher_email]) - end - - it 'should return recipients' do - project.update_attributes(email_add_pusher: false, - email_recipients: 'rec1 rec2') - is_expected.to eq(['rec1', 'rec2']) - end - - it 'should return unique recipients only' do - project.update_attributes(email_add_pusher: true, - email_recipients: "rec1 rec1 #{pusher_email}") - is_expected.to eq(['rec1', pusher_email]) - end - end -end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index acff1ddf0fc..371add4ee59 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -23,6 +23,8 @@ describe Ci::Commit do let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } it { is_expected.to belong_to(:gl_project) } + 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 } @@ -47,10 +49,12 @@ describe Ci::Commit do @second = FactoryGirl.create :ci_build, commit: commit end - it "creates new build" do + it "creates only a new build" do expect(commit.builds.count(:all)).to eq 2 + expect(commit.statuses.count(:all)).to eq 2 commit.retry expect(commit.builds.count(:all)).to eq 3 + expect(commit.statuses.count(:all)).to eq 3 end end @@ -78,8 +82,8 @@ describe Ci::Commit do subject { commit.stage } before do - @second = FactoryGirl.create :ci_build, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: :pending - @first = FactoryGirl.create :ci_build, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: :pending + @second = FactoryGirl.create :commit_status, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: 'pending' + @first = FactoryGirl.create :commit_status, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: 'pending' end it 'returns first running stage' do @@ -88,7 +92,7 @@ describe Ci::Commit do context 'first build succeeded' do before do - @first.update_attributes(status: :success) + @first.success end it 'returns last running stage' do @@ -98,8 +102,8 @@ describe Ci::Commit do context 'all builds succeeded' do before do - @first.update_attributes(status: :success) - @second.update_attributes(status: :success) + @first.success + @second.success end it 'returns nil' do @@ -111,6 +115,33 @@ describe Ci::Commit do describe :create_next_builds do end + describe :refs do + subject { commit.refs } + + before do + FactoryGirl.create :commit_status, commit: commit, name: 'deploy' + FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'develop' + FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'master' + end + + it 'returns all refs' do + is_expected.to contain_exactly('master', 'develop') + end + end + + describe :retried do + subject { commit.retried } + + before do + @commit1 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy' + @commit2 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy' + end + + it 'returns old builds' do + is_expected.to contain_exactly(@commit1) + end + end + describe :create_builds do let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } @@ -252,10 +283,10 @@ describe Ci::Commit do describe :should_create_next_builds? do before do - @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: :success - @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: :failed - @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: :failed - @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :success + @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: 'success' + @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: 'failed' + @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: 'failed' + @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'success' end context 'for success' do @@ -266,7 +297,7 @@ describe Ci::Commit do context 'for failed' do before do - @build4.update_attributes(status: :failed) + @build4.update_attributes(status: 'failed') end it 'to not create' do @@ -286,7 +317,7 @@ describe Ci::Commit do context 'for running' do before do - @build4.update_attributes(status: :running) + @build4.update_attributes(status: 'running') end it 'to not create' do @@ -296,7 +327,7 @@ describe Ci::Commit do context 'for retried' do before do - @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :failed + @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'failed' end it 'to not create' do diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb index 04e870dce7f..2ce5c2b4707 100644 --- a/spec/models/ci/project_services/mail_service_spec.rb +++ b/spec/models/ci/project_services/mail_service_spec.rb @@ -58,7 +58,7 @@ describe Ci::MailService do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) } let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -86,7 +86,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -115,7 +115,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -144,7 +144,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb new file mode 100644 index 00000000000..c1af50afb7c --- /dev/null +++ b/spec/models/commit_status_spec.rb @@ -0,0 +1,157 @@ +require 'spec_helper' + +describe CommitStatus do + let(:commit) { FactoryGirl.create :ci_commit } + let(:commit_status) { FactoryGirl.create :commit_status, commit: commit } + + it { is_expected.to belong_to(:commit) } + it { is_expected.to belong_to(:user) } + 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(:commit) } + it { is_expected.to delegate_method(:short_sha).to(:commit) } + it { is_expected.to delegate_method(:gl_project).to(:commit) } + + 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 :started? do + subject { commit_status.started? } + + context 'without started_at' do + before { commit_status.started_at = nil } + + it { is_expected.to be_falsey } + end + + %w(running success failed).each do |status| + context "if commit status is #{status}" do + before { commit_status.status = status } + + it { is_expected.to be_truthy } + end + end + + %w(pending canceled).each do |status| + context "if commit status is #{status}" do + before { commit_status.status = status } + + 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 { commit_status.status = state } + + it { is_expected.to be_truthy } + end + end + + %w(success failed canceled).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + 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 { commit_status.status = state } + + it { is_expected.to be_truthy } + end + end + + %w(pending running).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + it { is_expected.to be_falsey } + 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 { CommitStatus.latest.order(:id) } + + before do + @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' + @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' + @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'cc', status: 'success' + @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'bb', status: 'success' + @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'success' + end + + it 'return unique statuses' do + is_expected.to eq([@commit2, @commit3, @commit4, @commit5]) + end + end + + describe :for_ref do + subject { CommitStatus.for_ref('bb').order(:id) } + + before do + @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' + @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' + @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' + end + + it 'return statuses with equal and nil ref set' do + is_expected.to eq([@commit1, @commit3]) + end + end + + describe :running_or_pending do + subject { CommitStatus.running_or_pending.order(:id) } + + before do + @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' + @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' + @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' + @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed' + @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled' + end + + it 'return statuses that are running or pending' do + is_expected.to eq([@commit1, @commit2]) + end + end +end diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb new file mode 100644 index 00000000000..f442fa5fbe5 --- /dev/null +++ b/spec/models/generic_commit_status_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe GenericCommitStatus do + let(:commit) { FactoryGirl.create :ci_commit } + let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, commit: commit } + + describe :context do + subject { generic_commit_status.context } + before { generic_commit_status.context = 'my_context' } + + it { is_expected.to eq(generic_commit_status.name) } + end + + describe :tags do + subject { generic_commit_status.tags } + + it { is_expected.to eq([:external]) } + end + + describe :set_default_values do + before do + generic_commit_status.context = nil + generic_commit_status.stage = nil + generic_commit_status.save + end + + describe :context do + subject { generic_commit_status.context } + + it { is_expected.to_not be_nil } + end + + describe :stage do + subject { generic_commit_status.stage } + + it { is_expected.to_not be_nil } + end + end +end diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb new file mode 100644 index 00000000000..d1dc97e1846 --- /dev/null +++ b/spec/requests/api/commit_status_spec.rb @@ -0,0 +1,135 @@ +require 'spec_helper' + +describe API::API, api: true do + include ApiHelpers + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:project) { create(:project, creator_id: user.id) } + let!(:reporter) { create(:project_member, user: user, project: project, access_level: ProjectMember::REPORTER) } + let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } + let(:commit) { project.repository.commit } + let!(:ci_commit) { project.ensure_ci_commit(commit.id) } + let(:commit_status) { create(:commit_status, commit: ci_commit) } + + describe "GET /projects/:id/repository/commits/:sha/statuses" do + context "reporter user" do + let(:statuses_id) { json_response.map { |status| status['id'] } } + + before do + @status1 = create(:commit_status, commit: ci_commit, status: 'running') + @status2 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'pending') + @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running') + @status4 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'success') + @status5 = create(:commit_status, commit: ci_commit, ref: 'develop', status: 'success') + @status6 = create(:commit_status, commit: ci_commit, status: 'success') + end + + it "should return latest commit statuses" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id) + end + + it "should return all commit statuses" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status1.id, @status2.id, @status3.id, @status4.id, @status5.id, @status6.id) + end + + it "should return latest commit statuses for specific ref" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status3.id, @status5.id) + end + + it "should return latest commit statuses for specific name" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status3.id, @status4.id) + end + end + + context "guest user" do + it "should not return project commits" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user2) + expect(response.status).to eq(403) + end + end + + context "unauthorized user" do + it "should not return project commits" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses") + expect(response.status).to eq(401) + end + end + end + + describe 'POST /projects/:id/repository/commits/:sha/status' do + let(:post_url) { "/projects/#{project.id}/repository/commits/#{commit.id}/status" } + + context 'reporter user' do + context 'should create commit status' do + it 'with only required parameters' do + post api(post_url, user), state: 'success' + expect(response.status).to eq(201) + expect(json_response['sha']).to eq(commit.id) + expect(json_response['status']).to eq('success') + expect(json_response['name']).to eq('default') + expect(json_response['ref']).to be_nil + expect(json_response['target_url']).to be_nil + expect(json_response['description']).to be_nil + end + + it 'with all optional parameters' do + post api(post_url, user), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test' + expect(response.status).to eq(201) + expect(json_response['sha']).to eq(commit.id) + expect(json_response['status']).to eq('success') + expect(json_response['name']).to eq('coverage') + expect(json_response['ref']).to eq('develop') + expect(json_response['target_url']).to eq('url') + expect(json_response['description']).to eq('test') + end + end + + context 'should not create commit status' do + it 'with invalid state' do + post api(post_url, user), state: 'invalid' + expect(response.status).to eq(400) + end + + it 'without state' do + post api(post_url, user) + expect(response.status).to eq(400) + end + + it 'invalid commit' do + post api("/projects/#{project.id}/repository/commits/invalid_sha/status", user), state: 'running' + expect(response.status).to eq(404) + end + end + end + + context 'guest user' do + it 'should not create commit status' do + post api(post_url, user2) + expect(response.status).to eq(403) + end + end + + context 'unauthorized user' do + it 'should not create commit status' do + post api(post_url) + expect(response.status).to eq(401) + end + end + end +end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index a1c248c636e..49acc3368f4 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -47,6 +47,19 @@ describe API::API, api: true do get api("/projects/#{project.id}/repository/commits/invalid_sha", user) expect(response.status).to eq(404) end + + it "should return not_found for CI status" do + get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + expect(response.status).to eq(200) + expect(json_response['status']).to eq('not_found') + end + + it "should return status for CI" do + ci_commit = project.ensure_ci_commit(project.repository.commit.sha) + get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + expect(response.status).to eq(200) + expect(json_response['status']).to eq(ci_commit.status) + end end context "unauthorized user" do -- cgit v1.2.3 From 887494761ee41e8e647753e597fab534ed738058 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 12:15:35 +0200 Subject: Fix commit status POST URL --- spec/requests/api/commit_status_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb index d1dc97e1846..b9e6dfc15a7 100644 --- a/spec/requests/api/commit_status_spec.rb +++ b/spec/requests/api/commit_status_spec.rb @@ -72,8 +72,8 @@ describe API::API, api: true do end end - describe 'POST /projects/:id/repository/commits/:sha/status' do - let(:post_url) { "/projects/#{project.id}/repository/commits/#{commit.id}/status" } + describe 'POST /projects/:id/statuses/:sha' do + let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" } context 'reporter user' do context 'should create commit status' do @@ -112,7 +112,7 @@ describe API::API, api: true do end it 'invalid commit' do - post api("/projects/#{project.id}/repository/commits/invalid_sha/status", user), state: 'running' + post api("/projects/#{project.id}/statuses/invalid_sha", user), state: 'running' expect(response.status).to eq(404) end end -- cgit v1.2.3 From 7ef156a24292f98d89bc424cc245f00548831863 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 12:15:48 +0200 Subject: Add author to statuses --- spec/models/commit_status_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'spec') diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index c1af50afb7c..cbefa7798de 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -18,6 +18,13 @@ describe CommitStatus do it { is_expected.to respond_to :running? } it { is_expected.to respond_to :pending? } + describe :author do + subject { commit_status.author } + before { commit_status.author = User.new } + + it { is_expected.to eq(commit_status.user) } + end + describe :started? do subject { commit_status.started? } -- cgit v1.2.3 From 2e9c1608e56a20b93608ddcd569c6a45d3a229eb Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 16:35:58 +0200 Subject: Fix commit skipping --- spec/models/ci/project_services/mail_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb index 2ce5c2b4707..d9b3d34ff15 100644 --- a/spec/models/ci/project_services/mail_service_spec.rb +++ b/spec/models/ci/project_services/mail_service_spec.rb @@ -35,7 +35,7 @@ describe Ci::MailService do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) } let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -167,7 +167,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) } before do allow(mail).to receive_messages( -- cgit v1.2.3 From c61dc1315077ad4e175a3c76991872c0e7b1d2ab Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 16:41:36 +0200 Subject: Fix some changes --- spec/models/commit_status_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index cbefa7798de..c96a606fdaa 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -142,7 +142,7 @@ describe CommitStatus do end it 'return statuses with equal and nil ref set' do - is_expected.to eq([@commit1, @commit3]) + is_expected.to eq([@commit1]) end end -- cgit v1.2.3 From daca1c6511c3a09d5f0c33a8c4d29487e668afc2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 21:35:52 +0200 Subject: Fix broken tests --- spec/models/ci/commit_spec.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 371add4ee59..330971174fb 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -125,7 +125,7 @@ describe Ci::Commit do end it 'returns all refs' do - is_expected.to contain_exactly('master', 'develop') + is_expected.to contain_exactly('master', 'develop', nil) end end @@ -225,9 +225,10 @@ describe Ci::Commit do it 'rebuilds commit' do expect(commit.status).to eq('skipped') expect(create_builds(trigger_request)).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(2) - expect(commit.status).to eq('pending') + + # since everything in Ci::Commit is cached we need to fetch a new object + new_commit = Ci::Commit.find_by_id(commit.id) + expect(new_commit.status).to eq('pending') end end end -- cgit v1.2.3