BigW Consortium Gitlab

runners_spec.rb 15.4 KB
Newer Older
1 2
require 'spec_helper'

3
describe API::Runners do
4 5 6
  let(:admin) { create(:user, :admin) }
  let(:user) { create(:user) }
  let(:user2) { create(:user) }
7

8 9
  let(:project) { create(:empty_project, creator_id: user.id) }
  let(:project2) { create(:empty_project, creator_id: user.id) }
10

11 12
  let!(:shared_runner) { create(:ci_runner, :shared) }
  let!(:unused_specific_runner) { create(:ci_runner) }
13 14

  let!(:specific_runner) do
15 16 17
    create(:ci_runner).tap do |runner|
      create(:ci_runner_project, runner: runner, project: project)
    end
18 19 20
  end

  let!(:two_projects_runner) do
21 22 23 24
    create(:ci_runner).tap do |runner|
      create(:ci_runner_project, runner: runner, project: project)
      create(:ci_runner_project, runner: runner, project: project2)
    end
25 26 27 28
  end

  before do
    # Set project access for users
29 30 31
    create(:project_member, :master, user: user, project: project)
    create(:project_member, :master, user: user, project: project2)
    create(:project_member, :reporter, user: user2, project: project)
32
  end
33 34

  describe 'GET /runners' do
35
    context 'authorized user' do
36
      it 'returns user available runners' do
37 38
        get api('/runners', user)

39
        shared = json_response.any?{ |r| r['is_shared'] }
40
        expect(response).to have_http_status(200)
41
        expect(response).to include_pagination_headers
42 43 44 45
        expect(json_response).to be_an Array
        expect(shared).to be_falsey
      end

46
      it 'filters runners by scope' do
47
        get api('/runners?scope=active', user)
48

49
        shared = json_response.any?{ |r| r['is_shared'] }
50
        expect(response).to have_http_status(200)
51
        expect(response).to include_pagination_headers
52 53 54 55
        expect(json_response).to be_an Array
        expect(shared).to be_falsey
      end

56
      it 'avoids filtering if scope is invalid' do
57
        get api('/runners?scope=unknown', user)
58
        expect(response).to have_http_status(400)
59 60 61 62
      end
    end

    context 'unauthorized user' do
63
      it 'does not return runners' do
64 65
        get api('/runners')

66
        expect(response).to have_http_status(401)
67 68 69 70 71 72 73
      end
    end
  end

  describe 'GET /runners/all' do
    context 'authorized user' do
      context 'with admin privileges' do
74
        it 'returns all runners' do
75
          get api('/runners/all', admin)
76

77
          shared = json_response.any?{ |r| r['is_shared'] }
78
          expect(response).to have_http_status(200)
79
          expect(response).to include_pagination_headers
80 81 82 83
          expect(json_response).to be_an Array
          expect(shared).to be_truthy
        end
      end
84

85
      context 'without admin privileges' do
86
        it 'does not return runners list' do
87
          get api('/runners/all', user)
88

89
          expect(response).to have_http_status(403)
90
        end
91 92
      end

93
      it 'filters runners by scope' do
94
        get api('/runners/all?scope=specific', admin)
95

96
        shared = json_response.any?{ |r| r['is_shared'] }
97
        expect(response).to have_http_status(200)
98
        expect(response).to include_pagination_headers
99 100 101 102
        expect(json_response).to be_an Array
        expect(shared).to be_falsey
      end

103
      it 'avoids filtering if scope is invalid' do
104
        get api('/runners?scope=unknown', admin)
105
        expect(response).to have_http_status(400)
106 107 108 109
      end
    end

    context 'unauthorized user' do
110
      it 'does not return runners' do
111 112
        get api('/runners')

113
        expect(response).to have_http_status(401)
114 115 116 117 118 119
      end
    end
  end

  describe 'GET /runners/:id' do
    context 'admin user' do
120
      context 'when runner is shared' do
121
        it "returns runner's details" do
122
          get api("/runners/#{shared_runner.id}", admin)
