summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMayra Cabrera <mcabrera@gitlab.com>2019-04-26 14:17:26 -0500
committerMayra Cabrera <mcabrera@gitlab.com>2019-04-30 11:45:18 -0500
commit98c4f3ac2dd728892bba57e668c83e4b9f16a276 (patch)
treebf86c9ad5b97e20a9fae977c4c3a7d80a865b3af
parent6469d4cc8d8e6ddae4183855d41fe4dffc13d143 (diff)
downloadgitlab-ce-28741-play-all-manual-jobs.tar.gz
Addresses backend comments28741-play-all-manual-jobs
- Creates a stages_controller with a play_manual action - Renames blocked_or_skipped to playable - Make new service re-use the Ci::Build#play method. - Rename playable? to manual_playable? so the action is more descriptive - Log an error in case the user does not have access to one of the instances
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue2
-rw-r--r--app/controllers/projects/pipelines_controller.rb19
-rw-r--r--app/controllers/projects/stages_controller.rb25
-rw-r--r--app/models/ci/legacy_stage.rb2
-rw-r--r--app/models/ci/pipeline.rb4
-rw-r--r--app/models/ci/stage.rb4
-rw-r--r--app/models/concerns/has_status.rb4
-rw-r--r--app/serializers/stage_entity.rb12
-rw-r--r--app/serializers/stages/play_manual_entity.rb9
-rw-r--r--app/services/ci/play_manual_stage_service.rb29
-rw-r--r--app/services/ci/play_stage_manual_builds_service.rb27
-rw-r--r--config/routes/project.rb7
-rw-r--r--lib/gitlab/ci/status/stage/common.rb4
-rw-r--r--lib/gitlab/ci/status/stage/play_manual.rb14
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb46
-rw-r--r--spec/controllers/projects/stages_controller_spec.rb72
-rw-r--r--spec/lib/gitlab/ci/status/stage/play_manual_spec.rb8
-rw-r--r--spec/models/ci/legacy_stage_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb8
-rw-r--r--spec/models/ci/stage_spec.rb2
-rw-r--r--spec/models/concerns/has_status_spec.rb24
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb1
-rw-r--r--spec/serializers/stage_entity_spec.rb10
-rw-r--r--spec/serializers/stage_serializer_spec.rb27
-rw-r--r--spec/services/ci/play_manual_stage_service_spec.rb79
-rw-r--r--spec/services/ci/play_stage_manual_builds_service_spec.rb77
-rw-r--r--spec/support/shared_examples/ci/stage_shared_examples.rb27
27 files changed, 272 insertions, 273 deletions
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index 62b81d8d937..ba0dea626dc 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -24,7 +24,7 @@ export default {
:groups="stage.groups"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"
- :action="stage.play_manual_details"
+ :action="stage.status.action"
@refreshPipelineGraph="refreshPipelineGraph"
/>
</ul>
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 64b440ba23b..22c4b8eef1f 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -6,7 +6,7 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_read_pipeline!
before_action :authorize_read_build!, only: [:index]
before_action :authorize_create_pipeline!, only: [:new, :create]
- before_action :authorize_update_pipeline!, only: [:retry, :cancel, :play_all_manual]
+ before_action :authorize_update_pipeline!, only: [:retry, :cancel]
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
@@ -147,19 +147,6 @@ class Projects::PipelinesController < Projects::ApplicationController
@counts[:failed] = @project.all_pipelines.failed.count(:all)
end
- def play_all_manual
- return not_found unless pipeline_stage
-
- options = { pipeline: pipeline, stage: pipeline_stage }
-
- ::Ci::PlayStageManualBuildsService.new(@project, current_user, options)
- .execute
-
- render json: StageSerializer
- .new(project: @project, current_user: @current_user)
- .represent(pipeline_stage, play_all: false)
- end
-
private
def serialize_pipelines
@@ -209,8 +196,4 @@ class Projects::PipelinesController < Projects::ApplicationController
view_context.limited_counter_with_delimiter(finder.execute)
end
-
- def pipeline_stage
- @pipeline_stage ||= pipeline.find_stage_by_name(params[:stage])
- end
end
diff --git a/app/controllers/projects/stages_controller.rb b/app/controllers/projects/stages_controller.rb
new file mode 100644
index 00000000000..c8db5b1277f
--- /dev/null
+++ b/app/controllers/projects/stages_controller.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class Projects::StagesController < Projects::PipelinesController
+ before_action :authorize_update_pipeline!
+
+ def play_manual
+ ::Ci::PlayManualStageService
+ .new(@project, current_user, pipeline: pipeline)
+ .execute(stage)
+
+ respond_to do |format|
+ format.json do
+ render json: StageSerializer
+ .new(project: @project, current_user: @current_user)
+ .represent(stage)
+ end
+ end
+ end
+
+ private
+
+ def stage
+ @pipeline_stage ||= pipeline.find_stage_by_name!(params[:stage_name])
+ end
+end
diff --git a/app/models/ci/legacy_stage.rb b/app/models/ci/legacy_stage.rb
index a15875773ba..930c8a71453 100644
--- a/app/models/ci/legacy_stage.rb
+++ b/app/models/ci/legacy_stage.rb
@@ -59,7 +59,7 @@ module Ci
end
end
- def blocked_or_skipped?
+ def manual_playable?
%[manual scheduled skipped].include?(status.to_s)
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 20c0088fd10..80401ca0a1e 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -771,8 +771,8 @@ module Ci
Gitlab::Utils.slugify(source_ref.to_s)
end
- def find_stage_by_name(name)
- stages.find_by(name: name)
+ def find_stage_by_name!(name)
+ stages.find_by!(name: name)
end
private
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index b25b0369666..d90339d90dc 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -120,5 +120,9 @@ module Ci
.new(self, current_user)
.fabricate!
end
+
+ def manual_playable?
+ blocked? || skipped?
+ end
end
end
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index 921d3cbef19..8882f48c281 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -122,10 +122,6 @@ module HasStatus
BLOCKED_STATUS.include?(status)
end
- def blocked_or_skipped?
- blocked? || status == 'skipped'
- end
-
private
def calculate_duration
diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb
index 07b0fd46567..029dd3d0684 100644
--- a/app/serializers/stage_entity.rb
+++ b/app/serializers/stage_entity.rb
@@ -42,20 +42,14 @@ class StageEntity < Grape::Entity
format: :json)
end
- expose :play_manual_details,
- if: -> (status, opts) { show_play_manual_details?(opts) },
- with: Stages::PlayManualEntity
-
private
alias_method :stage, :object
def detailed_status
- @detailed_status ||= stage.detailed_status(request.current_user)
+ stage.detailed_status(request.current_user)
end
- alias_method :play_manual_details, :detailed_status
-
def grouped_statuses
@grouped_statuses ||= stage.statuses.latest_ordered.group_by(&:status)
end
@@ -75,8 +69,4 @@ class StageEntity < Grape::Entity
grouped_retried_statuses.fetch(ordered_status, [])
end.flatten
end
-
- def show_play_manual_details?(opts)
- opts.fetch(:play_all, true) && detailed_status.has_manual_builds?
- end
end
diff --git a/app/serializers/stages/play_manual_entity.rb b/app/serializers/stages/play_manual_entity.rb
deleted file mode 100644
index 618b38c9743..00000000000
--- a/app/serializers/stages/play_manual_entity.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-class Stages::PlayManualEntity < Grape::Entity
- expose :action_icon, as: :icon
- expose :action_button_title, as: :button_title
- expose :action_title, as: :title
- expose :action_path, as: :path
- expose :action_method, as: :method
-end
diff --git a/app/services/ci/play_manual_stage_service.rb b/app/services/ci/play_manual_stage_service.rb
new file mode 100644
index 00000000000..2497fc52e6b
--- /dev/null
+++ b/app/services/ci/play_manual_stage_service.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Ci
+ class PlayManualStageService < BaseService
+ def initialize(project, current_user, params)
+ super
+
+ @pipeline = params[:pipeline]
+ end
+
+ def execute(stage)
+ stage.builds.manual.each do |build|
+ next unless build.playable?
+
+ build.play(current_user)
+ rescue Gitlab::Access::AccessDeniedError
+ logger.error(message: 'Unable to play manual action', build_id: build.id)
+ end
+ end
+
+ private
+
+ attr_reader :pipeline, :current_user
+
+ def logger
+ Gitlab::AppLogger
+ end
+ end
+end
diff --git a/app/services/ci/play_stage_manual_builds_service.rb b/app/services/ci/play_stage_manual_builds_service.rb
deleted file mode 100644
index 2512c5310ef..00000000000
--- a/app/services/ci/play_stage_manual_builds_service.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- class PlayStageManualBuildsService < BaseService
- def initialize(project, current_user, params)
- super
-
- @pipeline = params[:pipeline]
- @stage = params[:stage]
- end
-
- def execute
- raise Gitlab::Access::AccessDeniedError unless can?(current_user, :update_pipeline, pipeline)
-
- manual_builds.map(&:enqueue)
- manual_builds.update_all(user_id: current_user.id, updated_at: Time.now)
- end
-
- private
-
- attr_reader :pipeline, :stage
-
- def manual_builds
- @manual_builds ||= stage.builds.manual
- end
- end
-end
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 892bacc6af2..93d746f3282 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -200,7 +200,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :builds
get :failures
get :status
- post :play_all_manual, path: 'stages/:stage/play_all_manual'
+ end
+
+ member do
+ resources :stages, only: [], param: :name do
+ post :play_manual
+ end
end
end
diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb
index f2fe6a94acb..f12daaa9676 100644
--- a/lib/gitlab/ci/status/stage/common.rb
+++ b/lib/gitlab/ci/status/stage/common.rb
@@ -18,10 +18,6 @@ module Gitlab
def has_action?
false
end
-
- def has_manual_builds?
- false
- end
end
end
end
diff --git a/lib/gitlab/ci/status/stage/play_manual.rb b/lib/gitlab/ci/status/stage/play_manual.rb
index 554b650b0aa..ac3fc0912fa 100644
--- a/lib/gitlab/ci/status/stage/play_manual.rb
+++ b/lib/gitlab/ci/status/stage/play_manual.rb
@@ -11,10 +11,6 @@ module Gitlab
'play'
end
- def action_button_title
- _('Play all manual')
- end
-
def action_title
'Play all manual'
end
@@ -22,18 +18,22 @@ module Gitlab
def action_path
pipeline = subject.pipeline
- play_all_manual_project_pipeline_path(pipeline.project, pipeline, subject.name)
+ project_stage_play_manual_path(pipeline.project, pipeline, subject.name)
end
def action_method
:post
end
+ def action_button_title
+ _('Play all manual')
+ end
+
def self.matches?(stage, user)
- stage.blocked_or_skipped?
+ stage.manual_playable?
end
- def has_manual_builds?
+ def has_action?
true
end
end
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 51a46916f40..9a50ea79f5e 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -393,50 +393,4 @@ describe Projects::PipelinesController do
end
end
end
-
- describe 'POST #play_all_manual.json' do
- let(:pipeline) { create(:ci_pipeline, project: project) }
- let(:stage_name) { 'test' }
-
- before do
- create_manual_build(pipeline, 'test', 'rspec 1/3')
- create_manual_build(pipeline, 'test', 'rspec 2/3')
- create_manual_build(pipeline, 'test', 'rspec 3/3')
-
- pipeline.reload
- end
-
- context 'when the stage does not exists' do
- let(:stage_name) { 'deploy' }
-
- it 'fails to play all manual' do
- play_all_manual!
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'when the stage exists' do
- it 'starts all manual jobs' do
- expect(pipeline.builds.manual.count).to eq(3)
- play_all_manual!
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(pipeline.builds.manual.count).to eq(0)
- end
- end
-
- def play_all_manual!
- post :play_all_manual, params: {
- namespace_id: project.namespace,
- project_id: project,
- id: pipeline.id,
- stage: stage_name
- }, format: :json
- end
-
- def create_manual_build(pipeline, stage, name)
- create(:ci_build, :manual, pipeline: pipeline, stage: stage, name: name)
- end
- end
end
diff --git a/spec/controllers/projects/stages_controller_spec.rb b/spec/controllers/projects/stages_controller_spec.rb
new file mode 100644
index 00000000000..a91e3523fd7
--- /dev/null
+++ b/spec/controllers/projects/stages_controller_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::StagesController do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'POST #play_manual.json' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:stage_name) { 'test' }
+
+ before do
+ create_manual_build(pipeline, 'test', 'rspec 1/2')
+ create_manual_build(pipeline, 'test', 'rspec 2/2')
+
+ pipeline.reload
+ end
+
+ context 'when user does not have access' do
+ it 'returns not authorized' do
+ play_manual_stage!
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when user has access' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'when the stage does not exists' do
+ let(:stage_name) { 'deploy' }
+
+ it 'fails to play all manual' do
+ play_manual_stage!
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when the stage exists' do
+ it 'starts all manual jobs' do
+ expect(pipeline.builds.manual.count).to eq(2)
+
+ play_manual_stage!
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(pipeline.builds.manual.count).to eq(0)
+ end
+ end
+ end
+
+ def play_manual_stage!
+ post :play_manual, params: {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: pipeline.id,
+ stage_name: stage_name
+ }, format: :json
+ end
+
+ def create_manual_build(pipeline, stage, name)
+ create(:ci_build, :manual, pipeline: pipeline, stage: stage, name: name)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/stage/play_manual_spec.rb b/spec/lib/gitlab/ci/status/stage/play_manual_spec.rb
index a357881103d..b0113b00ef0 100644
--- a/spec/lib/gitlab/ci/status/stage/play_manual_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/play_manual_spec.rb
@@ -31,7 +31,7 @@ describe Gitlab::Ci::Status::Stage::PlayManual do
subject { play_manual.action_path }
- it { is_expected.to eq("/#{pipeline.project.full_path}/pipelines/#{pipeline.id}/stages/#{stage.name}/play_all_manual") }
+ it { is_expected.to eq("/#{pipeline.project.full_path}/pipelines/#{pipeline.id}/stages/#{stage.name}/play_manual") }
end
describe '#action_method' do
@@ -71,10 +71,4 @@ describe Gitlab::Ci::Status::Stage::PlayManual do
end
end
end
-
- describe '#has_manual_builds?' do
- subject { play_manual.has_manual_builds? }
-
- it { is_expected.to be_truthy }
- end
end
diff --git a/spec/models/ci/legacy_stage_spec.rb b/spec/models/ci/legacy_stage_spec.rb
index be0307518eb..bb812cc0533 100644
--- a/spec/models/ci/legacy_stage_spec.rb
+++ b/spec/models/ci/legacy_stage_spec.rb
@@ -272,4 +272,6 @@ describe Ci::LegacyStage do
def create_job(type, status: 'success', stage: stage_name, **opts)
create(type, pipeline: pipeline, stage: stage, status: status, **opts)
end
+
+ it_behaves_like 'manual playable stage', :ci_stage
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 7a353ebd92b..af455a72f50 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -2958,7 +2958,7 @@ describe Ci::Pipeline, :mailer do
create_list(:ci_build, 2, pipeline: pipeline, stage: stage.name)
end
- subject { pipeline.find_stage_by_name(stage_name) }
+ subject { pipeline.find_stage_by_name!(stage_name) }
context 'when stage exists' do
it { is_expected.to eq(stage) }
@@ -2967,7 +2967,11 @@ describe Ci::Pipeline, :mailer do
context 'when stage does not exist' do
let(:stage_name) { 'build' }
- it { is_expected.to be_nil }
+ it 'raises an ActiveRecord exception' do
+ expect do
+ subject
+ end.to raise_exception(ActiveRecord::RecordNotFound)
+ end
end
end
end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index 661958390e2..85cd32fb03a 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -285,4 +285,6 @@ describe Ci::Stage, :models do
end
end
end
+
+ it_behaves_like 'manual playable stage', :ci_stage_entity
end
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb
index 05480ad0285..a217dc42537 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/has_status_spec.rb
@@ -367,28 +367,4 @@ describe HasStatus do
puts subject
end
end
-
- describe '#blocked_or_skipped?' do
- %w[ci_pipeline ci_stage ci_build generic_commit_status].each do |type|
- let(:object) { build(type, status: status) }
-
- context 'when status is scheduled' do
- let(:status) { :scheduled }
-
- it { is_expected.to be_truthy }
- end
-
- context 'when status is manual' do
- let(:status) { :manual }
-
- it { is_expected.to be_truthy }
- end
-
- context 'when status is skipped' do
- let(:status) { :skipped }
-
- it { is_expected.to be_truthy }
- end
- end
- end
end
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index 88f18209dce..54e6abc2d3a 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -157,6 +157,7 @@ describe PipelineSerializer do
it 'verifies number of queries', :request_store do
recorded = ActiveRecord::QueryRecorder.new { subject }
expected_queries = Gitlab.ee? ? 38 : 31
+
expect(recorded.count).to be_within(2).of(expected_queries)
expect(recorded.cached_count).to eq(0)
end
diff --git a/spec/serializers/stage_entity_spec.rb b/spec/serializers/stage_entity_spec.rb
index 114a1593d72..6b1185d1283 100644
--- a/spec/serializers/stage_entity_spec.rb
+++ b/spec/serializers/stage_entity_spec.rb
@@ -48,8 +48,8 @@ describe StageEntity do
expect(subject[:title]).to eq 'test: passed'
end
- it 'does not contain play_manual_details info' do
- expect(subject[:play_manual_details]).not_to be_present
+ it 'does not contain play_details info' do
+ expect(subject[:status][:action]).not_to be_present
end
context 'when the jobs should be grouped' do
@@ -75,7 +75,7 @@ describe StageEntity do
let(:stage) { create(:ci_stage_entity, status: 'skipped') }
it 'contains play_all_manual' do
- expect(subject[:play_manual_details]).to be_present
+ expect(subject[:status][:action]).to be_present
end
end
@@ -83,7 +83,7 @@ describe StageEntity do
let(:stage) { create(:ci_stage_entity, status: 'scheduled') }
it 'contains play_all_manual' do
- expect(subject[:play_manual_details]).to be_present
+ expect(subject[:status][:action]).to be_present
end
end
@@ -91,7 +91,7 @@ describe StageEntity do
let(:stage) { create(:ci_stage_entity, status: 'manual') }
it 'contains play_all_manual' do
- expect(subject[:play_manual_details]).to be_present
+ expect(subject[:status][:action]).to be_present
end
end
end
diff --git a/spec/serializers/stage_serializer_spec.rb b/spec/serializers/stage_serializer_spec.rb
index 94d72910b32..aae17cfbcb9 100644
--- a/spec/serializers/stage_serializer_spec.rb
+++ b/spec/serializers/stage_serializer_spec.rb
@@ -27,32 +27,5 @@ describe StageSerializer do
expect(subject).not_to be_empty
end
end
-
- context 'with manual builds' do
- let(:resource) { create(:ci_stage_entity, status: 'manual') }
-
- subject { serializer.represent(resource, { play_all: play_all }) }
-
- before do
- create_list(:ci_build, 3, :manual,
- stage: resource.name, stage_id: resource.id)
- end
-
- context 'with play all option' do
- let(:play_all) { true }
-
- it 'returns play manual details' do
- expect(subject[:play_manual_details]).to be_present
- end
- end
-
- context 'without play all option' do
- let(:play_all) { false }
-
- it 'does not return play manual details' do
- expect(subject[:play_manual_details]).not_to be_present
- end
- end
- end
end
end
diff --git a/spec/services/ci/play_manual_stage_service_spec.rb b/spec/services/ci/play_manual_stage_service_spec.rb
new file mode 100644
index 00000000000..5d812745c7f
--- /dev/null
+++ b/spec/services/ci/play_manual_stage_service_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::PlayManualStageService, '#execute' do
+ let(:current_user) { create(:user) }
+ let(:pipeline) { create(:ci_pipeline, user: current_user) }
+ let(:project) { pipeline.project }
+ let(:service) { described_class.new(project, current_user, pipeline: pipeline) }
+ let(:stage_status) { 'manual' }
+
+ let(:stage) do
+ create(:ci_stage_entity,
+ pipeline: pipeline,
+ project: project,
+ name: 'test')
+ end
+
+ before do
+ project.add_maintainer(current_user)
+ create_builds_for_stage(status: stage_status)
+ end
+
+ context 'when pipeline has manual builds' do
+ before do
+ service.execute(stage)
+ end
+
+ it 'starts manual builds from pipeline' do
+ expect(pipeline.builds.manual.count).to eq(0)
+ end
+
+ it 'updates manual builds' do
+ pipeline.builds.each do |build|
+ expect(build.user).to eq(current_user)
+ end
+ end
+ end
+
+ context 'when pipeline has no manual builds' do
+ let(:stage_status) { 'failed' }
+
+ before do
+ service.execute(stage)
+ end
+
+ it 'does not update the builds' do
+ expect(pipeline.builds.failed.count).to eq(3)
+ end
+ end
+
+ context 'when user does not have permission on a specific build' do
+ before do
+ allow_any_instance_of(Ci::Build).to receive(:play)
+ .and_raise(Gitlab::Access::AccessDeniedError)
+
+ service.execute(stage)
+ end
+
+ it 'logs the error' do
+ expect(Gitlab::AppLogger).to receive(:error)
+ .exactly(stage.builds.manual.count)
+
+ service.execute(stage)
+ end
+ end
+
+ def create_builds_for_stage(options)
+ options.merge!({
+ when: 'manual',
+ pipeline: pipeline,
+ stage: stage.name,
+ stage_id: stage.id,
+ user: pipeline.user
+ })
+
+ create_list(:ci_build, 3, options)
+ end
+end
diff --git a/spec/services/ci/play_stage_manual_builds_service_spec.rb b/spec/services/ci/play_stage_manual_builds_service_spec.rb
deleted file mode 100644
index 8ffb33d82c7..00000000000
--- a/spec/services/ci/play_stage_manual_builds_service_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Ci::PlayStageManualBuildsService, '#execute' do
- let(:current_user) { create(:user) }
- let(:pipeline) { create(:ci_pipeline, user: current_user) }
- let(:project) { pipeline.project }
-
- let(:stage) do
- create(:ci_stage_entity,
- pipeline: pipeline,
- project: project,
- name: 'test')
- end
-
- let(:options) do
- {
- pipeline: stage.pipeline,
- stage: stage
- }
- end
-
- let(:service) do
- described_class.new(project, current_user, options)
- end
-
- context 'when user does not have access' do
- it 'throws an exception' do
- project.add_reporter(current_user)
-
- expect do
- service.execute
- end.to raise_error(Gitlab::Access::AccessDeniedError)
- end
- end
-
- context 'when user has access' do
- before do
- project.add_maintainer(current_user)
- end
-
- context 'when pipeline has manual builds' do
- before do
- create_builds_for_stage('manual')
- service.execute
- pipeline.reload
- end
-
- it 'starts manual builds from pipeline' do
- expect(pipeline.builds.manual.count).to eq(0)
- end
-
- it 'updates manual builds' do
- pipeline.builds.each do |build|
- expect(build.user).to eq(current_user)
- end
- end
- end
-
- context 'when pipeline has no manual builds' do
- before do
- create_builds_for_stage('failed')
- service.execute
- pipeline.reload
- end
-
- it 'does not update the builds' do
- expect(pipeline.builds.failed.count).to eq(3)
- end
- end
- end
-
- def create_builds_for_stage(status)
- create_list(:ci_build, 3, status: status, pipeline: pipeline, stage: stage.name, stage_id: stage.id, user: pipeline.user)
- end
-end
diff --git a/spec/support/shared_examples/ci/stage_shared_examples.rb b/spec/support/shared_examples/ci/stage_shared_examples.rb
new file mode 100644
index 00000000000..925974ed11e
--- /dev/null
+++ b/spec/support/shared_examples/ci/stage_shared_examples.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+shared_examples 'manual playable stage' do |stage_type|
+ let(:stage) { build(stage_type, status: status) }
+
+ describe '#manual_playable?' do
+ subject { stage.manual_playable? }
+
+ context 'when is manual' do
+ let(:status) { 'manual' }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when is scheduled' do
+ let(:status) { 'scheduled' }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when is skipped' do
+ let(:status) { 'skipped' }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+end