summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/ci/config/entry/retry_spec.rb
blob: 164a9ed4c3d2177595ab455ff57de2098ffc85b9 (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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
require 'spec_helper'

describe Gitlab::Ci::Config::Entry::Retry do
  let(:entry) { described_class.new(config) }

  shared_context 'when retry value is a numeric', :numeric do
    let(:config) { max }
    let(:max) {}
  end

  shared_context 'when retry value is a hash', :hash do
    let(:config) { { max: max, when: public_send(:when) }.compact }
    let(:when) {}
    let(:max) {}
  end

  describe '#value' do
    subject(:value) { entry.value }

    context 'when retry value is a numeric', :numeric do
      let(:max) { 2 }

      it 'is returned as a hash with max key' do
        expect(value).to eq(max: 2)
      end
    end

    context 'when retry value is a hash', :hash do
      context 'and `when` is a string' do
        let(:when) { 'unknown_failure' }

        it 'returns when wrapped in an array' do
          expect(value).to eq(when: ['unknown_failure'])
        end
      end

      context 'and `when` is an array' do
        let(:when) { %w[unknown_failure runner_system_failure] }

        it 'returns when as it was passed' do
          expect(value).to eq(when: %w[unknown_failure runner_system_failure])
        end
      end
    end
  end

  describe 'validation' do
    context 'when retry value is correct' do
      context 'when it is a numeric', :numeric do
        let(:max) { 2 }

        it 'is valid' do
          expect(entry).to be_valid
        end
      end

      context 'when it is a hash', :hash do
        context 'with max' do
          let(:max) { 2 }

          it 'is valid' do
            expect(entry).to be_valid
          end
        end

        context 'with string when' do
          let(:when) { 'unknown_failure' }

          it 'is valid' do
            expect(entry).to be_valid
          end
        end

        context 'with string when always' do
          let(:when) { 'always' }

          it 'is valid' do
            expect(entry).to be_valid
          end
        end

        context 'with array when' do
          let(:when) { %w[unknown_failure runner_system_failure] }

          it 'is valid' do
            expect(entry).to be_valid
          end
        end

        # Those values are documented at `doc/ci/yaml/README.md`. If any of
        # those values gets invalid, documentation must be updated. To make
        # sure this is catched, check explicitly that all of the documented
        # values are valid. If they are not it means the documentation and this
        # array must be updated.
        RETRY_WHEN_IN_DOCUMENTATION = %w[
            always
            unknown_failure
            script_failure
            api_failure
            stuck_or_timeout_failure
            runner_system_failure
            missing_dependency_failure
            runner_unsupported
        ].freeze

        RETRY_WHEN_IN_DOCUMENTATION.each do |reason|
          context "with when from documentation `#{reason}`" do
            let(:when) { reason }

            it 'is valid' do
              expect(entry).to be_valid
            end
          end
        end

        ::Ci::Build.failure_reasons.each_key do |reason|
          context "with when from CommitStatus.failure_reasons `#{reason}`" do
            let(:when) { reason }

            it 'is valid' do
              expect(entry).to be_valid
            end
          end
        end
      end
    end

    context 'when retry value is not correct' do
      context 'when it is not a numeric nor an array' do
        let(:config) { true }

        it 'returns error about invalid type' do
          expect(entry).not_to be_valid
          expect(entry.errors).to include 'retry config has to be either an integer or a hash'
        end
      end

      context 'when it is a numeric', :numeric do
        context 'when it is lower than zero' do
          let(:max) { -1 }

          it 'returns error about value too low' do
            expect(entry).not_to be_valid
            expect(entry.errors)
              .to include 'retry config must be greater than or equal to 0'
          end
        end

        context 'when it is not an integer' do
          let(:max) { 1.5 }

          it 'returns error about wrong value' do
            expect(entry).not_to be_valid
            expect(entry.errors).to include 'retry config has to be either an integer or a hash'
          end
        end

        context 'when the value is too high' do
          let(:max) { 10 }

          it 'returns error about value too high' do
            expect(entry).not_to be_valid
            expect(entry.errors).to include 'retry config must be less than or equal to 2'
          end
        end
      end

      context 'when it is a hash', :hash do
        context 'with unknown keys' do
          let(:config) { { max: 2, unknown_key: :something, one_more: :key } }

          it 'returns error about the unknown key' do
            expect(entry).not_to be_valid
            expect(entry.errors)
              .to include 'retry config contains unknown keys: unknown_key, one_more'
          end
        end

        context 'with max lower than zero' do
          let(:max) { -1 }

          it 'returns error about value too low' do
            expect(entry).not_to be_valid
            expect(entry.errors)
              .to include 'retry max must be greater than or equal to 0'
          end
        end

        context 'with max not an integer' do
          let(:max) { 1.5 }

          it 'returns error about wrong value' do
            expect(entry).not_to be_valid
            expect(entry.errors).to include 'retry max must be an integer'
          end
        end

        context 'iwth max too high' do
          let(:max) { 10 }

          it 'returns error about value too high' do
            expect(entry).not_to be_valid
            expect(entry.errors).to include 'retry max must be less than or equal to 2'
          end
        end

        context 'with when in wrong format' do
          let(:when) { true }

          it 'returns error about the wrong format' do
            expect(entry).not_to be_valid
            expect(entry.errors).to include 'retry when should be an array of strings or a string'
          end
        end

        context 'with an unknown when string' do
          let(:when) { 'unknown_reason' }

          it 'returns error about the wrong format' do
            expect(entry).not_to be_valid
            expect(entry.errors).to include 'retry when is not included in the list'
          end
        end

        context 'with an unknown failure reason in a when array' do
          let(:when) { %w[unknown_reason runner_system_failure] }

          it 'returns error about the wrong format' do
            expect(entry).not_to be_valid
            expect(entry.errors).to include 'retry when contains unknown values: unknown_reason'
          end
        end
      end
    end
  end
end