projects_spec.rb 38.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 46 47
    context 'when unauthenticated' do
      it 'should return authentication error' do
        get api('/projects')
48
        expect(response.status).to eq(401)
49
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
50 51
    end

52 53 54
    context 'when authenticated' do
      it 'should return an array of projects' do
        get api('/projects', user)
55 56 57 58
        expect(response.status).to eq(200)
        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 62
      it 'should include the project labels as the tag_list' do
        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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
      it 'should include open_issues_count' do
        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

      it 'should not include open_issues_count' do
        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 85 86
      context 'and using search' do
        it 'should return searched project' do
          get api('/projects', user), { search: project.name }
87 88 89
          expect(response.status).to eq(200)
          expect(json_response).to be_an Array
          expect(json_response.length).to eq(1)
90 91 92
        end
      end

Josh Frye's avatar
Josh Frye committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
      context 'and using the visibility filter' do
        it 'should filter based on private visibility param' do
          get api('/projects', user), { visibility: 'private' }
          expect(response.status).to eq(200)
          expect(json_response).to be_an Array
          expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).count)
        end

        it 'should filter based on internal visibility param' do
          get api('/projects', user), { visibility: 'internal' }
          expect(response.status).to eq(200)
          expect(json_response).to be_an Array
          expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).count)
        end

        it 'should filter based on public visibility param' do
          get api('/projects', user), { visibility: 'public' }
          expect(response.status).to eq(200)
          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

116
      context 'and using sorting' do
117 118 119 120 121
        before do
          project2
          project3
        end

122
        it 'should return the correct order when sorted by id' do
123
          get api('/projects', user), { order_by: 'id', sort: 'desc' }
124 125 126
          expect(response.status).to eq(200)
          expect(json_response).to be_an Array
          expect(json_response.first['id']).to eq(project3.id)
127 128
        end
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
129 130 131
    end
  end

132
  describe 'GET /projects/all' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
133 134
    before { project }

135 136 137
    context 'when unauthenticated' do
      it 'should return authentication error' do
        get api('/projects/all')
138
        expect(response.status).to eq(401)
139 140 141
      end
    end

142 143 144
    context 'when authenticated as regular user' do
      it 'should return authentication error' do
        get api('/projects/all', user)
145
        expect(response.status).to eq(403)
146 147 148
      end
    end

149 150 151
    context 'when authenticated as admin' do
      it 'should return an array of all projects' do
        get api('/projects/all', admin)
152 153
        expect(response.status).to eq(200)
        expect(json_response).to be_an Array
Marin Jankovski's avatar
Marin Jankovski committed
154

155 156
        expect(json_response).to satisfy do |response|
          response.one? do |entry|
157
            entry.has_key?('permissions') &&
158
            entry['name'] == project.name &&
159
              entry['owner']['username'] == user.username
160 161
          end
        end
162 163 164 165
      end
    end
  end

166
  describe 'GET /projects/starred' do
167 168
    let(:public_project) { create(:project, :public) }

169
    before do
170 171
      project_member2
      user3.update_attributes(starred_projects: [project, project2, project3, public_project])
172 173
    end

174 175
    it 'should return the starred projects viewable by the user' do
      get api('/projects/starred', user3)
176 177
      expect(response.status).to eq(200)
      expect(json_response).to be_an Array
178
      expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id)
179 180 181
    end
  end

182 183
  describe 'POST /projects' do
    context 'maximum number of projects reached' do
184
      it 'should not create new project and respond with 403' do
185
        allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
186 187
        expect { post api('/projects', user2), name: 'foo' }.
          to change {Project.count}.by(0)
188
        expect(response.status).to eq(403)
189 190 191
      end
    end

192 193 194
    it 'should create new project without path and return 201' do
      expect { post api('/projects', user), name: 'foo' }.
        to change { Project.count }.by(1)
195
      expect(response.status).to eq(201)
196 197
    end

198
    it 'should create last project before reaching project limit' do
199
      allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1)
200
      post api('/projects', user2), name: 'foo'
