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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
|
# frozen_string_literal: true
require 'spec_helper'
describe Ci::Stage, :models do
let_it_be(:pipeline) { create(:ci_empty_pipeline) }
let(:stage) { create(:ci_stage_entity, pipeline: pipeline, project: pipeline.project) }
it_behaves_like 'having unique enum values'
describe 'associations' do
before do
create(:ci_build, stage_id: stage.id)
create(:commit_status, stage_id: stage.id)
end
describe '#statuses' do
it 'returns all commit statuses' do
expect(stage.statuses.count).to be 2
end
end
describe '#builds' do
it 'returns only builds' do
expect(stage.builds).to be_one
end
end
end
describe '#status' do
context 'when stage is pending' do
let(:stage) { create(:ci_stage_entity, status: 'pending') }
it 'has a correct status value' do
expect(stage.status).to eq 'pending'
end
end
context 'when stage is success' do
let(:stage) { create(:ci_stage_entity, status: 'success') }
it 'has a correct status value' do
expect(stage.status).to eq 'success'
end
end
context 'when stage status is not defined' do
before do
stage.update_column(:status, nil)
end
it 'sets the default value' do
expect(described_class.find(stage.id).status)
.to eq 'created'
end
end
end
describe '#set_status' do
where(:from_status, :to_status) do
from_status_names = described_class.state_machines[:status].states.map(&:name)
to_status_names = from_status_names - [:created] # we never want to transition into created
from_status_names.product(to_status_names)
end
with_them do
it do
stage.status = from_status.to_s
if from_status != to_status
expect(stage.set_status(to_status.to_s))
.to eq(true)
else
expect(stage.set_status(to_status.to_s))
.to eq(false), "loopback transitions are not allowed"
end
end
end
end
describe '#update_status' do
context 'when stage objects needs to be updated' do
before do
create(:ci_build, :success, stage_id: stage.id)
create(:ci_build, :running, stage_id: stage.id)
end
it 'updates stage status correctly' do
expect { stage.update_legacy_status }
.to change { stage.reload.status }
.to eq 'running'
end
end
context 'when stage has only created builds' do
let(:stage) { create(:ci_stage_entity, status: :created) }
before do
create(:ci_build, :created, stage_id: stage.id)
end
it 'updates status to skipped' do
expect(stage.reload.status).to eq 'created'
end
end
context 'when stage is skipped because of skipped builds' do
before do
create(:ci_build, :skipped, stage_id: stage.id)
end
it 'updates status to skipped' do
expect { stage.update_legacy_status }
.to change { stage.reload.status }
.to eq 'skipped'
end
end
context 'when stage is scheduled because of scheduled builds' do
before do
create(:ci_build, :scheduled, stage_id: stage.id)
end
it 'updates status to scheduled' do
expect { stage.update_legacy_status }
.to change { stage.reload.status }
.to 'scheduled'
end
end
context 'when build is waiting for resource' do
before do
create(:ci_build, :waiting_for_resource, stage_id: stage.id)
end
it 'updates status to waiting for resource' do
expect { stage.update_legacy_status }
.to change { stage.reload.status }
.to 'waiting_for_resource'
end
end
context 'when stage is skipped because is empty' do
it 'updates status to skipped' do
expect { stage.update_legacy_status }
.to change { stage.reload.status }
.to eq('skipped')
end
end
context 'when stage object is locked' do
before do
create(:ci_build, :failed, stage_id: stage.id)
end
it 'retries a lock to update a stage status' do
stage.lock_version = 100
stage.update_legacy_status
expect(stage.reload).to be_failed
end
end
context 'when statuses status was not recognized' do
before do
allow(stage)
.to receive(:latest_stage_status)
.and_return(:unknown)
end
it 'raises an exception' do
expect { stage.update_legacy_status }
.to raise_error(HasStatus::UnknownStatusError)
end
end
end
describe '#detailed_status' do
using RSpec::Parameterized::TableSyntax
let(:user) { create(:user) }
let(:stage) { create(:ci_stage_entity, status: :created) }
subject { stage.detailed_status(user) }
where(:statuses, :label) do
%w[created] | :created
%w[success] | :passed
%w[pending] | :pending
%w[skipped] | :skipped
%w[canceled] | :canceled
%w[success failed] | :failed
%w[running pending] | :running
end
with_them do
before do
statuses.each do |status|
create(:commit_status, project: stage.project,
pipeline: stage.pipeline,
stage_id: stage.id,
status: status)
stage.update_legacy_status
end
end
it 'has a correct label' do
expect(subject.label).to eq(label.to_s)
end
end
context 'when stage has warnings' do
before do
create(:ci_build, project: stage.project,
pipeline: stage.pipeline,
stage_id: stage.id,
status: :failed,
allow_failure: true)
stage.update_legacy_status
end
it 'is passed with warnings' do
expect(subject.label).to eq s_('CiStatusLabel|passed with warnings')
end
end
end
describe '#groups' do
before do
create(:ci_build, stage_id: stage.id, name: 'rspec 0 1')
create(:ci_build, stage_id: stage.id, name: 'rspec 0 2')
end
it 'groups stage builds by name' do
expect(stage.groups).to be_one
expect(stage.groups.first.name).to eq 'rspec'
end
end
describe '#delay' do
subject { stage.delay }
let(:stage) { create(:ci_stage_entity, status: :created) }
it 'updates stage status' do
subject
expect(stage).to be_scheduled
end
end
describe '#position' do
context 'when stage has been imported and does not have position index set' do
before do
stage.update_column(:position, nil)
end
context 'when stage has statuses' do
before do
create(:ci_build, :running, stage_id: stage.id, stage_idx: 10)
end
it 'recalculates index before updating status' do
expect(stage.reload.position).to be_nil
stage.update_legacy_status
expect(stage.reload.position).to eq 10
end
end
context 'when stage does not have statuses' do
it 'fallbacks to zero' do
expect(stage.reload.position).to be_nil
stage.update_legacy_status
expect(stage.reload.position).to eq 0
end
end
end
end
context 'when stage has warnings' do
before do
create(:ci_build, :failed, :allowed_to_fail, stage_id: stage.id)
end
describe '#has_warnings?' do
it 'returns true' do
expect(stage).to have_warnings
end
end
describe '#number_of_warnings' do
it 'returns a lazy stage warnings counter' do
lazy_queries = ActiveRecord::QueryRecorder.new do
stage.number_of_warnings
end
synced_queries = ActiveRecord::QueryRecorder.new do
stage.number_of_warnings.to_i
end
expect(lazy_queries.count).to eq 0
expect(synced_queries.count).to eq 1
expect(stage.number_of_warnings.inspect).to include 'BatchLoader'
expect(stage.number_of_warnings).to eq 1
end
end
end
context 'when stage does not have warnings' do
describe '#has_warnings?' do
it 'returns false' do
expect(stage).not_to have_warnings
end
end
end
it_behaves_like 'manual playable stage', :ci_stage_entity
end
|