projects_spec.rb 53.9 KB
Newer Older
1
# -*- coding: utf-8 -*-
N
Nihad Abbasov 已提交
2 3
require 'spec_helper'

4
describe API::Projects, api: true  do
5
  include ApiHelpers
6
  include Gitlab::CurrentSettings
7 8 9
  let(:user) { create(:user) }
  let(:user2) { create(:user) }
  let(:user3) { create(:user) }
A
Angus MacArthur 已提交
10
  let(:admin) { create(:admin) }
11 12
  let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
  let(:project2) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
13
  let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
14 15
  let(:project_member) { create(:project_member, :master, user: user, project: project) }
  let(:project_member2) { create(:project_member, :developer, user: user3, project: project) }
16
  let(:user4) { create(:user) }
17 18
  let(:project3) do
    create(:project,
19
    :private,
20
    :repository,
21 22 23 24 25 26
    name: 'second_project',
    path: 'second_project',
    creator_id: user.id,
    namespace: user.namespace,
    merge_requests_enabled: false,
    issues_enabled: false, wiki_enabled: false,
27
    snippets_enabled: false)
28 29 30 31 32 33 34 35
  end
  let(:project_member3) do
    create(:project_member,
    user: user4,
    project: project3,
    access_level: ProjectMember::MASTER)
  end
  let(:project4) do
36
    create(:empty_project,
37 38 39 40 41 42 43
    name: 'third_project',
    path: 'third_project',
    creator_id: user4.id,
    namespace: user4.namespace)
  end

  describe 'GET /projects' do
T
Toon Claes 已提交
44 45
    shared_examples_for 'projects response' do
      it 'returns an array of projects' do
46
        get api('/projects', current_user), filter
T
Toon Claes 已提交
47 48

        expect(response).to have_http_status(200)
49
        expect(response).to include_pagination_headers
T
Toon Claes 已提交
50 51 52 53 54 55 56 57 58 59 60 61
        expect(json_response).to be_an Array
        expect(json_response.map { |p| p['id'] }).to contain_exactly(*projects.map(&:id))
      end
    end

    let!(:public_project) { create(:empty_project, :public, name: 'public_project') }
    before do
      project
      project2
      project3
      project4
    end
D
Dmitriy Zaporozhets 已提交
62

63
    context 'when unauthenticated' do
T
Toon Claes 已提交
64
      it_behaves_like 'projects response' do
65
        let(:filter) { {} }
T
Toon Claes 已提交
66 67
        let(:current_user) { nil }
        let(:projects) { [public_project] }
68
      end
N
Nihad Abbasov 已提交
69 70
    end

M
Markus Koller 已提交
71
    context 'when authenticated as regular user' do
T
Toon Claes 已提交
72
      it_behaves_like 'projects response' do
73
        let(:filter) { {} }
T
Toon Claes 已提交
74 75
        let(:current_user) { user }
        let(:projects) { [public_project, project, project2, project3] }
N
Nihad Abbasov 已提交
76
      end
77

78
      it 'includes the project labels as the tag_list' do
79
        get api('/projects', user)
T
Toon Claes 已提交
80

81
        expect(response.status).to eq 200
82
        expect(response).to include_pagination_headers
83 84
        expect(json_response).to be_an Array
        expect(json_response.first.keys).to include('tag_list')
85
      end
86

87
      it 'includes open_issues_count' do
S
Stan Hu 已提交
88
        get api('/projects', user)
T
Toon Claes 已提交
89

S
Stan Hu 已提交
90
        expect(response.status).to eq 200
91
        expect(response).to include_pagination_headers
S
Stan Hu 已提交
92 93 94 95
        expect(json_response).to be_an Array
        expect(json_response.first.keys).to include('open_issues_count')
      end

T
Toon Claes 已提交
96
      it 'does not include open_issues_count if issues are disabled' do
F
Felipe Artur 已提交
97
        project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
S
Stan Hu 已提交
98 99

        get api('/projects', user)
T
Toon Claes 已提交
100

S
Stan Hu 已提交
101
        expect(response.status).to eq 200
102
        expect(response).to include_pagination_headers
S
Stan Hu 已提交
103
        expect(json_response).to be_an Array
T
Toon Claes 已提交
104 105 106 107 108 109 110
        expect(json_response.find { |hash| hash['id'] == project.id }.keys).not_to include('open_issues_count')
      end

      it "does not include statistics by default" do
        get api('/projects', user)

        expect(response).to have_http_status(200)
111
        expect(response).to include_pagination_headers
T
Toon Claes 已提交
112 113
        expect(json_response).to be_an Array
        expect(json_response.first).not_to include('statistics')
S
Stan Hu 已提交
114 115
      end

T
Toon Claes 已提交
116 117 118 119
      it "includes statistics if requested" do
        get api('/projects', user), statistics: true

        expect(response).to have_http_status(200)
120
        expect(response).to include_pagination_headers
T
Toon Claes 已提交
121 122
        expect(json_response).to be_an Array
        expect(json_response.first).to include 'statistics'
S
Stan Hu 已提交
123 124
      end

T
Toon Claes 已提交
125
      context 'and with simple=true' do
T
tiagonbotelho 已提交
126
        it 'returns a simplified version of all the projects' do
D
Douwe Maan 已提交
127
          expected_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace)
128

129
          get api('/projects?simple=true', user)
T
tiagonbotelho 已提交
130

131
          expect(response).to have_http_status(200)
132
          expect(response).to include_pagination_headers
133
          expect(json_response).to be_an Array
134
          expect(json_response.first.keys).to match_array expected_keys
135 136 137
        end
      end

138
      context 'and using search' do
139 140 141 142 143 144
        it_behaves_like 'projects response' do
          let(:filter) { { search: project.name } }
          let(:current_user) { user }
          let(:projects) { [project] }
        end
      end
T
Toon Claes 已提交
145

146
      context 'and membership=true' do
147
        it_behaves_like 'projects response' do
148
          let(:filter) { { membership: true } }
149 150
          let(:current_user) { user }
          let(:projects) { [project, project2, project3] }
151 152 153
        end
      end

J
Josh Frye 已提交
154
      context 'and using the visibility filter' do
155
        it 'filters based on private visibility param' do
J
Josh Frye 已提交
156
          get api('/projects', user), { visibility: 'private' }
T
Toon Claes 已提交
157

Z
Z.J. van de Weg 已提交
158
          expect(response).to have_http_status(200)
159
          expect(response).to include_pagination_headers
J
Josh Frye 已提交
160
          expect(json_response).to be_an Array
T
Toon Claes 已提交
161
          expect(json_response.map { |p| p['id'] }).to contain_exactly(project.id, project2.id, project3.id)
J
Josh Frye 已提交
162 163
        end

164
        it 'filters based on internal visibility param' do
T
Toon Claes 已提交
165 166
          project2.update_attribute(:visibility_level, Gitlab::VisibilityLevel::INTERNAL)

J
Josh Frye 已提交
167
          get api('/projects', user), { visibility: 'internal' }
T
Toon Claes 已提交
168

Z
Z.J. van de Weg 已提交
169
          expect(response).to have_http_status(200)
170
          expect(response).to include_pagination_headers
J
Josh Frye 已提交
171
          expect(json_response).to be_an Array
