BigW Consortium Gitlab

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

3
describe API::Runners, api: true  do
4 5 6 7 8
  include ApiHelpers

  let(:admin) { create(:user, :admin) }
  let(:user) { create(:user) }
  let(:user2) { create(:user) }
9

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

13 14
  let!(:shared_runner) { create(:ci_runner, :shared) }
  let!(:unused_specific_runner) { create(:ci_runner) }
15 16

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

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

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

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

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

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

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

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

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

68
        expect(response).to have_http_status(401)
69 70 71 72 73 74 75
      end
    end
  end

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

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

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

91
          expect(response).to have_http_status(403)
92
        end
93 94
      end

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

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

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

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

115
        expect(response).to have_http_status(401)
116 117 118 119 120 121
      end
    end
  end

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

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

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

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

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

143
        expect(response).to have_http_status(404)
144 145 146 147
      end
    end

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

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

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

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

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

171
        expect(response).to have_http_status(403)
172 173 174 175
      end
    end

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

179
        expect(response).to have_http_status(401)
180 181 182 183 184 185
      end
    end
  end

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

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

199
          expect(response).to have_http_status(200)
200 201 202
          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')
203 204
          expect(shared_runner.run_untagged?).to be(false)
          expect(shared_runner.locked?).to be(true)
205 206
          expect(shared_runner.ensure_runner_queue_value)
            .not_to eq(runner_queue_value)
207
        end
208 209
      end

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

215
          update_runner(specific_runner.id, admin, description: 'test')
216
          specific_runner.reload
217

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

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

229
        expect(response).to have_http_status(404)
230
      end
231 232 233 234

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

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

242
          expect(response).to have_http_status(403)
243
        end
244 245
      end

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

250
          expect(response).to have_http_status(403)
251
        end
252

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

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

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

269
        expect(response).to have_http_status(401)
270 271 272 273 274 275
      end
    end
  end

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

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

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

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

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

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

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

307
        expect(response).to have_http_status(404)
308 309 310 311
      end
    end

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

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

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

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

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

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

344
        expect(response).to have_http_status(401)
345 346 347 348 349 350
      end
    end
  end

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

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

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

366
        expect(response).to have_http_status(403)
367 368 369 370
      end
    end

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

374
        expect(response).to have_http_status(401)
375 376 377
      end
    end
  end
378

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

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

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

401
      it 'does not enable locked runner' do
402 403 404 405 406 407
        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)

408
        expect(response).to have_http_status(403)
409 410
      end

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

414
        expect(response).to have_http_status(403)
415 416 417
      end

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

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

430
          expect(response).to have_http_status(403)
431 432
        end
      end
433

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

437
        expect(response).to have_http_status(400)
438
      end
439 440 441
    end

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

445
        expect(response).to have_http_status(403)
446 447 448 449
      end
    end

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

453
        expect(response).to have_http_status(401)
454 455 456 457 458 459 460
      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
461
        it "disables project's runner" do
462 463
          expect do
            delete api("/projects/#{project.id}/runners/#{two_projects_runner.id}", user)
464 465

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

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

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

482
        expect(response).to have_http_status(404)
483 484 485 486
      end
    end

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

490
        expect(response).to have_http_status(403)
491 492 493 494
      end
    end

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

498
        expect(response).to have_http_status(401)
499 500 501
      end
    end
  end
502
end