123

124
          expect(response).to have_http_status(200)
125 126
          expect(json_response['description']).to eq(shared_runner.description)
        end
127 128
      end

129
      context 'when runner is not shared' do
130
        it "returns runner's details" do
131
          get api("/runners/#{specific_runner.id}", admin)
132

133
          expect(response).to have_http_status(200)
134 135
          expect(json_response['description']).to eq(specific_runner.description)
        end
136 137
      end

138
      it 'returns 404 if runner does not exists' do
139 140
        get api('/runners/9999', admin)

141
        expect(response).to have_http_status(404)
142 143 144 145
      end
    end

    context "runner project's administrative user" do
146
      context 'when runner is not shared' do
147
        it "returns runner's details" do
148
          get api("/runners/#{specific_runner.id}", user)
149

150
          expect(response).to have_http_status(200)
151 152
          expect(json_response['description']).to eq(specific_runner.description)
        end
153 154
      end

155
      context 'when runner is shared' do
156
        it "returns runner's details" do
157
          get api("/runners/#{shared_runner.id}", user)
158

159
          expect(response).to have_http_status(200)
160 161
          expect(json_response['description']).to eq(shared_runner.description)
        end
162 163 164 165
      end
    end

    context 'other authorized user' do
166
      it "does not return runner's details" do
167 168
        get api("/runners/#{specific_runner.id}", user2)

169
        expect(response).to have_http_status(403)
170 171 172 173
      end
    end

    context 'unauthorized user' do
174
      it "does not return runner's details" do
175 176
        get api("/runners/#{specific_runner.id}")

177
        expect(response).to have_http_status(401)
178 179 180 181 182 183
      end
    end
  end

  describe 'PUT /runners/:id' do
    context 'admin user' do
184
      context 'when runner is shared' do
185
        it 'updates runner' do
186 187
          description = shared_runner.description
          active = shared_runner.active
188
          runner_queue_value = shared_runner.ensure_runner_queue_value
189

190 191 192
          update_runner(shared_runner.id, admin, description: "#{description}_updated",
                                                 active: !active,
                                                 tag_list: ['ruby2.1', 'pgsql', 'mysql'],
193 194
                                                 run_untagged: 'false',
                                                 locked: 'true')
195
          shared_runner.reload
196

197
          expect(response).to have_http_status(200)
198 199 200
          expect(shared_runner.description).to eq("#{description}_updated")
          expect(shared_runner.active).to eq(!active)
          expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql')
201 202
          expect(shared_runner.run_untagged?).to be(false)
          expect(shared_runner.locked?).to be(true)
203 204
          expect(shared_runner.ensure_runner_queue_value)
            .not_to eq(runner_queue_value)
205
        end
206 207
      end

208
      context 'when runner is not shared' do
209
        it 'updates runner' do
210
          description = specific_runner.description
211 212
          runner_queue_value = specific_runner.ensure_runner_queue_value

213
          update_runner(specific_runner.id, admin, description: 'test')
214
          specific_runner.reload
215

216
          expect(response).to have_http_status(200)
217 218
          expect(specific_runner.description).to eq('test')
          expect(specific_runner.description).not_to eq(description)
219 220
          expect(specific_runner.ensure_runner_queue_value)
            .not_to eq(runner_queue_value)
221
        end
222 223
      end

224
      it 'returns 404 if runner does not exists' do
225
        update_runner(9999, admin, description: 'test')
226

227
        expect(response).to have_http_status(404)
228
      end
229 230 231 232

      def update_runner(id, user, args)
        put api("/runners/#{id}", user), args
      end
233 234 235
    end

    context 'authorized user' do
236
      context 'when runner is shared' do
237
        it 'does not update runner' do
Robert Schilling committed
238
          put api("/runners/#{shared_runner.id}", user), description: 'test'
239

240
          expect(response).to have_http_status(403)
241
        end
242 243
      end

244
      context 'when runner is not shared' do