T
Toon Claes 已提交
172
          expect(json_response.map { |p| p['id'] }).to contain_exactly(project2.id)
J
Josh Frye 已提交
173 174
        end

175
        it 'filters based on public visibility param' do
J
Josh Frye 已提交
176
          get api('/projects', user), { visibility: 'public' }
T
Toon Claes 已提交
177

Z
Z.J. van de Weg 已提交
178
          expect(response).to have_http_status(200)
179
          expect(response).to include_pagination_headers
J
Josh Frye 已提交
180
          expect(json_response).to be_an Array
T
Toon Claes 已提交
181
          expect(json_response.map { |p| p['id'] }).to contain_exactly(public_project.id)
J
Josh Frye 已提交
182 183 184
        end
      end

185
      context 'and using sorting' do
186
        it 'returns the correct order when sorted by id' do
187
          get api('/projects', user), { order_by: 'id', sort: 'desc' }
T
Toon Claes 已提交
188

Z
Z.J. van de Weg 已提交
189
          expect(response).to have_http_status(200)
190
          expect(response).to include_pagination_headers
191 192
          expect(json_response).to be_an Array
          expect(json_response.first['id']).to eq(project3.id)
193 194
        end
      end
D
Dmitriy Zaporozhets 已提交
195

T
Toon Claes 已提交
196 197 198
      context 'and with owned=true' do
        it 'returns an array of projects the user owns' do
          get api('/projects', user4), owned: true
D
Dmitriy Zaporozhets 已提交
199

T
Toon Claes 已提交
200
          expect(response).to have_http_status(200)
201
          expect(response).to include_pagination_headers
T
Toon Claes 已提交
202 203 204
          expect(json_response).to be_an Array
          expect(json_response.first['name']).to eq(project4.name)
          expect(json_response.first['owner']['username']).to eq(user4.username)
205
        end
D
Dmitriy Zaporozhets 已提交
206
      end
M
Markus Koller 已提交
207

T
Toon Claes 已提交
208 209
      context 'and with starred=true' do
        let(:public_project) { create(:empty_project, :public) }
210

T
Toon Claes 已提交
211 212 213 214
        before do
          project_member2
          user3.update_attributes(starred_projects: [project, project2, project3, public_project])
        end
M
Markus Koller 已提交
215

T
Toon Claes 已提交
216 217
        it 'returns the starred projects viewable by the user' do
          get api('/projects', user3), starred: true
M
Markus Koller 已提交
218

T
Toon Claes 已提交
219
          expect(response).to have_http_status(200)
220
          expect(response).to include_pagination_headers
T
Toon Claes 已提交
221 222 223
          expect(json_response).to be_an Array
          expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id)
        end
M
Markus Koller 已提交
224
      end
225

T
Toon Claes 已提交
226
      context 'and with all query parameters' do
227
        let!(:project5) { create(:empty_project, :public, path: 'gitlab5', namespace: create(:namespace)) }
T
Toon Claes 已提交
228 229 230 231
        let!(:project6) { create(:empty_project, :public, path: 'project6', namespace: user.namespace) }
        let!(:project7) { create(:empty_project, :public, path: 'gitlab7', namespace: user.namespace) }
        let!(:project8) { create(:empty_project, path: 'gitlab8', namespace: user.namespace) }
        let!(:project9) { create(:empty_project, :public, path: 'gitlab9') }
232

T
Toon Claes 已提交
233
        before do
234
          user.update_attributes(starred_projects: [project5, project7, project8, project9])
T
Toon Claes 已提交
235
        end
236

237
        context 'including owned filter' do
238
          it 'returns only projects that satisfy all query parameters' do
239
            get api('/projects', user), { visibility: 'public', owned: true, starred: true, search: 'gitlab' }
240

241 242 243 244 245 246 247 248
            expect(response).to have_http_status(200)
            expect(response).to include_pagination_headers
            expect(json_response).to be_an Array
            expect(json_response.size).to eq(1)
            expect(json_response.first['id']).to eq(project7.id)
          end
        end

249
        context 'including membership filter' do
250 251 252 253 254 255 256
          before do
            create(:project_member,
                   user: user,
                   project: project5,
                   access_level: ProjectMember::MASTER)
          end

257 258
          it 'returns only projects that satisfy all query parameters' do
            get api('/projects', user), { visibility: 'public', membership: true, starred: true, search: 'gitlab' }
259 260 261 262 263

            expect(response).to have_http_status(200)
            expect(response).to include_pagination_headers
            expect(json_response).to be_an Array
            expect(json_response.size).to eq(2)
264
            expect(json_response.map { |project| project['id'] }).to contain_exactly(project5.id, project7.id)
265
          end
T
Toon Claes 已提交
266
        end
267 268
      end
    end
269

270
    context 'when authenticated as a different user' do
T
Toon Claes 已提交
271
      it_behaves_like 'projects response' do
272
        let(:filter) { {} }
273 274 275
        let(:current_user) { user2 }
        let(:projects) { [public_project] }
      end
276
    end
277

T
Toon Claes 已提交
278 279
    context 'when authenticated as admin' do
      it_behaves_like 'projects response' do
280
        let(:filter) { {} }
T
Toon Claes 已提交
281 282 283
        let(:current_user) { admin }
        let(:projects) { Project.all }
      end
284 285 286
    end
  end

287 288
  describe 'POST /projects' do
    context 'maximum number of projects reached' do
289
      it 'does not create new project and respond with 403' do
290
        allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
291 292
        expect { post api('/projects', user2), name: 'foo' }.
          to change {Project.count}.by(0)
Z
Z.J. van de Weg 已提交
293
        expect(response).to have_http_status(403)
294 295 296
      end
    end

297 298
    it 'creates new project without path but with name and returns 201' do
      expect { post api('/projects', user), name: 'Foo Project' }.
299
        to change { Project.count }.by(1)
Z
Z.J. van de Weg 已提交
300
      expect(response).to have_http_status(201)
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327

      project = Project.first

      expect(project.name).to eq('Foo Project')
      expect(project.path).to eq('foo-project')
    end

    it 'creates new project without name but with path and returns 201' do
      expect { post api('/projects', user), path: 'foo_project' }.
        to change { Project.count }.by(1)
      expect(response).to have_http_status(201)

      project = Project.first

      expect(project.name).to eq('foo_project')
      expect(project.path).to eq('foo_project')
    end

    it 'creates new project name and path and returns 201' do
      expect { post api('/projects', user), path: 'foo-Project', name: 'Foo Project' }.
        to change { Project.count }.by(1)
      expect(response).to have_http_status(201)

      project = Project.first

      expect(project.name).to eq('Foo Project')
      expect(project.path).to eq('foo-Project')
328 329
    end

330
    it 'creates last project before reaching project limit' do
331
      allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1)
332
      post api('/projects', user2), name: 'foo'
Z
Z.J. van de Weg 已提交
333
      expect(response).to have_http_status(201)
334 335
    end

336
    it 'does not create new project without name or path and returns 400' do
337
      expect { post api('/projects', user) }.not_to change { Project.count }
Z
Z.J. van de Weg 已提交
338
      expect(response).to have_http_status(400)
339
    end
A
Alex Denisov 已提交
340