201
      expect(response.status).to eq(201)
202 203
    end

204
    it 'should not create new project without name and return 400' do
205
      expect { post api('/projects', user) }.not_to change { Project.count }
206
      expect(response.status).to eq(400)
207
    end
Alex Denisov's avatar
Alex Denisov committed
208

209
    it "should assign attributes to project" do
210
      project = attributes_for(:project, {
211
        path: 'camelCasePath',
Robert Speicher's avatar
Robert Speicher committed
212
        description: FFaker::Lorem.sentence,
213 214 215
        issues_enabled: false,
        merge_requests_enabled: false,
        wiki_enabled: false
Alex Denisov's avatar
Alex Denisov committed
216 217
      })

218
      post api('/projects', user), project
Alex Denisov's avatar
Alex Denisov committed
219

220
      project.each_pair do |k,v|
221
        expect(json_response[k.to_s]).to eq(v)
Alex Denisov's avatar
Alex Denisov committed
222
      end
223
    end
224

225
    it 'should set a project as public' do
226
      project = attributes_for(:project, :public)
227
      post api('/projects', user), project
228 229
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
230 231
    end

232
    it 'should set a project as public using :public' do
233
      project = attributes_for(:project, { public: true })
234
      post api('/projects', user), project
235 236
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
237 238
    end

239
    it 'should set a project as internal' do
240
      project = attributes_for(:project, :internal)
241
      post api('/projects', user), project
242 243
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
244 245
    end

246
    it 'should set a project as internal overriding :public' do
247
      project = attributes_for(:project, :internal, { public: true })
248
      post api('/projects', user), project
249 250
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
251 252
    end

253
    it 'should set a project as private' do
254
      project = attributes_for(:project, :private)
255
      post api('/projects', user), project
256 257
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
258 259
    end

260
    it 'should set a project as private using :public' do
261
      project = attributes_for(:project, { public: false })
262
      post api('/projects', user), project
263 264
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
265
    end
266 267 268 269

    context 'when a visibility level is restricted' do
      before do
        @project = attributes_for(:project, { public: true })
270
        stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
271 272 273 274
      end

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

276 277 278 279 280 281 282 283 284 285 286 287 288 289
        expect(response.status).to eq(400)
        expect(json_response['message']['visibility_level'].first).to(
          match('restricted by your GitLab administrator')
        )
      end

      it 'should allow an admin to override restricted visibility settings' do
        post api('/projects', admin), @project
        expect(json_response['public']).to be_truthy
        expect(json_response['visibility_level']).to(
          eq(Gitlab::VisibilityLevel::PUBLIC)
        )
      end
    end
290 291
  end

292
  describe 'POST /projects/user/:id' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
293
    before { project }
Angus MacArthur's avatar
Angus MacArthur committed
294 295
    before { admin }

296
    it 'should create new project without path and return 201' do
297
      expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
298
      expect(response.status).to eq(201)
Angus MacArthur's avatar
Angus MacArthur committed
299 300
    end

301 302
    it 'should respond with 400 on failure and not project' do
      expect { post api("/projects/user/#{user.id}", admin) }.
303
        not_to change { Project.count }
304

305 306
      expect(response.status).to eq(400)
      expect(json_response['message']['name']).to eq([
307 308
        'can\'t be blank',
        'is too short (minimum is 0 characters)',
Douwe Maan's avatar
Douwe Maan committed
309
        Gitlab::Regex.project_name_regex_message
310 311
      ])
      expect(json_response['message']['path']).to eq([
312 313
        'can\'t be blank',
        'is too short (minimum is 0 characters)',
Douwe Maan's avatar
Douwe Maan committed
314
        Gitlab::Regex.send(:project_path_regex_message)
315
      ])
Angus MacArthur's avatar
Angus MacArthur committed
316 317
    end

318
    it 'should assign attributes to project' do
