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

4
describe API::Projects do
5
  include Gitlab::CurrentSettings
6

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
  let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
15
  let(:user4) { create(:user) }
16 17
  let(:project3) do
    create(:project,
18
    :private,
19
    :repository,
20 21 22 23 24 25
    name: 'second_project',
    path: 'second_project',
    creator_id: user.id,
    namespace: user.namespace,
    merge_requests_enabled: false,
    issues_enabled: false, wiki_enabled: false,
W
winniehell 已提交
26
    builds_enabled: false,
27
    snippets_enabled: false)
28
  end
29
  let(:project_member2) do
30 31 32 33 34 35
    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
        before do
212
          project_member
T
Toon Claes 已提交
213 214
          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',
344
        issues_enabled: false,
W
winniehell 已提交
345
        jobs_enabled: false,
346
        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

W
winniehell 已提交
355 356
      expect(response).to have_http_status(201)

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

      # 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)
367
    end
368

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

372
      post api('/projects', user), project
373 374

      expect(json_response['visibility']).to eq('public')
375 376
    end

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

380
      post api('/projects', user), project
381 382

      expect(json_response['visibility']).to eq('internal')
383 384
    end

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

388
      post api('/projects', user), project
389 390

      expect(json_response['visibility']).to eq('private')
391 392
    end

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

J
James Lopez 已提交
399 400
    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 })
401
      post api('/projects', user), project
J
James Lopez 已提交
402
      expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_truthy
403 404
    end

405 406 407 408 409 410 411 412
    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

413 414 415 416 417 418 419 420
    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

421 422 423 424 425 426 427 428
    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

429 430 431 432 433 434 435 436
    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

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

440
      before do
441
        stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
442 443
      end

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

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

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

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

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

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

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

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

478
    it 'assigns attributes to project' do
A
Angus MacArthur 已提交
479
      project = attributes_for(:project, {
480 481
        issues_enabled: false,
        merge_requests_enabled: false,
482 483
        wiki_enabled: false,
        request_access_enabled: true
A
Angus MacArthur 已提交
484 485 486 487
      })

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

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

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

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

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

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

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

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

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

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

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

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

J
James Lopez 已提交
527 528
    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 })
529
      post api("/projects/user/#{user.id}", admin), project
J
James Lopez 已提交
530
      expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_truthy
531
    end
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547

    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 已提交
548 549
  end

D
Douwe Maan 已提交
550 551 552 553 554 555
  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 已提交
556
      expect(response).to have_http_status(201)
D
Douwe Maan 已提交
557 558 559 560 561 562
      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

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

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

570 571 572 573 574
        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
575
    end
N
Nihad Abbasov 已提交
576

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

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

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

589 590 591 592 593 594
        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
595
        expect(json_response['visibility']).to be_present
596 597 598 599 600 601 602 603 604 605
        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 已提交
606
        expect(json_response['jobs_enabled']).to be_present
607 608 609 610 611 612 613 614 615 616
        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 已提交
617
        expect(json_response['public_jobs']).to be_present
618 619 620 621 622
        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 已提交
623
        expect(json_response['only_allow_merge_if_pipeline_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds)
624 625
        expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
      end
626

627 628 629 630 631
      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
632

633 634 635 636 637
      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
638

639 640 641 642
      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)
643 644
      end

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

649 650 651 652 653
        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

654 655 656 657 658 659 660 661 662
      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,
663
          'full_path' => user.namespace.full_path
664 665 666
        })
      end

667 668 669 670 671 672 673 674 675 676 677 678 679 680
      it "does not include statistics by default" do
        get api("/projects/#{project.id}", user)

        expect(response).to have_http_status(200)
        expect(json_response).not_to include 'statistics'
      end

      it "includes statistics if requested" do
        get api("/projects/#{project.id}", user), statistics: true

        expect(response).to have_http_status(200)
        expect(json_response).to include 'statistics'
      end

681 682 683 684 685 686 687 688
      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)
689 690
            expect(json_response.first['permissions']['project_access']['access_level']).
            to eq(Gitlab::Access::MASTER)
691 692 693 694 695 696 697 698 699 700
            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)