341
    it "assigns attributes to project" do
342
      project = attributes_for(:project, {
343
        path: 'camelCasePath',
R
Robert Speicher 已提交
344
        description: FFaker::Lorem.sentence,
345 346
        issues_enabled: false,
        merge_requests_enabled: false,
347
        wiki_enabled: false,
J
James Lopez 已提交
348
        only_allow_merge_if_pipeline_succeeds: false,
349 350
        request_access_enabled: true,
        only_allow_merge_if_all_discussions_are_resolved: false
A
Alex Denisov 已提交
351 352
      })

353
      post api('/projects', user), project
A
Alex Denisov 已提交
354

355
      project.each_pair do |k, v|
356
        next if %i[has_external_issue_tracker issues_enabled merge_requests_enabled wiki_enabled].include?(k)
357
        expect(json_response[k.to_s]).to eq(v)
A
Alex Denisov 已提交
358
      end
F
Felipe Artur 已提交
359 360 361 362 363 364

      # Check feature permissions attributes
      project = Project.find_by_path(project[:path])
      expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED)
      expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::DISABLED)
      expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::DISABLED)
365
    end
366

367
    it 'sets a project as public' do
368 369
      project = attributes_for(:project, visibility: 'public')

370
      post api('/projects', user), project
371 372

      expect(json_response['visibility']).to eq('public')
373 374
    end

375
    it 'sets a project as internal' do
376 377
      project = attributes_for(:project, visibility: 'internal')

378
      post api('/projects', user), project
379 380

      expect(json_response['visibility']).to eq('internal')
381 382
    end

383
    it 'sets a project as private' do
384 385
      project = attributes_for(:project, visibility: 'private')

386
      post api('/projects', user), project
387 388

      expect(json_response['visibility']).to eq('private')
389 390
    end

391
    it 'sets a project as allowing merge even if build fails' do
J
James Lopez 已提交
392
      project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: false })
393
      post api('/projects', user), project
J
James Lopez 已提交
394
      expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_falsey
395 396
    end

J
James Lopez 已提交
397 398
    it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
      project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: true })
399
      post api('/projects', user), project
J
James Lopez 已提交
400
      expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_truthy
401 402
    end

403 404 405 406 407 408 409 410
    it 'sets a project as allowing merge even if discussions are unresolved' do
      project = attributes_for(:project, { only_allow_merge_if_all_discussions_are_resolved: false })

      post api('/projects', user), project

      expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_falsey
    end

411 412 413 414 415 416 417 418
    it 'sets a project as allowing merge if only_allow_merge_if_all_discussions_are_resolved is nil' do
      project = attributes_for(:project, only_allow_merge_if_all_discussions_are_resolved: nil)

      post api('/projects', user), project

      expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_falsey
    end

419 420 421 422 423 424 425 426
    it 'sets a project as allowing merge only if all discussions are resolved' do
      project = attributes_for(:project, { only_allow_merge_if_all_discussions_are_resolved: true })

      post api('/projects', user), project

      expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_truthy
    end

427 428 429 430 431 432 433 434
    it 'ignores import_url when it is nil' do
      project = attributes_for(:project, { import_url: nil })

      post api('/projects', user), project

      expect(response).to have_http_status(201)
    end

435
    context 'when a visibility level is restricted' do
436
      let(:project_param) { attributes_for(:project, visibility: 'public') }
437

438
      before do
439
        stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
440 441
      end

442
      it 'does not allow a non-admin to use a restricted visibility level' do
443
        post api('/projects', user), project_param
F
Felipe Artur 已提交
444

Z
Z.J. van de Weg 已提交
445
        expect(response).to have_http_status(400)
446 447 448 449 450
        expect(json_response['message']['visibility_level'].first).to(
          match('restricted by your GitLab administrator')
        )
      end

451
      it 'allows an admin to override restricted visibility settings' do
452 453
        post api('/projects', admin), project_param

454
        expect(json_response['visibility']).to eq('public')
455 456
      end
    end
457 458
  end

459
  describe 'POST /projects/user/:id' do
D
Dmitriy Zaporozhets 已提交
460
    before { project }
A
Angus MacArthur 已提交
461 462
    before { admin }

V
Valery Sizov 已提交
463
    it 'creates new project without path and return 201' do
464
      expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
Z
Z.J. van de Weg 已提交
465
      expect(response).to have_http_status(201)
A
Angus MacArthur 已提交
466 467
    end

468
    it 'responds with 400 on failure and not project' do
469 470
      expect { post api("/projects/user/#{user.id}", admin) }.
        not_to change { Project.count }
471

Z
Z.J. van de Weg 已提交
472
      expect(response).to have_http_status(400)
R
Robert Schilling 已提交
473
      expect(json_response['error']).to eq('name is missing')
A
Angus MacArthur 已提交
474 475
    end

476
    it 'assigns attributes to project' do
A
Angus MacArthur 已提交
477
      project = attributes_for(:project, {
R
Robert Speicher 已提交
478
        description: FFaker::Lorem.sentence,
479 480
        issues_enabled: false,
        merge_requests_enabled: false,
481 482
        wiki_enabled: false,
        request_access_enabled: true
A
Angus MacArthur 已提交
483 484 485 486
      })

      post api("/projects/user/#{user.id}", admin), project

R
Robert Schilling 已提交
487
      expect(response).to have_http_status(201)
488
      project.each_pair do |k, v|
489
        next if %i[has_external_issue_tracker path].include?(k)
490
        expect(json_response[k.to_s]).to eq(v)
A
Angus MacArthur 已提交
491 492
      end
    end
493

494
    it 'sets a project as public' do
495 496
      project = attributes_for(:project, visibility: 'public')

497
      post api("/projects/user/#{user.id}", admin), project
R
Robert Schilling 已提交
498 499

      expect(response).to have_http_status(201)
500
      expect(json_response['visibility']).to eq('public')
501 502
    end

503
    it 'sets a project as internal' do
504 505
      project = attributes_for(:project, visibility: 'internal')

506
      post api("/projects/user/#{user.id}", admin), project
R
Robert Schilling 已提交
507 508

      expect(response).to have_http_status(201)
509
      expect(json_response['visibility']).to eq('internal')
510 511
    end

512
    it 'sets a project as private' do
513 514
      project = attributes_for(:project, visibility: 'private')

515
      post api("/projects/user/#{user.id}", admin), project
516 517

      expect(json_response['visibility']).to eq('private')
518 519
    end

520
    it 'sets a project as allowing merge even if build fails' do
J
James Lopez 已提交
521
      project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: false })
522
      post api("/projects/user/#{user.id}", admin), project
J
James Lopez 已提交
523
      expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_falsey
524 525
    end

J
James Lopez 已提交
526 527
    it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
      project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: true })
528
      post api("/projects/user/#{user.id}", admin), project
J
James Lopez 已提交
529
      expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_truthy
530
    end
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546

    it 'sets a project as allowing merge even if discussions are unresolved' do
      project = attributes_for(:project, { only_allow_merge_if_all_discussions_are_resolved: false })

      post api("/projects/user/#{user.id}", admin), project

      expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_falsey
    end

    it 'sets a project as allowing merge only if all discussions are resolved' do
      project = attributes_for(:project, { only_allow_merge_if_all_discussions_are_resolved: true })

      post api("/projects/user/#{user.id}", admin), project

      expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_truthy
    end
