Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-06-03 06:09:20 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-06-03 06:09:20 +0300
commit531a68a424fddcc936ba6271fbb10a5aa1da75a7 (patch)
tree6c4484fede42157f3d1187825d824988a971d0ca /spec
parent94aee277313c742f9b54b329fd4043cde7a50915 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/incidents_controller_spec.rb1
-rw-r--r--spec/factories/terraform/state.rb4
-rw-r--r--spec/features/incidents/incidents_list_spec.rb13
-rw-r--r--spec/frontend/incidents/components/incidents_list_spec.js15
-rw-r--r--spec/graphql/mutations/issues/set_escalation_status_spec.rb10
-rw-r--r--spec/graphql/mutations/terraform/state/delete_spec.rb10
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb16
-rw-r--r--spec/graphql/types/issue_type_spec.rb8
-rw-r--r--spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb81
-rw-r--r--spec/models/concerns/issuable_spec.rb8
-rw-r--r--spec/models/merge_request/cleanup_schedule_spec.rb35
-rw-r--r--spec/models/terraform/state_spec.rb18
-rw-r--r--spec/models/terraform/state_version_spec.rb1
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb8
-rw-r--r--spec/requests/api/graphql/mutations/user_preferences/update_spec.rb22
-rw-r--r--spec/requests/api/graphql/terraform/state/delete_spec.rb8
-rw-r--r--spec/requests/api/terraform/state_spec.rb31
-rw-r--r--spec/serializers/issue_sidebar_basic_entity_spec.rb10
-rw-r--r--spec/services/alert_management/alerts/update_service_spec.rb8
-rw-r--r--spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb8
-rw-r--r--spec/services/issues/update_service_spec.rb8
-rw-r--r--spec/services/terraform/remote_state_handler_spec.rb40
-rw-r--r--spec/services/terraform/states/destroy_service_spec.rb35
-rw-r--r--spec/services/terraform/states/trigger_destroy_service_spec.rb45
-rw-r--r--spec/workers/schedule_merge_request_cleanup_refs_worker_spec.rb6
-rw-r--r--spec/workers/terraform/states/destroy_worker_spec.rb30
26 files changed, 309 insertions, 170 deletions
diff --git a/spec/controllers/projects/incidents_controller_spec.rb b/spec/controllers/projects/incidents_controller_spec.rb
index 20cf0dcfd3a..460821634b0 100644
--- a/spec/controllers/projects/incidents_controller_spec.rb
+++ b/spec/controllers/projects/incidents_controller_spec.rb
@@ -43,7 +43,6 @@ RSpec.describe Projects::IncidentsController do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
- expect(Gon.features).to include('incidentEscalations' => true)
end
context 'when user is unauthorized' do
diff --git a/spec/factories/terraform/state.rb b/spec/factories/terraform/state.rb
index fb63c845073..c40fa14b4f8 100644
--- a/spec/factories/terraform/state.rb
+++ b/spec/factories/terraform/state.rb
@@ -12,6 +12,10 @@ FactoryBot.define do
locked_by_user { association(:user) }
end
+ trait :deletion_in_progress do
+ deleted_at { Time.current }
+ end
+
trait :with_version do
after(:create) do |state|
create(:terraform_state_version, terraform_state: state)
diff --git a/spec/features/incidents/incidents_list_spec.rb b/spec/features/incidents/incidents_list_spec.rb
index 789cc89e083..3241e71f537 100644
--- a/spec/features/incidents/incidents_list_spec.rb
+++ b/spec/features/incidents/incidents_list_spec.rb
@@ -44,18 +44,5 @@ RSpec.describe 'Incident Management index', :js do
expect(table).to have_content('Date created')
expect(table).to have_content('Assignees')
end
-
- context 'when :incident_escalations feature is disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it 'does not include the Status columns' do
- visit project_incidents_path(project)
- wait_for_requests
-
- expect(page.find('.gl-table')).not_to have_content('Status')
- end
- end
end
end
diff --git a/spec/frontend/incidents/components/incidents_list_spec.js b/spec/frontend/incidents/components/incidents_list_spec.js
index a556f3c17f3..356480f931e 100644
--- a/spec/frontend/incidents/components/incidents_list_spec.js
+++ b/spec/frontend/incidents/components/incidents_list_spec.js
@@ -85,7 +85,6 @@ describe('Incidents List', () => {
assigneeUsernameQuery: '',
slaFeatureAvailable: true,
canCreateIncident: true,
- incidentEscalationsAvailable: true,
...provide,
},
stubs: {
@@ -211,20 +210,6 @@ describe('Incidents List', () => {
expect(status.classes('gl-text-truncate')).toBe(true);
});
});
-
- describe('when feature is disabled', () => {
- beforeEach(() => {
- mountComponent({
- data: { incidents: { list: mockIncidents }, incidentsCount },
- provide: { incidentEscalationsAvailable: false },
- loading: false,
- });
- });
-
- it('is absent if feature flag is disabled', () => {
- expect(findEscalationStatus().length).toBe(0);
- });
- });
});
it('contains a link to the incident details page', async () => {
diff --git a/spec/graphql/mutations/issues/set_escalation_status_spec.rb b/spec/graphql/mutations/issues/set_escalation_status_spec.rb
index d41118b1812..f04d396efb8 100644
--- a/spec/graphql/mutations/issues/set_escalation_status_spec.rb
+++ b/spec/graphql/mutations/issues/set_escalation_status_spec.rb
@@ -50,16 +50,6 @@ RSpec.describe Mutations::Issues::SetEscalationStatus do
expect { result }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature unavailable for provided issue')
end
end
-
- context 'with feature disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it 'raises an error' do
- expect { result }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature unavailable for provided issue')
- end
- end
end
end
end
diff --git a/spec/graphql/mutations/terraform/state/delete_spec.rb b/spec/graphql/mutations/terraform/state/delete_spec.rb
index 313a85a4bac..66d4b50741f 100644
--- a/spec/graphql/mutations/terraform/state/delete_spec.rb
+++ b/spec/graphql/mutations/terraform/state/delete_spec.rb
@@ -34,12 +34,12 @@ RSpec.describe Mutations::Terraform::State::Delete do
state.project.add_maintainer(user)
end
- it 'deletes the state', :aggregate_failures do
- expect do
- expect(subject).to eq(errors: [])
- end.to change { ::Terraform::State.count }.by(-1)
+ it 'schedules the state for deletion', :aggregate_failures do
+ expect_next_instance_of(Terraform::States::TriggerDestroyService, state, current_user: user) do |service|
+ expect(service).to receive(:execute).once.and_return(ServiceResponse.success)
+ end
- expect { state.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ subject
end
end
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index e6ec9d8c895..1f2e2aec023 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -573,22 +573,6 @@ RSpec.describe Resolvers::IssuesResolver do
issues = resolve_issues(sort: :created_desc).to_a
expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
end
-
- context 'when incident_escalations feature flag is disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it 'defaults ascending status sort to created_desc' do
- issues = resolve_issues(sort: :escalation_status_asc).to_a
- expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
- end
-
- it 'defaults descending status sort to created_desc' do
- issues = resolve_issues(sort: :escalation_status_desc).to_a
- expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
- end
- end
end
context 'when sorting with non-stable cursors' do
diff --git a/spec/graphql/types/issue_type_spec.rb b/spec/graphql/types/issue_type_spec.rb
index 920aadd2faf..e7454b85357 100644
--- a/spec/graphql/types/issue_type_spec.rb
+++ b/spec/graphql/types/issue_type_spec.rb
@@ -291,14 +291,6 @@ RSpec.describe GitlabSchema.types['Issue'] do
let!(:escalation_status) { create(:incident_management_issuable_escalation_status, issue: issue) }
it { is_expected.to eq(escalation_status.status_name.to_s.upcase) }
-
- context 'with feature disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it { is_expected.to be_nil }
- end
end
end
end
diff --git a/spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb b/spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb
new file mode 100644
index 00000000000..30e5fbbd803
--- /dev/null
+++ b/spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Load balancer behavior with errors inside a transaction', :redis, :delete do
+ let(:model) { ApplicationRecord }
+ let(:db_host) { model.connection_pool.db_config.host }
+
+ let(:test_table_name) { '_test_foo' }
+
+ before do
+ # Patch in our load balancer config, simply pointing at the test database twice
+ allow(Gitlab::Database::LoadBalancing::Configuration).to receive(:for_model) do |base_model|
+ Gitlab::Database::LoadBalancing::Configuration.new(base_model, [db_host, db_host])
+ end
+
+ Gitlab::Database::LoadBalancing::Setup.new(ApplicationRecord).setup
+
+ model.connection.execute(<<~SQL)
+ CREATE TABLE IF NOT EXISTS #{test_table_name} (id SERIAL PRIMARY KEY, value INTEGER)
+ SQL
+ end
+
+ after do
+ model.connection.execute(<<~SQL)
+ DROP TABLE IF EXISTS #{test_table_name}
+ SQL
+ end
+
+ def execute(conn)
+ conn.execute("INSERT INTO #{test_table_name} (value) VALUES (1)")
+ backend_pid = conn.execute("SELECT pg_backend_pid() AS pid").to_a.first['pid']
+
+ # This will result in a PG error, which is not raised.
+ # Instead, we retry the statement on a fresh connection (where the pid is different and it does nothing)
+ # and the load balancer continues with a fresh connection and no transaction if a transaction was open previously
+ conn.execute(<<~SQL)
+ SELECT CASE
+ WHEN pg_backend_pid() = #{backend_pid} THEN
+ pg_terminate_backend(#{backend_pid})
+ END
+ SQL
+
+ # This statement will execute on a new connection, and violate transaction semantics
+ # if we were in a transaction before
+ conn.execute("INSERT INTO #{test_table_name} (value) VALUES (2)")
+ end
+
+ it 'logs a warning when violating transaction semantics with writes' do
+ conn = model.connection
+
+ expect(::Gitlab::Database::LoadBalancing::Logger).to receive(:warn).with(hash_including(event: :transaction_leak))
+
+ conn.transaction do
+ expect(conn).to be_transaction_open
+
+ execute(conn)
+
+ expect(conn).not_to be_transaction_open
+ end
+
+ values = conn.execute("SELECT value FROM #{test_table_name}").to_a.map { |row| row['value'] }
+ expect(values).to contain_exactly(2) # Does not include 1 because the transaction was aborted and leaked
+ end
+
+ it 'does not log a warning when no transaction is open to be leaked' do
+ conn = model.connection
+
+ expect(::Gitlab::Database::LoadBalancing::Logger)
+ .not_to receive(:warn).with(hash_including(event: :transaction_leak))
+
+ expect(conn).not_to be_transaction_open
+
+ execute(conn)
+
+ expect(conn).not_to be_transaction_open
+
+ values = conn.execute("SELECT value FROM #{test_table_name}").to_a.map { |row| row['value'] }
+ expect(values).to contain_exactly(1, 2) # Includes both rows because there was no transaction to roll back
+ end
+end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index e8e9c263d23..87821de3cf5 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -982,14 +982,6 @@ RSpec.describe Issuable do
subject { issuable.supports_escalation? }
it { is_expected.to eq(supports_escalation) }
-
- context 'with feature disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it { is_expected.to eq(false) }
- end
end
end
diff --git a/spec/models/merge_request/cleanup_schedule_spec.rb b/spec/models/merge_request/cleanup_schedule_spec.rb
index 85208f901fd..9c50b64f2bd 100644
--- a/spec/models/merge_request/cleanup_schedule_spec.rb
+++ b/spec/models/merge_request/cleanup_schedule_spec.rb
@@ -120,6 +120,41 @@ RSpec.describe MergeRequest::CleanupSchedule do
end
end
+ describe '.stuck' do
+ let!(:cleanup_schedule_1) { create(:merge_request_cleanup_schedule, updated_at: 1.day.ago) }
+ let!(:cleanup_schedule_2) { create(:merge_request_cleanup_schedule, :running, updated_at: 5.hours.ago) }
+ let!(:cleanup_schedule_3) { create(:merge_request_cleanup_schedule, :running, updated_at: 7.hours.ago) }
+ let!(:cleanup_schedule_4) { create(:merge_request_cleanup_schedule, :completed, updated_at: 1.day.ago) }
+ let!(:cleanup_schedule_5) { create(:merge_request_cleanup_schedule, :failed, updated_at: 1.day.ago) }
+
+ it 'returns records that has been in running state for more than 6 hours' do
+ expect(described_class.stuck).to match_array([cleanup_schedule_3])
+ end
+ end
+
+ describe '.stuck_retry!' do
+ let!(:cleanup_schedule_1) { create(:merge_request_cleanup_schedule, :running, updated_at: 5.hours.ago) }
+ let!(:cleanup_schedule_2) { create(:merge_request_cleanup_schedule, :running, updated_at: 7.hours.ago) }
+
+ it 'sets stuck records to unstarted' do
+ expect { described_class.stuck_retry! }.to change { cleanup_schedule_2.reload.unstarted? }.from(false).to(true)
+ end
+
+ context 'when there are more than 5 stuck schedules' do
+ before do
+ create_list(:merge_request_cleanup_schedule, 5, :running, updated_at: 1.day.ago)
+ end
+
+ it 'only retries 5 stuck schedules at once' do
+ expect(described_class.stuck.count).to eq 6
+
+ described_class.stuck_retry!
+
+ expect(described_class.stuck.count).to eq 1
+ end
+ end
+ end
+
describe '.start_next' do
let!(:cleanup_schedule_1) { create(:merge_request_cleanup_schedule, :completed, scheduled_at: 1.day.ago) }
let!(:cleanup_schedule_2) { create(:merge_request_cleanup_schedule, scheduled_at: 2.days.ago) }
diff --git a/spec/models/terraform/state_spec.rb b/spec/models/terraform/state_spec.rb
index a113ae37203..a484952bfe9 100644
--- a/spec/models/terraform/state_spec.rb
+++ b/spec/models/terraform/state_spec.rb
@@ -11,8 +11,6 @@ RSpec.describe Terraform::State do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_presence_of(:project_id) }
- it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
-
describe 'scopes' do
describe '.ordered_by_name' do
let_it_be(:project) { create(:project) }
@@ -40,22 +38,6 @@ RSpec.describe Terraform::State do
end
end
- describe '#destroy' do
- let(:terraform_state) { create(:terraform_state) }
- let(:user) { terraform_state.project.creator }
-
- it 'deletes when the state is unlocked' do
- expect(terraform_state.destroy).to be_truthy
- end
-
- it 'fails to delete when the state is locked', :aggregate_failures do
- terraform_state.update!(lock_xid: SecureRandom.uuid, locked_by_user: user, locked_at: Time.current)
-
- expect(terraform_state.destroy).to be_falsey
- expect(terraform_state.errors.full_messages).to eq(["You cannot remove the State file because it's locked. Unlock the State file first before removing it."])
- end
- end
-
describe '#latest_file' do
let(:terraform_state) { create(:terraform_state, :with_version) }
let(:latest_version) { terraform_state.latest_version }
diff --git a/spec/models/terraform/state_version_spec.rb b/spec/models/terraform/state_version_spec.rb
index 7af9b7897ff..22b1397f30a 100644
--- a/spec/models/terraform/state_version_spec.rb
+++ b/spec/models/terraform/state_version_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Terraform::StateVersion do
it { is_expected.to be_a FileStoreMounter }
+ it { is_expected.to be_a EachBatch }
it { is_expected.to belong_to(:terraform_state).required }
it { is_expected.to belong_to(:created_by_user).class_name('User').optional }
diff --git a/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb b/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb
index 0166871502b..a81364d37b2 100644
--- a/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb
@@ -49,14 +49,6 @@ RSpec.describe 'Setting the escalation status of an incident' do
it_behaves_like 'a mutation that returns top-level errors', errors: ['Feature unavailable for provided issue']
end
- context 'with feature disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it_behaves_like 'a mutation that returns top-level errors', errors: ['Feature unavailable for provided issue']
- end
-
it 'sets given escalation_policy to the escalation status for the issue' do
post_graphql_mutation(mutation, current_user: current_user)
diff --git a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb
index 85194e6eb20..e1c7fd9d60d 100644
--- a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb
@@ -28,17 +28,6 @@ RSpec.describe Mutations::UserPreferences::Update do
expect(current_user.user_preference.persisted?).to eq(true)
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
end
-
- context 'when incident_escalations feature flag is disabled' do
- let(:sort_value) { 'ESCALATION_STATUS_ASC' }
-
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: ['Feature flag `incident_escalations` must be enabled to use this sort order.']
- end
end
context 'when user has existing preference' do
@@ -56,16 +45,5 @@ RSpec.describe Mutations::UserPreferences::Update do
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
end
-
- context 'when incident_escalations feature flag is disabled' do
- let(:sort_value) { 'ESCALATION_STATUS_DESC' }
-
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it_behaves_like 'a mutation that returns top-level errors',
- errors: ['Feature flag `incident_escalations` must be enabled to use this sort order.']
- end
end
end
diff --git a/spec/requests/api/graphql/terraform/state/delete_spec.rb b/spec/requests/api/graphql/terraform/state/delete_spec.rb
index 35927d03b49..ba0619ea611 100644
--- a/spec/requests/api/graphql/terraform/state/delete_spec.rb
+++ b/spec/requests/api/graphql/terraform/state/delete_spec.rb
@@ -12,12 +12,12 @@ RSpec.describe 'delete a terraform state' do
let(:mutation) { graphql_mutation(:terraform_state_delete, id: state.to_global_id.to_s) }
before do
+ expect_next_instance_of(Terraform::States::TriggerDestroyService, state, current_user: user) do |service|
+ expect(service).to receive(:execute).once.and_return(ServiceResponse.success)
+ end
+
post_graphql_mutation(mutation, current_user: user)
end
include_examples 'a working graphql query'
-
- it 'deletes the state' do
- expect { state.reload }.to raise_error(ActiveRecord::RecordNotFound)
- end
end
diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb
index f49e799cf5d..e8458db4a4d 100644
--- a/spec/requests/api/terraform/state_spec.rb
+++ b/spec/requests/api/terraform/state_spec.rb
@@ -71,6 +71,18 @@ RSpec.describe API::Terraform::State, :snowplow do
end
end
+ shared_context 'cannot access a state that is scheduled for deletion' do
+ before do
+ state.update!(deleted_at: Time.current)
+ end
+
+ it 'returns unprocessable entity' do
+ request
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
+ end
+
describe 'GET /projects/:id/terraform/state/:name' do
subject(:request) { get api(state_path), headers: auth_header }
@@ -106,6 +118,8 @@ RSpec.describe API::Terraform::State, :snowplow do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ it_behaves_like 'cannot access a state that is scheduled for deletion'
end
context 'with developer permissions' do
@@ -191,6 +205,8 @@ RSpec.describe API::Terraform::State, :snowplow do
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
end
+
+ it_behaves_like 'cannot access a state that is scheduled for deletion'
end
context 'without body' do
@@ -269,13 +285,19 @@ RSpec.describe API::Terraform::State, :snowplow do
context 'with maintainer permissions' do
let(:current_user) { maintainer }
+ let(:deletion_service) { instance_double(Terraform::States::TriggerDestroyService) }
- it 'deletes the state and returns empty body' do
- expect { request }.to change { Terraform::State.count }.by(-1)
+ it 'schedules the state for deletion and returns empty body' do
+ expect(Terraform::States::TriggerDestroyService).to receive(:new).and_return(deletion_service)
+ expect(deletion_service).to receive(:execute).once
+
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to be_empty
end
+
+ it_behaves_like 'cannot access a state that is scheduled for deletion'
end
context 'with developer permissions' do
@@ -305,6 +327,7 @@ RSpec.describe API::Terraform::State, :snowplow do
subject(:request) { post api("#{state_path}/lock"), headers: auth_header, params: params }
it_behaves_like 'endpoint with unique user tracking'
+ it_behaves_like 'cannot access a state that is scheduled for deletion'
it 'locks the terraform state' do
request
@@ -359,6 +382,10 @@ RSpec.describe API::Terraform::State, :snowplow do
let(:lock_id) { 'irrelevant to this test, just needs to be present' }
end
+ it_behaves_like 'cannot access a state that is scheduled for deletion' do
+ let(:lock_id) { 'irrelevant to this test, just needs to be present' }
+ end
+
context 'with the correct lock id' do
let(:lock_id) { '123-456' }
diff --git a/spec/serializers/issue_sidebar_basic_entity_spec.rb b/spec/serializers/issue_sidebar_basic_entity_spec.rb
index 564ffb1aea9..64a271e359a 100644
--- a/spec/serializers/issue_sidebar_basic_entity_spec.rb
+++ b/spec/serializers/issue_sidebar_basic_entity_spec.rb
@@ -59,16 +59,6 @@ RSpec.describe IssueSidebarBasicEntity do
expect(entity[:current_user][:can_update_escalation_status]).to be(false)
end
end
-
- context 'with :incident_escalations feature flag disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it 'is not present' do
- expect(entity[:current_user]).not_to include(:can_update_escalation_status)
- end
- end
end
end
end
diff --git a/spec/services/alert_management/alerts/update_service_spec.rb b/spec/services/alert_management/alerts/update_service_spec.rb
index f02607b8174..9bdc9970807 100644
--- a/spec/services/alert_management/alerts/update_service_spec.rb
+++ b/spec/services/alert_management/alerts/update_service_spec.rb
@@ -291,14 +291,6 @@ RSpec.describe AlertManagement::Alerts::UpdateService do
it_behaves_like 'does not sync with the incident status'
end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it_behaves_like 'does not sync with the incident status'
- end
end
end
end
diff --git a/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb b/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb
index 25164df40ca..6c99631fcb0 100644
--- a/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb
+++ b/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb
@@ -42,14 +42,6 @@ RSpec.describe IncidentManagement::IssuableEscalationStatuses::PrepareUpdateServ
it_behaves_like 'successful response', { status_event: :acknowledge }
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it_behaves_like 'availability error response'
- end
-
context 'when user is anonymous' do
let(:current_user) { nil }
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index d496857bb25..d11fe772023 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -1230,14 +1230,6 @@ RSpec.describe Issues::UpdateService, :mailer do
it_behaves_like 'updates the escalation status record', :acknowledged
end
-
- context 'with :incident_escalations feature flag disabled' do
- before do
- stub_feature_flags(incident_escalations: false)
- end
-
- it_behaves_like 'does not change the status record'
- end
end
context 'when issue type is not incident' do
diff --git a/spec/services/terraform/remote_state_handler_spec.rb b/spec/services/terraform/remote_state_handler_spec.rb
index ca392849d49..19c1d4109e9 100644
--- a/spec/services/terraform/remote_state_handler_spec.rb
+++ b/spec/services/terraform/remote_state_handler_spec.rb
@@ -33,6 +33,14 @@ RSpec.describe Terraform::RemoteStateHandler do
it 'returns the state' do
expect(subject.find_with_lock).to eq(state)
end
+
+ context 'with a state scheduled for deletion' do
+ let!(:state) { create(:terraform_state, :deletion_in_progress, project: project, name: 'state') }
+
+ it 'raises an exception' do
+ expect { subject.find_with_lock }.to raise_error(described_class::StateDeletedError)
+ end
+ end
end
end
end
@@ -84,6 +92,13 @@ RSpec.describe Terraform::RemoteStateHandler do
.to raise_error(described_class::StateLockedError)
end
+ it 'raises an exception if the state is scheduled for deletion' do
+ create(:terraform_state, :deletion_in_progress, project: project, name: 'new-state')
+
+ expect { handler.handle_with_lock }
+ .to raise_error(described_class::StateDeletedError)
+ end
+
context 'user does not have permission to modify state' do
let(:user) { developer }
@@ -127,24 +142,28 @@ RSpec.describe Terraform::RemoteStateHandler do
expect { handler.lock! }.to raise_error(described_class::StateLockedError)
end
+
+ it 'raises an exception when the state exists and is scheduled for deletion' do
+ create(:terraform_state, :deletion_in_progress, project: project, name: 'new-state')
+
+ expect { handler.lock! }.to raise_error(described_class::StateDeletedError)
+ end
end
describe '#unlock!' do
- let(:lock_id) { 'abc-abc' }
+ let_it_be(:state) { create(:terraform_state, :locked, project: project, name: 'new-state', lock_xid: 'abc-abc') }
+
+ let(:lock_id) { state.lock_xid }
subject(:handler) do
described_class.new(
project,
user,
- name: 'new-state',
+ name: state.name,
lock_id: lock_id
)
end
- before do
- create(:terraform_state, :locked, project: project, name: 'new-state', lock_xid: 'abc-abc')
- end
-
it 'unlocks the state' do
state = handler.unlock!
@@ -169,6 +188,15 @@ RSpec.describe Terraform::RemoteStateHandler do
.to raise_error(described_class::StateLockedError)
end
end
+
+ context 'with a state scheduled for deletion' do
+ it 'raises an exception' do
+ state.update!(deleted_at: Time.current)
+
+ expect { handler.unlock! }
+ .to raise_error(described_class::StateDeletedError)
+ end
+ end
end
end
end
diff --git a/spec/services/terraform/states/destroy_service_spec.rb b/spec/services/terraform/states/destroy_service_spec.rb
new file mode 100644
index 00000000000..16fcc8f93db
--- /dev/null
+++ b/spec/services/terraform/states/destroy_service_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Terraform::States::DestroyService do
+ let_it_be(:state) { create(:terraform_state, :with_version, :deletion_in_progress) }
+ let_it_be(:file) { double }
+
+ before do
+ allow_next_found_instance_of(Terraform::StateVersion) do |version|
+ allow(version).to receive(:file).and_return(file)
+ end
+ end
+
+ describe '#execute' do
+ subject { described_class.new(state).execute }
+
+ it 'removes version files from object storage, followed by the state record' do
+ expect(file).to receive(:remove!).once
+ expect(state).to receive(:destroy!)
+
+ subject
+ end
+
+ context 'state is not marked for deletion' do
+ let(:state) { create(:terraform_state) }
+
+ it 'does not delete the state' do
+ expect(state).not_to receive(:destroy!)
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/services/terraform/states/trigger_destroy_service_spec.rb b/spec/services/terraform/states/trigger_destroy_service_spec.rb
new file mode 100644
index 00000000000..2e96331779c
--- /dev/null
+++ b/spec/services/terraform/states/trigger_destroy_service_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Terraform::States::TriggerDestroyService do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user, maintainer_projects: [project]) }
+
+ describe '#execute', :aggregate_failures do
+ let_it_be(:state) { create(:terraform_state, project: project) }
+
+ subject { described_class.new(state, current_user: user).execute }
+
+ it 'marks the state as deleted and schedules a cleanup worker' do
+ expect(Terraform::States::DestroyWorker).to receive(:perform_async).with(state.id).once
+
+ expect(subject).to be_success
+ expect(state.deleted_at).to be_like_time(Time.current)
+ end
+
+ shared_examples 'unable to delete state' do
+ it 'does not modify the state' do
+ expect(Terraform::States::DestroyWorker).not_to receive(:perform_async)
+
+ expect { subject }.not_to change(state, :deleted_at)
+ expect(subject).to be_error
+ expect(subject.message).to eq(message)
+ end
+ end
+
+ context 'user does not have permission' do
+ let(:user) { create(:user, developer_projects: [project]) }
+ let(:message) { 'You have insufficient permissions to delete this state' }
+
+ include_examples 'unable to delete state'
+ end
+
+ context 'state is locked' do
+ let(:state) { create(:terraform_state, :locked, project: project) }
+ let(:message) { 'Cannot remove a locked state' }
+
+ include_examples 'unable to delete state'
+ end
+ end
+end
diff --git a/spec/workers/schedule_merge_request_cleanup_refs_worker_spec.rb b/spec/workers/schedule_merge_request_cleanup_refs_worker_spec.rb
index ef515e43474..49730d9ab8c 100644
--- a/spec/workers/schedule_merge_request_cleanup_refs_worker_spec.rb
+++ b/spec/workers/schedule_merge_request_cleanup_refs_worker_spec.rb
@@ -25,6 +25,12 @@ RSpec.describe ScheduleMergeRequestCleanupRefsWorker do
end
end
+ it 'retries stuck cleanup schedules' do
+ expect(MergeRequest::CleanupSchedule).to receive(:stuck_retry!)
+
+ worker.perform
+ end
+
include_examples 'an idempotent worker' do
it 'schedules MergeRequestCleanupRefsWorker to be performed with capacity' do
expect(MergeRequestCleanupRefsWorker).to receive(:perform_with_capacity).twice
diff --git a/spec/workers/terraform/states/destroy_worker_spec.rb b/spec/workers/terraform/states/destroy_worker_spec.rb
new file mode 100644
index 00000000000..02e79373279
--- /dev/null
+++ b/spec/workers/terraform/states/destroy_worker_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Terraform::States::DestroyWorker do
+ let(:state) { create(:terraform_state) }
+
+ describe '#perform' do
+ let(:state_id) { state.id }
+ let(:deletion_service) { instance_double(Terraform::States::DestroyService, execute: true) }
+
+ subject { described_class.new.perform(state_id) }
+
+ it 'calls the deletion service' do
+ expect(deletion_service).to receive(:execute).once
+ expect(Terraform::States::DestroyService).to receive(:new)
+ .with(state).and_return(deletion_service)
+
+ subject
+ end
+
+ context 'state no longer exists' do
+ let(:state_id) { -1 }
+
+ it 'completes without error' do
+ expect { subject }.not_to raise_error
+ end
+ end
+ end
+end