701 702
            expect(json_response['permissions']['project_access']['access_level']).
            to eq(Gitlab::Access::MASTER)
703 704
            expect(json_response['permissions']['group_access']).to be_nil
          end
705
        end
706

707
        context 'group project' do
708
          let(:project2) { create(:empty_project, group: create(:group)) }
709

710
          before { project2.group.add_owner(user) }
711

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

715 716
            expect(response).to have_http_status(200)
            expect(json_response['permissions']['project_access']).to be_nil
717 718
            expect(json_response['permissions']['group_access']['access_level']).
            to eq(Gitlab::Access::OWNER)
719
          end
720
        end
721 722
      end
    end
N
Nihad Abbasov 已提交
723 724
  end

725
  describe 'GET /projects/:id/events' do
726 727 728 729
    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 已提交
730 731 732
        note = create(:note_on_issue, note: 'What an awesome day!', project: project)
        EventCreateService.new.leave_note(note, note.author)

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

735
        expect(response).to have_http_status(200)
736 737
        expect(response).to include_pagination_headers
        expect(json_response).to be_an Array
D
Dmitriy Zaporozhets 已提交
738

739 740 741
        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 已提交
742

743 744 745 746
        last_event = json_response.last

        expect(last_event['action_name']).to eq('joined')
        expect(last_event['project_id'].to_i).to eq(project.id)
747 748
        expect(last_event['author_username']).to eq(member.username)
        expect(last_event['author']['name']).to eq(member.name)
D
Dmitriy Zaporozhets 已提交
749
      end
D
Dmitriy Zaporozhets 已提交
750 751
    end

752 753
    context 'when unauthenticated' do
      it_behaves_like 'project events response' do
754
        let(:project) { create(:empty_project, :public) }
755 756 757
        let(:current_user) { nil }
      end
    end
758

759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
    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 已提交
780
    end
781
  end
D
Dmitriy Zaporozhets 已提交
782

783 784 785 786
  describe 'GET /projects/:id/users' do
    shared_examples_for 'project users response' do
      it 'returns the project users' do
        get api("/projects/#{project.id}/users", current_user)
787

788 789
        user = project.namespace.owner

790
        expect(response).to have_http_status(200)
791
        expect(response).to include_pagination_headers
792 793 794 795
        expect(json_response).to be_an Array
        expect(json_response.size).to eq(1)

        first_user = json_response.first
796 797
        expect(first_user['username']).to eq(user.username)
        expect(first_user['name']).to eq(user.name)
798 799 800 801 802 803
        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
804
        let(:project) { create(:empty_project, :public) }
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
        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 已提交
830 831 832
    end
  end

833
  describe 'GET /projects/:id/snippets' do
D
Dmitriy Zaporozhets 已提交
834 835
    before { snippet }

836
    it 'returns an array of project snippets' do
837
      get api("/projects/#{project.id}/snippets", user)
838

Z
Z.J. van de Weg 已提交
839
      expect(response).to have_http_status(200)
840
      expect(response).to include_pagination_headers
841 842
      expect(json_response).to be_an Array
      expect(json_response.first['title']).to eq(snippet.title)
N
Nihad Abbasov 已提交
843 844 845
    end
  end

846
  describe 'GET /projects/:id/snippets/:snippet_id' do
847
    it 'returns a project snippet' do
848
      get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
Z
Z.J. van de Weg 已提交
849
      expect(response).to have_http_status(200)
850
      expect(json_response['title']).to eq(snippet.title)
N
Nihad Abbasov 已提交
851
    end
852

853
    it 'returns a 404 error if snippet id not found' do
854
      get api("/projects/#{project.id}/snippets/1234", user)
Z
Z.J. van de Weg 已提交
855
      expect(response).to have_http_status(404)
856
    end
N
Nihad Abbasov 已提交
857 858
  end

859
  describe 'POST /projects/:id/snippets' do
860
    it 'creates a new project snippet' do
861
      post api("/projects/#{project.id}/snippets", user),
862
        title: 'api test', file_name: 'sample.rb', code: 'test', visibility: 'private'
Z
Z.J. van de Weg 已提交
863
      expect(response).to have_http_status(201)
864
      expect(json_response['title']).to eq('api test')