A
Angus MacArthur 已提交
547 548
  end

D
Douwe Maan 已提交
549 550 551 552 553 554
  describe "POST /projects/:id/uploads" do
    before { project }

    it "uploads the file and returns its info" do
      post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")

Z
Z.J. van de Weg 已提交
555
      expect(response).to have_http_status(201)
D
Douwe Maan 已提交
556 557 558 559 560 561
      expect(json_response['alt']).to eq("dk")
      expect(json_response['url']).to start_with("/uploads/")
      expect(json_response['url']).to end_with("/dk.png")
    end
  end

562
  describe 'GET /projects/:id' do
563 564
    context 'when unauthenticated' do
      it 'returns the public projects' do
565
        public_project = create(:empty_project, :public)
566

567
        get api("/projects/#{public_project.id}")
568

569 570 571 572 573
        expect(response).to have_http_status(200)
        expect(json_response['id']).to eq(public_project.id)
        expect(json_response['description']).to eq(public_project.description)
        expect(json_response.keys).not_to include('permissions')
      end
574
    end
N
Nihad Abbasov 已提交
575

576 577 578 579 580
    context 'when authenticated' do
      before do
        project
        project_member
      end
581

582 583 584
      it 'returns a project by id' do
        group = create(:group)
        link = create(:project_group_link, project: project, group: group)
585

586
        get api("/projects/#{project.id}", user)
587

588 589 590 591 592 593
        expect(response).to have_http_status(200)
        expect(json_response['id']).to eq(project.id)
        expect(json_response['description']).to eq(project.description)
        expect(json_response['default_branch']).to eq(project.default_branch)
        expect(json_response['tag_list']).to be_an Array
        expect(json_response['archived']).to be_falsey
594
        expect(json_response['visibility']).to be_present
595 596 597 598 599 600 601 602 603 604
        expect(json_response['ssh_url_to_repo']).to be_present
        expect(json_response['http_url_to_repo']).to be_present
        expect(json_response['web_url']).to be_present
        expect(json_response['owner']).to be_a Hash
        expect(json_response['owner']).to be_a Hash
        expect(json_response['name']).to eq(project.name)
        expect(json_response['path']).to be_present
        expect(json_response['issues_enabled']).to be_present
        expect(json_response['merge_requests_enabled']).to be_present
        expect(json_response['wiki_enabled']).to be_present
T
Toon Claes 已提交
605
        expect(json_response['jobs_enabled']).to be_present
606 607 608 609 610 611 612 613 614 615
        expect(json_response['snippets_enabled']).to be_present
        expect(json_response['container_registry_enabled']).to be_present
        expect(json_response['created_at']).to be_present
        expect(json_response['last_activity_at']).to be_present
        expect(json_response['shared_runners_enabled']).to be_present
        expect(json_response['creator_id']).to be_present
        expect(json_response['namespace']).to be_present
        expect(json_response['avatar_url']).to be_nil
        expect(json_response['star_count']).to be_present
        expect(json_response['forks_count']).to be_present
T
Toon Claes 已提交
616
        expect(json_response['public_jobs']).to be_present
617 618 619 620 621
        expect(json_response['shared_with_groups']).to be_an Array
        expect(json_response['shared_with_groups'].length).to eq(1)
        expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
        expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
        expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
J
James Lopez 已提交
622
        expect(json_response['only_allow_merge_if_pipeline_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds)
623 624
        expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
      end
625

626 627 628 629 630
      it 'returns a project by path name' do
        get api("/projects/#{project.id}", user)
        expect(response).to have_http_status(200)
        expect(json_response['name']).to eq(project.name)
      end
631

632 633 634 635 636
      it 'returns a 404 error if not found' do
        get api('/projects/42', user)
        expect(response).to have_http_status(404)
        expect(json_response['message']).to eq('404 Project Not Found')
      end
637

638 639 640 641
      it 'returns a 404 error if user is not a member' do
        other_user = create(:user)
        get api("/projects/#{project.id}", other_user)
        expect(response).to have_http_status(404)
642 643
      end

644 645
      it 'handles users with dots' do
        dot_user = create(:user, username: 'dot.user')
646
        project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace)
647

648 649 650 651 652
        get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user)
        expect(response).to have_http_status(200)
        expect(json_response['name']).to eq(project.name)
      end

653 654 655 656 657 658 659 660 661
      it 'exposes namespace fields' do
        get api("/projects/#{project.id}", user)

        expect(response).to have_http_status(200)
        expect(json_response['namespace']).to eq({
          'id' => user.namespace.id,
          'name' => user.namespace.name,
          'path' => user.namespace.path,
          'kind' => user.namespace.kind,
662
          'full_path' => user.namespace.full_path,
663 664 665
        })
      end

666 667 668 669 670 671 672 673
      describe 'permissions' do
        context 'all projects' do
          before { project.team << [user, :master] }

          it 'contains permission information' do
            get api("/projects", user)

            expect(response).to have_http_status(200)
674 675
            expect(json_response.first['permissions']['project_access']['access_level']).
            to eq(Gitlab::Access::MASTER)
676 677 678 679 680 681 682 683 684 685
            expect(json_response.first['permissions']['group_access']).to be_nil
          end
        end

        context 'personal project' do
          it 'sets project access and returns 200' do
            project.team << [user, :master]
            get api("/projects/#{project.id}", user)

            expect(response).to have_http_status(200)
686 687
            expect(json_response['permissions']['project_access']['access_level']).
            to eq(Gitlab::Access::MASTER)
688 689
            expect(json_response['permissions']['group_access']).to be_nil
          end
690
        end
691

692
        context 'group project' do
693
          let(:project2) { create(:empty_project, group: create(:group)) }
694

695
          before { project2.group.add_owner(user) }
696

697 698
          it 'sets the owner and return 200' do
            get api("/projects/#{project2.id}", user)
699

700 701
            expect(response).to have_http_status(200)
            expect(json_response['permissions']['project_access']).to be_nil
702 703
            expect(json_response['permissions']['group_access']['access_level']).
            to eq(Gitlab::Access::OWNER)
704
          end
705
        end
706 707
      end
    end
N
Nihad Abbasov 已提交
708 709
  end

710
  describe 'GET /projects/:id/events' do
711 712 713 714
    shared_examples_for 'project events response' do
      it 'returns the project events' do
        member = create(:user)
        create(:project_member, :developer, user: member, project: project)
D
Dmitriy Zaporozhets 已提交
715 716 717
        note = create(:note_on_issue, note: 'What an awesome day!', project: project)
        EventCreateService.new.leave_note(note, note.author)

718
        get api("/projects/#{project.id}/events", current_user)
D
Dmitriy Zaporozhets 已提交
719

720
        expect(response).to have_http_status(200)
721 722
        expect(response).to include_pagination_headers
        expect(json_response).to be_an Array
D
Dmitriy Zaporozhets 已提交
723

724 725 726
        first_event = json_response.first
        expect(first_event['action_name']).to eq('commented on')
        expect(first_event['note']['body']).to eq('What an awesome day!')
