pipelines_controller_spec.rb 11.8 KB
Newer Older
1 2 3
require 'spec_helper'

describe Projects::PipelinesController do
4 5
  include ApiHelpers

6
  set(:user) { create(:user) }
B
Bob Van Landuyt 已提交
7
  let(:project) { create(:project, :public, :repository) }
8
  let(:feature) { ProjectFeature::ENABLED }
9 10

  before do
11 12
    stub_not_protect_default_branch
    project.add_developer(user)
13
    project.project_feature.update(builds_access_level: feature)
14

15 16 17
    sign_in(user)
  end

18 19
  describe 'GET index.json' do
    before do
20
      %w(pending running success failed canceled).each_with_index do |status, index|
21 22
        create_pipeline(status, project.commit("HEAD~#{index}"))
      end
23 24
    end

25 26 27
    context 'when using persisted stages', :request_store do
      before do
        stub_feature_flags(ci_pipeline_persisted_stages: true)
28
      end
29

30 31 32 33
      it 'returns serialized pipelines', :request_store do
        queries = ActiveRecord::QueryRecorder.new do
          get_pipelines_index_json
        end
34

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to match_response_schema('pipeline')

        expect(json_response).to include('pipelines')
        expect(json_response['pipelines'].count).to eq 5
        expect(json_response['count']['all']).to eq '5'
        expect(json_response['count']['running']).to eq '1'
        expect(json_response['count']['pending']).to eq '1'
        expect(json_response['count']['finished']).to eq '3'

        json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages|
          expect(stages.count).to eq 3
        end

        expect(queries.count).to be
      end
    end

53
    context 'when using legacy stages', :request_store do
54 55 56 57
      before do
        stub_feature_flags(ci_pipeline_persisted_stages: false)
      end

58 59
      it 'returns JSON with serialized pipelines' do
        get_pipelines_index_json
60 61 62 63 64 65 66 67 68 69 70 71 72 73

        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to match_response_schema('pipeline')

        expect(json_response).to include('pipelines')
        expect(json_response['pipelines'].count).to eq 5
        expect(json_response['count']['all']).to eq '5'
        expect(json_response['count']['running']).to eq '1'
        expect(json_response['count']['pending']).to eq '1'
        expect(json_response['count']['finished']).to eq '3'

        json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages|
          expect(stages.count).to eq 3
        end
74 75 76 77 78 79
      end

      it 'does not execute N+1 queries' do
        queries = ActiveRecord::QueryRecorder.new do
          get_pipelines_index_json
        end
80

81
        expect(queries.count).to be <= 36
82
      end
83
    end
84

85
    it 'does not include coverage data for the pipelines' do
86
      get_pipelines_index_json
87 88 89 90

      expect(json_response['pipelines'][0]).not_to include('coverage')
    end

91 92
    context 'when performing gitaly calls', :request_store do
      it 'limits the Gitaly requests' do
93 94 95 96 97
        # Isolate from test preparation (Repository#exists? is also cached in RequestStore)
        RequestStore.end!
        RequestStore.clear!
        RequestStore.begin!

98 99
        expect { get_pipelines_index_json }
          .to change { Gitlab::GitalyClient.get_request_count }.by(2)
100 101
      end
    end
102

B
Bob Van Landuyt 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
    context 'when the project is private' do
      let(:project) { create(:project, :private, :repository) }

      it 'returns `not_found` when the user does not have access' do
        sign_in(create(:user))

        get_pipelines_index_json

        expect(response).to have_gitlab_http_status(:not_found)
      end

      it 'returns the pipelines when the user has access' do
        get_pipelines_index_json

        expect(json_response['pipelines'].size).to eq(5)
      end
    end

121
    def get_pipelines_index_json
B
blackst0ne 已提交
122 123 124 125
      get :index, params: {
                    namespace_id: project.namespace,
                    project_id: project
                  },
126 127 128
                  format: :json
    end

129 130 131 132 133 134 135 136 137 138
    def create_pipeline(status, sha)
      pipeline = create(:ci_empty_pipeline, status: status,
                                            project: project,
                                            sha: sha)

      create_build(pipeline, 'build', 1, 'build')
      create_build(pipeline, 'test', 2, 'test')
      create_build(pipeline, 'deploy', 3, 'deploy')
    end