N
Nihad Abbasov 已提交
865
    end
866

867
    it 'returns a 400 error if invalid snippet is given' do
868 869
      post api("/projects/#{project.id}/snippets", user)
      expect(status).to eq(400)
870
    end
N
Nihad Abbasov 已提交
871 872
  end

873
  describe 'PUT /projects/:id/snippets/:snippet_id' do
874
    it 'updates an existing project snippet' do
875
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
876
        code: 'updated code'
Z
Z.J. van de Weg 已提交
877
      expect(response).to have_http_status(200)
878 879
      expect(json_response['title']).to eq('example')
      expect(snippet.reload.content).to eq('updated code')
880
    end
881

882
    it 'updates an existing project snippet with new title' do
883
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
884
        title: 'other api test'
Z
Z.J. van de Weg 已提交
885
      expect(response).to have_http_status(200)
886
      expect(json_response['title']).to eq('other api test')
887
    end
888 889
  end

890
  describe 'DELETE /projects/:id/snippets/:snippet_id' do
D
Dmitriy Zaporozhets 已提交
891 892
    before { snippet }

893
    it 'deletes existing project snippet' do
894
      expect do
895
        delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
896 897

        expect(response).to have_http_status(204)
898
      end.to change { Snippet.count }.by(-1)
899 900
    end

901
    it 'returns 404 when deleting unknown snippet id' do
902
      delete api("/projects/#{project.id}/snippets/1234", user)
Z
Z.J. van de Weg 已提交
903
      expect(response).to have_http_status(404)
N
Nihad Abbasov 已提交
904 905
    end
  end
906

907
  describe 'GET /projects/:id/snippets/:snippet_id/raw' do
908
    it 'gets a raw project snippet' do
909
      get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
Z
Z.J. van de Weg 已提交
910
      expect(response).to have_http_status(200)
911
    end
912

913
    it 'returns a 404 error if raw project snippet not found' do
914
      get api("/projects/#{project.id}/snippets/5555/raw", user)
Z
Z.J. van de Weg 已提交
915
      expect(response).to have_http_status(404)
916
    end
917
  end
918

919
  describe 'fork management' do
920 921
    let(:project_fork_target) { create(:empty_project) }
    let(:project_fork_source) { create(:empty_project, :public) }
922

923
    describe 'POST /projects/:id/fork/:forked_from_id' do
924
      let(:new_project_fork_source) { create(:empty_project, :public) }
925

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

931
      it 'allows project to be forked from an existing project' do
932
        expect(project_fork_target.forked?).not_to be_truthy
933
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
Z
Z.J. van de Weg 已提交
934
        expect(response).to have_http_status(201)
935
        project_fork_target.reload
936 937 938
        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
939 940
      end

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

946
      it 'fails with 409 if already forked' do
947 948
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
        project_fork_target.reload
949
        expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
950
        post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
Z
Z.J. van de Weg 已提交
951
        expect(response).to have_http_status(409)
952
        project_fork_target.reload
953 954
        expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
        expect(project_fork_target.forked?).to be_truthy
955 956 957
      end
    end

958
    describe 'DELETE /projects/:id/fork' do
959
      it "is not visible to users outside group" do
960
        delete api("/projects/#{project_fork_target.id}/fork", user)
Z
Z.J. van de Weg 已提交
961
        expect(response).to have_http_status(404)
962 963
      end

964
      context 'when users belong to project group' do
965
        let(:project_fork_target) { create(:empty_project, group: create(:group)) }
966

967 968 969 970 971
        before do
          project_fork_target.group.add_owner user
          project_fork_target.group.add_developer user2
        end

972
        it 'is forbidden to non-owner users' do
973
          delete api("/projects/#{project_fork_target.id}/fork", user2)
Z
Z.J. van de Weg 已提交
974
          expect(response).to have_http_status(403)
975 976
        end

977
        it 'makes forked project unforked' do
978 979 980 981
          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
982

983
          delete api("/projects/#{project_fork_target.id}/fork", admin)
984 985

          expect(response).to have_http_status(204)
986 987 988 989 990
          project_fork_target.reload
          expect(project_fork_target.forked_from_project).to be_nil
          expect(project_fork_target.forked?).not_to be_truthy
        end