D
Dmitriy Zaporozhets 已提交
727

728 729 730 731
        last_event = json_response.last

        expect(last_event['action_name']).to eq('joined')
        expect(last_event['project_id'].to_i).to eq(project.id)
732 733
        expect(last_event['author_username']).to eq(member.username)
        expect(last_event['author']['name']).to eq(member.name)
D
Dmitriy Zaporozhets 已提交
734
      end
D
Dmitriy Zaporozhets 已提交
735 736
    end

737 738
    context 'when unauthenticated' do
      it_behaves_like 'project events response' do
739
        let(:project) { create(:empty_project, :public) }
740 741 742
        let(:current_user) { nil }
      end
    end
743

744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
    context 'when authenticated' do
      context 'valid request' do
        it_behaves_like 'project events response' do
          let(:current_user) { user }
        end
      end

      it 'returns a 404 error if not found' do
        get api('/projects/42/events', user)

        expect(response).to have_http_status(404)
        expect(json_response['message']).to eq('404 Project Not Found')
      end

      it 'returns a 404 error if user is not a member' do
        other_user = create(:user)

        get api("/projects/#{project.id}/events", other_user)

        expect(response).to have_http_status(404)
      end
D
Dmitriy Zaporozhets 已提交
765
    end
766
  end
D
Dmitriy Zaporozhets 已提交
767

768 769 770 771 772
  describe 'GET /projects/:id/users' do
    shared_examples_for 'project users response' do
      it 'returns the project users' do
        member = create(:user)
        create(:project_member, :developer, user: member, project: project)
773

774
        get api("/projects/#{project.id}/users", current_user)
775

776
        expect(response).to have_http_status(200)
777
        expect(response).to include_pagination_headers
778 779 780 781 782 783 784 785 786 787 788 789
        expect(json_response).to be_an Array
        expect(json_response.size).to eq(1)

        first_user = json_response.first
        expect(first_user['username']).to eq(member.username)
        expect(first_user['name']).to eq(member.name)
        expect(first_user.keys).to contain_exactly(*%w[name username id state avatar_url web_url])
      end
    end

    context 'when unauthenticated' do
      it_behaves_like 'project users response' do
790
        let(:project) { create(:empty_project, :public) }
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
        let(:current_user) { nil }
      end
    end

    context 'when authenticated' do
      context 'valid request' do
        it_behaves_like 'project users response' do
          let(:current_user) { user }
        end
      end

      it 'returns a 404 error if not found' do
        get api('/projects/42/users', user)

        expect(response).to have_http_status(404)
        expect(json_response['message']).to eq('404 Project Not Found')
      end

      it 'returns a 404 error if user is not a member' do
        other_user = create(:user)

        get api("/projects/#{project.id}/users", other_user)

        expect(response).to have_http_status(404)
      end
D
Dmitriy Zaporozhets 已提交
816 817 818
    end
  end

819
  describe 'GET /projects/:id/snippets' do
D
Dmitriy Zaporozhets 已提交
820 821
    before { snippet }

822
    it 'returns an array of project snippets' do
823
      get api("/projects/#{project.id}/snippets", user)
824

Z
Z.J. van de Weg 已提交
825
      expect(response).to have_http_status(200)
826
      expect(response).to include_pagination_headers
827 828
      expect(json_response).to be_an Array
      expect(json_response.first['title']).to eq(snippet.title)
N
Nihad Abbasov 已提交
829 830 831
    end
  end

832
  describe 'GET /projects/:id/snippets/:snippet_id' do
833
    it 'returns a project snippet' do
834
      get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
Z
Z.J. van de Weg 已提交
835
      expect(response).to have_http_status(200)
836
      expect(json_response['title']).to eq(snippet.title)
N
Nihad Abbasov 已提交
837
    end
838

839
    it 'returns a 404 error if snippet id not found' do
840
      get api("/projects/#{project.id}/snippets/1234", user)
Z
Z.J. van de Weg 已提交
841
      expect(response).to have_http_status(404)
842
    end
N
Nihad Abbasov 已提交
843 844
  end

845
  describe 'POST /projects/:id/snippets' do
846
    it 'creates a new project snippet' do
847
      post api("/projects/#{project.id}/snippets", user),
848
        title: 'api test', file_name: 'sample.rb', code: 'test', visibility: 'private'
Z
Z.J. van de Weg 已提交
849
      expect(response).to have_http_status(201)
850
      expect(json_response['title']).to eq('api test')
N
Nihad Abbasov 已提交
851
    end
852

853
    it 'returns a 400 error if invalid snippet is given' do
854 855
      post api("/projects/#{project.id}/snippets", user)
      expect(status).to eq(400)
856
    end
N
Nihad Abbasov 已提交
857 858
  end

859
  describe 'PUT /projects/:id/snippets/:snippet_id' do
860
    it 'updates an existing project snippet' do
861
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
862
        code: 'updated code'
Z
Z.J. van de Weg 已提交
863
      expect(response).to have_http_status(200)
864 865
      expect(json_response['title']).to eq('example')
      expect(snippet.reload.content).to eq('updated code')
866
    end
867

868
    it 'updates an existing project snippet with new title' do
869
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
870
        title: 'other api test'
Z
Z.J. van de Weg 已提交
871
      expect(response).to have_http_status(200)
872
      expect(json_response['title']).to eq('other api test')
873
    end
874 875
  end

876
  describe 'DELETE /projects/:id/snippets/:snippet_id' do
D
Dmitriy Zaporozhets 已提交
877 878
    before { snippet }

879
    it 'deletes existing project snippet' do
880
      expect do
881
        delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
882 883

        expect(response).to have_http_status(204)
884
      end.to change { Snippet.count }.by(-1)
885 886
    end

887
    it 'returns 404 when deleting unknown snippet id' do
888
      delete api("/projects/#{project.id}/snippets/1234", user)
Z
Z.J. van de Weg 已提交
889
      expect(response).to have_http_status(404)
N
Nihad Abbasov 已提交
890 891
    end
  end
892

893
  describe 'GET /projects/:id/snippets/:snippet_id/raw' do
894
    it 'gets a raw project snippet' do
895
      get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
Z
Z.J. van de Weg 已提交
896
      expect(response).to have_http_status(200)
897
    end
898

899
    it 'returns a 404 error if raw project snippet not found' do
900
      get api("/projects/#{project.id}/snippets/5555/raw", user)
Z
Z.J. van de Weg 已提交
901
      expect(response).to have_http_status(404)
902
    end
903
  end
904

905
  describe :fork_admin do
906 907
    let(:project_fork_target) { create(:empty_project) }
    let(:project_fork_source) { create(:empty_project, :public) }
908

909
    describe 'POST /projects/:id/fork/:forked_from_id' do
910
      let(:new_project_fork_source) { create(:empty_project, :public) }
911

912
      it "is not available for non admin users" do
913
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
Z
Z.J. van de Weg 已提交
914
        expect(response).to have_http_status(403)
915 916
      end

917
      it 'allows project to be forked from an existing project' do
918
        expect(project_fork_target.forked?).not_to be_truthy
919
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
Z
Z.J. van de Weg 已提交
920
        expect(response).to have_http_status(201)
921
        project_fork_target.reload