139
    def create_build(pipeline, stage, stage_idx, name)
140 141
      status = %w[created running pending success failed canceled].sample
      create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, status: status)
142
    end
143 144
  end

145
  describe 'GET show.json' do
146
    let(:pipeline) { create(:ci_pipeline_with_one_job, project: project) }
147 148 149 150

    it 'returns the pipeline' do
      get_pipeline_json

151
      expect(response).to have_gitlab_http_status(:ok)
152 153 154 155 156
      expect(json_response).not_to be_an(Array)
      expect(json_response['id']).to be(pipeline.id)
      expect(json_response['details']).to have_key 'stages'
    end

157
    context 'when the pipeline has multiple stages and groups', :request_store do
158 159 160 161 162 163 164 165
      let(:project) { create(:project, :repository) }

      let(:pipeline) do
        create(:ci_empty_pipeline, project: project,
                                   user: user,
                                   sha: project.commit.id)
      end

166 167 168 169 170 171 172
      before do
        create_build('build', 0, 'build')
        create_build('test', 1, 'rspec 0')
        create_build('deploy', 2, 'production')
        create_build('post deploy', 3, 'pages 0')
      end

173 174 175
      it 'does not perform N + 1 queries' do
        control_count = ActiveRecord::QueryRecorder.new { get_pipeline_json }.count

176 177 178 179 180 181
        create_build('test', 1, 'rspec 1')
        create_build('test', 1, 'spinach 0')
        create_build('test', 1, 'spinach 1')
        create_build('test', 1, 'audit')
        create_build('post deploy', 3, 'pages 1')
        create_build('post deploy', 3, 'pages 2')
182

183
        new_count = ActiveRecord::QueryRecorder.new { get_pipeline_json }.count
184

185
        expect(new_count).to be_within(12).of(control_count)
186 187 188
      end
    end

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
    context 'when builds are disabled' do
      let(:feature) { ProjectFeature::DISABLED }

      it 'users can not see internal pipelines' do
        get_pipeline_json

        expect(response).to have_gitlab_http_status(:not_found)
      end

      context 'when pipeline is external' do
        let(:pipeline) { create(:ci_pipeline, source: :external, project: project) }

        it 'users can see the external pipeline' do
          get_pipeline_json

          expect(response).to have_gitlab_http_status(:ok)
          expect(json_response['id']).to be(pipeline.id)
        end
      end
    end

210
    def get_pipeline_json
B
blackst0ne 已提交
211
      get :show, params: { namespace_id: project.namespace, project_id: project, id: pipeline }, format: :json
212
    end
213 214 215 216

    def create_build(stage, stage_idx, name)
      create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name)
    end
217 218
  end

219
  describe 'GET stages.json' do
220 221
    let(:pipeline) { create(:ci_pipeline, project: project) }

222 223
    context 'when accessing existing stage' do
      before do
224
        create(:ci_build, :retried, :failed, pipeline: pipeline, stage: 'build')
225
        create(:ci_build, pipeline: pipeline, stage: 'build')
226 227 228 229 230 231
      end

      context 'without retried' do
        before do
          get_stage('build')
        end
232

233 234 235 236 237 238
        it 'returns pipeline jobs without the retried builds' do
          expect(response).to have_gitlab_http_status(:ok)
          expect(response).to match_response_schema('pipeline_stage')
          expect(json_response['latest_statuses'].length).to eq 1
          expect(json_response).not_to have_key('retried')
        end
239 240
      end

241 242 243 244 245 246 247 248 249 250 251
      context 'with retried' do
        before do
          get_stage('build', retried: true)
        end

        it 'returns pipelines jobs with the retried builds' do
          expect(response).to have_gitlab_http_status(:ok)
          expect(response).to match_response_schema('pipeline_stage')
          expect(json_response['latest_statuses'].length).to eq 1
          expect(json_response['retried'].length).to eq 1
        end
252 253 254 255 256 257 258 259
      end
    end

    context 'when accessing unknown stage' do
      before do
        get_stage('test')
      end

260
      it 'responds with not found' do
261
        expect(response).to have_gitlab_http_status(:not_found)