245
        it 'does not update runner without access to it' do
Robert Schilling committed
246
          put api("/runners/#{specific_runner.id}", user2), description: 'test'
247

248
          expect(response).to have_http_status(403)
249
        end
250

251
        it 'updates runner with access to it' do
252 253 254
          description = specific_runner.description
          put api("/runners/#{specific_runner.id}", admin), description: 'test'
          specific_runner.reload
255

256
          expect(response).to have_http_status(200)
257 258 259
          expect(specific_runner.description).to eq('test')
          expect(specific_runner.description).not_to eq(description)
        end
260 261 262 263
      end
    end

    context 'unauthorized user' do
264
      it 'does not delete runner' do
265
        put api("/runners/#{specific_runner.id}")
266

267
        expect(response).to have_http_status(401)
268 269 270 271 272 273
      end
    end
  end

  describe 'DELETE /runners/:id' do
    context 'admin user' do
274
      context 'when runner is shared' do
275
        it 'deletes runner' do
276 277
          expect do
            delete api("/runners/#{shared_runner.id}", admin)
278 279

            expect(response).to have_http_status(204)
280 281
          end.to change{ Ci::Runner.shared.count }.by(-1)
        end
282 283
      end

284
      context 'when runner is not shared' do
285
        it 'deletes unused runner' do
286 287
          expect do
            delete api("/runners/#{unused_specific_runner.id}", admin)
288 289

            expect(response).to have_http_status(204)
290 291
          end.to change{ Ci::Runner.specific.count }.by(-1)
        end
292

293
        it 'deletes used runner' do
294 295
          expect do
            delete api("/runners/#{specific_runner.id}", admin)
296 297

            expect(response).to have_http_status(204)
298 299
          end.to change{ Ci::Runner.specific.count }.by(-1)
        end
300 301
      end

302
      it 'returns 404 if runner does not exists' do
303 304
        delete api('/runners/9999', admin)

305
        expect(response).to have_http_status(404)
306 307 308 309
      end
    end

    context 'authorized user' do
310
      context 'when runner is shared' do
311
        it 'does not delete runner' do
312
          delete api("/runners/#{shared_runner.id}", user)
313
          expect(response).to have_http_status(403)
314
        end
315 316
      end

317
      context 'when runner is not shared' do
318
        it 'does not delete runner without access to it' do
319
          delete api("/runners/#{specific_runner.id}", user2)
320
          expect(response).to have_http_status(403)
321
        end
322

323
        it 'does not delete runner with more than one associated project' do
324
          delete api("/runners/#{two_projects_runner.id}", user)
325
          expect(response).to have_http_status(403)
326
        end
327

328
        it 'deletes runner for one owned project' do
329 330
          expect do
            delete api("/runners/#{specific_runner.id}", user)
331 332

            expect(response).to have_http_status(204)
333 334
          end.to change{ Ci::Runner.specific.count }.by(-1)
        end
335 336 337 338
      end
    end

    context 'unauthorized user' do
339
      it 'does not delete runner' do
340 341
        delete api("/runners/#{specific_runner.id}")

342
        expect(response).to have_http_status(401)
343 344 345 346 347 348
      end
    end
  end

  describe 'GET /projects/:id/runners' do
    context 'authorized user with master privileges' do
349
      it "returns project's runners" do
350 351
        get api("/projects/#{project.id}/runners", user)

352
        shared = json_response.any?{ |r| r['is_shared'] }
353
        expect(response).to have_http_status(200)
354
        expect(response).to include_pagination_headers
355 356 357 358 359 360
        expect(json_response).to be_an Array
        expect(shared).to be_truthy
      end
    end

    context 'authorized user without master privileges' do
361
      it "does not return project's runners" do
362 363
        get api("/projects/#{project.id}/runners", user2)

364
        expect(response).to have_http_status(403)
365 366 367 368
      end
    end

    context 'unauthorized user' do
369
      it "does not return project's runners" do
370 371
        get api("/projects/#{project.id}/runners")

