projects_spec.rb 40 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 77 78 79 80 81 82 83
        project.update_attributes( { issues_enabled: false } )

        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
        expect(json_response[k.to_s]).to eq(v)
Alex Denisov's avatar
Alex Denisov committed
235
      end
236
    end
237

238
    it 'sets a project as public' do
239
      project = attributes_for(:project, :public)
240
      post api('/projects', user), project
241 242
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
243 244
    end

245
    it 'sets a project as public using :public' do
246
      project = attributes_for(:project, { public: true })
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 internal' do
253
      project = attributes_for(:project, :internal)
254
      post api('/projects', user), project
255 256
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
257 258
    end

259
    it 'sets a project as internal overriding :public' do
260
      project = attributes_for(:project, :internal, { public: true })
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 private' do
267
      project = attributes_for(:project, :private)
268
      post api('/projects', user), project
269 270
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
271 272
    end

273
    it 'sets a project as private using :public' do
274
      project = attributes_for(:project, { public: false })
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
    end
279

280 281 282 283 284 285 286 287 288 289 290 291
    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

292 293 294
    context 'when a visibility level is restricted' do
      before do
        @project = attributes_for(:project, { public: true })
295
        stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
296 297
      end

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

301
        expect(response).to have_http_status(400)
302 303 304 305 306
        expect(json_response['message']['visibility_level'].first).to(
          match('restricted by your GitLab administrator')
        )
      end

307
      it 'allows an admin to override restricted visibility settings' do
308 309 310 311 312 313 314
        post api('/projects', admin), @project
        expect(json_response['public']).to be_truthy
        expect(json_response['visibility_level']).to(
          eq(Gitlab::VisibilityLevel::PUBLIC)
        )
      end
    end
315 316
  end

317
  describe 'POST /projects/user/:id' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
318
    before { project }
Angus MacArthur's avatar
Angus MacArthur committed
319 320
    before { admin }

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

326
    it 'responds with 400 on failure and not project' do
327
      expect { post api("/projects/user/#{user.id}", admin) }.
328
        not_to change { Project.count }
329

330
      expect(response).to have_http_status(400)
331
      expect(json_response['message']['name']).to eq([
332 333
        'can\'t be blank',
        'is too short (minimum is 0 characters)',
Douwe Maan's avatar
Douwe Maan committed
334
        Gitlab::Regex.project_name_regex_message
335 336
      ])
      expect(json_response['message']['path']).to eq([
337 338
        'can\'t be blank',
        'is too short (minimum is 0 characters)',
Douwe Maan's avatar
Douwe Maan committed
339
        Gitlab::Regex.send(:project_path_regex_message)
340
      ])
Angus MacArthur's avatar
Angus MacArthur committed
341 342
    end

343
    it 'assigns attributes to project' do
Angus MacArthur's avatar
Angus MacArthur committed
344
      project = attributes_for(:project, {
Robert Speicher's avatar
Robert Speicher committed
345
        description: FFaker::Lorem.sentence,
346 347 348
        issues_enabled: false,
        merge_requests_enabled: false,
        wiki_enabled: false
Angus MacArthur's avatar
Angus MacArthur committed
349 350 351 352
      })

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

353
      project.each_pair do |k, v|
Angus MacArthur's avatar
Angus MacArthur committed
354
        next if k == :path
355
        expect(json_response[k.to_s]).to eq(v)
Angus MacArthur's avatar
Angus MacArthur committed
356 357
      end
    end
358

359
    it 'sets a project as public' do
360
      project = attributes_for(:project, :public)
361
      post api("/projects/user/#{user.id}", admin), project
362 363
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
364 365
    end

366
    it 'sets a project as public using :public' do
367 368
      project = attributes_for(:project, { public: true })
      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
    end
372

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

380
    it 'sets a project as internal overriding :public' do
381
      project = attributes_for(:project, :internal, { public: true })
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
    end
386

387
    it 'sets a project as private' do
388
      project = attributes_for(:project, :private)
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::PRIVATE)
392 393
    end

394
    it 'sets a project as private using :public' do
395 396
      project = attributes_for(:project, { public: false })
      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
    end
400 401 402 403 404 405 406 407 408 409 410 411

    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
412 413
  end

414 415 416 417 418 419
  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")

420
      expect(response).to have_http_status(201)
421 422 423 424 425 426
      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

427
  describe 'GET /projects/:id' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
