BigW Consortium Gitlab

branches_spec.rb 16.8 KB
Newer Older
1 2 3
require 'spec_helper'
require 'mime/types'

4
describe API::Branches do
5
  let(:user) { create(:user) }
6
  let!(:project) { create(:project, :repository, creator: user) }
7
  let!(:master) { create(:project_member, :master, user: user, project: project) }
8
  let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } }
9 10
  let!(:branch_name) { 'feature' }
  let!(:branch_sha) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' }
11
  let(:branch_with_dot) { CreateBranchService.new(project, user).execute("with.1.2.3", "master")[:branch] }
12 13

  describe "GET /projects/:id/repository/branches" do
14
    let(:route) { "/projects/#{project.id}/repository/branches" }
15

16 17 18
    shared_examples_for 'repository branches' do
      it 'returns the repository branches' do
        get api(route, current_user), per_page: 100
19

20 21 22 23 24 25 26 27 28 29 30 31 32 33
        expect(response).to have_http_status(200)
        expect(response).to include_pagination_headers
        expect(json_response).to be_an Array
        branch_names = json_response.map { |x| x['name'] }
        expect(branch_names).to match_array(project.repository.branch_names)
      end

      context 'when repository is disabled' do
        include_context 'disabled repository'

        it_behaves_like '403 response' do
          let(:request) { get api(route, current_user) }
        end
      end
34 35
    end

36 37 38 39 40 41
    context 'when unauthenticated', 'and project is public' do
      it_behaves_like 'repository branches' do
        let(:project) { create(:project, :public, :repository) }
        let(:current_user) { nil }
      end
    end
42

43 44 45 46 47
    context 'when unauthenticated', 'and project is private' do
      it_behaves_like '404 response' do
        let(:request) { get api(route) }
        let(:message) { '404 Project Not Found' }
      end
48 49
    end

50 51 52 53 54
    context 'when authenticated', 'as a developer' do
      it_behaves_like 'repository branches' do
        let(:current_user) { user }
      end
    end
55

56 57 58 59
    context 'when authenticated', 'as a guest' do
      it_behaves_like '403 response' do
        let(:request) { get api(route, guest) }
      end
60
    end
61 62 63 64
  end

  describe "GET /projects/:id/repository/branches/:branch" do
    let(:route) { "/projects/#{project.id}/repository/branches/#{branch_name}" }
65

66 67 68
    shared_examples_for 'repository branch' do |merged: false|
      it 'returns the repository branch' do
        get api(route, current_user)
69 70

        expect(response).to have_http_status(200)
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
        expect(json_response['name']).to eq(branch_name)
        expect(json_response['merged']).to eq(merged)
        expect(json_response['protected']).to eq(false)
        expect(json_response['developers_can_push']).to eq(false)
        expect(json_response['developers_can_merge']).to eq(false)

        json_commit = json_response['commit']
        expect(json_commit['id']).to eq(branch_sha)
        expect(json_commit).to have_key('short_id')
        expect(json_commit).to have_key('title')
        expect(json_commit).to have_key('message')
        expect(json_commit).to have_key('author_name')
        expect(json_commit).to have_key('author_email')
        expect(json_commit).to have_key('authored_date')
        expect(json_commit).to have_key('committer_name')
        expect(json_commit).to have_key('committer_email')
        expect(json_commit).to have_key('committed_date')
        expect(json_commit).to have_key('parent_ids')
      end

      context 'when branch does not exist' do
        let(:branch_name) { 'unknown' }

        it_behaves_like '404 response' do
          let(:request) { get api(route, current_user) }
          let(:message) { '404 Branch Not Found' }
        end
      end

      context 'when repository is disabled' do
        include_context 'disabled repository'

        it_behaves_like '403 response' do
          let(:request) { get api(route, current_user) }
        end
106 107 108
      end
    end

109 110 111 112 113
    context 'when unauthenticated', 'and project is public' do
      it_behaves_like 'repository branch' do
        let(:project) { create(:project, :public, :repository) }
        let(:current_user) { nil }
      end
114 115
    end

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
    context 'when unauthenticated', 'and project is private' do
      it_behaves_like '404 response' do
        let(:request) { get api(route) }
        let(:message) { '404 Project Not Found' }
      end
    end

    context 'when authenticated', 'as a developer' do
      let(:current_user) { user }
      it_behaves_like 'repository branch'

      context 'when branch contains a dot' do
        let(:branch_name) { branch_with_dot.name }
        let(:branch_sha) { project.commit('master').sha }

        it_behaves_like 'repository branch'
      end

      context 'when branch is merged' do
        let(:branch_name) { 'merge-test' }
        let(:branch_sha) { project.commit('merge-test').sha }

        it_behaves_like 'repository branch', merged: true
      end
    end

    context 'when authenticated', 'as a guest' do
      it_behaves_like '403 response' do
        let(:request) { get api(route, guest) }
      end
