projects_spec.rb 40.5 KB
Newer Older
1
# -*- coding: utf-8 -*-
Nihad Abbasov's avatar
Nihad Abbasov committed
2 3
require 'spec_helper'

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
4
describe API::API, 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) }
Angus MacArthur's avatar
Angus MacArthur committed
10
  let(:admin) { create(:admin) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
11
  let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
12
  let(:project2) { create(: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 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,
26
    snippets_enabled: false)
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
  end
  let(:project_member3) do
    create(:project_member,
    user: user4,
    project: project3,
    access_level: ProjectMember::MASTER)
  end
  let(:project4) do
    create(:project,
    name: 'third_project',
    path: 'third_project',
    creator_id: user4.id,
    namespace: user4.namespace)
  end

  describe 'GET /projects' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
43 44
    before { project }

45
    context 'when unauthenticated' do
46
      it 'returns authentication error' do
47
        get api('/projects')
48
        expect(response).to have_http_status(401)
49
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
50 51
    end

52
    context 'when authenticated' do
53
      it 'returns an array of projects' do
54
        get api('/projects', user)
55
        expect(response).to have_http_status(200)
56 57 58
        expect(json_response).to be_an Array
        expect(json_response.first['name']).to eq(project.name)
        expect(json_response.first['owner']['username']).to eq(user.username)
Nihad Abbasov's avatar
Nihad Abbasov committed
59
      end
60

61
      it 'includes the project labels as the tag_list' do
62
        get api('/projects', user)
63 64 65
        expect(response.status).to eq 200
        expect(json_response).to be_an Array
        expect(json_response.first.keys).to include('tag_list')
66
      end
67

68
      it 'includes open_issues_count' do
69 70 71 72 73 74
        get api('/projects', user)
        expect(response.status).to eq 200
        expect(json_response).to be_an Array
        expect(json_response.first.keys).to include('open_issues_count')
      end

75
      it 'does not include open_issues_count' do
76
        project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
77 78 79 80 81 82 83

        get api('/projects', user)
        expect(response.status).to eq 200
        expect(json_response).to be_an Array
        expect(json_response.first.keys).not_to include('open_issues_count')
      end

84
      context 'GET /projects?simple=true' do
tiagonbotelho's avatar
tiagonbotelho committed
85
        it 'returns a simplified version of all the projects' do
86
          expected_keys = ["id", "http_url_to_repo", "web_url", "name", "name_with_namespace", "path", "path_with_namespace"]
87

88
          get api('/projects?simple=true', user)
tiagonbotelho's avatar
tiagonbotelho committed
89

90 91
          expect(response).to have_http_status(200)
          expect(json_response).to be_an Array
92
          expect(json_response.first.keys).to match_array expected_keys
93 94 95
        end
      end

96
      context 'and using search' do
97
        it 'returns searched project' do
98
          get api('/projects', user), { search: project.name }
99
          expect(response).to have_http_status(200)
100 101
          expect(json_response).to be_an Array
          expect(json_response.length).to eq(1)
102 103 104
        end
      end

Josh Frye's avatar
Josh Frye committed
105
      context 'and using the visibility filter' do
106
        it 'filters based on private visibility param' do
Josh Frye's avatar
Josh Frye committed
107
          get api('/projects', user), { visibility: 'private' }
108
          expect(response).to have_http_status(200)
Josh Frye's avatar
Josh Frye committed
109 110 111 112
          expect(json_response).to be_an Array
          expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).count)
        end

113
        it 'filters based on internal visibility param' do
Josh Frye's avatar
Josh Frye committed
114
          get api('/projects', user), { visibility: 'internal' }
115
          expect(response).to have_http_status(200)
Josh Frye's avatar
Josh Frye committed
116 117 118 119
          expect(json_response).to be_an Array
          expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).count)
        end

120
        it 'filters based on public visibility param' do
Josh Frye's avatar
Josh Frye committed
121
          get api('/projects', user), { visibility: 'public' }
122
          expect(response).to have_http_status(200)
Josh Frye's avatar
Josh Frye committed
123 124 125 126 127
          expect(json_response).to be_an Array
          expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).count)
        end
      end

128
      context 'and using sorting' do
129 130 131 132 133
        before do
          project2
          project3
        end

134
        it 'returns the correct order when sorted by id' do
135
          get api('/projects', user), { order_by: 'id', sort: 'desc' }