991
        it 'is idempotent if not forked' do
992 993
          expect(project_fork_target.forked_from_project).to be_nil
          delete api("/projects/#{project_fork_target.id}/fork", admin)
R
Robert Schilling 已提交
994
          expect(response).to have_http_status(304)
995 996
          expect(project_fork_target.reload.forked_from_project).to be_nil
        end
997 998 999
      end
    end
  end
1000

1001 1002 1003
  describe "POST /projects/:id/share" do
    let(:group) { create(:group) }

1004
    it "shares project with group" do
1005 1006
      expires_at = 10.days.from_now.to_date

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

R
Robert Schilling 已提交
1011
      expect(response).to have_http_status(201)
1012 1013 1014
      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)
1015 1016
    end

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

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

1027
    it "returns a 400 error when sharing is disabled" do
1028 1029
      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 已提交
1030
      expect(response).to have_http_status(400)
1031 1032
    end

1033 1034 1035 1036 1037
    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 已提交
1038
      expect(response).to have_http_status(404)
1039 1040 1041 1042 1043
    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 已提交
1044
      expect(response).to have_http_status(404)
1045 1046
    end

R
Robert Schilling 已提交
1047
    it "returns a 400 error when wrong params passed" do
1048
      post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: 1234
R
Robert Schilling 已提交
1049 1050 1051

      expect(response).to have_http_status(400)
      expect(json_response['error']).to eq 'group_access does not have a valid value'
1052 1053 1054
    end
  end

1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
  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

1085
  describe 'PUT /projects/:id' do
1086 1087 1088 1089 1090 1091 1092
    before { project }
    before { user }
    before { user3 }
    before { user4 }
    before { project3 }
    before { project4 }
    before { project_member2 }
1093
    before { project_member }
1094

1095 1096
    it 'returns 400 when nothing sent' do
      project_param = {}
W
winniehell 已提交
1097

1098
      put api("/projects/#{project.id}", user), project_param
W
winniehell 已提交
1099

1100 1101 1102 1103
      expect(response).to have_http_status(400)
      expect(json_response['error']).to match('at least one parameter must be provided')
    end

1104
    context 'when unauthenticated' do
1105
      it 'returns authentication error' do
1106
        project_param = { name: 'bar' }
W
winniehell 已提交
1107

1108
        put api("/projects/#{project.id}"), project_param
W
winniehell 已提交
1109

Z
Z.J. van de Weg 已提交
1110
        expect(response).to have_http_status(401)
1111 1112 1113 1114
      end
    end

    context 'when authenticated as project owner' do
1115
      it 'updates name' do
1116
        project_param = { name: 'bar' }
W
winniehell 已提交
1117

1118
        put api("/projects/#{project.id}", user), project_param
W
winniehell 已提交
1119

Z
Z.J. van de Weg 已提交
1120
        expect(response).to have_http_status(200)
W
winniehell 已提交
1121

1122
        project_param.each_pair do |k, v|
1123
          expect(json_response[k.to_s]).to eq(v)
1124 1125 1126
        end
      end

1127
      it 'updates visibility_level' do
1128
        project_param = { visibility: 'public' }
W
winniehell 已提交
1129

1130
        put api("/projects/#{project3.id}", user), project_param
W
winniehell 已提交
1131

Z
Z.J. van de Weg 已提交
1132
        expect(response).to have_http_status(200)
W
winniehell 已提交
1133

1134
        project_param.each_pair do |k, v|
1135
          expect(json_response[k.to_s]).to eq(v)
1136 1137 1138
        end
      end

1139
      it 'updates visibility_level from public to private' do
1140
        project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
1141
        project_param = { visibility: 'private' }
W
winniehell 已提交
1142

1143
        put api("/projects/#{project3.id}", user), project_param
W
winniehell 已提交
1144

Z
Z.J. van de Weg 已提交
1145
        expect(response).to have_http_status(200)
W
winniehell 已提交
1146

1147 1148 1149
        project_param.each_pair do |k, v|
          expect(json_response[k.to_s]).to eq(v)
        end
W
winniehell 已提交
1150

1151
        expect(json_response['visibility']).to eq('private')
1152 1153
      end