146 147 148
    end
  end

149
  describe 'PUT /projects/:id/repository/branches/:branch/protect' do
150 151 152
    context "when a protected branch doesn't already exist" do
      it 'protects a single branch' do
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
153

154 155 156 157 158 159 160
        expect(response).to have_http_status(200)
        expect(json_response['name']).to eq(branch_name)
        expect(json_response['commit']['id']).to eq(branch_sha)
        expect(json_response['protected']).to eq(true)
        expect(json_response['developers_can_push']).to eq(false)
        expect(json_response['developers_can_merge']).to eq(false)
      end
161

162
      it "protects a single branch with dots in the name" do
163
        put api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}/protect", user)
164 165

        expect(response).to have_http_status(200)
166
        expect(json_response['name']).to eq(branch_with_dot.name)
167 168 169
        expect(json_response['protected']).to eq(true)
      end

170 171 172
      it 'protects a single branch and developers can push' do
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
            developers_can_push: true
173

174 175 176 177 178 179 180
        expect(response).to have_http_status(200)
        expect(json_response['name']).to eq(branch_name)
        expect(json_response['commit']['id']).to eq(branch_sha)
        expect(json_response['protected']).to eq(true)
        expect(json_response['developers_can_push']).to eq(true)
        expect(json_response['developers_can_merge']).to eq(false)
      end
181

182 183 184
      it 'protects a single branch and developers can merge' do
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
            developers_can_merge: true
185

186 187 188 189 190 191 192
        expect(response).to have_http_status(200)
        expect(json_response['name']).to eq(branch_name)
        expect(json_response['commit']['id']).to eq(branch_sha)
        expect(json_response['protected']).to eq(true)
        expect(json_response['developers_can_push']).to eq(false)
        expect(json_response['developers_can_merge']).to eq(true)
      end
193

194 195 196
      it 'protects a single branch and developers can push and merge' do
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user),
            developers_can_push: true, developers_can_merge: true
197

198 199 200 201 202 203 204
        expect(response).to have_http_status(200)
        expect(json_response['name']).to eq(branch_name)
        expect(json_response['commit']['id']).to eq(branch_sha)
        expect(json_response['protected']).to eq(true)
        expect(json_response['developers_can_push']).to eq(true)
        expect(json_response['developers_can_merge']).to eq(true)
      end
205 206
    end

207
    context 'for an existing protected branch' do
208
      before do
209
        project.repository.add_branch(user, protected_branch.name, 'master')
210 211
      end

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
      context "when developers can push and merge" do
        let(:protected_branch) { create(:protected_branch, :developers_can_push, :developers_can_merge, project: project, name: 'protected_branch') }

        it 'updates that a developer cannot push or merge' do
          put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user),
              developers_can_push: false, developers_can_merge: false

          expect(response).to have_http_status(200)
          expect(json_response['name']).to eq(protected_branch.name)
          expect(json_response['protected']).to eq(true)
          expect(json_response['developers_can_push']).to eq(false)
          expect(json_response['developers_can_merge']).to eq(false)
        end

        it "doesn't result in 0 access levels when 'developers_can_push' is switched off" do
          put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user),
              developers_can_push: false

          expect(response).to have_http_status(200)
          expect(json_response['name']).to eq(protected_branch.name)
          expect(protected_branch.reload.push_access_levels.first).to be_present
          expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MASTER)
        end

        it "doesn't result in 0 access levels when 'developers_can_merge' is switched off" do
          put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user),
              developers_can_merge: false

          expect(response).to have_http_status(200)
          expect(json_response['name']).to eq(protected_branch.name)
          expect(protected_branch.reload.merge_access_levels.first).to be_present
          expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MASTER)
        end
      end

      context "when developers cannot push or merge" do
        let(:protected_branch) { create(:protected_branch, project: project, name: 'protected_branch') }

        it 'updates that a developer can push and merge' do
          put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user),
              developers_can_push: true, developers_can_merge: true

          expect(response).to have_http_status(200)
          expect(json_response['name']).to eq(protected_branch.name)
          expect(json_response['protected']).to eq(true)
          expect(json_response['developers_can_push']).to eq(true)
          expect(json_response['developers_can_merge']).to eq(true)
        end
      end
    end

    context "multiple API calls" do
      it "returns success when `protect` is called twice" do
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
267 268

        expect(response).to have_http_status(200)
269
        expect(json_response['name']).to eq(branch_name)
270 271
        expect(json_response['protected']).to eq(true)
        expect(json_response['developers_can_push']).to eq(false)
272
        expect(json_response['developers_can_merge']).to eq(false)
273 274
      end

275 276 277
      it "returns success when `protect` is called twice with `developers_can_push` turned on" do
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true
278 279

        expect(response).to have_http_status(200)
280
        expect(json_response['name']).to eq(branch_name)
