summaryrefslogtreecommitdiff
path: root/spec/support/shared_examples/services
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support/shared_examples/services')
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb161
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb113
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb52
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb44
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb34
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb34
-rw-r--r--spec/support/shared_examples/services/alert_management_shared_examples.rb136
-rw-r--r--spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb65
-rw-r--r--spec/support/shared_examples/services/boards/create_service_shared_examples.rb25
-rw-r--r--spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/services/common_system_notes_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/destroy_label_links_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb24
-rw-r--r--spec/support/shared_examples/services/issuable_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/merge_request_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb91
-rw-r--r--spec/support/shared_examples/services/updating_mentions_shared_examples.rb2
23 files changed, 729 insertions, 122 deletions
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
new file mode 100644
index 00000000000..218a3462c35
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+# This shared_example requires the following variables:
+# - `service`, the service which includes AlertManagement::AlertProcessing
+RSpec.shared_examples 'creates an alert management alert or errors' do
+ it { is_expected.to be_success }
+
+ it 'creates AlertManagement::Alert' do
+ expect(Gitlab::AppLogger).not_to receive(:warn)
+
+ expect { subject }.to change(AlertManagement::Alert, :count).by(1)
+ end
+
+ it 'executes the alert service hooks' do
+ expect_next_instance_of(AlertManagement::Alert) do |alert|
+ expect(alert).to receive(:execute_services)
+ end
+
+ subject
+ end
+
+ context 'and fails to save' do
+ let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] })}
+
+ before do
+ allow(service).to receive(:alert).and_call_original
+ allow(service).to receive_message_chain(:alert, :save).and_return(false)
+ allow(service).to receive_message_chain(:alert, :errors).and_return(errors)
+ end
+
+ it_behaves_like 'alerts service responds with an error', :bad_request
+
+ it 'writes a warning to the log' do
+ expect(Gitlab::AppLogger).to receive(:warn).with(
+ message: "Unable to create AlertManagement::Alert from #{source}",
+ project_id: project.id,
+ alert_errors: { hosts: ['hosts array is over 255 chars'] }
+ )
+
+ subject
+ end
+ end
+end
+
+# This shared_example requires the following variables:
+# - last_alert_attributes, last created alert
+# - project, project that alert created
+# - payload_raw, hash representation of payload
+# - environment, project's environment
+# - fingerprint, fingerprint hash
+RSpec.shared_examples 'properly assigns the alert properties' do
+ specify do
+ subject
+
+ expect(last_alert_attributes).to match({
+ project_id: project.id,
+ title: payload_raw.fetch(:title),
+ started_at: Time.zone.parse(payload_raw.fetch(:start_time)),
+ severity: payload_raw.fetch(:severity, nil),
+ status: AlertManagement::Alert.status_value(:triggered),
+ events: 1,
+ domain: domain,
+ hosts: payload_raw.fetch(:hosts, nil),
+ payload: payload_raw.with_indifferent_access,
+ issue_id: nil,
+ description: payload_raw.fetch(:description, nil),
+ monitoring_tool: payload_raw.fetch(:monitoring_tool, nil),
+ service: payload_raw.fetch(:service, nil),
+ fingerprint: Digest::SHA1.hexdigest(fingerprint),
+ environment_id: environment.id,
+ ended_at: nil,
+ prometheus_alert_id: nil
+ }.with_indifferent_access)
+ end
+end
+
+RSpec.shared_examples 'does not create an alert management alert' do
+ specify do
+ expect { subject }.not_to change(AlertManagement::Alert, :count)
+ end
+end
+
+# This shared_example requires the following variables:
+# - `alert`, the alert for which events should be incremented
+RSpec.shared_examples 'adds an alert management alert event' do
+ specify do
+ expect(alert).not_to receive(:execute_services)
+
+ expect { subject }.to change { alert.reload.events }.by(1)
+
+ expect(subject).to be_success
+ end
+
+ it_behaves_like 'does not create an alert management alert'
+end
+
+# This shared_example requires the following variables:
+# - `alert`, the alert for which events should not be incremented
+RSpec.shared_examples 'does not add an alert management alert event' do
+ specify do
+ expect { subject }.not_to change { alert.reload.events }
+ end
+end
+
+RSpec.shared_examples 'processes new firing alert' do
+ include_examples 'processes never-before-seen alert'
+
+ context 'for an existing alert with the same fingerprint' do
+ let_it_be(:gitlab_fingerprint) { Digest::SHA1.hexdigest(fingerprint) }
+
+ context 'which is triggered' do
+ let_it_be(:alert) { create(:alert_management_alert, :triggered, fingerprint: gitlab_fingerprint, project: project) }
+
+ it_behaves_like 'adds an alert management alert event'
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'processes incident issues if enabled', with_issue: true
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+
+ context 'with an existing resolved alert as well' do
+ let_it_be(:resolved_alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: gitlab_fingerprint) }
+
+ it_behaves_like 'adds an alert management alert event'
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'processes incident issues if enabled', with_issue: true
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+ end
+ end
+
+ context 'which is acknowledged' do
+ let_it_be(:alert) { create(:alert_management_alert, :acknowledged, fingerprint: gitlab_fingerprint, project: project) }
+
+ it_behaves_like 'adds an alert management alert event'
+ it_behaves_like 'processes incident issues if enabled', with_issue: true
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+ it_behaves_like 'does not send alert notification emails'
+ end
+
+ context 'which is ignored' do
+ let_it_be(:alert) { create(:alert_management_alert, :ignored, fingerprint: gitlab_fingerprint, project: project) }
+
+ it_behaves_like 'adds an alert management alert event'
+ it_behaves_like 'processes incident issues if enabled', with_issue: true
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+ it_behaves_like 'does not send alert notification emails'
+ end
+
+ context 'which is resolved' do
+ let_it_be(:alert) { create(:alert_management_alert, :resolved, fingerprint: gitlab_fingerprint, project: project) }
+
+ include_examples 'processes never-before-seen alert'
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb
new file mode 100644
index 00000000000..86e7da5bcbe
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_recovery_shared_examples.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+# This shared_example requires the following variables:
+# - `alert`, the alert to be resolved
+RSpec.shared_examples 'resolves an existing alert management alert' do
+ it 'sets the end time and status' do
+ expect(Gitlab::AppLogger).not_to receive(:warn)
+
+ expect { subject }
+ .to change { alert.reload.resolved? }.to(true)
+ .and change { alert.ended_at.present? }.to(true)
+
+ expect(subject).to be_success
+ end
+end
+
+# This shared_example requires the following variables:
+# - `alert`, the alert not to be updated
+RSpec.shared_examples 'does not change the alert end time' do
+ specify do
+ expect { subject }.not_to change { alert.reload.ended_at }
+ end
+end
+
+# This shared_example requires the following variables:
+# - `project`, expected project for an incoming alert
+# - `service`, a service which includes AlertManagement::AlertProcessing
+# - `alert` (optional), the alert which should fail to resolve. If not
+# included, the log is expected to correspond to a new alert
+RSpec.shared_examples 'writes a warning to the log for a failed alert status update' do
+ before do
+ allow(service).to receive(:alert).and_call_original
+ allow(service).to receive_message_chain(:alert, :resolve).and_return(false)
+ end
+
+ specify do
+ expect(Gitlab::AppLogger).to receive(:warn).with(
+ message: 'Unable to update AlertManagement::Alert status to resolved',
+ project_id: project.id,
+ alert_id: alert ? alert.id : (last_alert_id + 1)
+ )
+
+ # Failure to resolve a recovery alert is not a critical failure
+ expect(subject).to be_success
+ end
+
+ private
+
+ def last_alert_id
+ AlertManagement::Alert.connection
+ .select_value("SELECT nextval('#{AlertManagement::Alert.sequence_name}')")
+ end
+end
+
+RSpec.shared_examples 'processes recovery alert' do
+ context 'seen for the first time' do
+ let(:alert) { AlertManagement::Alert.last }
+
+ include_examples 'processes never-before-seen recovery alert'
+ end
+
+ context 'for an existing alert with the same fingerprint' do
+ let_it_be(:gitlab_fingerprint) { Digest::SHA1.hexdigest(fingerprint) }
+
+ context 'which is triggered' do
+ let_it_be(:alert) { create(:alert_management_alert, :triggered, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
+
+ it_behaves_like 'resolves an existing alert management alert'
+ it_behaves_like 'creates expected system notes for alert', :recovery_alert, :resolve_alert
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'closes related incident if enabled'
+ it_behaves_like 'writes a warning to the log for a failed alert status update'
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'does not add an alert management alert event'
+ end
+
+ context 'which is ignored' do
+ let_it_be(:alert) { create(:alert_management_alert, :ignored, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
+
+ it_behaves_like 'resolves an existing alert management alert'
+ it_behaves_like 'creates expected system notes for alert', :recovery_alert, :resolve_alert
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'closes related incident if enabled'
+ it_behaves_like 'writes a warning to the log for a failed alert status update'
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'does not add an alert management alert event'
+ end
+
+ context 'which is acknowledged' do
+ let_it_be(:alert) { create(:alert_management_alert, :acknowledged, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
+
+ it_behaves_like 'resolves an existing alert management alert'
+ it_behaves_like 'creates expected system notes for alert', :recovery_alert, :resolve_alert
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'closes related incident if enabled'
+ it_behaves_like 'writes a warning to the log for a failed alert status update'
+
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'does not add an alert management alert event'
+ end
+
+ context 'which is resolved' do
+ let_it_be(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
+
+ include_examples 'processes never-before-seen recovery alert'
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb
new file mode 100644
index 00000000000..c6ac07b6dd5
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/incident_creation_shared_examples.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+# Expects usage of 'incident settings enabled' context.
+#
+# This shared_example includes the following option:
+# - with_issue: includes a test for when the defined `alert` has an associated issue
+#
+# This shared_example requires the following variables:
+# - `alert`, required if :with_issue is true
+RSpec.shared_examples 'processes incident issues if enabled' do |with_issue: false|
+ include_examples 'processes incident issues', with_issue
+
+ context 'with incident setting disabled' do
+ let(:create_issue) { false }
+
+ it_behaves_like 'does not process incident issues'
+ end
+end
+
+RSpec.shared_examples 'processes incident issues' do |with_issue: false|
+ before do
+ allow_next_instance_of(AlertManagement::Alert) do |alert|
+ allow(alert).to receive(:execute_services)
+ end
+ end
+
+ specify do
+ expect(IncidentManagement::ProcessAlertWorkerV2)
+ .to receive(:perform_async)
+ .with(kind_of(Integer))
+
+ Sidekiq::Testing.inline! do
+ expect(subject).to be_success
+ end
+ end
+
+ context 'with issue', if: with_issue do
+ before do
+ alert.update!(issue: create(:issue, project: project))
+ end
+
+ it_behaves_like 'does not process incident issues'
+ end
+end
+
+RSpec.shared_examples 'does not process incident issues' do
+ specify do
+ expect(IncidentManagement::ProcessAlertWorkerV2).not_to receive(:perform_async)
+
+ subject
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb
new file mode 100644
index 00000000000..132f1e0422e
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/incident_resolution_shared_examples.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+# Expects usage of 'incident settings enabled' context.
+#
+# This shared_example requires the following variables:
+# - `alert`, alert for which related incidents should be closed
+# - `project`, project of the alert
+RSpec.shared_examples 'closes related incident if enabled' do
+ context 'with issue' do
+ before do
+ alert.update!(issue: create(:issue, project: project))
+ end
+
+ it { expect { subject }.to change { alert.issue.reload.closed? }.from(false).to(true) }
+ it { expect { subject }.to change(ResourceStateEvent, :count).by(1) }
+ end
+
+ context 'without issue' do
+ it { expect { subject }.not_to change { alert.reload.issue } }
+ it { expect { subject }.not_to change(ResourceStateEvent, :count) }
+ end
+
+ context 'with incident setting disabled' do
+ let(:auto_close_incident) { false }
+
+ it_behaves_like 'does not close related incident'
+ end
+end
+
+RSpec.shared_examples 'does not close related incident' do
+ context 'with issue' do
+ before do
+ alert.update!(issue: create(:issue, project: project))
+ end
+
+ it { expect { subject }.not_to change { alert.issue.reload.state } }
+ it { expect { subject }.not_to change(ResourceStateEvent, :count) }
+ end
+
+ context 'without issue' do
+ it { expect { subject }.not_to change { alert.reload.issue } }
+ it { expect { subject }.not_to change(ResourceStateEvent, :count) }
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb
new file mode 100644
index 00000000000..5f30b58176b
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/notifications_shared_examples.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+# Expects usage of 'incident settings enabled' context.
+#
+# This shared_example includes the following option:
+# - count: number of notifications expected to be sent
+RSpec.shared_examples 'sends alert notification emails if enabled' do |count: 1|
+ include_examples 'sends alert notification emails', count
+
+ context 'with email setting disabled' do
+ let(:send_email) { false }
+
+ it_behaves_like 'does not send alert notification emails'
+ end
+end
+
+RSpec.shared_examples 'sends alert notification emails' do |count: 1|
+ let(:notification_async) { double(NotificationService::Async) }
+
+ specify do
+ allow(NotificationService).to receive_message_chain(:new, :async).and_return(notification_async)
+ expect(notification_async).to receive(:prometheus_alerts_fired).exactly(count).times
+
+ subject
+ end
+end
+
+RSpec.shared_examples 'does not send alert notification emails' do
+ specify do
+ expect(NotificationService).not_to receive(:new)
+
+ subject
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb
new file mode 100644
index 00000000000..57d598c0259
--- /dev/null
+++ b/spec/support/shared_examples/services/alert_management/alert_processing/system_notes_shared_examples.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+# This shared_example includes the following option:
+# - notes: any of [:new_alert, :recovery_alert, :resolve_alert].
+# Represents which notes are expected to be created.
+#
+# This shared_example requires the following variables:
+# - `source` (optional), the monitoring tool or integration name
+# expected in the applicable system notes
+RSpec.shared_examples 'creates expected system notes for alert' do |*notes|
+ let(:expected_note_count) { expected_notes.length }
+ let(:new_notes) { Note.last(expected_note_count).pluck(:note) }
+ let(:expected_notes) do
+ {
+ new_alert: source,
+ recovery_alert: source,
+ resolve_alert: 'Resolved'
+ }.slice(*notes)
+ end
+
+ it "for #{notes.join(', ')}" do
+ expect { subject }.to change(Note, :count).by(expected_note_count)
+
+ expected_notes.each_value.with_index do |value, index|
+ expect(new_notes[index]).to include(value)
+ end
+ end
+end
+
+RSpec.shared_examples 'does not create a system note for alert' do
+ specify do
+ expect { subject }.not_to change(Note, :count)
+ end
+end
diff --git a/spec/support/shared_examples/services/alert_management_shared_examples.rb b/spec/support/shared_examples/services/alert_management_shared_examples.rb
index d9f28a97a0f..827ae42f970 100644
--- a/spec/support/shared_examples/services/alert_management_shared_examples.rb
+++ b/spec/support/shared_examples/services/alert_management_shared_examples.rb
@@ -1,111 +1,77 @@
# frozen_string_literal: true
-RSpec.shared_examples 'creates an alert management alert' do
- it { is_expected.to be_success }
+RSpec.shared_examples 'alerts service responds with an error and takes no actions' do |http_status|
+ include_examples 'alerts service responds with an error', http_status
- it 'creates AlertManagement::Alert' do
- expect { subject }.to change(AlertManagement::Alert, :count).by(1)
- end
-
- it 'executes the alert service hooks' do
- expect_next_instance_of(AlertManagement::Alert) do |alert|
- expect(alert).to receive(:execute_services)
- end
+ it_behaves_like 'does not create an alert management alert'
+ it_behaves_like 'does not create a system note for alert'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'does not send alert notification emails'
+end
- subject
+RSpec.shared_examples 'alerts service responds with an error' do |http_status|
+ specify do
+ expect(subject).to be_error
+ expect(subject.http_status).to eq(http_status)
end
end
# This shared_example requires the following variables:
-# - last_alert_attributes, last created alert
-# - project, project that alert created
-# - payload_raw, hash representation of payload
-# - environment, project's environment
-# - fingerprint, fingerprint hash
-RSpec.shared_examples 'assigns the alert properties' do
- it 'ensures that created alert has all data properly assigned' do
- subject
-
- expect(last_alert_attributes).to match(
- project_id: project.id,
- title: payload_raw.fetch(:title),
- started_at: Time.zone.parse(payload_raw.fetch(:start_time)),
- severity: payload_raw.fetch(:severity),
- status: AlertManagement::Alert.status_value(:triggered),
- events: 1,
- domain: domain,
- hosts: payload_raw.fetch(:hosts),
- payload: payload_raw.with_indifferent_access,
- issue_id: nil,
- description: payload_raw.fetch(:description),
- monitoring_tool: payload_raw.fetch(:monitoring_tool),
- service: payload_raw.fetch(:service),
- fingerprint: Digest::SHA1.hexdigest(fingerprint),
- environment_id: environment.id,
- ended_at: nil,
- prometheus_alert_id: nil
+# - `service`, a service which includes ::IncidentManagement::Settings
+RSpec.shared_context 'incident management settings enabled' do
+ let(:auto_close_incident) { true }
+ let(:create_issue) { true }
+ let(:send_email) { true }
+
+ let(:incident_management_setting) do
+ double(
+ auto_close_incident?: auto_close_incident,
+ create_issue?: create_issue,
+ send_email?: send_email
)
end
-end
-RSpec.shared_examples 'does not an create alert management alert' do
- it 'does not create alert' do
- expect { subject }.not_to change(AlertManagement::Alert, :count)
+ before do
+ allow(ProjectServiceWorker).to receive(:perform_async)
+ allow(service)
+ .to receive(:incident_management_setting)
+ .and_return(incident_management_setting)
end
end
-RSpec.shared_examples 'adds an alert management alert event' do
- it { is_expected.to be_success }
-
- it 'does not create an alert' do
- expect { subject }.not_to change(AlertManagement::Alert, :count)
- end
-
- it 'increases alert events count' do
- expect { subject }.to change { alert.reload.events }.by(1)
- end
-
- it 'does not executes the alert service hooks' do
- expect(alert).not_to receive(:execute_services)
-
- subject
- end
+RSpec.shared_examples 'processes never-before-seen alert' do
+ it_behaves_like 'creates an alert management alert or errors'
+ it_behaves_like 'creates expected system notes for alert', :new_alert
+ it_behaves_like 'processes incident issues if enabled'
+ it_behaves_like 'sends alert notification emails if enabled'
end
-RSpec.shared_examples 'processes incident issues' do
- let(:create_incident_service) { spy }
-
- before do
- allow_any_instance_of(AlertManagement::Alert).to receive(:execute_services)
+RSpec.shared_examples 'processes never-before-seen recovery alert' do
+ it_behaves_like 'creates an alert management alert or errors'
+ it_behaves_like 'creates expected system notes for alert', :new_alert, :recovery_alert, :resolve_alert
+ it_behaves_like 'sends alert notification emails if enabled'
+ it_behaves_like 'does not process incident issues'
+ it_behaves_like 'writes a warning to the log for a failed alert status update' do
+ let(:alert) { nil } # Ensure the next alert id is used
end
- it 'processes issues' do
- expect(IncidentManagement::ProcessAlertWorker)
- .to receive(:perform_async)
- .with(nil, nil, kind_of(Integer))
- .once
+ it 'resolves the alert' do
+ subject
- Sidekiq::Testing.inline! do
- expect(subject).to be_success
- end
+ expect(AlertManagement::Alert.last.ended_at).to be_present
+ expect(AlertManagement::Alert.last.resolved?).to be(true)
end
end
-RSpec.shared_examples 'does not process incident issues' do
- it 'does not process issues' do
- expect(IncidentManagement::ProcessAlertWorker)
- .not_to receive(:perform_async)
+RSpec.shared_examples 'processes one firing and one resolved prometheus alerts' do
+ it 'creates AlertManagement::Alert' do
+ expect(Gitlab::AppLogger).not_to receive(:warn)
- expect(subject).to be_success
+ expect { subject }
+ .to change(AlertManagement::Alert, :count).by(2)
+ .and change(Note, :count).by(4)
end
-end
-
-RSpec.shared_examples 'does not process incident issues due to error' do |http_status:|
- it 'does not process issues' do
- expect(IncidentManagement::ProcessAlertWorker)
- .not_to receive(:perform_async)
- expect(subject).to be_error
- expect(subject.http_status).to eq(http_status)
- end
+ it_behaves_like 'processes incident issues'
+ it_behaves_like 'sends alert notification emails', count: 2
end
diff --git a/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb b/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb
new file mode 100644
index 00000000000..68ea460dabc
--- /dev/null
+++ b/spec/support/shared_examples/services/boards/boards_recent_visit_shared_examples.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'boards recent visit' do
+ let_it_be(:user) { create(:user) }
+
+ describe '#visited' do
+ it 'creates a visit if one does not exists' do
+ expect { described_class.visited!(user, board) }.to change(described_class, :count).by(1)
+ end
+
+ shared_examples 'was visited previously' do
+ let_it_be(:visit) do
+ create(visit_relation,
+ board_parent_relation => board_parent,
+ board_relation => board,
+ user: user,
+ updated_at: 7.days.ago
+ )
+ end
+
+ it 'updates the timestamp' do
+ freeze_time do
+ described_class.visited!(user, board)
+
+ expect(described_class.count).to eq 1
+ expect(described_class.first.updated_at).to be_like_time(Time.zone.now)
+ end
+ end
+ end
+
+ it_behaves_like 'was visited previously'
+
+ context 'when we try to create a visit that is not unique' do
+ before do
+ expect(described_class).to receive(:find_or_create_by).and_raise(ActiveRecord::RecordNotUnique, 'record not unique')
+ expect(described_class).to receive(:find_or_create_by).and_return(visit)
+ end
+
+ it_behaves_like 'was visited previously'
+ end
+ end
+
+ describe '#latest' do
+ def create_visit(time)
+ create(visit_relation, board_parent_relation => board_parent, user: user, updated_at: time)
+ end
+
+ it 'returns the most recent visited' do
+ create_visit(7.days.ago)
+ create_visit(5.days.ago)
+ recent = create_visit(1.day.ago)
+
+ expect(described_class.latest(user, board_parent)).to eq recent
+ end
+
+ it 'returns last 3 visited boards' do
+ create_visit(7.days.ago)
+ visit1 = create_visit(3.days.ago)
+ visit2 = create_visit(2.days.ago)
+ visit3 = create_visit(5.days.ago)
+
+ expect(described_class.latest(user, board_parent, count: 3)).to eq([visit2, visit1, visit3])
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/boards/create_service_shared_examples.rb b/spec/support/shared_examples/services/boards/create_service_shared_examples.rb
new file mode 100644
index 00000000000..63b5e3a5a84
--- /dev/null
+++ b/spec/support/shared_examples/services/boards/create_service_shared_examples.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'boards recent visit create service' do
+ let_it_be(:user) { create(:user) }
+
+ subject(:service) { described_class.new(board.resource_parent, user) }
+
+ it 'returns nil when there is no user' do
+ service.current_user = nil
+
+ expect(service.execute(board)).to be_nil
+ end
+
+ it 'returns nil when database is read only' do
+ allow(Gitlab::Database).to receive(:read_only?) { true }
+
+ expect(service.execute(board)).to be_nil
+ end
+
+ it 'records the visit' do
+ expect(model).to receive(:visited!).once
+
+ service.execute(board)
+ end
+end
diff --git a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
index 4aa5d7d890b..7d4fbeea0dc 100644
--- a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
@@ -146,7 +146,7 @@ RSpec.shared_examples 'issues move service' do |group|
params.merge!(move_after_id: issue1.id, move_before_id: issue2.id)
match_params = { move_between_ids: [issue1.id, issue2.id], board_group_id: parent.id }
- expect(Issues::UpdateService).to receive(:new).with(issue.project, user, match_params).and_return(double(execute: build(:issue)))
+ expect(Issues::UpdateService).to receive(:new).with(project: issue.project, current_user: user, params: match_params).and_return(double(execute: build(:issue)))
described_class.new(parent, user, params).execute(issue)
end
diff --git a/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb b/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb
index 94da405e491..af88644ced7 100644
--- a/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/lists_destroy_service_shared_examples.rb
@@ -3,30 +3,27 @@
RSpec.shared_examples 'lists destroy service' do
context 'when list type is label' do
it 'removes list from board' do
- list = create(:list, board: board)
service = described_class.new(parent, user)
expect { service.execute(list) }.to change(board.lists, :count).by(-1)
end
it 'decrements position of higher lists' do
- development = create(:list, board: board, position: 0)
- review = create(:list, board: board, position: 1)
- staging = create(:list, board: board, position: 2)
- closed = board.lists.closed.first
+ development = create(list_type, params.merge(position: 0))
+ review = create(list_type, params.merge(position: 1))
+ staging = create(list_type, params.merge(position: 2))
described_class.new(parent, user).execute(development)
expect(review.reload.position).to eq 0
expect(staging.reload.position).to eq 1
- expect(closed.reload.position).to be_nil
+ expect(closed_list.reload.position).to be_nil
end
end
it 'does not remove list from board when list type is closed' do
- list = board.lists.closed.first
service = described_class.new(parent, user)
- expect { service.execute(list) }.not_to change(board.lists, :count)
+ expect { service.execute(closed_list) }.not_to change(board.lists, :count)
end
end
diff --git a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
index 7b277d4bede..ce412ef55de 100644
--- a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
+++ b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'system note creation' do |update_params, note_text|
- subject { described_class.new(project, user).execute(issuable, old_labels: []) }
+ subject { described_class.new(project: project, current_user: user).execute(issuable, old_labels: []) }
before do
issuable.assign_attributes(update_params)
@@ -18,7 +18,7 @@ RSpec.shared_examples 'system note creation' do |update_params, note_text|
end
RSpec.shared_examples 'draft notes creation' do |action|
- subject { described_class.new(project, user).execute(issuable, old_labels: []) }
+ subject { described_class.new(project: project, current_user: user).execute(issuable, old_labels: []) }
it 'creates Draft toggle and title change notes' do
expect { subject }.to change { Note.count }.from(0).to(2)
diff --git a/spec/support/shared_examples/services/destroy_label_links_shared_examples.rb b/spec/support/shared_examples/services/destroy_label_links_shared_examples.rb
new file mode 100644
index 00000000000..d2b52468c25
--- /dev/null
+++ b/spec/support/shared_examples/services/destroy_label_links_shared_examples.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples_for 'service deleting label links of an issuable' do
+ let_it_be(:label_link) { create(:label_link, target: target) }
+
+ def execute
+ described_class.new(target.id, target.class.name).execute
+ end
+
+ it 'deletes label links for specified target ID and type' do
+ control_count = ActiveRecord::QueryRecorder.new { execute }.count
+
+ # Create more label links for the target
+ create(:label_link, target: target)
+ create(:label_link, target: target)
+
+ expect { execute }.not_to exceed_query_limit(control_count)
+ expect(target.reload.label_links.count).to eq(0)
+ end
+end
diff --git a/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb
index ccc287c10de..e776c098fa0 100644
--- a/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb
@@ -1,10 +1,6 @@
# frozen_string_literal: true
shared_examples_for 'service deleting todos' do
- before do
- stub_feature_flags(destroy_issuable_todos_async: group)
- end
-
it 'destroys associated todos asynchronously' do
expect(TodosDestroyer::DestroyedIssuableWorker)
.to receive(:perform_async)
@@ -12,20 +8,14 @@ shared_examples_for 'service deleting todos' do
subject.execute(issuable)
end
+end
- context 'when destroy_issuable_todos_async feature is disabled for group' do
- before do
- stub_feature_flags(destroy_issuable_todos_async: false)
- end
-
- it 'destroy associated todos synchronously' do
- expect_next_instance_of(TodosDestroyer::DestroyedIssuableWorker) do |worker|
- expect(worker)
- .to receive(:perform)
- .with(issuable.id, issuable.class.name)
- end
+shared_examples_for 'service deleting label links' do
+ it 'destroys associated label links asynchronously' do
+ expect(Issuable::LabelLinksDestroyWorker)
+ .to receive(:perform_async)
+ .with(issuable.id, issuable.class.name)
- subject.execute(issuable)
- end
+ subject.execute(issuable)
end
end
diff --git a/spec/support/shared_examples/services/issuable_shared_examples.rb b/spec/support/shared_examples/services/issuable_shared_examples.rb
index 5b3e0f9e0b9..a50a386afe1 100644
--- a/spec/support/shared_examples/services/issuable_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_shared_examples.rb
@@ -4,14 +4,14 @@ RSpec.shared_examples 'cache counters invalidator' do
it 'invalidates counter cache for assignees' do
expect_any_instance_of(User).to receive(:invalidate_merge_request_cache_counts)
- described_class.new(project, user, {}).execute(merge_request)
+ described_class.new(project: project, current_user: user).execute(merge_request)
end
end
RSpec.shared_examples 'updating a single task' do
def update_issuable(opts)
issuable = try(:issue) || try(:merge_request)
- described_class.new(project, user, opts).execute(issuable)
+ described_class.new(project: project, current_user: user, params: opts).execute(issuable)
end
before do
diff --git a/spec/support/shared_examples/services/merge_request_shared_examples.rb b/spec/support/shared_examples/services/merge_request_shared_examples.rb
index 178b6bc47e1..d2595b92cbc 100644
--- a/spec/support/shared_examples/services/merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/services/merge_request_shared_examples.rb
@@ -70,7 +70,7 @@ RSpec.shared_examples 'merge request reviewers cache counters invalidator' do
it 'invalidates counter cache for reviewers' do
expect(merge_request.reviewers).to all(receive(:invalidate_merge_request_cache_counts))
- described_class.new(project, user, {}).execute(merge_request)
+ described_class.new(project: project, current_user: user).execute(merge_request)
end
end
@@ -86,7 +86,7 @@ RSpec.shared_examples_for 'a service that can create a merge request' do
context 'when project has been forked', :sidekiq_might_not_need_inline do
let(:forked_project) { fork_project(project, user1, repository: true) }
- let(:service) { described_class.new(forked_project, user1, changes, push_options) }
+ let(:service) { described_class.new(project: forked_project, current_user: user1, changes: changes, push_options: push_options) }
before do
allow(forked_project).to receive(:empty_repo?).and_return(false)
diff --git a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
index b6c33eac7b4..4df12f7849b 100644
--- a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
+++ b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
@@ -12,13 +12,22 @@ RSpec.shared_examples 'misconfigured dashboard service response' do |status_code
end
RSpec.shared_examples 'valid dashboard service response for schema' do
+ file_ref_resolver = proc do |uri|
+ file = Rails.root.join(uri.path)
+ raise StandardError, "Ref file #{uri.path} must be json" unless uri.path.ends_with?('.json')
+ raise StandardError, "File #{file.to_path} doesn't exists" unless file.exist?
+
+ Gitlab::Json.parse(File.read(file))
+ end
+
it 'returns a json representation of the dashboard' do
result = service_call
expect(result.keys).to contain_exactly(:dashboard, :status)
expect(result[:status]).to eq(:success)
- expect(JSON::Validator.fully_validate(dashboard_schema, result[:dashboard])).to be_empty
+ validator = JSONSchemer.schema(dashboard_schema, ref_resolver: file_ref_resolver)
+ expect(validator.valid?(result[:dashboard].with_indifferent_access)).to be true
end
end
diff --git a/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb b/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb
index 8398dd3c453..f7a6bd3676a 100644
--- a/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb
+++ b/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb
@@ -7,6 +7,8 @@ RSpec.shared_examples 'updating the namespace package setting attributes' do |fr
expect { subject }
.to change { namespace.package_settings.reload.maven_duplicates_allowed }.from(from[:maven_duplicates_allowed]).to(to[:maven_duplicates_allowed])
.and change { namespace.package_settings.reload.maven_duplicate_exception_regex }.from(from[:maven_duplicate_exception_regex]).to(to[:maven_duplicate_exception_regex])
+ .and change { namespace.package_settings.reload.generic_duplicates_allowed }.from(from[:generic_duplicates_allowed]).to(to[:generic_duplicates_allowed])
+ .and change { namespace.package_settings.reload.generic_duplicate_exception_regex }.from(from[:generic_duplicate_exception_regex]).to(to[:generic_duplicate_exception_regex])
end
end
@@ -26,6 +28,8 @@ RSpec.shared_examples 'creating the namespace package setting' do
expect(namespace.package_setting_relation.maven_duplicates_allowed).to eq(package_settings[:maven_duplicates_allowed])
expect(namespace.package_setting_relation.maven_duplicate_exception_regex).to eq(package_settings[:maven_duplicate_exception_regex])
+ expect(namespace.package_setting_relation.generic_duplicates_allowed).to eq(package_settings[:generic_duplicates_allowed])
+ expect(namespace.package_setting_relation.generic_duplicate_exception_regex).to eq(package_settings[:generic_duplicate_exception_regex])
end
it_behaves_like 'returning a success'
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index 4e34c191306..72878e925dc 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -203,7 +203,9 @@ RSpec.shared_examples 'filters on each package_type' do |is_project: false|
let_it_be(:package7) { create(:generic_package, project: project) }
let_it_be(:package8) { create(:golang_package, project: project) }
let_it_be(:package9) { create(:debian_package, project: project) }
- let_it_be(:package9) { create(:rubygems_package, project: project) }
+ let_it_be(:package10) { create(:rubygems_package, project: project) }
+ let_it_be(:package11) { create(:helm_package, project: project) }
+ let_it_be(:package12) { create(:terraform_module_package, project: project) }
Packages::Package.package_types.keys.each do |package_type|
context "for package type #{package_type}" do
diff --git a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
index 1fb1b9f79b2..275ddebc18c 100644
--- a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
@@ -47,7 +47,7 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
expect(original_repository_double).to receive(:remove)
end
- it "moves the project and its #{repository_type} repository to the new storage and unmarks the repository as read only" do
+ it "moves the project and its #{repository_type} repository to the new storage and unmarks the repository as read-only" do
old_project_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
project.repository.path_to_repo
end
diff --git a/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb b/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb
index e67fc4ab04a..97304680316 100644
--- a/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb
+++ b/spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb
@@ -27,7 +27,7 @@ RSpec.shared_examples 'moves repository shard in bulk' do
container.set_repository_read_only!
expect(subject).to receive(:log_info)
- .with(/Container #{container.full_path} \(#{container.id}\) was skipped: #{container.class} is read only/)
+ .with(/Container #{container.full_path} \(#{container.id}\) was skipped: #{container.class} is read-only/)
expect { subject.execute(source_storage_name, destination_storage_name) }
.to change(move_service_klass, :count).by(0)
end
diff --git a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
new file mode 100644
index 00000000000..538fd2bb513
--- /dev/null
+++ b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples_for 'services security ci configuration create service' do |skip_w_params|
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ describe '#execute' do
+ let(:params) { {} }
+
+ context 'user does not belong to project' do
+ it 'returns an error status' do
+ expect(result.status).to eq(:error)
+ expect(result.payload[:success_path]).to be_nil
+ end
+
+ it 'does not track a snowplow event' do
+ subject
+
+ expect_no_snowplow_event
+ end
+ end
+
+ context 'user belongs to project' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'does track the snowplow event' do
+ subject
+
+ expect_snowplow_event(**snowplow_event)
+ end
+
+ it 'raises exception if the user does not have permission to create a new branch' do
+ allow(project).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, "You are not allowed to create protected branches on this project.")
+
+ expect { subject }.to raise_error(Gitlab::Git::PreReceiveError)
+ end
+
+ context 'when exception is raised' do
+ let_it_be(:project) { create(:project, :repository) }
+
+ before do
+ allow(project.repository).to receive(:add_branch).and_raise(StandardError, "The unexpected happened!")
+ end
+
+ context 'when branch was created' do
+ before do
+ allow(project.repository).to receive(:branch_exists?).and_return(true)
+ end
+
+ it 'tries to rm branch' do
+ expect(project.repository).to receive(:rm_branch).with(user, branch_name)
+ expect { subject }.to raise_error(StandardError)
+ end
+ end
+
+ context 'when branch was not created' do
+ before do
+ allow(project.repository).to receive(:branch_exists?).and_return(false)
+ end
+
+ it 'does not try to rm branch' do
+ expect(project.repository).not_to receive(:rm_branch)
+ expect { subject }.to raise_error(StandardError)
+ end
+ end
+ end
+
+ context 'with no parameters' do
+ it 'returns the path to create a new merge request' do
+ expect(result.status).to eq(:success)
+ expect(result.payload[:success_path]).to match(/#{Gitlab::Routing.url_helpers.project_new_merge_request_url(project, {})}(.*)description(.*)source_branch/)
+ end
+ end
+
+ unless skip_w_params
+ context 'with parameters' do
+ let(:params) { non_empty_params }
+
+ it 'returns the path to create a new merge request' do
+ expect(result.status).to eq(:success)
+ expect(result.payload[:success_path]).to match(/#{Gitlab::Routing.url_helpers.project_new_merge_request_url(project, {})}(.*)description(.*)source_branch/)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/updating_mentions_shared_examples.rb b/spec/support/shared_examples/services/updating_mentions_shared_examples.rb
index 84f6c4d136a..13a2aa9ddac 100644
--- a/spec/support/shared_examples/services/updating_mentions_shared_examples.rb
+++ b/spec/support/shared_examples/services/updating_mentions_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'updating mentions' do |service_class|
def update_mentionable(opts)
perform_enqueued_jobs do
- service_class.new(project, user, opts).execute(mentionable)
+ service_class.new(project: project, current_user: user, params: opts).execute(mentionable)
end
mentionable.reload