Angus MacArthur's avatar
Angus MacArthur committed
319
      project = attributes_for(:project, {
Robert Speicher's avatar
Robert Speicher committed
320
        description: FFaker::Lorem.sentence,
321 322 323
        issues_enabled: false,
        merge_requests_enabled: false,
        wiki_enabled: false
Angus MacArthur's avatar
Angus MacArthur committed
324 325 326 327
      })

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

328
      project.each_pair do |k,v|
Angus MacArthur's avatar
Angus MacArthur committed
329
        next if k == :path
330
        expect(json_response[k.to_s]).to eq(v)
Angus MacArthur's avatar
Angus MacArthur committed
331 332
      end
    end
333

334
    it 'should set a project as public' do
335
      project = attributes_for(:project, :public)
336
      post api("/projects/user/#{user.id}", admin), project
337 338
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
339 340
    end

341
    it 'should set a project as public using :public' do
342 343
      project = attributes_for(:project, { public: true })
      post api("/projects/user/#{user.id}", admin), project
344 345
      expect(json_response['public']).to be_truthy
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
346
    end
347

348
    it 'should set a project as internal' do
349
      project = attributes_for(:project, :internal)
350
      post api("/projects/user/#{user.id}", admin), project
351 352
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
353 354
    end

355
    it 'should set a project as internal overriding :public' do
356
      project = attributes_for(:project, :internal, { public: true })
357
      post api("/projects/user/#{user.id}", admin), project
358 359
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
360
    end
361

362
    it 'should set a project as private' do
363
      project = attributes_for(:project, :private)
364
      post api("/projects/user/#{user.id}", admin), project
365 366
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
367 368
    end

369
    it 'should set a project as private using :public' do
370 371
      project = attributes_for(:project, { public: false })
      post api("/projects/user/#{user.id}", admin), project
372 373
      expect(json_response['public']).to be_falsey
      expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
374
    end
Angus MacArthur's avatar
Angus MacArthur committed
375 376
  end

377 378 379 380 381 382 383 384 385 386 387 388 389 390
  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")

      expect(response.status).to be(201)
      expect(json_response['alt']).to eq("dk")
      expect(json_response['url']).to start_with("/uploads/")
      expect(json_response['url']).to end_with("/dk.png")
      expect(json_response['is_image']).to eq(true)
    end
  end

391
  describe 'GET /projects/:id' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
392
    before { project }
393
    before { project_member }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
394

395
    it 'should return a project by id' do
Robert Speicher's avatar
Robert Speicher committed
396
      get api("/projects/#{project.id}", user)
397 398 399
      expect(response.status).to eq(200)
      expect(json_response['name']).to eq(project.name)
      expect(json_response['owner']['username']).to eq(user.username)
Nihad Abbasov's avatar
Nihad Abbasov committed
400
    end
401

402
    it 'should return a project by path name' do
403
      get api("/projects/#{project.id}", user)
404 405
      expect(response.status).to eq(200)
      expect(json_response['name']).to eq(project.name)
406
    end
407

408 409
    it 'should return a 404 error if not found' do
      get api('/projects/42', user)
410 411
      expect(response.status).to eq(404)
      expect(json_response['message']).to eq('404 Project Not Found')
412
    end
413

414
    it 'should return a 404 error if user is not a member' do
415 416
      other_user = create(:user)
      get api("/projects/#{project.id}", other_user)
417
      expect(response.status).to eq(404)
418
    end
419

420 421 422 423 424 425 426 427 428
    it 'should handle users with dots' do
      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)
      expect(response.status).to eq(200)
      expect(json_response['name']).to eq(project.name)
    end

429
    describe 'permissions' do
430
      context 'all projects' do
431 432 433
        before { project.team << [user, :master] }

        it 'contains permission information' do
434 435 436 437 438 439 440 441 442
          get api("/projects", user)

          expect(response.status).to eq(200)
          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

443
      context 'personal project' do
444
        it 'sets project access and returns 200' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
445 446
          project.team << [user, :master]
          get api("/projects/#{project.id}", user)
447

448 449 450 451 452
          expect(response.status).to eq(200)
          expect(json_response['permissions']['project_access']['access_level']).
            to eq(Gitlab::Access::MASTER)
          expect(json_response['permissions']['group_access']).to be_nil
        end
