diff options
Diffstat (limited to 'spec')
18 files changed, 250 insertions, 886 deletions
diff --git a/spec/controllers/projects/import/jira_controller_spec.rb b/spec/controllers/projects/import/jira_controller_spec.rb index b82735a56b3..37a7fce0c23 100644 --- a/spec/controllers/projects/import/jira_controller_spec.rb +++ b/spec/controllers/projects/import/jira_controller_spec.rb @@ -12,7 +12,6 @@ RSpec.describe Projects::Import::JiraController do def ensure_correct_config sign_in(user) project.add_maintainer(user) - stub_feature_flags(jira_issue_import: true) stub_jira_service_test end @@ -77,7 +76,6 @@ RSpec.describe Projects::Import::JiraController do before do sign_in(user) project.add_maintainer(user) - stub_feature_flags(jira_issue_import: true) end context 'when Jira service is not enabled for the project' do diff --git a/spec/factories/alert_management/alerts.rb b/spec/factories/alert_management/alerts.rb index d931947fff1..d0546657ccf 100644 --- a/spec/factories/alert_management/alerts.rb +++ b/spec/factories/alert_management/alerts.rb @@ -100,7 +100,7 @@ FactoryBot.define do end trait :prometheus do - monitoring_tool { Gitlab::AlertManagement::AlertParams::MONITORING_TOOLS[:prometheus] } + monitoring_tool { Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus] } payload do { annotations: { @@ -123,5 +123,17 @@ FactoryBot.define do with_description low end + + trait :from_payload do + after(:build) do |alert| + alert_params = ::Gitlab::AlertManagement::Payload.parse( + alert.project, + alert.payload, + monitoring_tool: alert.monitoring_tool + ).alert_params + + alert.assign_attributes(alert_params) + end + end end end diff --git a/spec/factories/alerting/alert.rb b/spec/factories/alerting/alert.rb deleted file mode 100644 index 285bb14efa2..00000000000 --- a/spec/factories/alerting/alert.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :alerting_alert, class: 'Gitlab::Alerting::Alert' do - project - payload { {} } - - transient do - metric_id { nil } - - after(:build) do |alert, evaluator| - unless alert.payload.key?('startsAt') - alert.payload['startsAt'] = Time.now.rfc3339 - end - - if metric_id = evaluator.metric_id - alert.payload['labels'] ||= {} - alert.payload['labels']['gitlab_alert_id'] = metric_id.to_s - end - end - end - - skip_create - end -end diff --git a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap index cf5451490eb..5faae5690db 100644 --- a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap +++ b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap @@ -52,27 +52,6 @@ exports[`packages_list_row renders 1`] = ` <!----> <div - class="gl-display-flex gl-align-items-center" - > - <gl-icon-stub - class="gl-ml-3 gl-mr-2 gl-min-w-0" - name="review-list" - size="16" - /> - - <gl-link-stub - class="gl-text-body gl-min-w-0" - data-testid="packages-row-project" - href="/foo/bar/baz" - > - <gl-truncate-stub - position="end" - text="foo/bar/baz" - /> - </gl-link-stub> - </div> - - <div class="d-flex align-items-center" data-testid="package-type" > @@ -86,6 +65,10 @@ exports[`packages_list_row renders 1`] = ` Maven </span> </div> + + <package-path-stub + path="foo/bar/baz" + /> </div> </div> </div> diff --git a/spec/frontend/packages/shared/components/package_list_row_spec.js b/spec/frontend/packages/shared/components/package_list_row_spec.js index f4eabf7bb67..0d0ea4e2122 100644 --- a/spec/frontend/packages/shared/components/package_list_row_spec.js +++ b/spec/frontend/packages/shared/components/package_list_row_spec.js @@ -1,6 +1,7 @@ import { shallowMount } from '@vue/test-utils'; import PackagesListRow from '~/packages/shared/components/package_list_row.vue'; import PackageTags from '~/packages/shared/components/package_tags.vue'; +import PackagePath from '~/packages/shared/components/package_path.vue'; import ListItem from '~/vue_shared/components/registry/list_item.vue'; import { packageList } from '../../mock_data'; @@ -11,7 +12,7 @@ describe('packages_list_row', () => { const [packageWithoutTags, packageWithTags] = packageList; const findPackageTags = () => wrapper.find(PackageTags); - const findProjectLink = () => wrapper.find('[data-testid="packages-row-project"]'); + const findPackagePath = () => wrapper.find(PackagePath); const findDeleteButton = () => wrapper.find('[data-testid="action-delete"]'); const findPackageType = () => wrapper.find('[data-testid="package-type"]'); @@ -63,8 +64,9 @@ describe('packages_list_row', () => { mountComponent({ isGroup: true }); }); - it('has project field', () => { - expect(findProjectLink().exists()).toBe(true); + it('has a package path component', () => { + expect(findPackagePath().exists()).toBe(true); + expect(findPackagePath().props()).toMatchObject({ path: 'foo/bar/baz' }); }); }); diff --git a/spec/frontend/packages/shared/components/package_path_spec.js b/spec/frontend/packages/shared/components/package_path_spec.js new file mode 100644 index 00000000000..40d455ac77c --- /dev/null +++ b/spec/frontend/packages/shared/components/package_path_spec.js @@ -0,0 +1,86 @@ +import { shallowMount } from '@vue/test-utils'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import PackagePath from '~/packages/shared/components/package_path.vue'; + +describe('PackagePath', () => { + let wrapper; + + const mountComponent = (propsData = { path: 'foo' }) => { + wrapper = shallowMount(PackagePath, { + propsData, + directives: { + GlTooltip: createMockDirective(), + }, + }); + }; + + const BASE_ICON = 'base-icon'; + const ROOT_LINK = 'root-link'; + const ROOT_CHEVRON = 'root-chevron'; + const ELLIPSIS_ICON = 'ellipsis-icon'; + const ELLIPSIS_CHEVRON = 'ellipsis-chevron'; + const LEAF_LINK = 'leaf-link'; + + const findItem = name => wrapper.find(`[data-testid="${name}"]`); + const findTooltip = w => getBinding(w.element, 'gl-tooltip'); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe.each` + path | rootUrl | shouldExist | shouldNotExist + ${'foo/bar'} | ${'/foo/bar'} | ${[]} | ${[ROOT_CHEVRON, ELLIPSIS_ICON, ELLIPSIS_CHEVRON, LEAF_LINK]} + ${'foo/bar/baz'} | ${'/foo/bar'} | ${[ROOT_CHEVRON, LEAF_LINK]} | ${[ELLIPSIS_ICON, ELLIPSIS_CHEVRON]} + ${'foo/bar/baz/baz2'} | ${'/foo/bar'} | ${[ROOT_CHEVRON, LEAF_LINK, ELLIPSIS_ICON, ELLIPSIS_CHEVRON]} | ${[]} + ${'foo/bar/baz/baz2/bar2'} | ${'/foo/bar'} | ${[ROOT_CHEVRON, LEAF_LINK, ELLIPSIS_ICON, ELLIPSIS_CHEVRON]} | ${[]} + `('given path $path', ({ path, shouldExist, shouldNotExist, rootUrl }) => { + const pathPieces = path.split('/').slice(1); + const hasTooltip = shouldExist.includes(ELLIPSIS_ICON); + + beforeEach(() => { + mountComponent({ path }); + }); + + it('should have a base icon', () => { + expect(findItem(BASE_ICON).exists()).toBe(true); + }); + + it('should have a root link', () => { + const root = findItem(ROOT_LINK); + expect(root.exists()).toBe(true); + expect(root.attributes('href')).toBe(rootUrl); + }); + + if (hasTooltip) { + it('should have a tooltip', () => { + const tooltip = findTooltip(findItem(ELLIPSIS_ICON)); + expect(tooltip).toBeDefined(); + expect(tooltip.value).toMatchObject({ + title: path, + }); + }); + } + + if (shouldExist.length) { + it.each(shouldExist)(`should have %s`, element => { + expect(findItem(element).exists()).toBe(true); + }); + } + + if (shouldNotExist.length) { + it.each(shouldNotExist)(`should not have %s`, element => { + expect(findItem(element).exists()).toBe(false); + }); + } + + if (shouldExist.includes(LEAF_LINK)) { + it('the last link should be the last piece of the path', () => { + const leaf = findItem(LEAF_LINK); + expect(leaf.attributes('href')).toBe(`/${path}`); + expect(leaf.text()).toBe(pathPieces[pathPieces.length - 1]); + }); + } + }); +}); diff --git a/spec/lib/gitlab/alert_management/alert_params_spec.rb b/spec/lib/gitlab/alert_management/alert_params_spec.rb deleted file mode 100644 index c3171be5e29..00000000000 --- a/spec/lib/gitlab/alert_management/alert_params_spec.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::AlertManagement::AlertParams do - let_it_be(:project) { create(:project, :repository, :private) } - - describe '.from_generic_alert' do - let(:started_at) { Time.current.change(usec: 0).rfc3339 } - let(:default_payload) do - { - 'title' => 'Alert title', - 'description' => 'Description', - 'monitoring_tool' => 'Monitoring tool name', - 'service' => 'Service', - 'hosts' => ['gitlab.com'], - 'start_time' => started_at, - 'some' => { 'extra' => { 'payload' => 'here' } } - } - end - - let(:payload) { default_payload } - - subject { described_class.from_generic_alert(project: project, payload: payload) } - - it 'returns Alert compatible parameters' do - is_expected.to eq( - project_id: project.id, - title: 'Alert title', - description: 'Description', - monitoring_tool: 'Monitoring tool name', - service: 'Service', - severity: 'critical', - hosts: ['gitlab.com'], - payload: payload, - started_at: started_at, - ended_at: nil, - fingerprint: nil, - environment: nil - ) - end - - context 'when severity given' do - let(:payload) { default_payload.merge(severity: 'low') } - - it 'returns Alert compatible parameters' do - expect(subject[:severity]).to eq('low') - end - end - - context 'when there are no hosts in the payload' do - let(:payload) { {} } - - it 'hosts param is an empty array' do - expect(subject[:hosts]).to be_empty - end - end - end - - describe '.from_prometheus_alert' do - let(:payload) do - { - 'status' => 'firing', - 'labels' => { - 'alertname' => 'GitalyFileServerDown', - 'channel' => 'gitaly', - 'pager' => 'pagerduty', - 'severity' => 's1' - }, - 'annotations' => { - 'description' => 'Alert description', - 'runbook' => 'troubleshooting/gitaly-down.md', - 'title' => 'Alert title' - }, - 'startsAt' => '2020-04-27T10:10:22.265949279Z', - 'endsAt' => '0001-01-01T00:00:00Z', - 'generatorURL' => 'http://8d467bd4607a:9090/graph?g0.expr=vector%281%29&g0.tab=1', - 'fingerprint' => 'b6ac4d42057c43c1' - } - end - - let(:parsed_alert) { Gitlab::Alerting::Alert.new(project: project, payload: payload) } - - subject { described_class.from_prometheus_alert(project: project, parsed_alert: parsed_alert) } - - it 'returns Alert-compatible params' do - is_expected.to eq( - project_id: project.id, - title: 'Alert title', - description: 'Alert description', - monitoring_tool: 'Prometheus', - payload: payload, - started_at: parsed_alert.starts_at, - ended_at: parsed_alert.ends_at, - fingerprint: parsed_alert.gitlab_fingerprint, - environment: parsed_alert.environment, - prometheus_alert: parsed_alert.gitlab_alert - ) - end - end -end diff --git a/spec/lib/gitlab/alert_management/payload/generic_spec.rb b/spec/lib/gitlab/alert_management/payload/generic_spec.rb index 538a822503e..3683dcd752f 100644 --- a/spec/lib/gitlab/alert_management/payload/generic_spec.rb +++ b/spec/lib/gitlab/alert_management/payload/generic_spec.rb @@ -86,4 +86,34 @@ RSpec.describe Gitlab::AlertManagement::Payload::Generic do it_behaves_like 'parsable alert payload field', 'gitlab_environment_name' end + + describe '#description' do + subject { parsed_payload.description } + + it_behaves_like 'parsable alert payload field', 'description' + end + + describe '#ends_at' do + let(:current_time) { Time.current.change(usec: 0).utc } + + subject { parsed_payload.ends_at } + + around do |example| + Timecop.freeze(current_time) { example.run } + end + + context 'without end_time' do + it { is_expected.to be_nil } + end + + context "with end_time" do + let(:value) { 10.minutes.ago.change(usec: 0).utc } + + before do + raw_payload['end_time'] = value.to_s + end + + it { is_expected.to eq(value) } + end + end end diff --git a/spec/lib/gitlab/alerting/alert_spec.rb b/spec/lib/gitlab/alerting/alert_spec.rb deleted file mode 100644 index b53b71e3f3e..00000000000 --- a/spec/lib/gitlab/alerting/alert_spec.rb +++ /dev/null @@ -1,299 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::Alerting::Alert do - 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) } - let(:gitlab_alert_id) { gitlab_alert.id } - - before do - payload['labels'] = { - 'gitlab_alert_id' => gitlab_alert.prometheus_metric_id.to_s, - 'gitlab_prometheus_alert_id' => gitlab_alert_id - } - end - end - - shared_context 'full query' do - before do - payload['generatorURL'] = 'http://localhost:9090/graph?g0.expr=vector%281%29' - end - end - - 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 - - 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 - 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.current.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' - include_context 'full query' - - it { is_expected.to eq(gitlab_alert.full_query) } - end - end - - 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 - - describe '#alert_markdown' do - subject { alert.alert_markdown } - - it_behaves_like 'parse payload', 'annotations/gitlab_incident_markdown' - end - - 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 - plain_fingerprint = [alert.metric_id, alert.starts_at_raw].join('/') - - 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 - plain_fingerprint = [alert.starts_at_raw, alert.title, alert.full_query].join('/') - - is_expected.to eq(Digest::SHA1.hexdigest(plain_fingerprint)) - end - end - end - - describe '#valid?' do - before do - payload.update( - 'annotations' => { 'title' => 'some title' }, - 'startsAt' => Time.current.rfc3339 - ) - end - - subject { alert } - - it { is_expected.to be_valid } - - context 'without project' do - let(:project) { nil } - - 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 diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb index c7d55c396ef..708e9a13aed 100644 --- a/spec/lib/gitlab/danger/helper_spec.rb +++ b/spec/lib/gitlab/danger/helper_spec.rb @@ -435,6 +435,28 @@ RSpec.describe Gitlab::Danger::Helper do end end + describe '#draft_mr?' do + it 'returns false when `gitlab_helper` is unavailable' do + expect(helper).to receive(:gitlab_helper).and_return(nil) + + expect(helper).not_to be_draft_mr + end + + it 'returns true for a draft MR' do + expect(fake_gitlab).to receive(:mr_json) + .and_return('title' => 'Draft: My MR title') + + expect(helper).to be_draft_mr + end + + it 'returns false for non draft MR' do + expect(fake_gitlab).to receive(:mr_json) + .and_return('title' => 'My MR title') + + expect(helper).not_to be_draft_mr + end + end + describe '#cherry_pick_mr?' do it 'returns false when `gitlab_helper` is unavailable' do expect(helper).to receive(:gitlab_helper).and_return(nil) diff --git a/spec/mailers/emails/projects_spec.rb b/spec/mailers/emails/projects_spec.rb index 599f62a8113..aa5947bf68e 100644 --- a/spec/mailers/emails/projects_spec.rb +++ b/spec/mailers/emails/projects_spec.rb @@ -30,107 +30,118 @@ RSpec.describe Emails::Projects do let_it_be(:user) { create(:user) } describe '#prometheus_alert_fired_email' do + let(:default_title) { Gitlab::AlertManagement::Payload::Generic::DEFAULT_TITLE } + let(:payload) { { 'startsAt' => Time.now.rfc3339 } } + let(:alert_attributes) { build(:alert_management_alert, :from_payload, payload: payload, project: project).attributes } + subject do - Notify.prometheus_alert_fired_email(project.id, user.id, alert_params) + Notify.prometheus_alert_fired_email(project.id, user.id, alert_attributes) end - let(:alert_params) do - { 'startsAt' => Time.now.rfc3339 } + context 'missing required attributes' do + let(:alert_attributes) { build(:alert_management_alert, :prometheus, :from_payload, payload: payload, project: project).attributes } + + it_behaves_like 'no email' end - context 'with a gitlab alert' do - before do - alert_params['labels'] = { 'gitlab_alert_id' => alert.prometheus_metric_id.to_s } - end + context 'with minimum required attributes' do + let(:payload) { {} } - let(:title) do - "#{alert.title} #{alert.computed_operator} #{alert.threshold}" - end + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - let(:metrics_url) do - metrics_project_environment_url(project, environment) + it 'has expected subject' do + is_expected.to have_subject("#{project.name} | Alert: #{default_title}") end - let(:environment) { alert.environment } + it 'has expected content' do + is_expected.to have_body_text('An alert has been triggered') + is_expected.to have_body_text(project.full_path) + is_expected.not_to have_body_text('Description:') + is_expected.not_to have_body_text('Environment:') + is_expected.not_to have_body_text('Metric:') + end + end - let!(:alert) { create(:prometheus_alert, project: project) } + context 'with description' do + let(:payload) { { 'description' => 'alert description' } } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'a user cannot unsubscribe through footer link' it 'has expected subject' do - is_expected.to have_subject("#{project.name} | Alert: #{environment.name}: #{title} for 5 minutes") + is_expected.to have_subject("#{project.name} | Alert: #{default_title}") end it 'has expected content' do is_expected.to have_body_text('An alert has been triggered') is_expected.to have_body_text(project.full_path) - is_expected.to have_body_text('Environment:') - is_expected.to have_body_text(environment.name) - is_expected.to have_body_text('Metric:') - is_expected.to have_body_text(alert.full_query) - is_expected.to have_body_text(metrics_url) + is_expected.to have_body_text('Description:') + is_expected.to have_body_text('alert description') + is_expected.not_to have_body_text('Environment:') + is_expected.not_to have_body_text('Metric:') end - - it_behaves_like 'shows the incident issues url' end - context 'with no payload' do - let(:alert_params) { {} } + context 'with environment' do + let_it_be(:environment) { create(:environment, project: project) } + let(:payload) { { 'gitlab_environment_name' => environment.name } } + let(:metrics_url) { metrics_project_environment_url(project, environment) } - it_behaves_like 'no email' - end + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - context 'with an unknown alert' do - before do - alert_params['labels'] = { 'gitlab_alert_id' => 'unknown' } + it 'has expected subject' do + is_expected.to have_subject("#{project.name} | Alert: #{environment.name}: #{default_title}") end - it_behaves_like 'no email' + it 'has expected content' do + is_expected.to have_body_text('An alert has been triggered') + is_expected.to have_body_text(project.full_path) + is_expected.to have_body_text('Environment:') + is_expected.to have_body_text(environment.name) + is_expected.not_to have_body_text('Description:') + is_expected.not_to have_body_text('Metric:') + end end - context 'with an external alert' do - let(:title) { 'alert title' } + context 'with gitlab alerting rule' do + let_it_be(:prometheus_alert) { create(:prometheus_alert, project: project) } + let_it_be(:environment) { prometheus_alert.environment } - let(:metrics_url) do - metrics_project_environments_url(project) - end + let(:alert_attributes) { build(:alert_management_alert, :prometheus, :from_payload, payload: payload, project: project).attributes } + let(:title) { "#{prometheus_alert.title} #{prometheus_alert.computed_operator} #{prometheus_alert.threshold}" } + let(:metrics_url) { metrics_project_environment_url(project, environment) } before do - alert_params['annotations'] = { 'title' => title } - alert_params['generatorURL'] = 'http://localhost:9090/graph?g0.expr=vector%281%29&g0.tab=1' + payload['labels'] = { + 'gitlab_alert_id' => prometheus_alert.prometheus_metric_id, + 'alertname' => prometheus_alert.title + } end it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'a user cannot unsubscribe through footer link' + it_behaves_like 'shows the incident issues url' it 'has expected subject' do - is_expected.to have_subject("#{project.name} | Alert: #{title}") + is_expected.to have_subject("#{project.name} | Alert: #{environment.name}: #{title} for 5 minutes") end it 'has expected content' do is_expected.to have_body_text('An alert has been triggered') is_expected.to have_body_text(project.full_path) + is_expected.to have_body_text('Environment:') + is_expected.to have_body_text(environment.name) + is_expected.to have_body_text('Metric:') + is_expected.to have_body_text(prometheus_alert.full_query) + is_expected.to have_body_text(metrics_url) is_expected.not_to have_body_text('Description:') - is_expected.not_to have_body_text('Environment:') end - - context 'with annotated description' do - let(:description) { 'description' } - - before do - alert_params['annotations']['description'] = description - end - - it 'shows the description' do - is_expected.to have_body_text('Description:') - is_expected.to have_body_text(description) - end - end - - it_behaves_like 'shows the incident issues url' end end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 1f335817d57..431865caf4c 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -824,7 +824,7 @@ RSpec.describe Issuable do where(:issuable_type, :supports_time_tracking) do :issue | true - :incident | false + :incident | true :merge_request | true end diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb index b12ad82920f..7e031bdd263 100644 --- a/spec/models/concerns/reactive_caching_spec.rb +++ b/spec/models/concerns/reactive_caching_spec.rb @@ -14,6 +14,7 @@ RSpec.describe ReactiveCaching, :use_clean_rails_memory_store_caching do self.reactive_cache_lifetime = 5.minutes self.reactive_cache_refresh_interval = 15.seconds + self.reactive_cache_work_type = :no_dependency attr_reader :id @@ -372,4 +373,14 @@ RSpec.describe ReactiveCaching, :use_clean_rails_memory_store_caching do it { expect(subject.reactive_cache_hard_limit).to be_nil } it { expect(subject.reactive_cache_worker_finder).to respond_to(:call) } end + + describe 'classes including this concern' do + it 'sets reactive_cache_work_type' do + classes = ObjectSpace.each_object(Class).select do |klass| + klass < described_class && klass.name + end + + expect(classes).to all(have_attributes(reactive_cache_work_type: be_in(described_class::WORK_TYPE.keys))) + end + end end diff --git a/spec/presenters/projects/prometheus/alert_presenter_spec.rb b/spec/presenters/projects/prometheus/alert_presenter_spec.rb deleted file mode 100644 index 98dba28829e..00000000000 --- a/spec/presenters/projects/prometheus/alert_presenter_spec.rb +++ /dev/null @@ -1,346 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::Prometheus::AlertPresenter do - include Gitlab::Routing.url_helpers - - let_it_be(:project, reload: true) { create(:project) } - - let(:presenter) { described_class.new(alert) } - let(:payload) { {} } - let(:alert) { create(:alerting_alert, project: project, payload: payload) } - - shared_context 'gitlab alert' do - let(:gitlab_alert) { create(:prometheus_alert, project: project) } - let(:metric_id) { gitlab_alert.prometheus_metric_id } - - let(:alert) do - create(:alerting_alert, project: project, metric_id: metric_id, payload: payload) - end - end - - describe '#project_full_path' do - subject { presenter.project_full_path } - - it { is_expected.to eq(project.full_path) } - end - - describe '#start_time' do - subject { presenter.start_time } - - let(:starts_at) { '2020-10-31T14:02:04Z' } - - before do - payload['startsAt'] = starts_at - end - - context 'with valid utc datetime' do - it { is_expected.to eq('31 October 2020, 2:02PM (UTC)') } - - context 'with admin time zone not UTC' do - before do - allow(Time).to receive(:zone).and_return(ActiveSupport::TimeZone.new('Perth')) - end - - it { is_expected.to eq('31 October 2020, 2:02PM (UTC)') } - end - end - - context 'with invalid datetime' do - let(:starts_at) { 'invalid' } - - it { is_expected.to be_nil } - end - end - - describe '#issue_summary_markdown' do - let(:markdown_line_break) { ' ' } - - subject { presenter.issue_summary_markdown } - - context 'without default payload' do - it do - is_expected.to eq( - <<~MARKDOWN.chomp - **Start time:** #{presenter.start_time} - - MARKDOWN - ) - end - end - - context 'with optional attributes' do - before do - payload['annotations'] = { - 'title' => 'Alert Title', - 'foo' => 'value1', - 'bar' => 'value2', - 'description' => 'Alert Description', - 'monitoring_tool' => 'monitoring_tool_name', - 'service' => 'service_name', - 'hosts' => ['http://localhost:3000', 'http://localhost:3001'] - } - payload['generatorURL'] = 'http://host?g0.expr=query' - end - - it do - is_expected.to eq( - <<~MARKDOWN.chomp - **Start time:** #{presenter.start_time}#{markdown_line_break} - **full_query:** `query`#{markdown_line_break} - **Service:** service_name#{markdown_line_break} - **Monitoring tool:** monitoring_tool_name#{markdown_line_break} - **Hosts:** http://localhost:3000 http://localhost:3001 - - MARKDOWN - ) - end - end - - context 'when hosts is a string' do - before do - payload['annotations'] = { 'hosts' => 'http://localhost:3000' } - end - - it do - is_expected.to eq( - <<~MARKDOWN.chomp - **Start time:** #{presenter.start_time}#{markdown_line_break} - **Hosts:** http://localhost:3000 - - MARKDOWN - ) - end - end - - context 'with embedded metrics' do - let(:starts_at) { '2018-03-12T09:06:00Z' } - - shared_examples_for 'markdown with metrics embed' do - let(:embed_regex) { /\n\[\]\(#{Regexp.quote(presenter.metrics_dashboard_url)}\)\z/ } - - context 'without a starting time available' do - around do |example| - Timecop.freeze(starts_at) { example.run } - end - - before do - payload.delete('startsAt') - end - - it { is_expected.to match(embed_regex) } - end - - context 'with a starting time available' do - it { is_expected.to match(embed_regex) } - end - end - - context 'for gitlab-managed prometheus alerts' do - include_context 'gitlab-managed prometheus alert attributes' - - let(:alert) do - create(:alerting_alert, project: project, metric_id: prometheus_metric_id, payload: payload) - end - - it_behaves_like 'markdown with metrics embed' - end - - context 'for alerts from a self-managed prometheus' do - include_context 'self-managed prometheus alert attributes' - - it_behaves_like 'markdown with metrics embed' - - context 'without y_label' do - let(:y_label) { title } - - before do - payload['annotations'].delete('gitlab_y_label') - end - - it_behaves_like 'markdown with metrics embed' - end - - context 'when not enough information is present for an embed' do - shared_examples_for 'does not include an embed' do - it { is_expected.not_to match(/\[\]\(.+\)/) } - end - - context 'without title' do - before do - payload['annotations'].delete('title') - end - - it_behaves_like 'does not include an embed' - end - - context 'without environment' do - before do - payload['labels'].delete('gitlab_environment_name') - end - - it_behaves_like 'does not include an embed' - end - - context 'without full_query' do - before do - payload.delete('generatorURL') - end - - it_behaves_like 'does not include an embed' - end - end - end - end - end - - describe '#show_performance_dashboard_link?' do - subject { presenter.show_performance_dashboard_link? } - - it { is_expected.to be_falsey } - - context 'with gitlab alert' do - include_context 'gitlab alert' - - it { is_expected.to eq(true) } - end - end - - describe '#show_incident_issues_link?' do - subject { presenter.show_incident_issues_link? } - - it { is_expected.to be_falsey } - - context 'create issue setting enabled' do - before do - create(:project_incident_management_setting, project: project, create_issue: true) - end - - it { is_expected.to eq(true) } - end - end - - describe '#details_url' do - subject { presenter.details_url } - - it { is_expected.to eq(nil) } - - context 'alert management alert present' do - let_it_be(:am_alert) { create(:alert_management_alert, project: project) } - let(:alert) { create(:alerting_alert, project: project, payload: payload, am_alert: am_alert) } - - it { is_expected.to eq("http://localhost/#{project.full_path}/-/alert_management/#{am_alert.iid}/details") } - end - end - - context 'with gitlab alert' do - include_context 'gitlab alert' - - describe '#full_title' do - let(:query_title) do - "#{gitlab_alert.title} #{gitlab_alert.computed_operator} #{gitlab_alert.threshold} for 5 minutes" - end - - let(:expected_subject) do - "#{alert.environment.name}: #{query_title}" - end - - subject { presenter.full_title } - - it { is_expected.to eq(expected_subject) } - end - - describe '#metric_query' do - subject { presenter.metric_query } - - it { is_expected.to eq(gitlab_alert.full_query) } - end - - describe '#environment_name' do - subject { presenter.environment_name } - - it { is_expected.to eq(alert.environment.name) } - end - - describe '#performance_dashboard_link' do - let(:expected_link) { metrics_project_environment_url(project, alert.environment) } - - subject { presenter.performance_dashboard_link } - - it { is_expected.to eq(expected_link) } - end - - describe '#incident_issues_link' do - let(:expected_link) { project_issues_url(project, label_name: described_class::INCIDENT_LABEL_NAME) } - - subject { presenter.incident_issues_link } - - it { is_expected.to eq(expected_link) } - end - end - - context 'without gitlab alert' do - describe '#full_title' do - subject { presenter.full_title } - - context 'with title' do - let(:title) { 'some title' } - - before do - expect(alert).to receive(:title).and_return(title) - end - - it { is_expected.to eq(title) } - end - - context 'without title' do - it { is_expected.to eq('') } - end - end - - describe '#metric_query' do - subject { presenter.metric_query } - - it { is_expected.to be_nil } - end - - describe '#environment_name' do - subject { presenter.environment_name } - - it { is_expected.to be_nil } - end - - describe '#performance_dashboard_link' do - let(:expected_link) { metrics_project_environments_url(project) } - - subject { presenter.performance_dashboard_link } - - it { is_expected.to eq(expected_link) } - end - end - - describe '#metrics_dashboard_url' do - subject { presenter.metrics_dashboard_url } - - context 'for a non-prometheus alert' do - it { is_expected.to be_nil } - end - - context 'for a self-managed prometheus alert' do - include_context 'self-managed prometheus alert attributes' - - let(:prometheus_payload) { payload } - - it { is_expected.to eq(dashboard_url_for_alert) } - end - - context 'for a gitlab-managed prometheus alert' do - include_context 'gitlab-managed prometheus alert attributes' - - let(:prometheus_payload) { payload } - - it { is_expected.to eq(dashboard_url_for_alert) } - end - end -end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index af988381017..473a06c4c8c 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -3082,32 +3082,25 @@ RSpec.describe NotificationService, :mailer do describe '#prometheus_alerts_fired' do let!(:project) { create(:project) } - let!(:prometheus_alert) { create(:prometheus_alert, project: project) } let!(:master) { create(:user) } let!(:developer) { create(:user) } + let(:alert_attributes) { build(:alert_management_alert, project: project).attributes } before do project.add_maintainer(master) end it 'sends the email to owners and masters' do - expect(Notify).to receive(:prometheus_alert_fired_email).with(project.id, master.id, prometheus_alert).and_call_original - expect(Notify).to receive(:prometheus_alert_fired_email).with(project.id, project.owner.id, prometheus_alert).and_call_original - expect(Notify).not_to receive(:prometheus_alert_fired_email).with(project.id, developer.id, prometheus_alert) + expect(Notify).to receive(:prometheus_alert_fired_email).with(project.id, master.id, alert_attributes).and_call_original + expect(Notify).to receive(:prometheus_alert_fired_email).with(project.id, project.owner.id, alert_attributes).and_call_original + expect(Notify).not_to receive(:prometheus_alert_fired_email).with(project.id, developer.id, alert_attributes) - subject.prometheus_alerts_fired(prometheus_alert.project, [prometheus_alert]) + subject.prometheus_alerts_fired(project, [alert_attributes]) end it_behaves_like 'project emails are disabled' do - before do - allow_next_instance_of(::Gitlab::Alerting::Alert) do |instance| - allow(instance).to receive(:valid?).and_return(true) - end - end - - let(:alert_params) { { 'labels' => { 'gitlab_alert_id' => 'unknown' } } } - let(:notification_target) { prometheus_alert.project } - let(:notification_trigger) { subject.prometheus_alerts_fired(prometheus_alert.project, [alert_params]) } + let(:notification_target) { project } + let(:notification_trigger) { subject.prometheus_alerts_fired(project, [alert_attributes]) } around do |example| perform_enqueued_jobs { example.run } diff --git a/spec/services/projects/alerting/notify_service_spec.rb b/spec/services/projects/alerting/notify_service_spec.rb index 68764990886..24741db40bc 100644 --- a/spec/services/projects/alerting/notify_service_spec.rb +++ b/spec/services/projects/alerting/notify_service_spec.rb @@ -197,11 +197,10 @@ RSpec.describe Projects::Alerting::NotifyService do end context 'with overlong payload' do - let(:payload_raw) do - { - title: 'a' * Gitlab::Utils::DeepSize::DEFAULT_MAX_SIZE, - start_time: starts_at.rfc3339 - } + let(:deep_size_object) { instance_double(Gitlab::Utils::DeepSize, valid?: false) } + + before do + allow(Gitlab::Utils::DeepSize).to receive(:new).and_return(deep_size_object) end it_behaves_like 'does not process incident issues due to error', http_status: :bad_request @@ -215,17 +214,6 @@ RSpec.describe Projects::Alerting::NotifyService do it_behaves_like 'processes incident issues' - context 'with an invalid payload' do - before do - allow(Gitlab::Alerting::NotificationPayloadParser) - .to receive(:call) - .and_raise(Gitlab::Alerting::NotificationPayloadParser::BadPayloadError) - end - - it_behaves_like 'does not process incident issues due to error', http_status: :bad_request - it_behaves_like 'does not an create alert management alert' - end - context 'when alert already exists' do let(:fingerprint_sha) { Digest::SHA1.hexdigest(fingerprint) } let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint_sha) } diff --git a/spec/simplecov_env.rb b/spec/simplecov_env.rb index 17b76205d9e..617a45ae449 100644 --- a/spec/simplecov_env.rb +++ b/spec/simplecov_env.rb @@ -2,7 +2,6 @@ require 'simplecov' require 'simplecov-cobertura' -require 'active_support/core_ext/numeric/time' require_relative '../lib/gitlab/utils' module SimpleCovEnv @@ -75,7 +74,7 @@ module SimpleCovEnv add_group 'Libraries', %w[/lib /ee/lib] add_group 'Tooling', %w[/haml_lint /rubocop /tooling] - merge_timeout 365.days + merge_timeout 365 * 24 * 3600 end end end diff --git a/spec/workers/incident_management/process_prometheus_alert_worker_spec.rb b/spec/workers/incident_management/process_prometheus_alert_worker_spec.rb index c294892a66f..2ca4193aa72 100644 --- a/spec/workers/incident_management/process_prometheus_alert_worker_spec.rb +++ b/spec/workers/incident_management/process_prometheus_alert_worker_spec.rb @@ -6,7 +6,7 @@ RSpec.describe IncidentManagement::ProcessPrometheusAlertWorker do describe '#perform' do let_it_be(:project) { create(:project) } let_it_be(:prometheus_alert) { create(:prometheus_alert, project: project) } - let(:payload_key) { Gitlab::Alerting::Alert.new(project: project, payload: alert_params).gitlab_fingerprint } + let(:payload_key) { Gitlab::AlertManagement::Payload::Prometheus.new(project: project, payload: alert_params).gitlab_fingerprint } let!(:prometheus_alert_event) { create(:prometheus_alert_event, prometheus_alert: prometheus_alert, payload_key: payload_key) } let!(:settings) { create(:project_incident_management_setting, project: project, create_issue: true) } |