922 923 924
        expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
        expect(project_fork_target.forked_project_link).not_to be_nil
        expect(project_fork_target.forked?).to be_truthy
925 926
      end

927
      it 'fails if forked_from project which does not exist' do
928
        post api("/projects/#{project_fork_target.id}/fork/9999", admin)
Z
Z.J. van de Weg 已提交
929
        expect(response).to have_http_status(404)
930 931
      end

932
      it 'fails with 409 if already forked' do
933 934
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
        project_fork_target.reload
935
        expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
936
        post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
Z
Z.J. van de Weg 已提交
937
        expect(response).to have_http_status(409)
938
        project_fork_target.reload
939 940
        expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
        expect(project_fork_target.forked?).to be_truthy
941 942 943
      end
    end

944
    describe 'DELETE /projects/:id/fork' do
945
      it "is not visible to users outside group" do
946
        delete api("/projects/#{project_fork_target.id}/fork", user)
Z
Z.J. van de Weg 已提交
947
        expect(response).to have_http_status(404)
948 949
      end

950
      context 'when users belong to project group' do
951
        let(:project_fork_target) { create(:empty_project, group: create(:group)) }
952

953 954 955 956 957
        before do
          project_fork_target.group.add_owner user
          project_fork_target.group.add_developer user2
        end

958
        it 'is forbidden to non-owner users' do
959
          delete api("/projects/#{project_fork_target.id}/fork", user2)
Z
Z.J. van de Weg 已提交
960
          expect(response).to have_http_status(403)
961 962
        end

963
        it 'makes forked project unforked' do
964 965 966 967
          post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
          project_fork_target.reload
          expect(project_fork_target.forked_from_project).not_to be_nil
          expect(project_fork_target.forked?).to be_truthy
968

969
          delete api("/projects/#{project_fork_target.id}/fork", admin)
970 971

          expect(response).to have_http_status(204)
972 973 974 975 976
          project_fork_target.reload
          expect(project_fork_target.forked_from_project).to be_nil
          expect(project_fork_target.forked?).not_to be_truthy
        end

977
        it 'is idempotent if not forked' do
978 979
          expect(project_fork_target.forked_from_project).to be_nil
          delete api("/projects/#{project_fork_target.id}/fork", admin)
R
Robert Schilling 已提交
980
          expect(response).to have_http_status(304)
981 982
          expect(project_fork_target.reload.forked_from_project).to be_nil
        end
983 984 985
      end
    end
  end
986

987 988 989
  describe "POST /projects/:id/share" do
    let(:group) { create(:group) }

990
    it "shares project with group" do
991 992
      expires_at = 10.days.from_now.to_date

993
      expect do
994
        post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER, expires_at: expires_at
995 996
      end.to change { ProjectGroupLink.count }.by(1)

R
Robert Schilling 已提交
997
      expect(response).to have_http_status(201)
998 999 1000
      expect(json_response['group_id']).to eq(group.id)
      expect(json_response['group_access']).to eq(Gitlab::Access::DEVELOPER)
      expect(json_response['expires_at']).to eq(expires_at.to_s)
1001 1002
    end

1003
    it "returns a 400 error when group id is not given" do
1004
      post api("/projects/#{project.id}/share", user), group_access: Gitlab::Access::DEVELOPER
R
Robert Schilling 已提交
1005
      expect(response).to have_http_status(400)
1006 1007
    end

1008
    it "returns a 400 error when access level is not given" do
1009
      post api("/projects/#{project.id}/share", user), group_id: group.id
R
Robert Schilling 已提交
1010
      expect(response).to have_http_status(400)
1011 1012
    end

1013
    it "returns a 400 error when sharing is disabled" do
1014 1015
      project.namespace.update(share_with_group_lock: true)
      post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER
R
Robert Schilling 已提交
1016
      expect(response).to have_http_status(400)
1017 1018
    end

1019 1020 1021 1022 1023
    it 'returns a 404 error when user cannot read group' do
      private_group = create(:group, :private)

      post api("/projects/#{project.id}/share", user), group_id: private_group.id, group_access: Gitlab::Access::DEVELOPER

R
Robert Schilling 已提交
1024
      expect(response).to have_http_status(404)
1025 1026 1027 1028 1029
    end

    it 'returns a 404 error when group does not exist' do
      post api("/projects/#{project.id}/share", user), group_id: 1234, group_access: Gitlab::Access::DEVELOPER

R
Robert Schilling 已提交
1030
      expect(response).to have_http_status(404)
1031 1032
    end

R
Robert Schilling 已提交
1033
    it "returns a 400 error when wrong params passed" do
1034
      post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: 1234
R
Robert Schilling 已提交
1035 1036 1037

      expect(response).to have_http_status(400)
      expect(json_response['error']).to eq 'group_access does not have a valid value'
1038 1039 1040
    end
  end

1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
  describe 'DELETE /projects/:id/share/:group_id' do
    it 'returns 204 when deleting a group share' do
      group = create(:group, :public)
      create(:project_group_link, group: group, project: project)

      delete api("/projects/#{project.id}/share/#{group.id}", user)

      expect(response).to have_http_status(204)
      expect(project.project_group_links).to be_empty
    end

    it 'returns a 400 when group id is not an integer' do
      delete api("/projects/#{project.id}/share/foo", user)

      expect(response).to have_http_status(400)
    end

    it 'returns a 404 error when group link does not exist' do
      delete api("/projects/#{project.id}/share/1234", user)

      expect(response).to have_http_status(404)
    end

    it 'returns a 404 error when project does not exist' do
      delete api("/projects/123/share/1234", user)

      expect(response).to have_http_status(404)
    end
  end

1071
  describe 'PUT /projects/:id' do
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
    before { project }
    before { user }
    before { user3 }
    before { user4 }
    before { project3 }
    before { project4 }
    before { project_member3 }
    before { project_member2 }

    context 'when unauthenticated' do
1082
      it 'returns authentication error' do
1083 1084
        project_param = { name: 'bar' }
        put api("/projects/#{project.id}"), project_param
Z
Z.J. van de Weg 已提交
1085
        expect(response).to have_http_status(401)
1086 1087 1088 1089
      end
    end

    context 'when authenticated as project owner' do
1090
      it 'updates name' do
1091 1092
        project_param = { name: 'bar' }
        put api("/projects/#{project.id}", user), project_param
Z
Z.J. van de Weg 已提交
1093
        expect(response).to have_http_status(200)
1094
        project_param.each_pair do |k, v|
1095
          expect(json_response[k.to_s]).to eq(v)
1096 1097 1098
        end
      end

1099
      it 'updates visibility_level' do
1100
        project_param = { visibility: 'public' }
1101
        put api("/projects/#{project3.id}", user), project_param
Z
Z.J. van de Weg 已提交
1102
        expect(response).to have_http_status(200)
1103
        project_param.each_pair do |k, v|
1104
          expect(json_response[k.to_s]).to eq(v)
1105 1106 1107
        end
      end

1108
      it 'updates visibility_level from public to private' do
1109
        project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
1110
        project_param = { visibility: 'private' }
1111
        put api("/projects/#{project3.id}", user), project_param
Z
Z.J. van de Weg 已提交
1112
        expect(response).to have_http_status(200)