428
    before { project }
429
    before { project_member }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
430

431 432 433 434
    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
435
      get api("/projects/#{project.id}", user)
436

437
      expect(response).to have_http_status(200)
438 439 440 441 442 443 444 445 446 447 448 449
      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
450
      expect(json_response['name']).to eq(project.name)
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
      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)
472
      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
473
    end
474

475
    it 'returns a project by path name' do
476
      get api("/projects/#{project.id}", user)
477
      expect(response).to have_http_status(200)
478
      expect(json_response['name']).to eq(project.name)
479
    end
480

481
    it 'returns a 404 error if not found' do
482
      get api('/projects/42', user)
483
      expect(response).to have_http_status(404)
484
      expect(json_response['message']).to eq('404 Project Not Found')
485
    end
486

487
    it 'returns a 404 error if user is not a member' do
488 489
      other_user = create(:user)
      get api("/projects/#{project.id}", other_user)
490
      expect(response).to have_http_status(404)
491
    end
492

493
    it 'handles users with dots' do
494 495 496 497
      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)
498
      expect(response).to have_http_status(200)
499 500 501
      expect(json_response['name']).to eq(project.name)
    end

502
    describe 'permissions' do
503
      context 'all projects' do
504 505 506
        before { project.team << [user, :master] }

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

509
          expect(response).to have_http_status(200)
510 511 512 513 514 515
          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

516
      context 'personal project' do
517
        it 'sets project access and returns 200' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
518 519
          project.team << [user, :master]
          get api("/projects/#{project.id}", user)
520

521
          expect(response).to have_http_status(200)
522 523 524 525
          expect(json_response['permissions']['project_access']['access_level']).
            to eq(Gitlab::Access::MASTER)
          expect(json_response['permissions']['group_access']).to be_nil
        end
526 527 528
      end

      context 'group project' do
529 530 531 532
        let(:project2) { create(:project, group: create(:group)) }

        before { project2.group.add_owner(user) }

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

536
          expect(response).to have_http_status(200)
537 538 539 540
          expect(json_response['permissions']['project_access']).to be_nil
          expect(json_response['permissions']['group_access']['access_level']).
            to eq(Gitlab::Access::OWNER)
        end
541 542
      end
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
543 544
  end

545
  describe 'GET /projects/:id/events' do
Douwe Maan's avatar
Douwe Maan committed
546
    before { project_member2 }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
547

548 549 550 551 552 553 554
    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

555
      it { expect(response).to have_http_status(200) }
556 557 558

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

560 561 562 563 564 565 566 567 568 569 570 571
        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
572 573
    end

574
    it 'returns a 404 error if not found' do
575
      get api('/projects/42/events', user)
576
      expect(response).to have_http_status(404)
577
      expect(json_response['message']).to eq('404 Project Not Found')
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
578 579
    end

580
    it 'returns a 404 error if user is not a member' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
581 582
      other_user = create(:user)
      get api("/projects/#{project.id}/events", other_user)
583
      expect(response).to have_http_status(404)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
584 585 586
    end
  end

587
  describe 'GET /projects/:id/snippets' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
588 589
    before { snippet }

590
    it 'returns an array of project snippets' do
591
      get api("/projects/#{project.id}/snippets", user)
592
      expect(response).to have_http_status(200)
593 594
      expect(json_response).to be_an Array
      expect(json_response.first['title']).to eq(snippet.title)
595 596 597
    end
  end

598
  describe 'GET /projects/:id/snippets/:snippet_id' do
599
    it 'returns a project snippet' do
600
      get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
601
      expect(response).to have_http_status(200)
602
      expect(json_response['title']).to eq(snippet.title)
Nihad Abbasov's avatar
Nihad Abbasov committed
603
    end
604

605
    it 'returns a 404 error if snippet id not found' do
606
      get api("/projects/#{project.id}/snippets/1234", user)
607
      expect(response).to have_http_status(404)
608
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
609 610
  end

611
  describe 'POST /projects/:id/snippets' do
612
    it 'creates a new project snippet' do
613
      post api("/projects/#{project.id}/snippets", user),
614 615
        title: 'api test', file_name: 'sample.rb', code: 'test',
        visibility_level: '0'
616
      expect(response).to have_http_status(201)
617
      expect(json_response['title']).to eq('api test')
Nihad Abbasov's avatar
Nihad Abbasov committed
618
    end
619