1154
      it 'does not update name to existing name' do
1155
        project_param = { name: project3.name }
W
winniehell 已提交
1156

1157
        put api("/projects/#{project.id}", user), project_param
W
winniehell 已提交
1158

Z
Z.J. van de Weg 已提交
1159
        expect(response).to have_http_status(400)
1160
        expect(json_response['message']['name']).to eq(['has already been taken'])
1161 1162
      end

1163 1164 1165 1166 1167 1168 1169 1170 1171
      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

1172
      it 'updates path & name to existing path & name in different namespace' do
1173
        project_param = { path: project4.path, name: project4.name }
W
winniehell 已提交
1174

1175
        put api("/projects/#{project3.id}", user), project_param
W
winniehell 已提交
1176

Z
Z.J. van de Weg 已提交
1177
        expect(response).to have_http_status(200)
W
winniehell 已提交
1178

1179
        project_param.each_pair do |k, v|
1180
          expect(json_response[k.to_s]).to eq(v)
1181 1182
        end
      end
W
winniehell 已提交
1183 1184 1185

      it 'updates jobs_enabled' do
        project_param = { jobs_enabled: true }
W
winniehell 已提交
1186

W
winniehell 已提交
1187
        put api("/projects/#{project3.id}", user), project_param
W
winniehell 已提交
1188

W
winniehell 已提交
1189
        expect(response).to have_http_status(200)
W
winniehell 已提交
1190

W
winniehell 已提交
1191 1192 1193 1194
        project_param.each_pair do |k, v|
          expect(json_response[k.to_s]).to eq(v)
        end
      end
1195 1196 1197
    end

    context 'when authenticated as project master' do
1198
      it 'updates path' do
1199 1200
        project_param = { path: 'bar' }
        put api("/projects/#{project3.id}", user4), project_param
Z
Z.J. van de Weg 已提交
1201
        expect(response).to have_http_status(200)
1202
        project_param.each_pair do |k, v|
1203
          expect(json_response[k.to_s]).to eq(v)
1204 1205 1206
        end
      end

1207
      it 'updates other attributes' do
1208 1209 1210 1211 1212 1213 1214
        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 已提交
1215
        expect(response).to have_http_status(200)
1216
        project_param.each_pair do |k, v|
1217
          expect(json_response[k.to_s]).to eq(v)
1218 1219 1220
        end
      end

1221
      it 'does not update path to existing path' do
1222 1223
        project_param = { path: project.path }
        put api("/projects/#{project3.id}", user4), project_param
Z
Z.J. van de Weg 已提交
1224
        expect(response).to have_http_status(400)
1225
        expect(json_response['message']['path']).to eq(['has already been taken'])
1226 1227
      end

1228
      it 'does not update name' do
1229 1230
        project_param = { name: 'bar' }
        put api("/projects/#{project3.id}", user4), project_param
Z
Z.J. van de Weg 已提交
1231
        expect(response).to have_http_status(403)
1232 1233
      end

1234
      it 'does not update visibility_level' do
1235
        project_param = { visibility: 'public' }
1236
        put api("/projects/#{project3.id}", user4), project_param
Z
Z.J. van de Weg 已提交
1237
        expect(response).to have_http_status(403)
1238 1239 1240 1241
      end
    end

    context 'when authenticated as project developer' do
1242
      it 'does not update other attributes' do
1243 1244 1245 1246 1247
        project_param = { path: 'bar',
                          issues_enabled: true,
                          wiki_enabled: true,
                          snippets_enabled: true,
                          merge_requests_enabled: true,
1248 1249
                          description: 'new description',
                          request_access_enabled: true }
1250
        put api("/projects/#{project.id}", user3), project_param
Z
Z.J. van de Weg 已提交
1251
        expect(response).to have_http_status(403)
1252 1253 1254 1255
      end
    end
  end

1256
  describe 'POST /projects/:id/archive' do
1257 1258
    context 'on an unarchived project' do
      it 'archives the project' do
1259
        post api("/projects/#{project.id}/archive", user)
1260

Z
Z.J. van de Weg 已提交
1261
        expect(response).to have_http_status(201)
1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
        expect(json_response['archived']).to be_truthy
      end
    end

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

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

