commit_controller_spec.rb 11.7 KB
Newer Older
1 2
# frozen_string_literal: true

3
require 'spec_helper'
4 5

describe Projects::CommitController do
6 7
  set(:project)  { create(:project, :repository) }
  set(:user)     { create(:user) }
8
  let(:commit)   { project.commit("master") }
9
  let(:master_pickable_sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
10
  let(:master_pickable_commit) { project.commit(master_pickable_sha) }
11 12 13

  before do
    sign_in(user)
14
    project.add_maintainer(user)
15 16
  end

Sean McGivern's avatar
Sean McGivern committed
17
  describe 'GET show' do
18 19
    render_views

20 21
    def go(extra_params = {})
      params = {
22 23
        namespace_id: project.namespace,
        project_id: project
24 25
      }

blackst0ne's avatar
blackst0ne committed
26
      get :show, params: params.merge(extra_params)
27 28
    end

29 30
    context 'with valid id' do
      it 'responds with 200' do
31
        go(id: commit.id)
32 33 34 35 36 37 38

        expect(response).to be_ok
      end
    end

    context 'with invalid id' do
      it 'responds with 404' do
39
        go(id: commit.id.reverse)
40 41 42 43 44

        expect(response).to be_not_found
      end
    end

45
    it 'handles binary files' do
46 47 48 49 50 51
      go(id: TestEnv::BRANCH_SHA['binary-encoding'], format: 'html')

      expect(response).to be_success
    end

    shared_examples "export as" do |format|
52
      it "does generally work" do
53 54 55 56 57
        go(id: commit.id, format: format)

        expect(response).to be_success
      end

58
      it "generates it" do
59 60 61 62 63
        expect_any_instance_of(Commit).to receive(:"to_#{format}")

        go(id: commit.id, format: format)
      end

64
      it "renders it" do
65 66 67 68 69
        go(id: commit.id, format: format)

        expect(response.body).to eq(commit.send(:"to_#{format}"))
      end

70
      it "does not escape Html" do
71 72
        allow_any_instance_of(Commit).to receive(:"to_#{format}")
          .and_return('HTML entities &<>" ')
73 74 75 76 77 78 79 80 81 82 83

        go(id: commit.id, format: format)

        expect(response.body).not_to include('&amp;')
        expect(response.body).not_to include('&gt;')
        expect(response.body).not_to include('&lt;')
        expect(response.body).not_to include('&quot;')
      end
    end

    describe "as diff" do
84 85
      it "triggers workhorse to serve the request" do
        go(id: commit.id, format: :diff)
86

87
        expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-diff:")
88 89 90 91
      end
    end

    describe "as patch" do
92
      it "contains a git diff" do
93
        go(id: commit.id, format: :patch)
94

95
        expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-format-patch:")
96 97 98 99 100 101 102 103 104 105 106
      end
    end

    context 'commit that removes a submodule' do
      render_views

      let(:fork_project) { create(:forked_project_with_submodules, visibility_level: 20) }
      let(:commit) { fork_project.commit('remove-submodule') }

      it 'renders it' do
        get(:show,
blackst0ne's avatar
blackst0ne committed
107 108 109 110 111
            params: {
              namespace_id: fork_project.namespace,
              project_id: fork_project,
              id: commit.id
            })
112 113 114 115

        expect(response).to be_success
      end
    end
116 117 118 119 120 121 122 123

    context 'in the context of a merge_request' do
      let(:merge_request) { create(:merge_request, source_project: project) }
      let(:commit) { merge_request.commits.first }

      it 'prepare diff notes in the context of the merge request' do
        go(id: commit.id, merge_request_iid: merge_request.iid)

124 125
        expect(assigns(:new_diff_note_attrs)).to eq({
                                                      noteable_type: 'MergeRequest',
126 127 128 129 130 131
                                                      noteable_id: merge_request.id,
                                                      commit_id: commit.id
                                                    })
        expect(response).to be_ok
      end
    end
132 133
  end

134 135
  describe 'GET branches' do
    it 'contains branch and tags information' do
136 137
      commit = project.commit('5937ac0a7beb003549fc5fd26fc247adbce4a52e')

138
      get(:branches,
blackst0ne's avatar
blackst0ne committed
139 140 141 142 143
          params: {
            namespace_id: project.namespace,
            project_id: project,
            id: commit.id
          })
144

145 146 147 148 149 150 151 152 153 154 155 156
      expect(assigns(:branches)).to include('master', 'feature_conflict')
      expect(assigns(:branches_limit_exceeded)).to be_falsey
      expect(assigns(:tags)).to include('v1.1.0')
      expect(assigns(:tags_limit_exceeded)).to be_falsey
    end

    it 'returns :limit_exceeded when number of branches/tags reach a threshhold' do
      commit = project.commit('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
      allow_any_instance_of(Repository).to receive(:branch_count).and_return(1001)
      allow_any_instance_of(Repository).to receive(:tag_count).and_return(1001)

      get(:branches,
blackst0ne's avatar
blackst0ne committed
157 158 159 160 161
          params: {
            namespace_id: project.namespace,
            project_id: project,
            id: commit.id
          })
162 163 164 165 166

      expect(assigns(:branches)).to eq([])
      expect(assigns(:branches_limit_exceeded)).to be_truthy
      expect(assigns(:tags)).to eq([])
      expect(assigns(:tags_limit_exceeded)).to be_truthy
167 168 169
    end
  end

Sean McGivern's avatar
Sean McGivern committed
170
  describe 'POST revert' do
171
    context 'when target branch is not provided' do
172
      it 'renders the 404 page' do
173
        post(:revert,
blackst0ne's avatar
blackst0ne committed
174 175 176 177 178
            params: {
              namespace_id: project.namespace,
              project_id: project,
              id: commit.id
            })
179 180

        expect(response).not_to be_success
181
        expect(response).to have_gitlab_http_status(404)
182 183 184 185
      end
    end

    context 'when the revert was successful' do
186
      it 'redirects to the commits page' do
187
        post(:revert,
blackst0ne's avatar
blackst0ne committed
188 189 190 191 192 193
            params: {
              namespace_id: project.namespace,
              project_id: project,
              start_branch: 'master',
              id: commit.id
            })
194

195
        expect(response).to redirect_to project_commits_path(project, 'master')
196 197 198 199 200 201 202
        expect(flash[:notice]).to eq('The commit has been successfully reverted.')
      end
    end

    context 'when the revert failed' do
      before do
        post(:revert,
blackst0ne's avatar
blackst0ne committed
203 204 205 206 207 208
            params: {
              namespace_id: project.namespace,
              project_id: project,
              start_branch: 'master',
              id: commit.id
            })
209 210
      end

211
      it 'redirects to the commit page' do
212 213
        # Reverting a commit that has been already reverted.
        post(:revert,
blackst0ne's avatar
blackst0ne committed
214 215 216 217 218 219
            params: {
              namespace_id: project.namespace,
              project_id: project,
              start_branch: 'master',
              id: commit.id
            })
220

221
        expect(response).to redirect_to project_commit_path(project, commit.id)
222 223 224 225 226
        expect(flash[:alert]).to match('Sorry, we cannot revert this commit automatically.')
      end
    end
  end

Sean McGivern's avatar
Sean McGivern committed
227
  describe 'POST cherry_pick' do
228
    context 'when target branch is not provided' do
229
      it 'renders the 404 page' do
230
        post(:cherry_pick,
blackst0ne's avatar
blackst0ne committed
231 232 233 234 235
            params: {
              namespace_id: project.namespace,
              project_id: project,
              id: master_pickable_commit.id
            })
236 237

        expect(response).not_to be_success
238
        expect(response).to have_gitlab_http_status(404)
239 240 241 242
      end
    end

    context 'when the cherry-pick was successful' do
243
      it 'redirects to the commits page' do
244
        post(:cherry_pick,
blackst0ne's avatar
blackst0ne committed
245 246 247 248 249 250
            params: {
              namespace_id: project.namespace,
              project_id: project,
              start_branch: 'master',
              id: master_pickable_commit.id
            })
251

252
        expect(response).to redirect_to project_commits_path(project, 'master')
253
        expect(flash[:notice]).to eq('The commit has been successfully cherry-picked into master.')
254
      end
255 256
    end

257 258 259
    context 'when the cherry_pick failed' do
      before do
        post(:cherry_pick,
blackst0ne's avatar
blackst0ne committed
260 261 262 263 264 265
            params: {
              namespace_id: project.namespace,
              project_id: project,
              start_branch: 'master',
              id: master_pickable_commit.id
            })
266 267
      end

268
      it 'redirects to the commit page' do
269 270
        # Cherry-picking a commit that has been already cherry-picked.
        post(:cherry_pick,
blackst0ne's avatar
blackst0ne committed
271 272 273 274 275 276
            params: {
              namespace_id: project.namespace,
              project_id: project,
              start_branch: 'master',
              id: master_pickable_commit.id
            })
277

278
        expect(response).to redirect_to project_commit_path(project, master_pickable_commit.id)
279 280 281 282 283
        expect(flash[:alert]).to match('Sorry, we cannot cherry-pick this commit automatically.')
      end
    end
  end

Sean McGivern's avatar
Sean McGivern committed
284
  describe 'GET diff_for_path' do
285 286
    def diff_for_path(extra_params = {})
      params = {
287 288
        namespace_id: project.namespace,
        project_id: project
289 290
      }

blackst0ne's avatar
blackst0ne committed
291
      get :diff_for_path, params: params.merge(extra_params)
292 293 294
    end

    let(:existing_path) { '.gitmodules' }
295
    let(:commit2) { project.commit('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
296 297 298 299 300

    context 'when the commit exists' do
      context 'when the user has access to the project' do
        context 'when the path exists in the diff' do
          it 'enables diff notes' do
301
            diff_for_path(id: commit2.id, old_path: existing_path, new_path: existing_path)
302 303

            expect(assigns(:diff_notes_disabled)).to be_falsey
304
            expect(assigns(:new_diff_note_attrs)).to eq(noteable_type: 'Commit',
Douwe Maan's avatar
Douwe Maan committed
305
                                                        commit_id: commit2.id)
306 307 308
          end

          it 'only renders the diffs for the path given' do
309 310 311
            expect(controller).to receive(:render_diff_for_path).and_wrap_original do |meth, diffs|
              expect(diffs.diff_files.map(&:new_path)).to contain_exactly(existing_path)
              meth.call(diffs)
312 313
            end

314
            diff_for_path(id: commit2.id, old_path: existing_path, new_path: existing_path)
315 316 317 318
          end
        end

        context 'when the path does not exist in the diff' do
319 320 321
          before do
            diff_for_path(id: commit.id, old_path: existing_path.succ, new_path: existing_path.succ)
          end
322 323

          it 'returns a 404' do
324
            expect(response).to have_gitlab_http_status(404)
325 326 327 328 329 330 331
          end
        end
      end

      context 'when the user does not have access to the project' do
        before do
          project.team.truncate
332
          diff_for_path(id: commit.id, old_path: existing_path, new_path: existing_path)
333 334 335
        end

        it 'returns a 404' do
336
          expect(response).to have_gitlab_http_status(404)
337 338 339 340 341
        end
      end
    end

    context 'when the commit does not exist' do
342
      before do
343
        diff_for_path(id: commit.id.reverse, old_path: existing_path, new_path: existing_path)
344
      end
345 346

      it 'returns a 404' do
347
        expect(response).to have_gitlab_http_status(404)
348
      end
349 350
    end
  end
351 352 353 354

  describe 'GET pipelines' do
    def get_pipelines(extra_params = {})
      params = {
355 356
        namespace_id: project.namespace,
        project_id: project
357 358
      }

blackst0ne's avatar
blackst0ne committed
359
      get :pipelines, params: params.merge(extra_params)
360 361 362
    end

    context 'when the commit exists' do
363 364 365 366 367 368 369 370 371 372 373 374
      context 'when the commit has pipelines' do
        before do
          create(:ci_pipeline, project: project, sha: commit.id)
        end

        context 'when rendering a HTML format' do
          it 'shows pipelines' do
            get_pipelines(id: commit.id)

            expect(response).to be_ok
          end
        end
375

376 377 378 379 380
        context 'when rendering a JSON format' do
          it 'responds with serialized pipelines' do
            get_pipelines(id: commit.id, format: :json)

            expect(response).to be_ok
381 382
            expect(JSON.parse(response.body)['pipelines']).not_to be_empty
            expect(JSON.parse(response.body)['count']['all']).to eq 1
383
            expect(response).to include_pagination_headers
384
          end
385 386 387 388 389 390 391 392 393 394
        end
      end
    end

    context 'when the commit does not exist' do
      before do
        get_pipelines(id: 'e7a412c8da9f6d0081a633a4a402dde1c4694ebd')
      end

      it 'returns a 404' do
395
        expect(response).to have_gitlab_http_status(404)
396 397 398
      end
    end
  end
399
end