620
    it 'returns a 400 error if invalid snippet is given' do
621 622
      post api("/projects/#{project.id}/snippets", user)
      expect(status).to eq(400)
623
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
624 625
  end

626
  describe 'PUT /projects/:id/snippets/:snippet_id' do
627
    it 'updates an existing project snippet' do
628
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
629
        code: 'updated code'
630
      expect(response).to have_http_status(200)
631 632
      expect(json_response['title']).to eq('example')
      expect(snippet.reload.content).to eq('updated code')
633
    end
634

635
    it 'updates an existing project snippet with new title' do
636
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
637
        title: 'other api test'
638
      expect(response).to have_http_status(200)
639
      expect(json_response['title']).to eq('other api test')
640
    end
641 642
  end

643
  describe 'DELETE /projects/:id/snippets/:snippet_id' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
644 645
    before { snippet }

646
    it 'deletes existing project snippet' do
647
      expect do
648
        delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
649
      end.to change { Snippet.count }.by(-1)
650
      expect(response).to have_http_status(200)
651 652
    end

653
    it 'returns 404 when deleting unknown snippet id' do
654
      delete api("/projects/#{project.id}/snippets/1234", user)
655
      expect(response).to have_http_status(404)
Nihad Abbasov's avatar
Nihad Abbasov committed
656 657
    end
  end
658

659
  describe 'GET /projects/:id/snippets/:snippet_id/raw' do
660
    it 'gets a raw project snippet' do
661
      get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
662
      expect(response).to have_http_status(200)
663
    end
664

665
    it 'returns a 404 error if raw project snippet not found' do
666
      get api("/projects/#{project.id}/snippets/5555/raw", user)
667
      expect(response).to have_http_status(404)
668
    end
669
  end
670

671 672
  describe :fork_admin do
    let(:project_fork_target) { create(:project) }
673
    let(:project_fork_source) { create(:project, :public) }
674

675
    describe 'POST /projects/:id/fork/:forked_from_id' do
676
      let(:new_project_fork_source) { create(:project, :public) }
677

678
      it "is not available for non admin users" do
679
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
680
        expect(response).to have_http_status(403)
681 682
      end

683
      it 'allows project to be forked from an existing project' do
684
        expect(project_fork_target.forked?).not_to be_truthy
685
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
686
        expect(response).to have_http_status(201)
687
        project_fork_target.reload
688 689 690
        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
691 692
      end

693
      it 'fails if forked_from project which does not exist' do
694
        post api("/projects/#{project_fork_target.id}/fork/9999", admin)
695
        expect(response).to have_http_status(404)
696 697
      end

698
      it 'fails with 409 if already forked' do
699 700
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
        project_fork_target.reload
701
        expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
702
        post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
703
        expect(response).to have_http_status(409)
704
        project_fork_target.reload
705 706
        expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
        expect(project_fork_target.forked?).to be_truthy
707 708 709
      end
    end

710
    describe 'DELETE /projects/:id/fork' do
711
      it "is not visible to users outside group" do
712
        delete api("/projects/#{project_fork_target.id}/fork", user)
713
        expect(response).to have_http_status(404)
714 715
      end

716 717
      context 'when users belong to project group' do
        let(:project_fork_target) { create(:project, group: create(:group)) }
718

719 720 721 722 723
        before do
          project_fork_target.group.add_owner user
          project_fork_target.group.add_developer user2
        end

724
        it 'is forbidden to non-owner users' do
725
          delete api("/projects/#{project_fork_target.id}/fork", user2)
726
          expect(response).to have_http_status(403)
727 728
        end

729
        it 'makes forked project unforked' do
730 731 732 733 734
          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)
735
          expect(response).to have_http_status(200)
736 737 738 739 740
          project_fork_target.reload
          expect(project_fork_target.forked_from_project).to be_nil
          expect(project_fork_target.forked?).not_to be_truthy
        end

741
        it 'is idempotent if not forked' do
742 743
          expect(project_fork_target.forked_from_project).to be_nil
          delete api("/projects/#{project_fork_target.id}/fork", admin)
744
          expect(response).to have_http_status(200)
745 746
          expect(project_fork_target.reload.forked_from_project).to be_nil
        end
747 748 749
      end
    end
  end
750

751 752 753
  describe "POST /projects/:id/share" do
    let(:group) { create(:group) }

754
    it "shares project with group" do
