summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
blob: 69b779d36eb41f52f2d28d69ec7730b3f7c2ff41 (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
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::Metrics::RequestsRackMiddleware do
  let(:app) { double('app') }

  subject { described_class.new(app) }

  describe '#call' do
    let(:status) { 100 }
    let(:env) { { 'REQUEST_METHOD' => 'GET' } }
    let(:stack_result) { [status, {}, 'body'] }

    before do
      allow(app).to receive(:call).and_return(stack_result)
    end

    context '@app.call succeeds with 200' do
      before do
        allow(app).to receive(:call).and_return([200, nil, nil])
      end

      it 'increments requests count' do
        expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get')

        subject.call(env)
      end

      RSpec::Matchers.define :a_positive_execution_time do
        match { |actual| actual > 0 }
      end

      it 'measures execution time' do
        expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ status: '200', method: 'get' }, a_positive_execution_time)

        Timecop.scale(3600) { subject.call(env) }
      end

      context 'request is a health check endpoint' do
        it 'increments health endpoint counter' do
          env['PATH_INFO'] = '/-/liveness'

          expect(described_class).to receive_message_chain(:http_health_requests_total, :increment).with(method: 'get')

          subject.call(env)
        end

        context 'with trailing slash' do
          before do
            env['PATH_INFO'] = '/-/liveness/'
          end

          it 'increments health endpoint counter' do
            expect(described_class).to receive_message_chain(:http_health_requests_total, :increment).with(method: 'get')

            subject.call(env)
          end
        end

        context 'with percent encoded values' do
          before do
            env['PATH_INFO'] = '/-/%6D%65%74%72%69%63%73' # /-/metrics
          end

          it 'increments health endpoint counter' do
            expect(described_class).to receive_message_chain(:http_health_requests_total, :increment).with(method: 'get')

            subject.call(env)
          end
        end
      end

      context 'request is not a health check endpoint' do
        it 'does not increment health endpoint counter' do
          env['PATH_INFO'] = '/-/ordinary-requests'

          expect(described_class).not_to receive(:http_health_requests_total)

          subject.call(env)
        end

        context 'path info is a root path' do
          before do
            env['PATH_INFO'] = '/-/'
          end

          it 'does not increment health endpoint counter' do
            expect(described_class).not_to receive(:http_health_requests_total)

            subject.call(env)
          end
        end

        context 'path info is a subpath' do
          before do
            env['PATH_INFO'] = '/-/health/subpath'
          end

          it 'does not increment health endpoint counter' do
            expect(described_class).not_to receive(:http_health_requests_total)

            subject.call(env)
          end
        end
      end
    end

    context '@app.call throws exception' do
      let(:http_request_duration_seconds) { double('http_request_duration_seconds') }

      before do
        allow(app).to receive(:call).and_raise(StandardError)
        allow(described_class).to receive(:http_request_duration_seconds).and_return(http_request_duration_seconds)
      end

      it 'increments exceptions count' do
        expect(described_class).to receive_message_chain(:rack_uncaught_errors_count, :increment)

        expect { subject.call(env) }.to raise_error(StandardError)
      end

      it 'increments requests count' do
        expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get')

        expect { subject.call(env) }.to raise_error(StandardError)
      end

      it "does't measure request execution time" do
        expect(described_class.http_request_duration_seconds).not_to receive(:increment)

        expect { subject.call(env) }.to raise_error(StandardError)
      end
    end

    describe '.initialize_http_request_duration_seconds' do
      it "sets labels" do
        expected_labels = []
        described_class::HTTP_METHODS.each do |method, statuses|
          statuses.each do |status|
            expected_labels << { method: method, status: status.to_s }
          end
        end

        described_class.initialize_http_request_duration_seconds
        expect(described_class.http_request_duration_seconds.values.keys).to include(*expected_labels)
      end
    end
  end
end