453 454 455
      end

      context 'group project' do
456 457 458 459
        let(:project2) { create(:project, group: create(:group)) }

        before { project2.group.add_owner(user) }

460
        it 'should set the owner and return 200' do
461 462
          get api("/projects/#{project2.id}", user)

463 464 465 466 467
          expect(response.status).to eq(200)
          expect(json_response['permissions']['project_access']).to be_nil
          expect(json_response['permissions']['group_access']['access_level']).
            to eq(Gitlab::Access::OWNER)
        end
468 469
      end
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
470 471
  end

472
  describe 'GET /projects/:id/events' do
Douwe Maan's avatar
Douwe Maan committed
473
    before { project_member2 }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
474

475 476 477 478 479 480 481 482 483 484 485
    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

      it { expect(response.status).to eq(200) }

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

487 488 489 490 491 492 493 494 495 496 497 498
        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
499 500
    end

501 502
    it 'should return a 404 error if not found' do
      get api('/projects/42/events', user)
503 504
      expect(response.status).to eq(404)
      expect(json_response['message']).to eq('404 Project Not Found')
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
505 506
    end

507
    it 'should return a 404 error if user is not a member' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
508 509
      other_user = create(:user)
      get api("/projects/#{project.id}/events", other_user)
510
      expect(response.status).to eq(404)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
511 512 513
    end
  end

514
  describe 'GET /projects/:id/snippets' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
515 516
    before { snippet }

517
    it 'should return an array of project snippets' do
518
      get api("/projects/#{project.id}/snippets", user)
519 520 521
      expect(response.status).to eq(200)
      expect(json_response).to be_an Array
      expect(json_response.first['title']).to eq(snippet.title)
522 523 524
    end
  end

525 526
  describe 'GET /projects/:id/snippets/:snippet_id' do
    it 'should return a project snippet' do
527
      get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
528 529
      expect(response.status).to eq(200)
      expect(json_response['title']).to eq(snippet.title)
Nihad Abbasov's avatar
Nihad Abbasov committed
530
    end
531

532
    it 'should return a 404 error if snippet id not found' do
533
      get api("/projects/#{project.id}/snippets/1234", user)
534
      expect(response.status).to eq(404)
535
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
536 537
  end

538 539
  describe 'POST /projects/:id/snippets' do
    it 'should create a new project snippet' do
540
      post api("/projects/#{project.id}/snippets", user),
541 542
        title: 'api test', file_name: 'sample.rb', code: 'test',
        visibility_level: '0'
543 544
      expect(response.status).to eq(201)
      expect(json_response['title']).to eq('api test')
Nihad Abbasov's avatar
Nihad Abbasov committed
545
    end
546

547 548 549
    it 'should return a 400 error if invalid snippet is given' do
      post api("/projects/#{project.id}/snippets", user)
      expect(status).to eq(400)
550
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
551 552
  end

553
  describe 'PUT /projects/:id/snippets/:snippet_id' do
554
    it 'should update an existing project snippet' do
555
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
556
        code: 'updated code'
557 558 559
      expect(response.status).to eq(200)
      expect(json_response['title']).to eq('example')
      expect(snippet.reload.content).to eq('updated code')
560
    end
561

562
    it 'should update an existing project snippet with new title' do
563
      put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
564
        title: 'other api test'
565 566
      expect(response.status).to eq(200)
      expect(json_response['title']).to eq('other api test')
567
    end
568 569
  end

570
  describe 'DELETE /projects/:id/snippets/:snippet_id' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
571 572
    before { snippet }

573
    it 'should delete existing project snippet' do
574
      expect do
575
        delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
576
      end.to change { Snippet.count }.by(-1)
577
      expect(response.status).to eq(200)
578 579
    end

580
    it 'should return 404 when deleting unknown snippet id' do
581
      delete api("/projects/#{project.id}/snippets/1234", user)
582
      expect(response.status).to eq(404)