755 756 757 758 759 760 761 762 763
      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

764
    it "returns a 400 error when group id is not given" do
765 766 767 768
      post api("/projects/#{project.id}/share", user), group_access: Gitlab::Access::DEVELOPER
      expect(response.status).to eq 400
    end

769
    it "returns a 400 error when access level is not given" do
770 771 772 773
      post api("/projects/#{project.id}/share", user), group_id: group.id
      expect(response.status).to eq 400
    end

774
    it "returns a 400 error when sharing is disabled" do
775 776 777 778 779
      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

780
    it "returns a 409 error when wrong params passed" do
781 782 783 784 785 786
      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

787
  describe 'GET /projects/search/:query' do
788
    let!(:query) { 'query'}
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
789 790 791 792 793
    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) }
794 795 796 797
    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') }
798

799
    context 'when unauthenticated' do
800
      it 'returns authentication error' do
801
        get api("/projects/search/#{query}")
802
        expect(response).to have_http_status(401)
803 804 805
      end
    end

806
    context 'when authenticated' do
807
      it 'returns an array of projects' do
808
        get api("/projects/search/#{query}", user)
809
        expect(response).to have_http_status(200)
810 811 812
        expect(json_response).to be_an Array
        expect(json_response.size).to eq(6)
        json_response.each {|project| expect(project['name']).to match(/.*query.*/)}
813 814 815
      end
    end

816
    context 'when authenticated as a different user' do
817
      it 'returns matching public projects' do
818
        get api("/projects/search/#{query}", user2)
819
        expect(response).to have_http_status(200)
820 821 822
        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/)}
823 824 825
      end
    end
  end
826

827 828 829 830 831 832 833 834 835 836 837
  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
838
      it 'returns authentication error' do
839 840
        project_param = { name: 'bar' }
        put api("/projects/#{project.id}"), project_param
841
        expect(response).to have_http_status(401)
842 843 844 845
      end
    end

    context 'when authenticated as project owner' do
846
      it 'updates name' do
847 848
        project_param = { name: 'bar' }
        put api("/projects/#{project.id}", user), project_param
849
        expect(response).to have_http_status(200)
850
        project_param.each_pair do |k, v|
851
          expect(json_response[k.to_s]).to eq(v)
852 853 854
        end
      end

855
      it 'updates visibility_level' do
856 857
        project_param = { visibility_level: 20 }
        put api("/projects/#{project3.id}", user), project_param
858
        expect(response).to have_http_status(200)
859
        project_param.each_pair do |k, v|
860
          expect(json_response[k.to_s]).to eq(v)
861 862 863
        end
      end

864
      it 'updates visibility_level from public to private' do
865 866 867 868
        project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })

        project_param = { public: false }
        put api("/projects/#{project3.id}", user), project_param
869
        expect(response).to have_http_status(200)
870 871 872 873 874 875
        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

876
      it 'does not update name to existing name' do
877 878
        project_param = { name: project3.name }
        put api("/projects/#{project.id}", user), project_param
879
        expect(response).to have_http_status(400)
880
        expect(json_response['message']['name']).to eq(['has already been taken'])
881 882
      end

883
      it 'updates path & name to existing path & name in different namespace' do
884 885
        project_param = { path: project4.path, name: project4.name }
        put api("/projects/#{project3.id}", user), project_param
886
        expect(response).to have_http_status(200)
887
        project_param.each_pair do |k, v|
888
          expect(json_response[k.to_s]).to eq(v)
889 890 891 892 893
        end
      end
    end

    context 'when authenticated as project master' do
894
      it 'updates path' do
895 896
        project_param = { path: 'bar' }
        put api("/projects/#{project3.id}", user4), project_param
897
        expect(response).to have_http_status(200)
898
        project_param.each_pair do |k, v|
899
          expect(json_response[k.to_s]).to eq(v)
900 901 902
        end
      end

903
      it 'updates other attributes' do
904 905 906 907 908 909 910
        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
911
        expect(response).to have_http_status(200)
912
        project_param.each_pair do |k, v|
913
          expect(json_response[k.to_s]).to eq(v)
914 915 916
        end
      end

917
      it 'does not update path to existing path' do
918 919
        project_param = { path: project.path }
        put api("/projects/#{project3.id}", user4), project_param
920
        expect(response).to have_http_status(400)
921
        expect(json_response['message']['path']).to eq(['has already been taken'])
922 923
      end

924
      it 'does not update name' do
925 926
        project_param = { name: 'bar' }
        put api("/projects/#{project3.id}", user4), project_param
927
        expect(response).to have_http_status(403)
928 929
      end

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

    context 'when authenticated as project developer' do
938
      it 'does not update other attributes' do
939 940 941 942 943 944 945
        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
946
        expect(response).to have_http_status(403)
947 948 949 950
      end
    end
  end

951
  describe 'POST /projects/:id/archive' do
952 953
    context 'on an unarchived project' do
      it 'archives the project' do
954
        post api("/projects/#{project.id}/archive", user)
955

956
        expect(response).to have_http_status(201)
957 958 959 960 961 962 963 964 965 966
        expect(json_response['archived']).to be_truthy
      end
    end

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

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

969
        expect(response).to have_http_status(201)
970 971
        expect(json_response['archived']).to be_truthy
      end
972
    end
973

974 975 976 977
    context 'user without archiving rights to the project' do
      before do
        project.team << [user3, :developer]
      end
978

979 980 981
      it 'rejects the action' do
        post api("/projects/#{project.id}/archive", user3)

982
        expect(response).to have_http_status(403)
983 984 985 986
      end
    end
  end

987
  describe 'POST /projects/:id/unarchive' do
988 989
    context 'on an unarchived project' do
      it 'remains unarchived' do
990
        post api("/projects/#{project.id}/unarchive", user)
991

992
        expect(response).to have_http_status(201)
993 994 995 996 997 998 999 1000 1001
        expect(json_response['archived']).to be_falsey
      end
    end

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

1002 1003
      it 'unarchives the project' do
        post api("/projects/#{project.id}/unarchive", user)
1004

1005
        expect(response).to have_http_status(201)
1006 1007
        expect(json_response['archived']).to be_falsey
      end
1008
    end
1009

1010 1011 1012 1013
    context 'user without archiving rights to the project' do
      before do
        project.team << [user3, :developer]
      end
1014

1015 1016 1017
      it 'rejects the action' do
        post api("/projects/#{project.id}/unarchive", user3)

1018
        expect(response).to have_http_status(403)
1019 1020 1021 1022
      end
    end
  end

1023 1024 1025
  describe 'POST /projects/:id/star' do
    context 'on an unstarred project' do
      it 'stars the project' do
1026
        expect { post api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1)
1027

1028
        expect(response).to have_http_status(201)
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
        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
1040
        expect { post api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
1041

1042
        expect(response).to have_http_status(304)
1043 1044 1045 1046
      end
    end
  end

1047
  describe 'DELETE /projects/:id/star' do
1048 1049 1050 1051 1052 1053 1054
    context 'on a starred project' do
      before do
        user.toggle_star(project)
        project.reload
      end

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

1057
        expect(response).to have_http_status(200)
1058 1059 1060 1061 1062 1063
        expect(json_response['star_count']).to eq(0)
      end
    end

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

1066
        expect(response).to have_http_status(304)
1067 1068 1069 1070
      end
    end
  end

1071 1072
  describe 'DELETE /projects/:id' do
    context 'when authenticated as user' do
1073
      it 'removes project' do
1074
        delete api("/projects/#{project.id}", user)
1075
        expect(response).to have_http_status(200)
1076 1077
      end

1078
      it 'does not remove a project if not an owner' do
1079 1080 1081
        user3 = create(:user)
        project.team << [user3, :developer]
        delete api("/projects/#{project.id}", user3)
1082
        expect(response).to have_http_status(403)
1083 1084
      end

1085
      it 'does not remove a non existing project' do
1086
        delete api('/projects/1328', user)
1087
        expect(response).to have_http_status(404)
1088 1089
      end

1090
      it 'does not remove a project not attached to user' do
1091
        delete api("/projects/#{project.id}", user2)
1092
        expect(response).to have_http_status(404)
1093 1094 1095
      end
    end

1096
    context 'when authenticated as admin' do
1097
      it 'removes any existing project' do
1098
        delete api("/projects/#{project.id}", admin)
1099
        expect(response).to have_http_status(200)
1100 1101
      end

1102
      it 'does not remove a non existing project' do
1103
        delete api('/projects/1328', admin)
1104
        expect(response).to have_http_status(404)
1105 1106 1107
      end
    end
  end
Nihad Abbasov's avatar
Nihad Abbasov committed
1108
end