136
          expect(response).to have_http_status(200)
137 138
          expect(json_response).to be_an Array
          expect(json_response.first['id']).to eq(project3.id)
139 140
        end
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
141 142 143
    end
  end

144
  describe 'GET /projects/all' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
145 146
    before { project }

147
    context 'when unauthenticated' do
148
      it 'returns authentication error' do
149
        get api('/projects/all')
150
        expect(response).to have_http_status(401)
151 152 153
      end
    end

154
    context 'when authenticated as regular user' do
155
      it 'returns authentication error' do
156
        get api('/projects/all', user)
157
        expect(response).to have_http_status(403)
158 159 160
      end
    end

161
    context 'when authenticated as admin' do
162
      it 'returns an array of all projects' do
163
        get api('/projects/all', admin)
164
        expect(response).to have_http_status(200)
165
        expect(json_response).to be_an Array
Marin Jankovski's avatar
Marin Jankovski committed
166

167 168
        expect(json_response).to satisfy do |response|
          response.one? do |entry|
169
            entry.has_key?('permissions') &&
170
            entry['name'] == project.name &&
171
              entry['owner']['username'] == user.username
172 173
          end
        end
174 175 176 177
      end
    end
  end

178
  describe 'GET /projects/starred' do
179 180
    let(:public_project) { create(:project, :public) }

181
    before do
182 183
      project_member2
      user3.update_attributes(starred_projects: [project, project2, project3, public_project])
184 185
    end

186
    it 'returns the starred projects viewable by the user' do
187
      get api('/projects/starred', user3)
188
      expect(response).to have_http_status(200)
189
      expect(json_response).to be_an Array
190
      expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id)
191 192 193
    end
  end

194 195
  describe 'POST /projects' do
    context 'maximum number of projects reached' do
196
      it 'does not create new project and respond with 403' do
197
        allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
198 199
        expect { post api('/projects', user2), name: 'foo' }.
          to change {Project.count}.by(0)
200
        expect(response).to have_http_status(403)
201 202 203
      end
    end

204
    it 'creates new project without path and return 201' do
205 206
      expect { post api('/projects', user), name: 'foo' }.
        to change { Project.count }.by(1)
207
      expect(response).to have_http_status(201)
208 209
    end

210
    it 'creates last project before reaching project limit' do
211
      allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1)
212
      post api('/projects', user2), name: 'foo'
213
      expect(response).to have_http_status(201)
214 215
    end

216
    it 'does not create new project without name and return 400' do
217
      expect { post api('/projects', user) }.not_to change { Project.count }
218
      expect(response).to have_http_status(400)
219
    end
Alex Denisov's avatar
Alex Denisov committed
220

221
    it "assigns attributes to project" do
222
      project = attributes_for(:project, {
223
        path: 'camelCasePath',
Robert Speicher's avatar
Robert Speicher committed
224
        description: FFaker::Lorem.sentence,
225 226
        issues_enabled: false,
        merge_requests_enabled: false,
227 228
        wiki_enabled: false,
        only_allow_merge_if_build_succeeds: false
Alex Denisov's avatar
Alex Denisov committed
229 230
      })

231
      post api('/projects', user), project
Alex Denisov's avatar
Alex Denisov committed
232

233
      project.each_pair do |k, v|
234
        next if %i{ issues_enabled merge_requests_enabled wiki_enabled }.include?(k)
235
        expect(json_response[k.to_s]).to eq(v)
Alex Denisov's avatar
Alex Denisov committed
236
      end
237 238 239 240 241 242

      # 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)
243
    end
244

245
    it 'sets a project as public' do
246
      project = attributes_for(:project, :public)
247
      post api('/projects', user), project
248 249
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
250 251
    end

252
    it 'sets a project as public using :public' do
253
      project = attributes_for(:project, { public: true })
254
      post api('/projects', user), project
255 256
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
257 258
    end

259
    it 'sets a project as internal' do
260
      project = attributes_for(:project, :internal)
261
      post api('/projects', user), project
262 263
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
264 265
    end

266
    it 'sets a project as internal overriding :public' do
267
      project = attributes_for(:project, :internal, { public: true })
268
      post api('/projects', user), project
269 270
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
271 272
    end

273
    it 'sets a project as private' do
274
      project = attributes_for(:project, :private)
275
      post api('/projects', user), project
276 277
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
278 279
    end

280
    it 'sets a project as private using :public' do