Nihad Abbasov's avatar
Nihad Abbasov committed
583 584
    end
  end
585

586 587
  describe 'GET /projects/:id/snippets/:snippet_id/raw' do
    it 'should get a raw project snippet' do
588
      get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
589
      expect(response.status).to eq(200)
590
    end
591

592
    it 'should return a 404 error if raw project snippet not found' do
593
      get api("/projects/#{project.id}/snippets/5555/raw", user)
594
      expect(response.status).to eq(404)
595
    end
596
  end
597

598 599 600
  describe :deploy_keys do
    let(:deploy_keys_project) { create(:deploy_keys_project, project: project) }
    let(:deploy_key) { deploy_keys_project.deploy_key }
Matt Humphrey's avatar
Matt Humphrey committed
601

602
    describe 'GET /projects/:id/keys' do
603
      before { deploy_key }
Matt Humphrey's avatar
Matt Humphrey committed
604

605
      it 'should return array of ssh keys' do
606
        get api("/projects/#{project.id}/keys", user)
607 608 609
        expect(response.status).to eq(200)
        expect(json_response).to be_an Array
        expect(json_response.first['title']).to eq(deploy_key.title)
610
      end
Matt Humphrey's avatar
Matt Humphrey committed
611 612
    end

613 614
    describe 'GET /projects/:id/keys/:key_id' do
      it 'should return a single key' do
615
        get api("/projects/#{project.id}/keys/#{deploy_key.id}", user)
616 617
        expect(response.status).to eq(200)
        expect(json_response['title']).to eq(deploy_key.title)
618
      end
Matt Humphrey's avatar
Matt Humphrey committed
619

620
      it 'should return 404 Not Found with invalid ID' do
621
        get api("/projects/#{project.id}/keys/404", user)
622
        expect(response.status).to eq(404)
623
      end
Matt Humphrey's avatar
Matt Humphrey committed
624 625
    end

626 627 628
    describe 'POST /projects/:id/keys' do
      it 'should not create an invalid ssh key' do
        post api("/projects/#{project.id}/keys", user), { title: 'invalid key' }
629 630
        expect(response.status).to eq(400)
        expect(json_response['message']['key']).to eq([
631 632 633
          'can\'t be blank',
          'is too short (minimum is 0 characters)',
          'is invalid'
634
        ])
635 636 637 638
      end

      it 'should not create a key without title' do
        post api("/projects/#{project.id}/keys", user), key: 'some key'
639 640
        expect(response.status).to eq(400)
        expect(json_response['message']['title']).to eq([
641 642
          'can\'t be blank',
          'is too short (minimum is 0 characters)'
643
        ])
644 645
      end

646
      it 'should create new ssh key' do
647
        key_attrs = attributes_for :key
648
        expect do
649
          post api("/projects/#{project.id}/keys", user), key_attrs
650
        end.to change{ project.deploy_keys.count }.by(1)
651
      end
Matt Humphrey's avatar
Matt Humphrey committed
652 653
    end

654
    describe 'DELETE /projects/:id/keys/:key_id' do
655 656
      before { deploy_key }

657
      it 'should delete existing key' do
658
        expect do
659
          delete api("/projects/#{project.id}/keys/#{deploy_key.id}", user)
660
        end.to change{ project.deploy_keys.count }.by(-1)
661 662
      end

663
      it 'should return 404 Not Found with invalid ID' do
664
        delete api("/projects/#{project.id}/keys/404", user)
665
        expect(response.status).to eq(404)
666
      end
Matt Humphrey's avatar
Matt Humphrey committed
667 668
    end
  end
669 670 671

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

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

      it "shouldn't available for non admin users" do
        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
679
        expect(response.status).to eq(403)
680 681
      end

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

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

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

709
    describe 'DELETE /projects/:id/fork' do
710

711
      it "shouldn't be visible to users outside group" do
712
        delete api("/projects/#{project_fork_target.id}/fork", user)