281 282
        expect(json_response['protected']).to eq(true)
        expect(json_response['developers_can_push']).to eq(true)
283 284 285 286 287 288 289 290 291 292 293
        expect(json_response['developers_can_merge']).to eq(false)
      end

      it "returns success when `protect` is called twice with `developers_can_merge` turned on" do
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true
        put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true

        expect(response).to have_http_status(200)
        expect(json_response['name']).to eq(branch_name)
        expect(json_response['protected']).to eq(true)
        expect(json_response['developers_can_push']).to eq(false)
294
        expect(json_response['developers_can_merge']).to eq(true)
295
      end
296 297
    end

298
    it "returns a 404 error if branch not found" do
299
      put api("/projects/#{project.id}/repository/branches/unknown/protect", user)
300
      expect(response).to have_http_status(404)
301 302
    end

303
    it "returns a 403 error if guest" do
304
      put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", guest)
305
      expect(response).to have_http_status(403)
306 307 308 309
    end
  end

  describe "PUT /projects/:id/repository/branches/:branch/unprotect" do
310
    it "unprotects a single branch" do
311
      put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user)
312
      expect(response).to have_http_status(200)
313

314 315 316
      expect(json_response['name']).to eq(branch_name)
      expect(json_response['commit']['id']).to eq(branch_sha)
      expect(json_response['protected']).to eq(false)
317 318
    end

319
    it "update branches with dots in branch name" do
320
      put api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}/unprotect", user)
321 322

      expect(response).to have_http_status(200)
323
      expect(json_response['name']).to eq(branch_with_dot.name)
324 325 326
      expect(json_response['protected']).to eq(false)
    end

327
    it "returns success when unprotect branch" do
328
      put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user)
329
      expect(response).to have_http_status(404)
330 331
    end

332
    it "returns success when unprotect branch again" do
333 334
      put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user)
      put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user)
335
      expect(response).to have_http_status(200)
336 337 338
    end
  end

339
  describe "POST /projects/:id/repository/branches" do
340
    it "creates a new branch" do
341
      post api("/projects/#{project.id}/repository/branches", user),
342
           branch: 'feature1',
343
           ref: branch_sha
344

345
      expect(response).to have_http_status(201)
346

347 348
      expect(json_response['name']).to eq('feature1')
      expect(json_response['commit']['id']).to eq(branch_sha)
349 350
    end

351
    it "denies for user without push access" do
352
      post api("/projects/#{project.id}/repository/branches", guest),
353
           branch: branch_name,
354
           ref: branch_sha
355
      expect(response).to have_http_status(403)
356
    end
357

358
    it 'returns 400 if branch name is invalid' do
359
      post api("/projects/#{project.id}/repository/branches", user),
360
           branch: 'new design',
361
           ref: branch_sha
362
      expect(response).to have_http_status(400)
Douglas Barbosa Alexandre committed
363
      expect(json_response['message']).to eq('Branch name is invalid')
364 365
    end

366
    it 'returns 400 if branch already exists' do
367
      post api("/projects/#{project.id}/repository/branches", user),
368
           branch: 'new_design1',
369
           ref: branch_sha
370
      expect(response).to have_http_status(201)
371 372

      post api("/projects/#{project.id}/repository/branches", user),
373
           branch: 'new_design1',
374
           ref: branch_sha
375
      expect(response).to have_http_status(400)
376
      expect(json_response['message']).to eq('Branch already exists')
377 378
    end

379
    it 'returns 400 if ref name is invalid' do
380
      post api("/projects/#{project.id}/repository/branches", user),
381
           branch: 'new_design3',
382
           ref: 'foo'
383
      expect(response).to have_http_status(400)
384
      expect(json_response['message']).to eq('Invalid reference name')
385
    end
386
  end
387 388

  describe "DELETE /projects/:id/repository/branches/:branch" do
389 390 391
    before do
      allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true)
    end
392

393
    it "removes branch" do
394
      delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
395 396

      expect(response).to have_http_status(204)
397 398
    end

399
    it "removes a branch with dots in the branch name" do
400
      delete api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}", user)
401

402
      expect(response).to have_http_status(204)
403 404
    end

405
    it 'returns 404 if branch not exists' do
406
      delete api("/projects/#{project.id}/repository/branches/foobar", user)
407
      expect(response).to have_http_status(404)
408
    end
409
  end
410 411 412 413 414 415

  describe "DELETE /projects/:id/repository/merged_branches" do
    before do
      allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true)
    end

416
    it 'returns 202 with json body' do
417
      delete api("/projects/#{project.id}/repository/merged_branches", user)
418 419 420

      expect(response).to have_http_status(202)
      expect(json_response['message']).to eql('202 Accepted')
421 422 423
    end

    it 'returns a 403 error if guest' do
424
      delete api("/projects/#{project.id}/repository/merged_branches", guest)
425 426 427
      expect(response).to have_http_status(403)
    end
  end
428
end