281
      project = attributes_for(:project, { public: false })
282
      post api('/projects', user), project
283 284
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
285
    end
286

287 288 289 290 291 292 293 294 295 296 297 298
    it 'sets a project as allowing merge even if build fails' do
      project = attributes_for(:project, { only_allow_merge_if_build_succeeds: false })
      post api('/projects', user), project
      expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
    end

    it 'sets a project as allowing merge only if build succeeds' do
      project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
      post api('/projects', user), project
      expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
    end

299 300 301
    context 'when a visibility level is restricted' do
      before do
        @project = attributes_for(:project, { public: true })
302
        stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
303 304
      end

305
      it 'does not allow a non-admin to use a restricted visibility level' do
306
        post api('/projects', user), @project
Felipe Artur's avatar
Felipe Artur committed
307

308
        expect(response).to have_http_status(400)
309 310 311 312 313
        expect(json_response['message']['visibility_level'].first).to(
          match('restricted by your GitLab administrator')
        )
      end

314
      it 'allows an admin to override restricted visibility settings' do
315 316 317 318 319 320 321
        post api('/projects', admin), @project
        expect(json_response['public']).to be_truthy
        expect(json_response['visibility_level']).to(
          eq(Gitlab::VisibilityLevel::PUBLIC)
        )
      end
    end
322 323
  end

324
  describe 'POST /projects/user/:id' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
325
    before { project }
Angus MacArthur's avatar
Angus MacArthur committed
326 327
    before { admin }

328
    it 'should create new project without path and return 201' do
329
      expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
330
      expect(response).to have_http_status(201)
Angus MacArthur's avatar
Angus MacArthur committed
331 332
    end

333
    it 'responds with 400 on failure and not project' do
334
      expect { post api("/projects/user/#{user.id}", admin) }.
335
        not_to change { Project.count }
336

337
      expect(response).to have_http_status(400)
338
      expect(json_response['message']['name']).to eq([
339 340
        'can\'t be blank',
        'is too short (minimum is 0 characters)',
Douwe Maan's avatar
Douwe Maan committed
341
        Gitlab::Regex.project_name_regex_message
342 343
      ])
      expect(json_response['message']['path']).to eq([
344 345
        'can\'t be blank',
        'is too short (minimum is 0 characters)',
Douwe Maan's avatar
Douwe Maan committed
346
        Gitlab::Regex.send(:project_path_regex_message)
347
      ])
Angus MacArthur's avatar
Angus MacArthur committed
348 349
    end

350
    it 'assigns attributes to project' do
Angus MacArthur's avatar
Angus MacArthur committed
351
      project = attributes_for(:project, {
Robert Speicher's avatar
Robert Speicher committed
352
        description: FFaker::Lorem.sentence,
353 354 355
        issues_enabled: false,
        merge_requests_enabled: false,
        wiki_enabled: false
Angus MacArthur's avatar
Angus MacArthur committed
356 357 358 359
      })

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

360
      project.each_pair do |k, v|
Angus MacArthur's avatar
Angus MacArthur committed
361
        next if k == :path
362
        expect(json_response[k.to_s]).to eq(v)
Angus MacArthur's avatar
Angus MacArthur committed
363 364
      end
    end
365

366
    it 'sets a project as public' do
367
      project = attributes_for(:project, :public)
368
      post api("/projects/user/#{user.id}", admin), project
369 370
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
371 372
    end

373
    it 'sets a project as public using :public' do
374 375
      project = attributes_for(:project, { public: true })
      post api("/projects/user/#{user.id}", admin), project
376 377
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
378
    end
379

380
    it 'sets a project as internal' do
381
      project = attributes_for(:project, :internal)
382
      post api("/projects/user/#{user.id}", admin), project
383 384
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
385 386
    end

387
    it 'sets a project as internal overriding :public' do
388
      project = attributes_for(:project, :internal, { public: true })
389
      post api("/projects/user/#{user.id}", admin), project
390 391
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
392
    end
393

394
    it 'sets a project as private' do
395
      project = attributes_for(:project, :private)
396
      post api("/projects/user/#{user.id}", admin), project
397 398
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
399 400
    end

401
    it 'sets a project as private using :public' do
402 403
      project = attributes_for(:project, { public: false })
      post api("/projects/user/#{user.id}", admin), project
404 405
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
406
    end
407 408 409 410 411 412 413 414 415 416 417 418

    it 'sets a project as allowing merge even if build fails' do
      project = attributes_for(:project, { only_allow_merge_if_build_succeeds: false })
      post api("/projects/user/#{user.id}", admin), project
      expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
    end

    it 'sets a project as allowing merge only if build succeeds' do
      project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
      post api("/projects/user/#{user.id}", admin), project
      expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
    end
Angus MacArthur's avatar
Angus MacArthur committed
419 420
  end

421 422 423 424 425 426
  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")

427
      expect(response).to have_http_status(201)
428 429 430 431 432 433
      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

434
  describe 'GET /projects/:id' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
435
    before { project }
436
    before { project_member }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
437

438 439 440 441
    it 'returns a project by id' do
      group = create(:group)
      link = create(:project_group_link, project: project, group: group)

Robert Speicher's avatar
Robert Speicher committed
442
      get api("/projects/#{project.id}", user)
443

444
      expect(response).to have_http_status(200)
445 446 447 448 449 450 451 452 453 454 455 456
      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['public']).to be_falsey
      expect(json_response['archived']).to be_falsey
      expect(json_response['visibility_level']).to be_present
      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
457
      expect(json_response['name']).to eq(project.name)
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
      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
      expect(json_response['builds_enabled']).to be_present
      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
      expect(json_response['public_builds']).to be_present
      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)
479
      expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_build_succeeds)
Nihad Abbasov's avatar
Nihad Abbasov committed
480
    end
481

482
    it 'returns a project by path name' do
483
      get api("/projects/#{project.id}", user)
484
      expect(response).to have_http_status(200)
485
      expect(json_response['name']).to eq(project.name)
486
    end
487

488
    it 'returns a 404 error if not found' do
489
      get api('/projects/42', user)
490
      expect(response).to have_http_status(404)
491
      expect(json_response['message']).to eq('404 Project Not Found')
492
    end
493

494
    it 'returns a 404 error if user is not a member' do
495 496
      other_user = create(:user)
      get api("/projects/#{project.id}", other_user)
497
      expect(response).to have_http_status(404)
498
    end
499

500
    it 'handles users with dots' do
501 502 503 504
      dot_user = create(:user, username: 'dot.user')
      project = create(:project, creator_id: dot_user.id, namespace: dot_user.namespace)

      get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user)
505
      expect(response).to have_http_status(200)
506 507 508
      expect(json_response['name']).to eq(project.name)
    end

509
    describe 'permissions' do
510
      context 'all projects' do
511 512 513
        before { project.team << [user, :master] }

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

516
          expect(response).to have_http_status(200)
517 518 519 520 521 522
          expect(json_response.first['permissions']['project_access']['access_level']).
              to eq(Gitlab::Access::MASTER)
          expect(json_response.first['permissions']['group_access']).to be_nil
        end
      end

523
      context 'personal project' do
524
        it 'sets project access and returns 200' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
525 526
          project.team << [user, :master]
          get api("/projects/#{project.id}", user)
527

528
          expect(response).to have_http_status(200)
529 530 531 532
          expect(json_response['permissions']['project_access']['access_level']).
            to eq(Gitlab::Access::MASTER)
          expect(json_response['permissions']['group_access']).to be_nil
        end
533 534 535
      end

      context 'group project' do
536 537 538 539
        let(:project2) { create(:project, group: create(:group)) }

        before { project2.group.add_owner(user) }

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

543
          expect(response).to have_http_status(200)
544 545 546 547
          expect(json_response['permissions']['project_access']).to be_nil
          expect(json_response['permissions']['group_access']['access_level']).
            to eq(Gitlab::Access::OWNER)
        end
548 549
      end
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
550 551
  end

552
  describe 'GET /projects/:id/events' do
Douwe Maan's avatar
Douwe Maan committed
553
    before { project_member2 }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
554

555 556 557 558 559 560 561
    context 'valid request' do
      before do
        note = create(:note_on_issue, note: 'What an awesome day!', project: project)
        EventCreateService.new.leave_note(note, note.author)
        get api("/projects/#{project.id}/events", user)
      end

562
      it { expect(response).to have_http_status(200) }
563 564 565

      context 'joined event' do
        let(:json_event) { json_response[1] }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
566

567 568 569 570 571 572 573 574 575 576 577 578
        it { expect(json_event['action_name']).to eq('joined') }
        it { expect(json_event['project_id'].to_i).to eq(project.id) }
        it { expect(json_event['author_username']).to eq(user3.username) }
        it { expect(json_event['author']['name']).to eq(user3.name) }
      end

      context 'comment event' do
        let(:json_event) { json_response.first }

        it { expect(json_event['action_name']).to eq('commented on') }
        it { expect(json_event['note']['body']).to eq('What an awesome day!') }
      end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
579 580
    end

581
    it 'returns a 404 error if not found' do
582
      get api('/projects/42/events', user)
583
      expect(response).to have_http_status(404)
584
      expect(json_response['message']).to eq('404 Project Not Found')
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
585 586
    end

587
    it 'returns a 404 error if user is not a member' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
588 589
      other_user = create(:user)
      get api("/projects/#{project.id}/events", other_user)
590
      expect(response).to have_http_status(404)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
591 592 593
    end
  end

594
  describe 'GET /projects/:id/snippets' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
595 596
    before { snippet }

597
    it 'returns an array of project snippets' do
598
      get api("/projects/#{project.id}/snippets", user)
599
      expect(response).to have_http_status(200)
600 601
      expect(json_response).to be_an Array
      expect(json_response.first['title']).to eq(snippet.title)
602 603 604
    end
  end

605
  describe 'GET /projects/:id/snippets/:snippet_id' do
606
    it 'returns a project snippet' do
607
      get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
608
      expect(response).to have_http_status(200)
609
      expect(json_response['title']).to eq(snippet.title)
Nihad Abbasov's avatar
Nihad Abbasov committed
610
    end
611

612
    it 'returns a 404 error if snippet id not found' do
613
      get api("/projects/#{project.id}/snippets/1234", user)
614
      expect(response).to have_http_status(404)
615
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
616 617
  end

618
  describe 'POST /projects/:id/snippets' do
619
    it 'creates a new project snippet' do
620
      post api("/projects/#{project.id}/snippets", user),
621 622
        title: 'api test', file_name: 'sample.rb', code: 'test',
        visibility_level: '0'
623
      expect(response).to have_http_status(201)
624
      expect(json_response['title']).to eq('api test')
Nihad Abbasov's avatar
Nihad Abbasov committed
625
    end
626

627
    it 'returns a 400 error if invalid snippet is given' do
628 629
      post api("/projects/#{project.id}/snippets", user)
      expect(status).to eq(400)
630
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
631 632
  end

633
  describe 'PUT /projects/:id/snippets/:snippet_id' do
634
    it 'updates an existing project snippet' do
635
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
636
        code: 'updated code'
637
      expect(response).to have_http_status(200)
638 639
      expect(json_response['title']).to eq('example')
      expect(snippet.reload.content).to eq('updated code')
640
    end
641

642
    it 'updates an existing project snippet with new title' do
643
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
644
        title: 'other api test'
645
      expect(response).to have_http_status(200)
646
      expect(json_response['title']).to eq('other api test')
647
    end
648 649
  end

650
  describe 'DELETE /projects/:id/snippets/:snippet_id' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
651 652
    before { snippet }

653
    it 'deletes existing project snippet' do
654
      expect do
655
        delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
656
      end.to change { Snippet.count }.by(-1)
657
      expect(response).to have_http_status(200)
658 659
    end

660
    it 'returns 404 when deleting unknown snippet id' do
661
      delete api("/projects/#{project.id}/snippets/1234", user)
662
      expect(response).to have_http_status(404)
Nihad Abbasov's avatar
Nihad Abbasov committed
663 664
    end
  end
665

666
  describe 'GET /projects/:id/snippets/:snippet_id/raw' do
667
    it 'gets a raw project snippet' do
668
      get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
669
      expect(response).to have_http_status(200)
670
    end
671

672
    it 'returns a 404 error if raw project snippet not found' do
673
      get api("/projects/#{project.id}/snippets/5555/raw", user)
674
      expect(response).to have_http_status(404)
675
    end
676
  end
677

678 679
  describe :fork_admin do
    let(:project_fork_target) { create(:project) }
680
    let(:project_fork_source) { create(:project, :public) }
681

682
    describe 'POST /projects/:id/fork/:forked_from_id' do
683
      let(:new_project_fork_source) { create(:project, :public) }
684

685
      it "is not available for non admin users" do
686
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
687
        expect(response).to have_http_status(403)
688 689
      end

690
      it 'allows project to be forked from an existing project' do
691
        expect(project_fork_target.forked?).not_to be_truthy
692
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
693
        expect(response).to have_http_status(201)
694
        project_fork_target.reload
695 696 697
        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
698 699
      end

700
      it 'fails if forked_from project which does not exist' do
701
        post api("/projects/#{project_fork_target.id}/fork/9999", admin)
702
        expect(response).to have_http_status(404)
703 704
      end

705
      it 'fails with 409 if already forked' do
706 707
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
        project_fork_target.reload
708
        expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
709
        post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
710
        expect(response).to have_http_status(409)
711
        project_fork_target.reload
712 713
        expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
        expect(project_fork_target.forked?).to be_truthy
714 715 716
      end
    end

717
    describe 'DELETE /projects/:id/fork' do
718
      it "is not visible to users outside group" do
719
        delete api("/projects/#{project_fork_target.id}/fork", user)
720
        expect(response).to have_http_status(404)
721 722
      end

723 724
      context 'when users belong to project group' do
        let(:project_fork_target) { create(:project, group: create(:group)) }
725

726 727 728 729 730
        before do
          project_fork_target.group.add_owner user
          project_fork_target.group.add_developer user2
        end

731
        it 'is forbidden to non-owner users' do
732
          delete api("/projects/#{project_fork_target.id}/fork", user2)
733
          expect(response).to have_http_status(403)
734 735
        end

736
        it 'makes forked project unforked' do
737 738 739 740 741
          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
          delete api("/projects/#{project_fork_target.id}/fork", admin)
742
          expect(response).to have_http_status(200)
743 744 745 746 747
          project_fork_target.reload
          expect(project_fork_target.forked_from_project).to be_nil
          expect(project_fork_target.forked?).not_to be_truthy
        end

748
        it 'is idempotent if not forked' do
749 750
          expect(project_fork_target.forked_from_project).to be_nil
          delete api("/projects/#{project_fork_target.id}/fork", admin)
751
          expect(response).to have_http_status(200)
752 753
          expect(project_fork_target.reload.forked_from_project).to be_nil
        end
754 755 756
      end
    end
  end
757

758 759 760
  describe "POST /projects/:id/share" do
    let(:group) { create(:group) }

761
    it "shares project with group" do
762 763 764 765 766 767 768 769 770
      expect do
        post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER
      end.to change { ProjectGroupLink.count }.by(1)

      expect(response.status).to eq 201
      expect(json_response['group_id']).to eq group.id
      expect(json_response['group_access']).to eq Gitlab::Access::DEVELOPER
    end

771
    it "returns a 400 error when group id is not given" do
772 773 774 775
      post api("/projects/#{project.id}/share", user), group_access: Gitlab::Access::DEVELOPER
      expect(response.status).to eq 400
    end

776
    it "returns a 400 error when access level is not given" do
777 778 779 780
      post api("/projects/#{project.id}/share", user), group_id: group.id
      expect(response.status).to eq 400
    end

781
    it "returns a 400 error when sharing is disabled" do
782 783 784 785 786
      project.namespace.update(share_with_group_lock: true)
      post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER
      expect(response.status).to eq 400
    end

787
    it "returns a 409 error when wrong params passed" do
788 789 790 791 792 793
      post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: 1234
      expect(response.status).to eq 409
      expect(json_response['message']).to eq 'Group access is not included in the list'
    end
  end

794
  describe 'GET /projects/search/:query' do