1113 1114 1115
        project_param.each_pair do |k, v|
          expect(json_response[k.to_s]).to eq(v)
        end
1116
        expect(json_response['visibility']).to eq('private')
1117 1118
      end

1119
      it 'does not update name to existing name' do
1120 1121
        project_param = { name: project3.name }
        put api("/projects/#{project.id}", user), project_param
Z
Z.J. van de Weg 已提交
1122
        expect(response).to have_http_status(400)
1123
        expect(json_response['message']['name']).to eq(['has already been taken'])
1124 1125
      end

1126 1127 1128 1129 1130 1131 1132 1133 1134
      it 'updates request_access_enabled' do
        project_param = { request_access_enabled: false }

        put api("/projects/#{project.id}", user), project_param

        expect(response).to have_http_status(200)
        expect(json_response['request_access_enabled']).to eq(false)
      end

1135
      it 'updates path & name to existing path & name in different namespace' do
1136 1137
        project_param = { path: project4.path, name: project4.name }
        put api("/projects/#{project3.id}", user), project_param
Z
Z.J. van de Weg 已提交
1138
        expect(response).to have_http_status(200)
1139
        project_param.each_pair do |k, v|
1140
          expect(json_response[k.to_s]).to eq(v)
1141 1142 1143 1144 1145
        end
      end
    end

    context 'when authenticated as project master' do
1146
      it 'updates path' do
1147 1148
        project_param = { path: 'bar' }
        put api("/projects/#{project3.id}", user4), project_param
Z
Z.J. van de Weg 已提交
1149
        expect(response).to have_http_status(200)
1150
        project_param.each_pair do |k, v|
1151
          expect(json_response[k.to_s]).to eq(v)
1152 1153 1154
        end
      end

1155
      it 'updates other attributes' do
1156 1157 1158 1159 1160 1161 1162
        project_param = { issues_enabled: true,
                          wiki_enabled: true,
                          snippets_enabled: true,
                          merge_requests_enabled: true,
                          description: 'new description' }

        put api("/projects/#{project3.id}", user4), project_param
Z
Z.J. van de Weg 已提交
1163
        expect(response).to have_http_status(200)
1164
        project_param.each_pair do |k, v|
1165
          expect(json_response[k.to_s]).to eq(v)
1166 1167 1168
        end
      end

1169
      it 'does not update path to existing path' do
1170 1171
        project_param = { path: project.path }
        put api("/projects/#{project3.id}", user4), project_param
Z
Z.J. van de Weg 已提交
1172
        expect(response).to have_http_status(400)
1173
        expect(json_response['message']['path']).to eq(['has already been taken'])
1174 1175
      end

1176
      it 'does not update name' do
1177 1178
        project_param = { name: 'bar' }
        put api("/projects/#{project3.id}", user4), project_param
Z
Z.J. van de Weg 已提交
1179
        expect(response).to have_http_status(403)
1180 1181
      end

1182
      it 'does not update visibility_level' do
1183
        project_param = { visibility: 'public' }
1184
        put api("/projects/#{project3.id}", user4), project_param
Z
Z.J. van de Weg 已提交
1185
        expect(response).to have_http_status(403)
1186 1187 1188 1189
      end
    end

    context 'when authenticated as project developer' do
1190
      it 'does not update other attributes' do
1191 1192 1193 1194 1195
        project_param = { path: 'bar',
                          issues_enabled: true,
                          wiki_enabled: true,
                          snippets_enabled: true,
                          merge_requests_enabled: true,
1196 1197
                          description: 'new description',
                          request_access_enabled: true }
1198
        put api("/projects/#{project.id}", user3), project_param
Z
Z.J. van de Weg 已提交
1199
        expect(response).to have_http_status(403)
1200 1201 1202 1203
      end
    end
  end

1204
  describe 'POST /projects/:id/archive' do
1205 1206
    context 'on an unarchived project' do
      it 'archives the project' do
1207
        post api("/projects/#{project.id}/archive", user)
1208

Z
Z.J. van de Weg 已提交
1209
        expect(response).to have_http_status(201)
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
        expect(json_response['archived']).to be_truthy
      end
    end

    context 'on an archived project' do
      before do
        project.archive!
      end

      it 'remains archived' do
1220
        post api("/projects/#{project.id}/archive", user)
1221

Z
Z.J. van de Weg 已提交
1222
        expect(response).to have_http_status(201)
1223 1224
        expect(json_response['archived']).to be_truthy
      end
1225
    end
1226

1227 1228 1229 1230
    context 'user without archiving rights to the project' do
      before do
        project.team << [user3, :developer]
      end
1231

1232 1233 1234
      it 'rejects the action' do
        post api("/projects/#{project.id}/archive", user3)

Z
Z.J. van de Weg 已提交
1235
        expect(response).to have_http_status(403)
1236 1237 1238 1239
      end
    end
  end

1240
  describe 'POST /projects/:id/unarchive' do
1241 1242
    context 'on an unarchived project' do
      it 'remains unarchived' do
1243
        post api("/projects/#{project.id}/unarchive", user)
1244

Z
Z.J. van de Weg 已提交
1245
        expect(response).to have_http_status(201)
1246 1247 1248 1249 1250 1251 1252 1253 1254
        expect(json_response['archived']).to be_falsey
      end
    end

    context 'on an archived project' do
      before do
        project.archive!
      end

1255 1256
      it 'unarchives the project' do
        post api("/projects/#{project.id}/unarchive", user)
1257

Z
Z.J. van de Weg 已提交
1258
        expect(response).to have_http_status(201)
1259 1260
        expect(json_response['archived']).to be_falsey
      end
1261
    end
1262

1263 1264 1265 1266
    context 'user without archiving rights to the project' do
      before do
        project.team << [user3, :developer]
      end
1267

1268 1269 1270
      it 'rejects the action' do
        post api("/projects/#{project.id}/unarchive", user3)

Z
Z.J. van de Weg 已提交
1271
        expect(response).to have_http_status(403)
1272 1273 1274 1275
      end
    end
  end

1276 1277 1278
  describe 'POST /projects/:id/star' do
    context 'on an unstarred project' do
      it 'stars the project' do
1279
        expect { post api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1)
1280

Z
Z.J. van de Weg 已提交
1281
        expect(response).to have_http_status(201)
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292
        expect(json_response['star_count']).to eq(1)
      end
    end

    context 'on a starred project' do
      before do
        user.toggle_star(project)
        project.reload
      end

      it 'does not modify the star count' do
