2020-02-06 16:08:48 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-06-24 05:08:32 -04:00
|
|
|
RSpec.describe Gitlab::Alerting::Alert do
|
2020-02-06 16:08:48 -05:00
|
|
|
let_it_be(:project) { create(:project) }
|
|
|
|
|
|
|
|
let(:alert) { build(:alerting_alert, project: project, payload: payload) }
|
|
|
|
let(:payload) { {} }
|
|
|
|
|
|
|
|
shared_context 'gitlab alert' do
|
|
|
|
let!(:gitlab_alert) { create(:prometheus_alert, project: project) }
|
2020-04-10 14:09:32 -04:00
|
|
|
let(:gitlab_alert_id) { gitlab_alert.id }
|
2020-02-06 16:08:48 -05:00
|
|
|
|
|
|
|
before do
|
2020-04-10 14:09:32 -04:00
|
|
|
payload['labels'] = {
|
|
|
|
'gitlab_alert_id' => gitlab_alert.prometheus_metric_id.to_s,
|
|
|
|
'gitlab_prometheus_alert_id' => gitlab_alert_id
|
|
|
|
}
|
2020-02-06 16:08:48 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-21 07:09:07 -05:00
|
|
|
shared_context 'full query' do
|
|
|
|
before do
|
|
|
|
payload['generatorURL'] = 'http://localhost:9090/graph?g0.expr=vector%281%29'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-06 16:08:48 -05:00
|
|
|
shared_examples 'invalid alert' do
|
|
|
|
it 'is invalid' do
|
|
|
|
expect(alert).not_to be_valid
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples 'parse payload' do |*pairs|
|
|
|
|
context 'without payload' do
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
pairs.each do |pair|
|
|
|
|
context "with #{pair}" do
|
|
|
|
let(:value) { 'some value' }
|
|
|
|
|
|
|
|
before do
|
|
|
|
section, name = pair.split('/')
|
|
|
|
payload[section] = { name => value }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(value) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#gitlab_alert' do
|
|
|
|
subject { alert.gitlab_alert }
|
|
|
|
|
|
|
|
context 'without payload' do
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with gitlab alert' do
|
|
|
|
include_context 'gitlab alert'
|
|
|
|
|
|
|
|
it { is_expected.to eq(gitlab_alert) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with unknown gitlab alert' do
|
|
|
|
include_context 'gitlab alert' do
|
|
|
|
let(:gitlab_alert_id) { 'unknown' }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
2020-04-10 14:09:32 -04:00
|
|
|
|
|
|
|
context 'when two alerts with the same metric exist' do
|
|
|
|
include_context 'gitlab alert'
|
|
|
|
|
|
|
|
let!(:second_gitlab_alert) do
|
|
|
|
create(:prometheus_alert,
|
|
|
|
project: project,
|
|
|
|
prometheus_metric_id: gitlab_alert.prometheus_metric_id
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'alert id given in params' do
|
|
|
|
before do
|
|
|
|
payload['labels'] = {
|
|
|
|
'gitlab_alert_id' => gitlab_alert.prometheus_metric_id.to_s,
|
|
|
|
'gitlab_prometheus_alert_id' => second_gitlab_alert.id
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(second_gitlab_alert) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'metric id given in params' do
|
|
|
|
# This tests the case when two alerts are found, as metric id
|
|
|
|
# is not unique.
|
|
|
|
|
|
|
|
# Note the metric id was incorrectly named as 'gitlab_alert_id'
|
|
|
|
# in PrometheusAlert#to_param.
|
|
|
|
before do
|
|
|
|
payload['labels'] = { 'gitlab_alert_id' => gitlab_alert.prometheus_metric_id }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
end
|
2020-02-06 16:08:48 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
describe '#title' do
|
|
|
|
subject { alert.title }
|
|
|
|
|
|
|
|
it_behaves_like 'parse payload',
|
|
|
|
'annotations/title',
|
|
|
|
'annotations/summary',
|
|
|
|
'labels/alertname'
|
|
|
|
|
|
|
|
context 'with gitlab alert' do
|
|
|
|
include_context 'gitlab alert'
|
|
|
|
|
|
|
|
context 'with annotations/title' do
|
|
|
|
let(:value) { 'annotation title' }
|
|
|
|
|
|
|
|
before do
|
|
|
|
payload['annotations'] = { 'title' => value }
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(gitlab_alert.title) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#description' do
|
|
|
|
subject { alert.description }
|
|
|
|
|
|
|
|
it_behaves_like 'parse payload', 'annotations/description'
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#annotations' do
|
|
|
|
subject { alert.annotations }
|
|
|
|
|
|
|
|
context 'without payload' do
|
|
|
|
it { is_expected.to eq([]) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with payload' do
|
|
|
|
before do
|
|
|
|
payload['annotations'] = { 'foo' => 'value1', 'bar' => 'value2' }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'parses annotations' do
|
|
|
|
expect(subject.size).to eq(2)
|
|
|
|
expect(subject.map(&:label)).to eq(%w[foo bar])
|
|
|
|
expect(subject.map(&:value)).to eq(%w[value1 value2])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#environment' do
|
|
|
|
subject { alert.environment }
|
|
|
|
|
|
|
|
context 'without gitlab_alert' do
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with gitlab alert' do
|
|
|
|
include_context 'gitlab alert'
|
|
|
|
|
|
|
|
it { is_expected.to eq(gitlab_alert.environment) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#starts_at' do
|
|
|
|
subject { alert.starts_at }
|
|
|
|
|
|
|
|
context 'with empty startsAt' do
|
|
|
|
before do
|
|
|
|
payload['startsAt'] = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with invalid startsAt' do
|
|
|
|
before do
|
|
|
|
payload['startsAt'] = 'invalid'
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with payload' do
|
|
|
|
let(:time) { Time.now.change(usec: 0) }
|
|
|
|
|
|
|
|
before do
|
|
|
|
payload['startsAt'] = time.rfc3339
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(time) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#full_query' do
|
|
|
|
using RSpec::Parameterized::TableSyntax
|
|
|
|
|
|
|
|
subject { alert.full_query }
|
|
|
|
|
|
|
|
where(:generator_url, :expected_query) do
|
|
|
|
nil | nil
|
|
|
|
'http://localhost' | nil
|
|
|
|
'invalid url' | nil
|
|
|
|
'http://localhost:9090/graph?g1.expr=vector%281%29' | nil
|
|
|
|
'http://localhost:9090/graph?g0.expr=vector%281%29' | 'vector(1)'
|
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
before do
|
|
|
|
payload['generatorURL'] = generator_url
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(expected_query) }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with gitlab alert' do
|
|
|
|
include_context 'gitlab alert'
|
2020-02-21 07:09:07 -05:00
|
|
|
include_context 'full query'
|
2020-02-06 16:08:48 -05:00
|
|
|
|
|
|
|
it { is_expected.to eq(gitlab_alert.full_query) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-10 05:09:39 -04:00
|
|
|
describe '#y_label' do
|
|
|
|
subject { alert.y_label }
|
|
|
|
|
|
|
|
it_behaves_like 'parse payload', 'annotations/gitlab_y_label'
|
|
|
|
|
|
|
|
context 'when y_label is not included in the payload' do
|
|
|
|
it_behaves_like 'parse payload', 'annotations/title'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-06 16:08:48 -05:00
|
|
|
describe '#alert_markdown' do
|
|
|
|
subject { alert.alert_markdown }
|
|
|
|
|
|
|
|
it_behaves_like 'parse payload', 'annotations/gitlab_incident_markdown'
|
|
|
|
end
|
|
|
|
|
2020-05-07 11:09:29 -04:00
|
|
|
describe '#gitlab_fingerprint' do
|
|
|
|
subject { alert.gitlab_fingerprint }
|
|
|
|
|
|
|
|
context 'when the alert is a GitLab managed alert' do
|
|
|
|
include_context 'gitlab alert'
|
|
|
|
|
|
|
|
it 'returns a fingerprint' do
|
2020-05-22 05:08:09 -04:00
|
|
|
plain_fingerprint = [alert.metric_id, alert.starts_at_raw].join('/')
|
2020-05-07 11:09:29 -04:00
|
|
|
|
|
|
|
is_expected.to eq(Digest::SHA1.hexdigest(plain_fingerprint))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the alert is from self managed Prometheus' do
|
|
|
|
include_context 'full query'
|
|
|
|
|
|
|
|
it 'returns a fingerprint' do
|
2020-05-22 05:08:09 -04:00
|
|
|
plain_fingerprint = [alert.starts_at_raw, alert.title, alert.full_query].join('/')
|
2020-05-07 11:09:29 -04:00
|
|
|
|
|
|
|
is_expected.to eq(Digest::SHA1.hexdigest(plain_fingerprint))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-06 16:08:48 -05:00
|
|
|
describe '#valid?' do
|
|
|
|
before do
|
|
|
|
payload.update(
|
|
|
|
'annotations' => { 'title' => 'some title' },
|
|
|
|
'startsAt' => Time.now.rfc3339
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
subject { alert }
|
|
|
|
|
|
|
|
it { is_expected.to be_valid }
|
|
|
|
|
|
|
|
context 'without project' do
|
2020-03-29 11:07:54 -04:00
|
|
|
let(:project) { nil }
|
2020-02-06 16:08:48 -05:00
|
|
|
|
|
|
|
it { is_expected.not_to be_valid }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'without starts_at' do
|
|
|
|
before do
|
|
|
|
payload['startsAt'] = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.not_to be_valid }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|