Commit 189565fa authored by Shinya Maeda's avatar Shinya Maeda

Persist and control auto stop date for environments

This commit persists and controls auto stop date for environments
parent 46f2255d
......@@ -7,14 +7,15 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :authorize_read_environment!
before_action :authorize_create_environment!, only: [:new, :create]
before_action :authorize_stop_environment!, only: [:stop]
before_action :authorize_update_environment!, only: [:edit, :update]
before_action :authorize_update_environment!, only: [:edit, :update, :cancel_auto_stop]
before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :metrics]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :metrics, :cancel_auto_stop]
before_action :verify_api_request!, only: :terminal_websocket_authorize
before_action :expire_etag_cache, only: [:index]
before_action :expire_etag_cache, only: [:index], unless: -> { request.format.json? }
before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do
push_frontend_feature_flag(:prometheus_computed_alerts)
end
after_action :expire_etag_cache, only: [:cancel_auto_stop]
def index
@environments = project.environments
......@@ -104,6 +105,27 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end
end
def cancel_auto_stop
result = Environments::ResetAutoStopService.new(project, current_user)
.execute(environment)
if result[:status] == :success
respond_to do |format|
message = _('Auto stop successfully canceled.')
format.html { redirect_back_or_default(default: { action: 'show' }, options: { notice: message }) }
format.json { render json: { message: message }, status: :ok }
end
else
respond_to do |format|
message = result[:message]
format.html { redirect_back_or_default(default: { action: 'show' }, options: { alert: message }) }
format.json { render json: { message: message }, status: :unprocessable_entity }
end
end
end
def terminal
# Currently, this acts as a hint to load the terminal details into the cache
# if they aren't there already. In the future, users will need these details
......@@ -175,8 +197,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end
def expire_etag_cache
return if request.format.json?
# this forces to reload json content
Gitlab::EtagCaching::Store.new.tap do |store|
store.touch(project_environments_path(project, format: :json))
......@@ -222,6 +242,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def authorize_stop_environment!
access_denied! unless can?(current_user, :stop_environment, environment)
end
def authorize_update_environment!
access_denied! unless can?(current_user, :update_environment, environment)
end
end
Projects::EnvironmentsController.prepend_if_ee('EE::Projects::EnvironmentsController')
......@@ -17,6 +17,7 @@ module Ci
delegate :timeout, to: :metadata, prefix: true, allow_nil: true
delegate :interruptible, to: :metadata, prefix: false, allow_nil: true
delegate :has_exposed_artifacts?, to: :metadata, prefix: false, allow_nil: true
delegate :environment_auto_stop_in, to: :metadata, prefix: false, allow_nil: true
before_create :ensure_metadata
end
......@@ -47,8 +48,11 @@ module Ci
def options=(value)
write_metadata_attribute(:options, :config_options, value)
ensure_metadata.tap do |metadata|
# Store presence of exposed artifacts in build metadata to make it easier to query
ensure_metadata.has_exposed_artifacts = value&.dig(:artifacts, :expose_as).present?
metadata.has_exposed_artifacts = value&.dig(:artifacts, :expose_as).present?
metadata.environment_auto_stop_in = value&.dig(:environment, :auto_stop_in)
end
end
def yaml_variables=(value)
......
......@@ -162,6 +162,10 @@ class Environment < ApplicationRecord
stop_action&.play(current_user)
end
def reset_auto_stop
update_column(:auto_stop_at, nil)
end
def actions_for(environment)
return [] unless manual_actions
......@@ -261,6 +265,17 @@ class Environment < ApplicationRecord
end
end
def auto_stop_in
auto_stop_at - Time.now if auto_stop_at
end
def auto_stop_in=(value)
return unless value
return unless parsed_result = ChronicDuration.parse(value)
self.auto_stop_at = parsed_result.seconds.from_now
end
private
def generate_slug
......
......@@ -262,6 +262,7 @@ class ProjectPolicy < BasePolicy
enable :update_container_image
enable :destroy_container_image
enable :create_environment
enable :update_environment
enable :create_deployment
enable :update_deployment
enable :create_release
......@@ -278,8 +279,6 @@ class ProjectPolicy < BasePolicy
enable :admin_board
enable :push_to_delete_protected_branch
enable :update_project_snippet
enable :update_environment
enable :update_deployment
enable :admin_project_snippet
enable :admin_project_member
enable :admin_note
......
......@@ -24,6 +24,10 @@ class EnvironmentEntity < Grape::Entity
stop_project_environment_path(environment.project, environment)
end
expose :cancel_auto_stop_path, if: -> (*) { can_update_environment? } do |environment|
cancel_auto_stop_project_environment_path(environment.project, environment)
end
expose :cluster_type, if: ->(environment, _) { cluster_platform_kubernetes? } do |environment|
cluster.cluster_type
end
......@@ -37,6 +41,7 @@ class EnvironmentEntity < Grape::Entity
end
expose :created_at, :updated_at
expose :auto_stop_at, expose_nil: false
expose :can_stop do |environment|
environment.available? && can?(current_user, :stop_environment, environment)
......@@ -54,6 +59,10 @@ class EnvironmentEntity < Grape::Entity
can?(request.current_user, :create_environment_terminal, environment)
end
def can_update_environment?
can?(current_user, :update_environment, environment)
end
def cluster_platform_kubernetes?
deployment_platform && deployment_platform.is_a?(Clusters::Platforms::Kubernetes)
end
......
......@@ -29,6 +29,7 @@ module Deployments
environment.external_url = url
end
renew_auto_stop_in
environment.fire_state_event(action)
if environment.save && !environment.stopped?
......@@ -63,6 +64,12 @@ module Deployments
def action
environment_options[:action] || 'start'
end
def renew_auto_stop_in
return unless deployable
environment.auto_stop_in = deployable.environment_auto_stop_in
end
end
end
......
# frozen_string_literal: true
module Environments
class ResetAutoStopService < ::BaseService
def execute(environment)
return error(_('Failed to cancel auto stop because you do not have permission to update the environment.')) unless can_update_environment?(environment)
return error(_('Failed to cancel auto stop because the environment is not set as auto stop.')) unless environment.auto_stop_at?
if environment.reset_auto_stop
success
else
error(_('Failed to cancel auto stop because failed to update the environment.'))
end
end
private
def can_update_environment?(environment)
can?(current_user, :update_environment, environment)
end
end
end
......@@ -5,7 +5,7 @@
- content_for :page_specific_javascripts do
= stylesheet_link_tag 'page_bundles/xterm'
- if can?(current_user, :stop_environment, @environment)
- if @environment.available? && can?(current_user, :stop_environment, @environment)
#stop-environment-modal.modal.fade{ tabindex: -1 }
.modal-dialog
.modal-content
......@@ -40,7 +40,7 @@
= render 'projects/environments/metrics_button', environment: @environment
- if can?(current_user, :update_environment, @environment)
= link_to _('Edit'), edit_project_environment_path(@project, @environment), class: 'btn'
- if can?(current_user, :stop_environment, @environment)
- if @environment.available? && can?(current_user, :stop_environment, @environment)
= button_tag class: 'btn btn-danger', type: 'button', data: { toggle: 'modal',
target: '#stop-environment-modal' } do
= sprite_icon('stop')
......
......@@ -224,6 +224,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :environments, except: [:destroy] do
member do
post :stop
post :cancel_auto_stop
get :terminal
get :metrics
get :additional_metrics
......
# frozen_string_literal: true
class AddAutoStopInToEnvironments < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :environments, :auto_stop_at, :datetime_with_timezone
end
end
# frozen_string_literal: true
class AddEnvironmentAutoStopInToCiBuildsMetadata < ActiveRecord::Migration[5.2]
DOWNTIME = false
def up
add_column :ci_builds_metadata, :environment_auto_stop_in, :string, limit: 255
end
def down
remove_column :ci_builds_metadata, :environment_auto_stop_in
end
end
......@@ -717,6 +717,7 @@ ActiveRecord::Schema.define(version: 2019_12_04_093410) do
t.jsonb "config_options"
t.jsonb "config_variables"
t.boolean "has_exposed_artifacts"
t.string "environment_auto_stop_in", limit: 255
t.index ["build_id"], name: "index_ci_builds_metadata_on_build_id", unique: true
t.index ["build_id"], name: "index_ci_builds_metadata_on_build_id_and_has_exposed_artifacts", where: "(has_exposed_artifacts IS TRUE)"
t.index ["build_id"], name: "index_ci_builds_metadata_on_build_id_and_interruptible", where: "(interruptible = true)"
......@@ -1447,6 +1448,7 @@ ActiveRecord::Schema.define(version: 2019_12_04_093410) do
t.string "environment_type"
t.string "state", default: "available", null: false
t.string "slug", null: false
t.datetime_with_timezone "auto_stop_at"
t.index ["name"], name: "index_environments_on_name_varchar_pattern_ops", opclass: :varchar_pattern_ops
t.index ["project_id", "name"], name: "index_environments_on_project_id_and_name", unique: true
t.index ["project_id", "slug"], name: "index_environments_on_project_id_and_slug", unique: true
......
......@@ -11,6 +11,7 @@ module EE
prevent :create_environment_terminal
prevent :create_deployment
prevent :update_deployment
prevent :update_environment
end
private
......
......@@ -282,6 +282,31 @@ describe Projects::EnvironmentsController do
end
end
describe 'POST #cancel_auto_stop' do
subject { post :cancel_auto_stop, params: params }
let(:params) { environment_params }
context 'when environment is set as auto-stop' do
let(:environment) { create(:environment, :will_auto_stop, name: 'staging', project: project) }
it_behaves_like 'successful response for #cancel_auto_stop'
context 'when the environment is protected' do
before do
stub_licensed_features(protected_environments: true)
create(:protected_environment, name: 'staging', project: project)
end
it 'shows NOT Found' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
def environment_params(opts = {})
opts.reverse_merge(namespace_id: project.namespace,
project_id: project,
......
......@@ -71,6 +71,8 @@
},
"can_stop": {
"type": "boolean"
}
},
"cancel_auto_stop_path": { "type": "string" },
"auto_stop_at": { "type": "string", "format": "date-time" }
}
}
......@@ -10,7 +10,7 @@ module Gitlab
class Environment < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Configurable
ALLOWED_KEYS = %i[name url action on_stop kubernetes].freeze
ALLOWED_KEYS = %i[name url action on_stop auto_stop_in kubernetes].freeze
entry :kubernetes, Entry::Kubernetes, description: 'Kubernetes deployment configuration.'
......@@ -49,6 +49,7 @@ module Gitlab
validates :on_stop, type: String, allow_nil: true
validates :kubernetes, type: Hash, allow_nil: true
validates :auto_stop_in, duration: true, allow_nil: true
end
end
......@@ -80,6 +81,10 @@ module Gitlab
value[:kubernetes]
end
def auto_stop_in
value[:auto_stop_in]
end
def value
case @config
when String then { name: @config, action: 'start' }
......
......@@ -2312,6 +2312,9 @@ msgstr ""
msgid "Auto License Compliance"
msgstr ""
msgid "Auto stop successfully canceled."
msgstr ""
msgid "Auto-cancel redundant, pending pipelines"
msgstr ""
......@@ -7251,6 +7254,15 @@ msgstr ""
msgid "Failed to assign a user because no user was found."
msgstr ""
msgid "Failed to cancel auto stop because failed to update the environment."
msgstr ""
msgid "Failed to cancel auto stop because the environment is not set as auto stop."
msgstr ""
msgid "Failed to cancel auto stop because you do not have permission to update the environment."
msgstr ""
msgid "Failed to change the owner"
msgstr ""
......
......@@ -5,16 +5,14 @@ require 'spec_helper'
describe Projects::EnvironmentsController do
include MetricsDashboardHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:maintainer) { create(:user, name: 'main-dos').tap { |u| project.add_maintainer(u) } }
let_it_be(:reporter) { create(:user, name: 'repo-dos').tap { |u| project.add_reporter(u) } }
let(:user) { maintainer }
let_it_be(:environment) do
create(:environment, name: 'production', project: project)
end
let!(:environment) { create(:environment, name: 'production', project: project) }
before do
project.add_maintainer(user)
sign_in(user)
end
......@@ -245,6 +243,36 @@ describe Projects::EnvironmentsController do
end
end
describe 'POST #cancel_auto_stop' do
subject { post :cancel_auto_stop, params: params }
let(:params) { environment_params }
context 'when environment is set as auto-stop' do
let(:environment) { create(:environment, :will_auto_stop, name: 'staging', project: project) }
it_behaves_like 'successful response for #cancel_auto_stop'
context 'when user is reporter' do
let(:user) { reporter }
it 'shows NOT Found' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'when environment is not set as auto-stop' do
let(:environment) { create(:environment, name: 'staging', project: project) }
it_behaves_like 'failed response for #cancel_auto_stop' do
let(:message) { 'the environment is not set as auto stop' }
end
end
end
describe 'GET #terminal' do
context 'with valid id' do
it 'responds with a status code 200' do
......@@ -320,23 +348,23 @@ describe Projects::EnvironmentsController do
end
describe 'GET #metrics_redirect' do
let(:project) { create(:project) }
it 'redirects to environment if it exists' do
environment = create(:environment, name: 'production', project: project)
get :metrics_redirect, params: { namespace_id: project.namespace, project_id: project }
expect(response).to redirect_to(environment_metrics_path(environment))
end
it 'redirects to empty metrics page if no environment exists' do
context 'when there are no environments' do
let(:environment) { }
it 'redirects to empty metrics page' do
get :metrics_redirect, params: { namespace_id: project.namespace, project_id: project }
expect(response).to be_ok
expect(response).to render_template 'empty_metrics'
end
end
end
describe 'GET #metrics' do
before do
......@@ -549,6 +577,10 @@ describe Projects::EnvironmentsController do
let(:project) { project_with_dashboard(dashboard_path, dashboard_yml) }
let(:environment) { create(:environment, name: 'production', project: project) }
before do
project.add_maintainer(user)
end
it_behaves_like 'the specified dashboard', 'Test Dashboard'
end
......
......@@ -44,5 +44,13 @@ FactoryBot.define do
status { 'created' }
self.when { 'manual' }
end
trait :auto_stopped do
auto_stop_at { 1.day.ago }
end
trait :will_auto_stop do
auto_stop_at { 1.day.from_now }
end
end
end
......@@ -144,8 +144,8 @@ describe 'Environments page', :js do
expect(page).to have_content('No deployments yet')
end
it 'does not show stip button when environment is not stoppable' do
expect(page).not_to have_selector(stop_button_selector)
it 'shows stop button when environment is not stoppable' do
expect(page).to have_selector(stop_button_selector)
end
end
......@@ -205,7 +205,7 @@ describe 'Environments page', :js do
end
it 'shows a stop button' do
expect(page).not_to have_selector(stop_button_selector)
expect(page).to have_selector(stop_button_selector)
end
it 'does not show external link button' do
......
......@@ -24,9 +24,11 @@
"has_stop_action": { "type": "boolean" },
"environment_path": { "type": "string" },
"stop_path": { "type": "string" },
"cancel_auto_stop_path": { "type": "string" },
"folder_path": { "type": "string" },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" },
"auto_stop_at": { "type": "string", "format": "date-time" },
"can_stop": { "type": "boolean" },
"cluster_type": { "type": "types/nullable_string.json" },
"terminal_path": { "type": "types/nullable_string.json" },
......
......@@ -206,6 +206,35 @@ describe Gitlab::Ci::Config::Entry::Environment do
end
end
context 'when auto_stop_in is specified' do
let(:config) do
{
name: 'review/$CI_COMMIT_REF_NAME',
url: 'https://$CI_COMMIT_REF_NAME.review.gitlab.com',
on_stop: 'stop_review',
auto_stop_in: auto_stop_in
}
end
context 'when auto_stop_in is correct format' do
let(:auto_stop_in) { '2 days' }
it 'becomes valid' do
expect(entry).to be_valid
expect(entry.auto_stop_in).to eq(auto_stop_in)
end
end
context 'when auto_stop_in is invalid format' do
let(:auto_stop_in) { 'invalid' }
it 'becomes invalid' do
expect(entry).not_to be_valid
expect(entry.errors).to include 'environment auto stop in should be a duration'
end
end
end
context 'when configuration is invalid' do
context 'when configuration is an array' do
let(:config) { ['env'] }
......
......@@ -4113,4 +4113,20 @@ describe Ci::Build do
end
end
end
describe '#environment_auto_stop_in' do
subject { build.environment_auto_stop_in }
context 'when build option has environment auto_stop_in' do
let(:build) { create(:ci_build, options: { environment: { name: 'test', auto_stop_in: '1 day' } }) }
it { is_expected.to eq('1 day') }
end
context 'when build option does not have environment auto_stop_in' do
let(:build) { create(:ci_build) }
it { is_expected.to be_nil }
end
end
end
......@@ -441,6 +441,16 @@ describe Environment, :use_clean_rails_memory_store_caching do
end
end
describe '#reset_auto_stop' do
subject { environment.reset_auto_stop }
let(:environment) { create(:environment, :auto_stopped) }
it 'nullifies the auto_stop_at' do
expect { subject }.to change(environment, :auto_stop_at).from(Time).to(nil)
end
end
describe '#actions_for' do
let(:deployment) { create(:deployment, :success, environment: environment) }
let(:pipeline) { deployment.deployable.pipeline }
......@@ -1088,6 +1098,52 @@ describe Environment, :use_clean_rails_memory_store_caching do
end
end
describe '#auto_stop_in' do
subject { environment.auto_stop_in }
context 'when environment will be expired' do
let(:environment) { build(:environment, :will_auto_stop) }
it 'returns when it will expire' do
Timecop.freeze { is_expected.to eq(1.day.to_i) }
end
end
context 'when environment is not expired' do
let(:environment) { build(:environment) }
it { is_expected.to be_nil }
end
end
describe '#auto_stop_in=' do
subject { environment.auto_stop_in = value }
let(:environment) { build(:environment) }
where(:value, :expected_result) do
'2 days' | 2.days.to_i
'1 week' | 1.week.to_i
'2h20min' | 2.hours.to_i + 20.minutes.to_i
'abcdef' | ChronicDuration::DurationParseError
'' | nil
nil | nil
end
with_them do
it 'sets correct auto_stop_in' do
Timecop.freeze do
if expected_result.is_a?(Integer) || expected_result.nil?
subject
expect(environment.auto_stop_in).to eq(expected_result)
else
expect { subject }.to raise_error(expected_result)
end
end
end
end
end
describe '.find_or_create_by_name' do
it 'finds an existing environment if it exists' do
env = create(:environment)
......
......@@ -68,7 +68,7 @@ describe EnvironmentPolicy do
nil | false
:guest | false
:reporter | false
:developer | false
:developer | true
:maintainer | true
end
......
......@@ -42,13 +42,13 @@ describe ProjectPolicy do
update_commit_status create_build update_build create_pipeline
update_pipeline create_merge_request_from create_wiki push_code
resolve_note create_container_image update_container_image destroy_container_image
create_environment create_deployment update_deployment create_release update_release
create_environment update_environment create_deployment update_deployment create_release update_release
]
end
let(:base_maintainer_permissions) do
%i[
push_to_delete_protected_branch update_project_snippet update_environment
push_to_delete_protected_branch update_project_snippet
admin_project_snippet admin_project_member admin_note admin_wiki admin_project
admin_commit_status admin_build admin_container_image
admin_pipeline admin_environment admin_deployment destroy_release add_cluster
......
......@@ -62,4 +62,12 @@ describe EnvironmentEntity do
end
end
end
context 'with auto_stop_in' do
let(:environment) { create(:environment, :will_auto_stop) }
it 'exposes auto stop related information' do
expect(subject).to include(:cancel_auto_stop_path, :auto_stop_at)
end
end
end
......@@ -781,6 +781,25 @@ describe Ci::CreatePipelineService do
end
end
context 'with environment with auto_stop_in' do
before do
config = YAML.dump(
deploy: {
environment: { name: "review/$CI_COMMIT_REF_NAME", auto_stop_in: '1 day' },
script: 'ls'
})
stub_ci_pipeline_yaml_file(config)
end
it 'creates the environment with auto stop in' do
result = execute_service
expect(result).to be_persisted
expect(result.builds.first.options[:environment][:auto_stop_in]).to eq('1 day')
end
end
context 'with environment name including persisted variables' do
before do
config = YAML.dump(
......
......@@ -115,6 +115,21 @@ describe Deployments::AfterCreateService do
expect(subject.environment.external_url).to eq('http://master.review-apps.gitlab.com')
end
end
context 'when auto_stop_in are used' do
let(:options) do
{ name: 'production', auto_stop_in: '1 day' }
end
it 'renews auto stop at' do
Timecop.freeze do
environment.update!(auto_stop_at: nil)
expect { subject.execute }
.to change { environment.reset.auto_stop_at&.round }.from(nil).to(1.day.since.round)
end
end
end
end
describe '#expanded_environment_url' do
......
# frozen_string_literal: true
require 'spec_helper'
describe Environments::ResetAutoStopService do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } }
let_it_be(:reporter) { create(:user).tap { |user| project.add_reporter(user) } }
let(:user) { developer }
let(:service) { described_class.new(project, user) }
describe '#execute' do
subject { service.execute(environment) }
context 'when environment will be stopped automatically' do
let(:environment) { create(:environment, :will_auto_stop, project: project) }
it 'resets auto stop' do
expect(environment).to receive(:reset_auto_stop).and_call_original
expect(subject[:status]).to eq(:success)
end
context 'when failed to reset auto stop' do
before do
expect(environment).to receive(:reset_auto_stop) { false }
end
it 'returns error' do
expect(subject[:status]).to eq(:error)
expect(subject[:message]).to eq('Failed to cancel auto stop because failed to update the environment.')
end
end
context 'when user is reporter' do
let(:user) { reporter }
it 'returns error' do
expect(subject[:status]).to eq(:error)
expect(subject[:message]).to eq('Failed to cancel auto stop because you do not have permission to update the environment.')
end
end
end
context 'when environment will not be stopped automatically' do
let(:environment) { create(:environment, project: project) }
it 'returns error' do
expect(subject[:status]).to eq(:error)
expect(subject[:message]).to eq('Failed to cancel auto stop because the environment is not set as auto stop.')
end
end
end
end
......@@ -39,12 +39,13 @@ RSpec.shared_context 'ProjectPolicy context' do
update_pipeline create_merge_request_from create_wiki push_code
resolve_note create_container_image update_container_image
create_environment create_deployment update_deployment create_release update_release
update_environment
]
end
let(:base_maintainer_permissions) do
%i[
push_to_delete_protected_branch update_project_snippet update_environment
push_to_delete_protected_branch update_project_snippet
admin_project_snippet admin_project_member admin_note admin_wiki admin_project
admin_commit_status admin_build admin_container_image
admin_pipeline admin_environment admin_deployment destroy_release add_cluster
......
# frozen_string_literal: true
shared_examples_for 'successful response for #cancel_auto_stop' do
include GitlabRoutingHelper
context 'when request is html' do
let(:params) { environment_params(format: :html) }
it 'redirects to show page' do
subject
expect(response).to redirect_to(environment_path(environment))
expect(flash[:notice]).to eq('Auto stop successfully canceled.')
end
it 'expires etag caching' do
expect_next_instance_of(Gitlab::EtagCaching::Store) do |etag_caching|
expect(etag_caching).to receive(:touch).with(project_environments_path(project, format: :json))
end
subject
end
end
context 'when request is js' do
let(:params) { environment_params(format: :json) }
it 'responds as ok' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['message']).to eq('Auto stop successfully canceled.')
end
it 'expires etag caching' do
expect_next_instance_of(Gitlab::EtagCaching::Store) do |etag_caching|
expect(etag_caching).to receive(:touch).with(project_environments_path(project, format: :json))
end
subject
end
end
end
shared_examples_for 'failed response for #cancel_auto_stop' do
context 'when request is html' do
let(:params) { environment_params(format: :html) }
it 'redirects to show page' do
subject
expect(response).to redirect_to(environment_path(environment))
expect(flash[:alert]).to eq("Failed to cancel auto stop because #{message}.")
end
end
context 'when request is js' do
let(:params) { environment_params(format: :json) }
it 'responds as unprocessable entity' do
subject
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response['message']).to eq("Failed to cancel auto stop because #{message}.")
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment