2020-05-24 23:13:21 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
RSpec.describe AlertManagement::Alert do
|
2020-10-24 23:57:45 +05:30
|
|
|
let_it_be(:project) { create(:project) }
|
|
|
|
let_it_be(:project2) { create(:project) }
|
|
|
|
let_it_be(:triggered_alert, reload: true) { create(:alert_management_alert, :triggered, project: project) }
|
|
|
|
let_it_be(:acknowledged_alert, reload: true) { create(:alert_management_alert, :acknowledged, project: project) }
|
|
|
|
let_it_be(:resolved_alert, reload: true) { create(:alert_management_alert, :resolved, project: project2) }
|
|
|
|
let_it_be(:ignored_alert, reload: true) { create(:alert_management_alert, :ignored, project: project2) }
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
describe 'associations' do
|
|
|
|
it { is_expected.to belong_to(:project) }
|
2020-07-28 23:09:34 +05:30
|
|
|
it { is_expected.to belong_to(:issue).optional }
|
|
|
|
it { is_expected.to belong_to(:prometheus_alert).optional }
|
|
|
|
it { is_expected.to belong_to(:environment).optional }
|
2020-06-23 00:09:42 +05:30
|
|
|
it { is_expected.to have_many(:assignees).through(:alert_assignees) }
|
|
|
|
it { is_expected.to have_many(:notes) }
|
|
|
|
it { is_expected.to have_many(:ordered_notes) }
|
|
|
|
it { is_expected.to have_many(:user_mentions) }
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
describe 'validations' do
|
|
|
|
it { is_expected.to validate_presence_of(:title) }
|
|
|
|
it { is_expected.to validate_presence_of(:events) }
|
|
|
|
it { is_expected.to validate_presence_of(:severity) }
|
|
|
|
it { is_expected.to validate_presence_of(:status) }
|
|
|
|
it { is_expected.to validate_presence_of(:started_at) }
|
|
|
|
|
|
|
|
it { is_expected.to validate_length_of(:title).is_at_most(200) }
|
|
|
|
it { is_expected.to validate_length_of(:description).is_at_most(1000) }
|
|
|
|
it { is_expected.to validate_length_of(:service).is_at_most(100) }
|
|
|
|
it { is_expected.to validate_length_of(:monitoring_tool).is_at_most(100) }
|
|
|
|
|
|
|
|
describe 'fingerprint' do
|
|
|
|
let_it_be(:fingerprint) { 'fingerprint' }
|
2021-02-22 17:27:13 +05:30
|
|
|
let_it_be(:project3, refind: true) { create(:project) }
|
2021-09-30 23:02:18 +05:30
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
let(:new_alert) { build(:alert_management_alert, fingerprint: fingerprint, project: project3) }
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
subject { new_alert }
|
|
|
|
|
|
|
|
context 'adding an alert with the same fingerprint' do
|
2020-07-28 23:09:34 +05:30
|
|
|
context 'same project, various states' do
|
|
|
|
using RSpec::Parameterized::TableSyntax
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
let_it_be(:existing_alert, refind: true) { create(:alert_management_alert, fingerprint: fingerprint, project: project3) }
|
2020-10-24 23:57:45 +05:30
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
# We are only validating uniqueness for non-resolved alerts
|
2021-10-27 15:23:28 +05:30
|
|
|
where(:existing_status_event, :new_status, :valid) do
|
|
|
|
:resolve | :triggered | true
|
|
|
|
:resolve | :acknowledged | true
|
|
|
|
:resolve | :ignored | true
|
|
|
|
:resolve | :resolved | true
|
|
|
|
:trigger | :triggered | false
|
|
|
|
:trigger | :acknowledged | false
|
|
|
|
:trigger | :ignored | false
|
|
|
|
:trigger | :resolved | true
|
|
|
|
:acknowledge | :triggered | false
|
|
|
|
:acknowledge | :acknowledged | false
|
|
|
|
:acknowledge | :ignored | false
|
|
|
|
:acknowledge | :resolved | true
|
|
|
|
:ignore | :triggered | false
|
|
|
|
:ignore | :acknowledged | false
|
|
|
|
:ignore | :ignored | false
|
|
|
|
:ignore | :resolved | true
|
2020-07-28 23:09:34 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
2021-02-22 17:27:13 +05:30
|
|
|
let(:new_alert) { build(:alert_management_alert, new_status, fingerprint: fingerprint, project: project3) }
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
before do
|
2021-10-27 15:23:28 +05:30
|
|
|
existing_alert.update!(status_event: existing_status_event)
|
2020-10-24 23:57:45 +05:30
|
|
|
end
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
if params[:valid]
|
|
|
|
it { is_expected.to be_valid }
|
|
|
|
else
|
|
|
|
it { is_expected.to be_invalid }
|
|
|
|
end
|
|
|
|
end
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'different project' do
|
2020-10-24 23:57:45 +05:30
|
|
|
let_it_be(:existing_alert) { create(:alert_management_alert, fingerprint: fingerprint, project: project2) }
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
it { is_expected.to be_valid }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'hosts' do
|
2020-10-24 23:57:45 +05:30
|
|
|
subject(:alert) { triggered_alert }
|
|
|
|
|
|
|
|
before do
|
|
|
|
triggered_alert.hosts = hosts
|
|
|
|
end
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
context 'over 255 total chars' do
|
|
|
|
let(:hosts) { ['111.111.111.111'] * 18 }
|
|
|
|
|
|
|
|
it { is_expected.not_to be_valid }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'under 255 chars' do
|
|
|
|
let(:hosts) { ['111.111.111.111'] * 17 }
|
|
|
|
|
|
|
|
it { is_expected.to be_valid }
|
|
|
|
end
|
2021-01-03 14:25:43 +05:30
|
|
|
|
|
|
|
context 'nested array' do
|
|
|
|
let(:hosts) { ['111.111.111.111', ['111.111.111.111']] }
|
|
|
|
|
|
|
|
it { is_expected.not_to be_valid }
|
|
|
|
end
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'enums' do
|
|
|
|
let(:severity_values) do
|
|
|
|
{ critical: 0, high: 1, medium: 2, low: 3, info: 4, unknown: 5 }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to define_enum_for(:severity).with_values(severity_values) }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'scopes' do
|
|
|
|
describe '.for_iid' do
|
2020-10-24 23:57:45 +05:30
|
|
|
subject { project.alert_management_alerts.for_iid(triggered_alert.iid) }
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
it { is_expected.to match_array(triggered_alert) }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.for_fingerprint' do
|
2020-10-24 23:57:45 +05:30
|
|
|
let(:fingerprint) { SecureRandom.hex }
|
|
|
|
let(:alert_with_fingerprint) { triggered_alert }
|
|
|
|
let(:unrelated_alert_with_finger_print) { resolved_alert }
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
subject { described_class.for_fingerprint(project, fingerprint) }
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
before do
|
|
|
|
alert_with_fingerprint.update!(fingerprint: fingerprint)
|
|
|
|
unrelated_alert_with_finger_print.update!(fingerprint: fingerprint)
|
|
|
|
end
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
it { is_expected.to contain_exactly(alert_with_fingerprint) }
|
|
|
|
end
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
describe '.for_environment' do
|
|
|
|
let(:environment) { create(:environment, project: project) }
|
2020-10-24 23:57:45 +05:30
|
|
|
let(:env_alert) { triggered_alert }
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
subject { described_class.for_environment(environment) }
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
before do
|
|
|
|
triggered_alert.update!(environment: environment)
|
|
|
|
end
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
it { is_expected.to match_array(env_alert) }
|
|
|
|
end
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
describe '.for_assignee_username' do
|
|
|
|
let_it_be(:alert) { triggered_alert }
|
|
|
|
let_it_be(:assignee) { create(:user) }
|
|
|
|
|
|
|
|
subject { AlertManagement::Alert.for_assignee_username(assignee_username) }
|
|
|
|
|
|
|
|
before_all do
|
|
|
|
alert.update!(assignees: [assignee])
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when matching assignee_username' do
|
|
|
|
let(:assignee_username) { assignee.username }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(alert) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unknown assignee_username' do
|
|
|
|
let(:assignee_username) { 'unknown username' }
|
|
|
|
|
|
|
|
it { is_expected.to be_empty }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with empty assignee_username' do
|
|
|
|
let(:assignee_username) { ' ' }
|
|
|
|
|
|
|
|
it { is_expected.to be_empty }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
describe '.order_severity_with_open_prometheus_alert' do
|
|
|
|
subject { described_class.where(project: alert_project).order_severity_with_open_prometheus_alert }
|
|
|
|
|
|
|
|
let_it_be(:alert_project) { create(:project) }
|
|
|
|
let_it_be(:resolved_critical_alert) { create(:alert_management_alert, :resolved, :critical, project: alert_project) }
|
|
|
|
let_it_be(:triggered_critical_alert) { create(:alert_management_alert, :triggered, :critical, project: alert_project) }
|
|
|
|
let_it_be(:triggered_high_alert) { create(:alert_management_alert, :triggered, :high, project: alert_project) }
|
|
|
|
|
|
|
|
it { is_expected.to eq([triggered_critical_alert, triggered_high_alert]) }
|
|
|
|
end
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
describe '.counts_by_project_id' do
|
|
|
|
subject { described_class.counts_by_project_id }
|
|
|
|
|
|
|
|
it do
|
|
|
|
is_expected.to eq(
|
2020-10-24 23:57:45 +05:30
|
|
|
project.id => 2,
|
|
|
|
project2.id => 2
|
2020-07-28 23:09:34 +05:30
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.open' do
|
|
|
|
subject { described_class.open }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(acknowledged_alert, triggered_alert) }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.not_resolved' do
|
|
|
|
subject { described_class.not_resolved }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(acknowledged_alert, triggered_alert, ignored_alert) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
it_behaves_like 'a model including Escalatable'
|
2021-01-03 14:25:43 +05:30
|
|
|
|
|
|
|
describe '.counts_by_status' do
|
|
|
|
subject { described_class.counts_by_status }
|
|
|
|
|
|
|
|
it do
|
|
|
|
is_expected.to eq(
|
|
|
|
triggered: 1,
|
|
|
|
acknowledged: 1,
|
|
|
|
resolved: 1,
|
|
|
|
ignored: 1
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
describe '.last_prometheus_alert_by_project_id' do
|
|
|
|
subject { described_class.last_prometheus_alert_by_project_id }
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
let!(:p1_alert_1) { triggered_alert }
|
|
|
|
let!(:p1_alert_2) { acknowledged_alert }
|
2020-07-28 23:09:34 +05:30
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
let!(:p2_alert_1) { resolved_alert }
|
|
|
|
let!(:p2_alert_2) { ignored_alert }
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
it 'returns the latest alert for each project' do
|
2020-10-24 23:57:45 +05:30
|
|
|
expect(subject).to contain_exactly(p1_alert_2, p2_alert_2)
|
2020-07-28 23:09:34 +05:30
|
|
|
end
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
describe '.search' do
|
2020-10-24 23:57:45 +05:30
|
|
|
let(:alert) { triggered_alert }
|
|
|
|
|
|
|
|
before do
|
|
|
|
alert.update!(title: 'Title', description: 'Desc', service: 'Service', monitoring_tool: 'Monitor')
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
subject { AlertManagement::Alert.search(query) }
|
|
|
|
|
|
|
|
context 'does not contain search string' do
|
|
|
|
let(:query) { 'something else' }
|
|
|
|
|
|
|
|
it { is_expected.to be_empty }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'title includes query' do
|
|
|
|
let(:query) { alert.title.upcase }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(alert) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'description includes query' do
|
|
|
|
let(:query) { alert.description.upcase }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(alert) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'service includes query' do
|
|
|
|
let(:query) { alert.service.upcase }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(alert) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'monitoring tool includes query' do
|
|
|
|
let(:query) { alert.monitoring_tool.upcase }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(alert) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-24 15:15:51 +05:30
|
|
|
describe '.reference_pattern' do
|
|
|
|
subject { described_class.reference_pattern }
|
|
|
|
|
|
|
|
it { is_expected.to match('gitlab-org/gitlab^alert#123') }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.link_reference_pattern' do
|
|
|
|
subject { described_class.link_reference_pattern }
|
|
|
|
|
|
|
|
it { is_expected.to match(triggered_alert.details_url) }
|
|
|
|
it { is_expected.not_to match("#{Gitlab.config.gitlab.url}/gitlab-org/gitlab/alert_management/123") }
|
|
|
|
it { is_expected.not_to match("#{Gitlab.config.gitlab.url}/gitlab-org/gitlab/issues/123") }
|
|
|
|
it { is_expected.not_to match("gitlab-org/gitlab/-/alert_management/123") }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.reference_valid?' do
|
|
|
|
using RSpec::Parameterized::TableSyntax
|
|
|
|
|
|
|
|
where(:ref, :result) do
|
|
|
|
'123456' | true
|
|
|
|
'1' | true
|
|
|
|
'-1' | false
|
|
|
|
nil | false
|
|
|
|
'123456891012345678901234567890' | false
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it { expect(described_class.reference_valid?(ref)).to eq(result) }
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
describe '.open_status?' do
|
|
|
|
using RSpec::Parameterized::TableSyntax
|
|
|
|
|
|
|
|
where(:status, :is_open_status) do
|
|
|
|
:triggered | true
|
|
|
|
:acknowledged | true
|
|
|
|
:resolved | false
|
|
|
|
:ignored | false
|
|
|
|
nil | false
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
it 'returns true when the status is open status' do
|
|
|
|
expect(described_class.open_status?(status)).to eq(is_open_status)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
describe '#open?' do
|
|
|
|
it 'returns true when the status is open status' do
|
|
|
|
expect(triggered_alert.open?).to be true
|
|
|
|
expect(acknowledged_alert.open?).to be true
|
|
|
|
expect(resolved_alert.open?).to be false
|
|
|
|
expect(ignored_alert.open?).to be false
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
describe '#to_reference' do
|
|
|
|
it { expect(triggered_alert.to_reference).to eq("^alert##{triggered_alert.iid}") }
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
2020-06-23 00:09:42 +05:30
|
|
|
|
|
|
|
describe '#register_new_event!' do
|
|
|
|
subject { alert.register_new_event! }
|
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
let(:alert) { triggered_alert }
|
2020-06-23 00:09:42 +05:30
|
|
|
|
|
|
|
it 'increments the events count by 1' do
|
|
|
|
expect { subject }.to change { alert.events }.by(1)
|
|
|
|
end
|
|
|
|
end
|
2021-01-03 14:25:43 +05:30
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
describe '#resolved_at' do
|
|
|
|
subject { resolved_alert.resolved_at }
|
2021-01-03 14:25:43 +05:30
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
it { is_expected.to eq(resolved_alert.ended_at) }
|
2021-01-03 14:25:43 +05:30
|
|
|
end
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
describe '#resolved_at=' do
|
|
|
|
let(:resolve_time) { Time.current }
|
2021-01-03 14:25:43 +05:30
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
it 'sets ended_at' do
|
|
|
|
triggered_alert.resolved_at = resolve_time
|
2021-01-03 14:25:43 +05:30
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
expect(triggered_alert.ended_at).to eq(resolve_time)
|
|
|
|
expect(triggered_alert.resolved_at).to eq(resolve_time)
|
2021-01-03 14:25:43 +05:30
|
|
|
end
|
|
|
|
end
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|