262
      end
263 264
    end

265
    def get_stage(name, params = {})
B
blackst0ne 已提交
266 267 268 269 270 271 272 273
      get :stage, params: {
**params.merge(
  namespace_id: project.namespace,
  project_id: project,
  id: pipeline.id,
  stage: name,
  format: :json)
}
274
    end
275
  end
S
Shinya Maeda 已提交
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
  describe 'GET stages_ajax.json' do
    let(:pipeline) { create(:ci_pipeline, project: project) }

    context 'when accessing existing stage' do
      before do
        create(:ci_build, pipeline: pipeline, stage: 'build')

        get_stage_ajax('build')
      end

      it 'returns html source for stage dropdown' do
        expect(response).to have_gitlab_http_status(:ok)
        expect(response).to render_template('projects/pipelines/_stage')
        expect(json_response).to include('html')
      end
    end

    context 'when accessing unknown stage' do
      before do
        get_stage_ajax('test')
      end

      it 'responds with not found' do
        expect(response).to have_gitlab_http_status(:not_found)
      end
    end

    def get_stage_ajax(name)
B
blackst0ne 已提交
305 306 307 308 309 310
      get :stage_ajax, params: {
                         namespace_id: project.namespace,
                         project_id: project,
                         id: pipeline.id,
                         stage: name
                       },
311
                       format: :json
312 313 314
    end
  end

S
Shinya Maeda 已提交
315
  describe 'GET status.json' do
316 317
    let(:pipeline) { create(:ci_pipeline, project: project) }
    let(:status) { pipeline.detailed_status(double('user')) }
S
Shinya Maeda 已提交
318

319
    before do
B
blackst0ne 已提交
320 321 322 323 324
      get :status, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: pipeline.id
                   },
325 326
                   format: :json
    end
S
Shinya Maeda 已提交
327

328
    it 'return a detailed pipeline status in json' do
329
      expect(response).to have_gitlab_http_status(:ok)
330 331 332
      expect(json_response['text']).to eq status.text
      expect(json_response['label']).to eq status.label
      expect(json_response['icon']).to eq status.icon
333
      expect(json_response['favicon']).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png")
S
Shinya Maeda 已提交
334 335
    end
  end
336 337 338 339 340 341

  describe 'POST retry.json' do
    let!(:pipeline) { create(:ci_pipeline, :failed, project: project) }
    let!(:build) { create(:ci_build, :failed, pipeline: pipeline) }

    before do
B
blackst0ne 已提交
342 343 344 345 346
      post :retry, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: pipeline.id
                   },
347 348 349
                   format: :json
    end

350 351 352
    it 'retries a pipeline without returning any content' do
      expect(response).to have_gitlab_http_status(:no_content)
      expect(build.reload).to be_retried
K
Kamil Trzcinski 已提交
353 354 355
    end

    context 'when builds are disabled' do
356 357
      let(:feature) { ProjectFeature::DISABLED }

K
Kamil Trzcinski 已提交
358
      it 'fails to retry pipeline' do
359
        expect(response).to have_gitlab_http_status(:not_found)
K
Kamil Trzcinski 已提交
360
      end
361 362 363 364 365 366
    end
  end

  describe 'POST cancel.json' do
    let!(:pipeline) { create(:ci_pipeline, project: project) }
    let!(:build) { create(:ci_build, :running, pipeline: pipeline) }
367

368
    before do
B
blackst0ne 已提交
369 370 371 372 373
      post :cancel, params: {
                      namespace_id: project.namespace,
                      project_id: project,
                      id: pipeline.id
                    },
374 375 376
                    format: :json
    end

377 378 379
    it 'cancels a pipeline without returning any content' do
      expect(response).to have_gitlab_http_status(:no_content)
      expect(pipeline.reload).to be_canceled
K
Kamil Trzcinski 已提交
380 381 382
    end

    context 'when builds are disabled' do
383 384
      let(:feature) { ProjectFeature::DISABLED }

K
Kamil Trzcinski 已提交
385
      it 'fails to retry pipeline' do
386
        expect(response).to have_gitlab_http_status(:not_found)
K
Kamil Trzcinski 已提交
387
      end
388 389
    end
  end
390
end