372
        expect(response).to have_http_status(401)
373 374 375
      end
    end
  end
376

377
  describe 'POST /projects/:id/runners' do
378
    context 'authorized user' do
379 380
      let(:specific_runner2) do
        create(:ci_runner).tap do |runner|
381 382
          create(:ci_runner_project, runner: runner, project: project2)
        end
383
      end
384

385
      it 'enables specific runner' do
386
        expect do
387
          post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id
388
        end.to change{ project.runners.count }.by(+1)
389
        expect(response).to have_http_status(201)
390 391
      end

392
      it 'avoids changes when enabling already enabled runner' do
393
        expect do
394
          post api("/projects/#{project.id}/runners", user), runner_id: specific_runner.id
395
        end.to change{ project.runners.count }.by(0)
396
        expect(response).to have_http_status(409)
397 398
      end

399
      it 'does not enable locked runner' do
400 401 402 403 404 405
        specific_runner2.update(locked: true)

        expect do
          post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id
        end.to change{ project.runners.count }.by(0)

406
        expect(response).to have_http_status(403)
407 408
      end

409
      it 'does not enable shared runner' do
410
        post api("/projects/#{project.id}/runners", user), runner_id: shared_runner.id
411

412
        expect(response).to have_http_status(403)
413 414 415
      end

      context 'user is admin' do
416
        it 'enables any specific runner' do
417
          expect do
418
            post api("/projects/#{project.id}/runners", admin), runner_id: unused_specific_runner.id
419
          end.to change{ project.runners.count }.by(+1)
420
          expect(response).to have_http_status(201)
421 422 423 424
        end
      end

      context 'user is not admin' do
425
        it 'does not enable runner without access to' do
426
          post api("/projects/#{project.id}/runners", user), runner_id: unused_specific_runner.id
427

428
          expect(response).to have_http_status(403)
429 430
        end
      end
431

432
      it 'raises an error when no runner_id param is provided' do
433 434
        post api("/projects/#{project.id}/runners", admin)

435
        expect(response).to have_http_status(400)
436
      end
437 438 439
    end

    context 'authorized user without permissions' do
440
      it 'does not enable runner' do
441
        post api("/projects/#{project.id}/runners", user2)
442

443
        expect(response).to have_http_status(403)
444 445 446 447
      end
    end

    context 'unauthorized user' do
448
      it 'does not enable runner' do
449
        post api("/projects/#{project.id}/runners")
450

451
        expect(response).to have_http_status(401)
452 453 454 455 456 457 458
      end
    end
  end

  describe 'DELETE /projects/:id/runners/:runner_id' do
    context 'authorized user' do
      context 'when runner have more than one associated projects' do
459
        it "disables project's runner" do
460 461
          expect do
            delete api("/projects/#{project.id}/runners/#{two_projects_runner.id}", user)
462 463

            expect(response).to have_http_status(204)
464 465 466 467 468
          end.to change{ project.runners.count }.by(-1)
        end
      end

      context 'when runner have one associated projects' do
469
        it "does not disable project's runner" do
470 471 472
          expect do
            delete api("/projects/#{project.id}/runners/#{specific_runner.id}", user)
          end.to change{ project.runners.count }.by(0)
473
          expect(response).to have_http_status(403)
474 475 476
        end
      end

477
      it 'returns 404 is runner is not found' do
478 479
        delete api("/projects/#{project.id}/runners/9999", user)

480
        expect(response).to have_http_status(404)
481 482 483 484
      end
    end

    context 'authorized user without permissions' do
485
      it "does not disable project's runner" do
486 487
        delete api("/projects/#{project.id}/runners/#{specific_runner.id}", user2)

488
        expect(response).to have_http_status(403)
489 490 491 492
      end
    end

    context 'unauthorized user' do
493
      it "does not disable project's runner" do
494 495
        delete api("/projects/#{project.id}/runners/#{specific_runner.id}")

496
        expect(response).to have_http_status(401)
497 498 499
      end
    end
  end
500
end