Z
Z.J. van de Weg 已提交
1274
        expect(response).to have_http_status(201)
1275 1276
        expect(json_response['archived']).to be_truthy
      end
1277
    end
1278

1279 1280 1281 1282
    context 'user without archiving rights to the project' do
      before do
        project.team << [user3, :developer]
      end
1283

1284 1285 1286
      it 'rejects the action' do
        post api("/projects/#{project.id}/archive", user3)

Z
Z.J. van de Weg 已提交
1287
        expect(response).to have_http_status(403)
1288 1289 1290 1291
      end
    end
  end

1292
  describe 'POST /projects/:id/unarchive' do
1293 1294
    context 'on an unarchived project' do
      it 'remains unarchived' do
1295
        post api("/projects/#{project.id}/unarchive", user)
1296

Z
Z.J. van de Weg 已提交
1297
        expect(response).to have_http_status(201)
1298 1299 1300 1301 1302 1303 1304 1305 1306
        expect(json_response['archived']).to be_falsey
      end
    end

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

1307 1308
      it 'unarchives the project' do
        post api("/projects/#{project.id}/unarchive", user)
1309

Z
Z.J. van de Weg 已提交
1310
        expect(response).to have_http_status(201)
1311 1312
        expect(json_response['archived']).to be_falsey
      end
1313
    end
1314

1315 1316 1317 1318
    context 'user without archiving rights to the project' do
      before do
        project.team << [user3, :developer]
      end
1319

1320 1321 1322
      it 'rejects the action' do
        post api("/projects/#{project.id}/unarchive", user3)

Z
Z.J. van de Weg 已提交
1323
        expect(response).to have_http_status(403)
1324 1325 1326 1327
      end
    end
  end

1328 1329 1330
  describe 'POST /projects/:id/star' do
    context 'on an unstarred project' do
      it 'stars the project' do
1331
        expect { post api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1)
1332

Z
Z.J. van de Weg 已提交
1333
        expect(response).to have_http_status(201)
1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
        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
1345
        expect { post api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
1346

Z
Z.J. van de Weg 已提交
1347
        expect(response).to have_http_status(304)
1348 1349 1350 1351
      end
    end
  end

1352
  describe 'POST /projects/:id/unstar' do
1353 1354 1355 1356 1357 1358 1359
    context 'on a starred project' do
      before do
        user.toggle_star(project)
        project.reload
      end

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

1362
        expect(response).to have_http_status(201)
1363 1364 1365 1366 1367 1368
        expect(json_response['star_count']).to eq(0)
      end
    end

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

Z
Z.J. van de Weg 已提交
1371
        expect(response).to have_http_status(304)
1372 1373 1374 1375
      end
    end
  end

1376 1377
  describe 'DELETE /projects/:id' do
    context 'when authenticated as user' do
1378
      it 'removes project' do
1379
        delete api("/projects/#{project.id}", user)
1380 1381 1382

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

1385
      it 'does not remove a project if not an owner' do
1386 1387 1388
        user3 = create(:user)
        project.team << [user3, :developer]
        delete api("/projects/#{project.id}", user3)
Z
Z.J. van de Weg 已提交
1389
        expect(response).to have_http_status(403)
1390 1391
      end

1392
      it 'does not remove a non existing project' do
1393
        delete api('/projects/1328', user)
Z
Z.J. van de Weg 已提交
1394
        expect(response).to have_http_status(404)
1395 1396
      end

1397
      it 'does not remove a project not attached to user' do
1398
        delete api("/projects/#{project.id}", user2)
Z
Z.J. van de Weg 已提交
1399
        expect(response).to have_http_status(404)
1400 1401 1402
      end
    end

1403
    context 'when authenticated as admin' do
1404
      it 'removes any existing project' do
1405
        delete api("/projects/#{project.id}", admin)
1406 1407 1408

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

1411
      it 'does not remove a non existing project' do
1412
        delete api('/projects/1328', admin)
Z
Z.J. van de Weg 已提交
1413
        expect(response).to have_http_status(404)
1414 1415 1416
      end
    end
  end
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 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 1540 1541 1542

  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
1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573

  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
1574
        project_member
1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591
      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 已提交
1592
end