1293
        expect { post api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
1294

Z
Z.J. van de Weg 已提交
1295
        expect(response).to have_http_status(304)
1296 1297 1298 1299
      end
    end
  end

1300
  describe 'POST /projects/:id/unstar' do
1301 1302 1303 1304 1305 1306 1307
    context 'on a starred project' do
      before do
        user.toggle_star(project)
        project.reload
      end

      it 'unstars the project' do
1308
        expect { post api("/projects/#{project.id}/unstar", user) }.to change { project.reload.star_count }.by(-1)
1309

1310
        expect(response).to have_http_status(201)
1311 1312 1313 1314 1315 1316
        expect(json_response['star_count']).to eq(0)
      end
    end

    context 'on an unstarred project' do
      it 'does not modify the star count' do
1317
        expect { post api("/projects/#{project.id}/unstar", user) }.not_to change { project.reload.star_count }
1318

Z
Z.J. van de Weg 已提交
1319
        expect(response).to have_http_status(304)
1320 1321 1322 1323
      end
    end
  end

1324 1325
  describe 'DELETE /projects/:id' do
    context 'when authenticated as user' do
1326
      it 'removes project' do
1327
        delete api("/projects/#{project.id}", user)
1328 1329 1330

        expect(response).to have_http_status(202)
        expect(json_response['message']).to eql('202 Accepted')
1331 1332
      end

1333
      it 'does not remove a project if not an owner' do
1334 1335 1336
        user3 = create(:user)
        project.team << [user3, :developer]
        delete api("/projects/#{project.id}", user3)
Z
Z.J. van de Weg 已提交
1337
        expect(response).to have_http_status(403)
1338 1339
      end

1340
      it 'does not remove a non existing project' do
1341
        delete api('/projects/1328', user)
Z
Z.J. van de Weg 已提交
1342
        expect(response).to have_http_status(404)
1343 1344
      end

1345
      it 'does not remove a project not attached to user' do
1346
        delete api("/projects/#{project.id}", user2)
Z
Z.J. van de Weg 已提交
1347
        expect(response).to have_http_status(404)
1348 1349 1350
      end
    end

1351
    context 'when authenticated as admin' do
1352
      it 'removes any existing project' do
1353
        delete api("/projects/#{project.id}", admin)
1354 1355 1356

        expect(response).to have_http_status(202)
        expect(json_response['message']).to eql('202 Accepted')
1357 1358
      end

1359
      it 'does not remove a non existing project' do
1360
        delete api('/projects/1328', admin)
Z
Z.J. van de Weg 已提交
1361
        expect(response).to have_http_status(404)
1362 1363 1364
      end
    end
  end
1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490

  describe 'POST /projects/:id/fork' do
    let(:project) do
      create(:project, :repository, creator: user, namespace: user.namespace)
    end
    let(:group) { create(:group) }
    let(:group2) do
      group = create(:group, name: 'group2_name')
      group.add_owner(user2)
      group
    end

    before do
      project.add_reporter(user2)
    end

    context 'when authenticated' do
      it 'forks if user has sufficient access to project' do
        post api("/projects/#{project.id}/fork", user2)

        expect(response).to have_http_status(201)
        expect(json_response['name']).to eq(project.name)
        expect(json_response['path']).to eq(project.path)
        expect(json_response['owner']['id']).to eq(user2.id)
        expect(json_response['namespace']['id']).to eq(user2.namespace.id)
        expect(json_response['forked_from_project']['id']).to eq(project.id)
      end

      it 'forks if user is admin' do
        post api("/projects/#{project.id}/fork", admin)

        expect(response).to have_http_status(201)
        expect(json_response['name']).to eq(project.name)
        expect(json_response['path']).to eq(project.path)
        expect(json_response['owner']['id']).to eq(admin.id)
        expect(json_response['namespace']['id']).to eq(admin.namespace.id)
        expect(json_response['forked_from_project']['id']).to eq(project.id)
      end

      it 'fails on missing project access for the project to fork' do
        new_user = create(:user)
        post api("/projects/#{project.id}/fork", new_user)

        expect(response).to have_http_status(404)
        expect(json_response['message']).to eq('404 Project Not Found')
      end

      it 'fails if forked project exists in the user namespace' do
        post api("/projects/#{project.id}/fork", user)

        expect(response).to have_http_status(409)
        expect(json_response['message']['name']).to eq(['has already been taken'])
        expect(json_response['message']['path']).to eq(['has already been taken'])
      end

      it 'fails if project to fork from does not exist' do
        post api('/projects/424242/fork', user)

        expect(response).to have_http_status(404)
        expect(json_response['message']).to eq('404 Project Not Found')
      end

      it 'forks with explicit own user namespace id' do
        post api("/projects/#{project.id}/fork", user2), namespace: user2.namespace.id

        expect(response).to have_http_status(201)
        expect(json_response['owner']['id']).to eq(user2.id)
      end

      it 'forks with explicit own user name as namespace' do
        post api("/projects/#{project.id}/fork", user2), namespace: user2.username

        expect(response).to have_http_status(201)
        expect(json_response['owner']['id']).to eq(user2.id)
      end

      it 'forks to another user when admin' do
        post api("/projects/#{project.id}/fork", admin), namespace: user2.username

        expect(response).to have_http_status(201)
        expect(json_response['owner']['id']).to eq(user2.id)
      end

      it 'fails if trying to fork to another user when not admin' do
        post api("/projects/#{project.id}/fork", user2), namespace: admin.namespace.id

        expect(response).to have_http_status(404)
      end

      it 'fails if trying to fork to non-existent namespace' do
        post api("/projects/#{project.id}/fork", user2), namespace: 42424242

        expect(response).to have_http_status(404)
        expect(json_response['message']).to eq('404 Target Namespace Not Found')
      end

      it 'forks to owned group' do
        post api("/projects/#{project.id}/fork", user2), namespace: group2.name

        expect(response).to have_http_status(201)
        expect(json_response['namespace']['name']).to eq(group2.name)
      end

      it 'fails to fork to not owned group' do
        post api("/projects/#{project.id}/fork", user2), namespace: group.name

        expect(response).to have_http_status(404)
      end

      it 'forks to not owned group when admin' do
        post api("/projects/#{project.id}/fork", admin), namespace: group.name

        expect(response).to have_http_status(201)
        expect(json_response['namespace']['name']).to eq(group.name)
      end
    end

    context 'when unauthenticated' do
      it 'returns authentication error' do
        post api("/projects/#{project.id}/fork")

        expect(response).to have_http_status(401)
        expect(json_response['message']).to eq('401 Unauthorized')
      end
    end
  end
1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539

  describe 'POST /projects/:id/housekeeping' do
    let(:housekeeping) { Projects::HousekeepingService.new(project) }

    before do
      allow(Projects::HousekeepingService).to receive(:new).with(project).and_return(housekeeping)
    end

    context 'when authenticated as owner' do
      it 'starts the housekeeping process' do
        expect(housekeeping).to receive(:execute).once

        post api("/projects/#{project.id}/housekeeping", user)

        expect(response).to have_http_status(201)
      end

      context 'when housekeeping lease is taken' do
        it 'returns conflict' do
          expect(housekeeping).to receive(:execute).once.and_raise(Projects::HousekeepingService::LeaseTaken)

          post api("/projects/#{project.id}/housekeeping", user)

          expect(response).to have_http_status(409)
          expect(json_response['message']).to match(/Somebody already triggered housekeeping for this project/)
        end
      end
    end

    context 'when authenticated as developer' do
      before do
        project_member2
      end

      it 'returns forbidden error' do
        post api("/projects/#{project.id}/housekeeping", user3)

        expect(response).to have_http_status(403)
      end
    end

    context 'when unauthenticated' do
      it 'returns authentication error' do
        post api("/projects/#{project.id}/housekeeping")

        expect(response).to have_http_status(401)
      end
    end
  end
N
Nihad Abbasov 已提交
1540
end