summaryrefslogtreecommitdiff
path: root/spec/models/alert_management/alert_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/alert_management/alert_spec.rb')
-rw-r--r--spec/models/alert_management/alert_spec.rb320
1 files changed, 320 insertions, 0 deletions
diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb
new file mode 100644
index 00000000000..1da0c6d4071
--- /dev/null
+++ b/spec/models/alert_management/alert_spec.rb
@@ -0,0 +1,320 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe AlertManagement::Alert do
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:issue) }
+ 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) }
+
+ context 'when status is triggered' do
+ context 'when ended_at is blank' do
+ subject { build(:alert_management_alert) }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when ended_at is present' do
+ subject { build(:alert_management_alert, ended_at: Time.current) }
+
+ it { is_expected.to be_invalid }
+ end
+ end
+
+ context 'when status is acknowledged' do
+ context 'when ended_at is blank' do
+ subject { build(:alert_management_alert, :acknowledged) }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when ended_at is present' do
+ subject { build(:alert_management_alert, :acknowledged, ended_at: Time.current) }
+
+ it { is_expected.to be_invalid }
+ end
+ end
+
+ context 'when status is resolved' do
+ context 'when ended_at is blank' do
+ subject { build(:alert_management_alert, :resolved, ended_at: nil) }
+
+ it { is_expected.to be_invalid }
+ end
+
+ context 'when ended_at is present' do
+ subject { build(:alert_management_alert, :resolved, ended_at: Time.current) }
+
+ it { is_expected.to be_valid }
+ end
+ end
+
+ context 'when status is ignored' do
+ context 'when ended_at is blank' do
+ subject { build(:alert_management_alert, :ignored) }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when ended_at is present' do
+ subject { build(:alert_management_alert, :ignored, ended_at: Time.current) }
+
+ it { is_expected.to be_invalid }
+ end
+ end
+
+ describe 'fingerprint' do
+ let_it_be(:fingerprint) { 'fingerprint' }
+ let_it_be(:existing_alert) { create(:alert_management_alert, fingerprint: fingerprint) }
+ let(:new_alert) { build(:alert_management_alert, fingerprint: fingerprint, project: project) }
+
+ subject { new_alert }
+
+ context 'adding an alert with the same fingerprint' do
+ context 'same project' do
+ let(:project) { existing_alert.project }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'different project' do
+ let(:project) { create(:project) }
+
+ it { is_expected.to be_valid }
+ end
+ end
+ end
+
+ describe 'hosts' do
+ subject(:alert) { build(:alert_management_alert, hosts: hosts) }
+
+ 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
+ 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
+ let_it_be(:project) { create(:project) }
+ let_it_be(:triggered_alert) { create(:alert_management_alert, project: project) }
+ let_it_be(:resolved_alert) { create(:alert_management_alert, :resolved, project: project) }
+ let_it_be(:ignored_alert) { create(:alert_management_alert, :ignored, project: project) }
+
+ describe '.for_iid' do
+ subject { AlertManagement::Alert.for_iid(triggered_alert.iid) }
+
+ it { is_expected.to match_array(triggered_alert) }
+ end
+
+ describe '.for_status' do
+ let(:status) { AlertManagement::Alert::STATUSES[:resolved] }
+
+ subject { AlertManagement::Alert.for_status(status) }
+
+ it { is_expected.to match_array(resolved_alert) }
+
+ context 'with multiple statuses' do
+ let(:status) { AlertManagement::Alert::STATUSES.values_at(:resolved, :ignored) }
+
+ it { is_expected.to match_array([resolved_alert, ignored_alert]) }
+ end
+ end
+
+ describe '.for_fingerprint' do
+ let_it_be(:fingerprint) { SecureRandom.hex }
+ let_it_be(:alert_with_fingerprint) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
+ let_it_be(:unrelated_alert_with_finger_print) { create(:alert_management_alert, fingerprint: fingerprint) }
+
+ subject { described_class.for_fingerprint(project, fingerprint) }
+
+ it { is_expected.to contain_exactly(alert_with_fingerprint) }
+ end
+
+ describe '.counts_by_status' do
+ subject { described_class.counts_by_status }
+
+ it do
+ is_expected.to eq(
+ triggered_alert.status => 1,
+ resolved_alert.status => 1,
+ ignored_alert.status => 1
+ )
+ end
+ end
+ end
+
+ describe '.search' do
+ let_it_be(:alert) do
+ create(:alert_management_alert,
+ title: 'Title',
+ description: 'Desc',
+ service: 'Service',
+ monitoring_tool: 'Monitor'
+ )
+ 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
+
+ describe '#details' do
+ let(:payload) do
+ {
+ 'title' => 'Details title',
+ 'custom' => {
+ 'alert' => {
+ 'fields' => %w[one two]
+ }
+ },
+ 'yet' => {
+ 'another' => 'field'
+ }
+ }
+ end
+ let(:alert) { build(:alert_management_alert, title: 'Details title', payload: payload) }
+
+ subject { alert.details }
+
+ it 'renders the payload as inline hash' do
+ is_expected.to eq(
+ 'custom.alert.fields' => %w[one two],
+ 'yet.another' => 'field'
+ )
+ end
+ end
+
+ describe '#trigger' do
+ subject { alert.trigger }
+
+ context 'when alert is in triggered state' do
+ let(:alert) { create(:alert_management_alert) }
+
+ it 'does not change the alert status' do
+ expect { subject }.not_to change { alert.reload.status }
+ end
+ end
+
+ context 'when alert not in triggered state' do
+ let(:alert) { create(:alert_management_alert, :resolved) }
+
+ it 'changes the alert status to triggered' do
+ expect { subject }.to change { alert.triggered? }.to(true)
+ end
+
+ it 'resets ended at' do
+ expect { subject }.to change { alert.reload.ended_at }.to nil
+ end
+ end
+ end
+
+ describe '#acknowledge' do
+ subject { alert.acknowledge }
+
+ let(:alert) { create(:alert_management_alert, :resolved) }
+
+ it 'changes the alert status to acknowledged' do
+ expect { subject }.to change { alert.acknowledged? }.to(true)
+ end
+
+ it 'resets ended at' do
+ expect { subject }.to change { alert.reload.ended_at }.to nil
+ end
+ end
+
+ describe '#resolve' do
+ let!(:ended_at) { Time.current }
+
+ subject do
+ alert.ended_at = ended_at
+ alert.resolve
+ end
+
+ context 'when alert already resolved' do
+ let(:alert) { create(:alert_management_alert, :resolved) }
+
+ it 'does not change the alert status' do
+ expect { subject }.not_to change { alert.reload.status }
+ end
+ end
+
+ context 'when alert is not resolved' do
+ let(:alert) { create(:alert_management_alert) }
+
+ it 'changes alert status to "resolved"' do
+ expect { subject }.to change { alert.resolved? }.to(true)
+ end
+ end
+ end
+
+ describe '#ignore' do
+ subject { alert.ignore }
+
+ let(:alert) { create(:alert_management_alert, :resolved) }
+
+ it 'changes the alert status to ignored' do
+ expect { subject }.to change { alert.ignored? }.to(true)
+ end
+
+ it 'resets ended at' do
+ expect { subject }.to change { alert.reload.ended_at }.to nil
+ end
+ end
+end