713
        expect(response.status).to eq(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 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
        before do
          project_fork_target.group.add_owner user
          project_fork_target.group.add_developer user2
        end

        it 'should be forbidden to non-owner users' do
          delete api("/projects/#{project_fork_target.id}/fork", user2)
          expect(response.status).to eq(403)
        end

        it 'should make forked project unforked' do
          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)
          expect(response.status).to eq(200)
          project_fork_target.reload
          expect(project_fork_target.forked_from_project).to be_nil
          expect(project_fork_target.forked?).not_to be_truthy
        end

        it 'should be idempotent if not forked' do
          expect(project_fork_target.forked_from_project).to be_nil
          delete api("/projects/#{project_fork_target.id}/fork", admin)
          expect(response.status).to eq(200)
          expect(project_fork_target.reload.forked_from_project).to be_nil
        end
747 748 749
      end
    end
  end
750

751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
  describe "POST /projects/:id/share" do
    let(:group) { create(:group) }

    it "should share project with group" do
      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

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

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

    it "should return a 400 error when sharing is disabled" do
      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

    it "should return a 409 error when wrong params passed" do
      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 800
    context 'when unauthenticated' do
      it 'should return authentication error' do
801
        get api("/projects/search/#{query}")
802
        expect(response.status).to eq(401)
803 804 805
      end
    end

806 807
    context 'when authenticated' do
      it 'should return an array of projects' do
808
        get api("/projects/search/#{query}",user)
809 810 811 812
        expect(response.status).to eq(200)
        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 817
    context 'when authenticated as a different user' do
      it 'should return matching public projects' do
818
        get api("/projects/search/#{query}", user2)
819 820 821 822
        expect(response.status).to eq(200)
        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 838 839 840
  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
      it 'should return authentication error' do
        project_param = { name: 'bar' }
        put api("/projects/#{project.id}"), project_param
841
        expect(response.status).to eq(401)
842 843 844 845 846 847 848
      end
    end

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

      it 'should update visibility_level' do
        project_param = { visibility_level: 20 }
        put api("/projects/#{project3.id}", user), project_param
858
        expect(response.status).to eq(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 865 866 867 868 869 870 871 872 873 874 875
      it 'should update visibility_level from public to private' do
        project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })

        project_param = { public: false }
        put api("/projects/#{project3.id}", user), project_param
        expect(response.status).to eq(200)
        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 877 878
      it 'should not update name to existing name' do
        project_param = { name: project3.name }
        put api("/projects/#{project.id}", user), project_param
879 880
        expect(response.status).to eq(400)
        expect(json_response['message']['name']).to eq(['has already been taken'])
881 882 883 884 885
      end

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

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

      it 'should update other attributes' do
        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.status).to eq(200)
912
        project_param.each_pair do |k, v|
913
          expect(json_response[k.to_s]).to eq(v)
914 915 916 917 918 919
        end
      end

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

      it 'should not update name' do
        project_param = { name: 'bar' }
        put api("/projects/#{project3.id}", user4), project_param
927
        expect(response.status).to eq(403)
928 929 930 931 932
      end

      it 'should not update visibility_level' do
        project_param = { visibility_level: 20 }
        put api("/projects/#{project3.id}", user4), project_param
933
        expect(response.status).to eq(403)
934 935 936 937 938 939 940 941 942 943 944 945
      end
    end

    context 'when authenticated as project developer' do
      it 'should not update other attributes' do
        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.status).to eq(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.status).to eq(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.status).to eq(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 982
      it 'rejects the action' do
        post api("/projects/#{project.id}/archive", user3)

        expect(response.status).to eq(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.status).to eq(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.status).to eq(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 1018
      it 'rejects the action' do
        post api("/projects/#{project.id}/unarchive", user3)

        expect(response.status).to eq(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 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039

        expect(response.status).to eq(201)
        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 1043 1044 1045 1046

        expect(response.status).to eq(304)
      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.status).to eq(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 1067 1068 1069 1070

        expect(response.status).to eq(304)
      end
    end
  end

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

1078
      it 'should 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.status).to eq(403)
1083 1084
      end

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

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

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

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