795
    let!(:query) { 'query'}
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
796 797 798 799 800
    let!(:search)           { create(:empty_project, name: query, creator_id: user.id, namespace: user.namespace) }
    let!(:pre)              { create(:empty_project, name: "pre_#{query}", creator_id: user.id, namespace: user.namespace) }
    let!(:post)             { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
    let!(:pre_post)         { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
    let!(:unfound)          { create(:empty_project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
801 802 803 804
    let!(:internal)         { create(:empty_project, :internal, name: "internal #{query}") }
    let!(:unfound_internal) { create(:empty_project, :internal, name: 'unfound internal') }
    let!(:public)           { create(:empty_project, :public, name: "public #{query}") }
    let!(:unfound_public)   { create(:empty_project, :public, name: 'unfound public') }
805

806
    context 'when unauthenticated' do
807
      it 'returns authentication error' do
808
        get api("/projects/search/#{query}")
809
        expect(response).to have_http_status(401)
810 811 812
      end
    end

813
    context 'when authenticated' do
814
      it 'returns an array of projects' do
815
        get api("/projects/search/#{query}", user)
816
        expect(response).to have_http_status(200)
817 818 819
        expect(json_response).to be_an Array
        expect(json_response.size).to eq(6)
        json_response.each {|project| expect(project['name']).to match(/.*query.*/)}
820 821 822
      end
    end

823
    context 'when authenticated as a different user' do
824
      it 'returns matching public projects' do
825
        get api("/projects/search/#{query}", user2)
826
        expect(response).to have_http_status(200)
827 828 829
        expect(json_response).to be_an Array
        expect(json_response.size).to eq(2)
        json_response.each {|project| expect(project['name']).to match(/(internal|public) query/)}
830 831 832
      end
    end
  end
833

834 835 836 837 838 839 840 841 842 843 844
  describe 'PUT /projects/:id̈́' do
    before { project }
    before { user }
    before { user3 }
    before { user4 }
    before { project3 }
    before { project4 }
    before { project_member3 }
    before { project_member2 }

    context 'when unauthenticated' do
845
      it 'returns authentication error' do
846 847
        project_param = { name: 'bar' }
        put api("/projects/#{project.id}"), project_param
848
        expect(response).to have_http_status(401)
849 850 851 852
      end
    end

    context 'when authenticated as project owner' do
853
      it 'updates name' do
854 855
        project_param = { name: 'bar' }
        put api("/projects/#{project.id}", user), project_param
856
        expect(response).to have_http_status(200)
857
        project_param.each_pair do |k, v|
858
          expect(json_response[k.to_s]).to eq(v)
859 860 861
        end
      end

862
      it 'updates visibility_level' do
863 864
        project_param = { visibility_level: 20 }
        put api("/projects/#{project3.id}", user), project_param
865
        expect(response).to have_http_status(200)
866
        project_param.each_pair do |k, v|
867
          expect(json_response[k.to_s]).to eq(v)
868 869 870
        end
      end

871
      it 'updates visibility_level from public to private' do
872 873 874 875
        project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })

        project_param = { public: false }
        put api("/projects/#{project3.id}", user), project_param
876
        expect(response).to have_http_status(200)
877 878 879 880 881 882
        project_param.each_pair do |k, v|
          expect(json_response[k.to_s]).to eq(v)
        end
        expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
      end

883
      it 'does not update name to existing name' do
884 885
        project_param = { name: project3.name }
        put api("/projects/#{project.id}", user), project_param
886
        expect(response).to have_http_status(400)
887
        expect(json_response['message']['name']).to eq(['has already been taken'])
888 889
      end

890
      it 'updates path & name to existing path & name in different namespace' do
891 892
        project_param = { path: project4.path, name: project4.name }
        put api("/projects/#{project3.id}", user), project_param
893
        expect(response).to have_http_status(200)
894
        project_param.each_pair do |k, v|
895
          expect(json_response[k.to_s]).to eq(v)
896 897 898 899 900
        end
      end
    end

    context 'when authenticated as project master' do
901
      it 'updates path' do
902 903
        project_param = { path: 'bar' }
        put api("/projects/#{project3.id}", user4), project_param
904
        expect(response).to have_http_status(200)
905
        project_param.each_pair do |k, v|
906
          expect(json_response[k.to_s]).to eq(v)
907 908 909
        end
      end

910
      it 'updates other attributes' do
911 912 913 914 915 916 917
        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
918
        expect(response).to have_http_status(200)
919
        project_param.each_pair do |k, v|
920
          expect(json_response[k.to_s]).to eq(v)
921 922 923
        end
      end

924
      it 'does not update path to existing path' do
925 926
        project_param = { path: project.path }
        put api("/projects/#{project3.id}", user4), project_param
927
        expect(response).to have_http_status(400)
928
        expect(json_response['message']['path']).to eq(['has already been taken'])
929 930
      end

931
      it 'does not update name' do
932 933
        project_param = { name: 'bar' }
        put api("/projects/#{project3.id}", user4), project_param
934
        expect(response).to have_http_status(403)
935 936
      end

937
      it 'does not update visibility_level' do
938 939
        project_param = { visibility_level: 20 }
        put api("/projects/#{project3.id}", user4), project_param
940
        expect(response).to have_http_status(403)
941 942 943 944
      end
    end

    context 'when authenticated as project developer' do
945
      it 'does not update other attributes' do
946 947 948 949 950 951 952
        project_param = { path: 'bar',
                          issues_enabled: true,
                          wiki_enabled: true,
                          snippets_enabled: true,
                          merge_requests_enabled: true,
                          description: 'new description' }
        put api("/projects/#{project.id}", user3), project_param
953
        expect(response).to have_http_status(403)
954 955 956 957
      end
    end
  end

958
  describe 'POST /projects/:id/archive' do
959 960
    context 'on an unarchived project' do
      it 'archives the project' do
961
        post api("/projects/#{project.id}/archive", user)
962

963
        expect(response).to have_http_status(201)
964 965 966 967 968 969 970 971 972 973
        expect(json_response['archived']).to be_truthy
      end
    end

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

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

976
        expect(response).to have_http_status(201)
977 978
        expect(json_response['archived']).to be_truthy
      end
979
    end
980

981 982 983 984
    context 'user without archiving rights to the project' do
      before do
        project.team << [user3, :developer]
      end
985

986 987 988
      it 'rejects the action' do
        post api("/projects/#{project.id}/archive", user3)

989
        expect(response).to have_http_status(403)
990 991 992 993
      end
    end
  end

994
  describe 'POST /projects/:id/unarchive' do
995 996
    context 'on an unarchived project' do
      it 'remains unarchived' do
997
        post api("/projects/#{project.id}/unarchive", user)
998

999
        expect(response).to have_http_status(201)
1000 1001 1002 1003 1004 1005 1006 1007 1008
        expect(json_response['archived']).to be_falsey
      end
    end

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

1009 1010
      it 'unarchives the project' do
        post api("/projects/#{project.id}/unarchive", user)
1011

1012
        expect(response).to have_http_status(201)
1013 1014
        expect(json_response['archived']).to be_falsey
      end
1015
    end
1016

1017 1018 1019 1020
    context 'user without archiving rights to the project' do
      before do
        project.team << [user3, :developer]
      end
1021

1022 1023 1024
      it 'rejects the action' do
        post api("/projects/#{project.id}/unarchive", user3)

1025
        expect(response).to have_http_status(403)
1026 1027 1028 1029
      end
    end
  end

1030 1031 1032
  describe 'POST /projects/:id/star' do
    context 'on an unstarred project' do
      it 'stars the project' do
1033
        expect { post api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1)
1034

1035
        expect(response).to have_http_status(201)
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
        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
1047
        expect { post api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
1048

1049
        expect(response).to have_http_status(304)
1050 1051 1052 1053
      end
    end
  end

1054
  describe 'DELETE /projects/:id/star' do
1055 1056 1057 1058 1059 1060 1061
    context 'on a starred project' do
      before do
        user.toggle_star(project)
        project.reload
      end

      it 'unstars the project' do
1062
        expect { delete api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(-1)
1063

1064
        expect(response).to have_http_status(200)
1065 1066 1067 1068 1069 1070
        expect(json_response['star_count']).to eq(0)
      end
    end

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

1073
        expect(response).to have_http_status(304)
1074 1075 1076 1077
      end
    end
  end

1078 1079
  describe 'DELETE /projects/:id' do
    context 'when authenticated as user' do
1080
      it 'removes project' do
1081
        delete api("/projects/#{project.id}", user)
1082
        expect(response).to have_http_status(200)
1083 1084
      end

1085
      it 'does not remove a project if not an owner' do
1086 1087 1088
        user3 = create(:user)
        project.team << [user3, :developer]
        delete api("/projects/#{project.id}", user3)
1089
        expect(response).to have_http_status(403)
1090 1091
      end

1092
      it 'does not remove a non existing project' do
1093
        delete api('/projects/1328', user)
1094
        expect(response).to have_http_status(404)
1095 1096
      end

1097
      it 'does not remove a project not attached to user' do
1098
        delete api("/projects/#{project.id}", user2)
1099
        expect(response).to have_http_status(404)
1100 1101 1102
      end
    end

1103
    context 'when authenticated as admin' do
1104
      it 'removes any existing project' do
1105
        delete api("/projects/#{project.id}", admin)
1106
        expect(response).to have_http_status(200)
1107 1108
      end

1109
      it 'does not remove a non existing project' do
1110
        delete api('/projects/1328', admin)
1111
        expect(response).to have_http_status(404)
1112 1113 1114
      end
    end
  end
Nihad Abbasov's avatar
Nihad Abbasov committed
1115
end