summaryrefslogtreecommitdiff
path: root/spec/controllers/projects/alerting/notifications_controller_spec.rb
blob: b3feeb7c07b9efbb9ed0ce8dc6ca9d86b9f7fdb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Projects::Alerting::NotificationsController do
  include HttpBasicAuthHelpers

  let_it_be(:project) { create(:project) }
  let_it_be(:environment) { create(:environment, project: project) }

  let(:params) { project_params }

  describe 'POST #create' do
    around do |example|
      ForgeryProtection.with_forgery_protection { example.run }
    end

    shared_examples 'process alert payload' do |notify_service_class|
      let(:alert_1) { build(:alert_management_alert, project: project) }
      let(:alert_2) { build(:alert_management_alert, project: project) }
      let(:service_response) { ServiceResponse.success(payload: { alerts: [alert_1, alert_2] }) }
      let(:notify_service) { instance_double(notify_service_class, execute: service_response) }

      before do
        allow(notify_service_class).to receive(:new).and_return(notify_service)
      end

      def make_request
        post :create, params: params, body: payload.to_json, as: :json
      end

      context 'when notification service succeeds' do
        let(:permitted_params) { ActionController::Parameters.new(payload).permit! }

        it 'responds with the alert data' do
          make_request

          expect(json_response).to contain_exactly(
            { 'iid' => alert_1.iid, 'title' => alert_1.title },
            { 'iid' => alert_2.iid, 'title' => alert_2.title }
          )
          expect(response).to have_gitlab_http_status(:ok)
        end

        it 'does not pass excluded parameters to the notify service' do
          make_request

          expect(notify_service_class)
            .to have_received(:new)
            .with(project, permitted_params)
        end
      end

      context 'when notification service fails' do
        let(:service_response) { ServiceResponse.error(message: 'Unauthorized', http_status: :unauthorized) }

        it 'responds with the service response' do
          make_request

          expect(response).to have_gitlab_http_status(:unauthorized)
        end
      end

      shared_examples 'a working token' do
        it 'extracts token' do
          expect(notify_service).to receive(:execute).with('some token', nil)

          make_request
        end

        context 'with a corresponding integration' do
          context 'with integration parameters specified' do
            let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project) }

            let(:params) { project_params(endpoint_identifier: integration.endpoint_identifier, name: integration.name) }

            context 'the integration is active' do
              it 'extracts and finds the integration' do
                expect(notify_service).to receive(:execute).with('some token', integration)

                make_request
              end
            end

            context 'when the integration is inactive' do
              before do
                integration.update!(active: false)
              end

              it 'does not find an integration' do
                expect(notify_service).to receive(:execute).with('some token', nil)

                make_request
              end
            end
          end

          context 'without integration parameters specified' do
            let_it_be(:integration) { create(:alert_management_http_integration, :legacy, project: project) }

            it 'extracts and finds the legacy integration' do
              expect(notify_service).to receive(:execute).with('some token', integration)

              make_request
            end
          end
        end
      end

      context 'with bearer token' do
        context 'when set' do
          before do
            request.headers.merge(build_token_auth_header('some token'))
          end

          it_behaves_like 'a working token'
        end
      end

      context 'with basic auth token' do
        before do
          request.headers.merge basic_auth_header(nil, 'some token')
        end

        it_behaves_like 'a working token'
      end

      context 'when inextractable token' do
        it 'passes nil for a non-bearer token' do
          request.headers['HTTP_AUTHORIZATION'] = 'some token'

          expect(notify_service).to receive(:execute).with(nil, nil)

          make_request
        end
      end

      context 'when missing token' do
        it 'passes nil' do
          expect(notify_service).to receive(:execute).with(nil, nil)

          make_request
        end
      end
    end

    context 'with generic alert payload' do
      it_behaves_like 'process alert payload', Projects::Alerting::NotifyService do
        let(:payload) { { title: 'Alert title' } }
      end
    end

    context 'with Prometheus alert payload' do
      include PrometheusHelpers

      it_behaves_like 'process alert payload', Projects::Prometheus::Alerts::NotifyService do
        let(:payload) { prometheus_alert_payload }
      end
    end
  end

  private

  def project_params(opts = {})
    opts.reverse_merge(namespace_id: project.namespace, project_id: project)
  end
end