Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests/api/projects_spec.rb')
-rw-r--r--spec/requests/api/projects_spec.rb321
1 files changed, 223 insertions, 98 deletions
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index f95a287a184..6ed68fcff09 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -52,6 +52,24 @@ describe API::Projects do
end
end
+ shared_examples_for 'projects response without N + 1 queries' do
+ it 'avoids N + 1 queries' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ get api('/projects', current_user)
+ end.count
+
+ if defined?(additional_project)
+ additional_project
+ else
+ create(:empty_project, :public)
+ end
+
+ expect do
+ get api('/projects', current_user)
+ end.not_to exceed_query_limit(control_count + 8)
+ end
+ end
+
let!(:public_project) { create(:empty_project, :public, name: 'public_project') }
before do
project
@@ -62,9 +80,13 @@ describe API::Projects do
context 'when unauthenticated' do
it_behaves_like 'projects response' do
- let(:filter) { {} }
+ let(:filter) { { search: project.name } }
+ let(:current_user) { user }
+ let(:projects) { [project] }
+ end
+
+ it_behaves_like 'projects response without N + 1 queries' do
let(:current_user) { nil }
- let(:projects) { [public_project] }
end
end
@@ -75,6 +97,21 @@ describe API::Projects do
let(:projects) { [public_project, project, project2, project3] }
end
+ it_behaves_like 'projects response without N + 1 queries' do
+ let(:current_user) { user }
+ end
+
+ context 'when some projects are in a group' do
+ before do
+ create(:empty_project, :public, group: create(:group))
+ end
+
+ it_behaves_like 'projects response without N + 1 queries' do
+ let(:current_user) { user }
+ let(:additional_project) { create(:empty_project, :public, group: create(:group)) }
+ end
+ end
+
it 'includes the project labels as the tag_list' do
get api('/projects', user)
@@ -122,6 +159,31 @@ describe API::Projects do
expect(json_response.first).to include 'statistics'
end
+ context 'when external issue tracker is enabled' do
+ let!(:jira_service) { create(:jira_service, project: project) }
+
+ it 'includes open_issues_count' do
+ get api('/projects', user)
+
+ expect(response.status).to eq 200
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.first.keys).to include('open_issues_count')
+ expect(json_response.find { |hash| hash['id'] == project.id }.keys).to include('open_issues_count')
+ end
+
+ it 'does not include open_issues_count if issues are disabled' do
+ project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
+
+ get api('/projects', user)
+
+ expect(response.status).to eq 200
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.find { |hash| hash['id'] == project.id }.keys).not_to include('open_issues_count')
+ end
+ end
+
context 'and with simple=true' do
it 'returns a simplified version of all the projects' do
expected_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace)
@@ -288,15 +350,15 @@ describe API::Projects do
context 'maximum number of projects reached' do
it 'does not create new project and respond with 403' do
allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
- expect { post api('/projects', user2), name: 'foo' }.
- to change {Project.count}.by(0)
+ expect { post api('/projects', user2), name: 'foo' }
+ .to change {Project.count}.by(0)
expect(response).to have_http_status(403)
end
end
it 'creates new project without path but with name and returns 201' do
- expect { post api('/projects', user), name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ expect { post api('/projects', user), name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -306,8 +368,8 @@ describe API::Projects do
end
it 'creates new project without name but with path and returns 201' do
- expect { post api('/projects', user), path: 'foo_project' }.
- to change { Project.count }.by(1)
+ expect { post api('/projects', user), path: 'foo_project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -316,15 +378,15 @@ describe API::Projects do
expect(project.path).to eq('foo_project')
end
- it 'creates new project name and path and returns 201' do
- expect { post api('/projects', user), path: 'foo-Project', name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ it 'creates new project with name and path and returns 201' do
+ expect { post api('/projects', user), path: 'path-project-Foo', name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
expect(project.name).to eq('Foo Project')
- expect(project.path).to eq('foo-Project')
+ expect(project.path).to eq('path-project-Foo')
end
it 'creates last project before reaching project limit' do
@@ -347,7 +409,8 @@ describe API::Projects do
wiki_enabled: false,
only_allow_merge_if_pipeline_succeeds: false,
request_access_enabled: true,
- only_allow_merge_if_all_discussions_are_resolved: false
+ only_allow_merge_if_all_discussions_are_resolved: false,
+ ci_config_path: 'a/custom/path'
})
post api('/projects', user), project
@@ -390,6 +453,23 @@ describe API::Projects do
expect(json_response['visibility']).to eq('private')
end
+ it 'sets tag list to a project' do
+ project = attributes_for(:project, tag_list: %w[tagFirst tagSecond])
+
+ post api('/projects', user), project
+
+ expect(json_response['tag_list']).to eq(%w[tagFirst tagSecond])
+ end
+
+ it 'uploads avatar for project a project' do
+ project = attributes_for(:project, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif'))
+
+ post api('/projects', user), project
+
+ project_id = json_response['id']
+ expect(json_response['avatar_url']).to eq("http://localhost/uploads/-/system/project/avatar/#{project_id}/banana_sample.gif")
+ end
+
it 'sets a project as allowing merge even if build fails' do
project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: false })
post api('/projects', user), project
@@ -458,18 +538,55 @@ describe API::Projects do
end
end
+ describe 'GET /users/:user_id/projects/' do
+ let!(:public_project) { create(:empty_project, :public, name: 'public_project', creator_id: user4.id, namespace: user4.namespace) }
+
+ it 'returns error when user not found' do
+ get api('/users/9999/projects/')
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+
+ it 'returns projects filtered by user' do
+ get api("/users/#{user4.id}/projects/", user)
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
+ end
+ end
+
describe 'POST /projects/user/:id' do
- before { project }
- before { admin }
+ before do
+ expect(project).to be_persisted
+ end
- it 'creates new project without path and return 201' do
- expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
+ it 'creates new project without path but with name and return 201' do
+ expect { post api("/projects/user/#{user.id}", admin), name: 'Foo Project' }.to change {Project.count}.by(1)
expect(response).to have_http_status(201)
+
+ project = Project.first
+
+ expect(project.name).to eq('Foo Project')
+ expect(project.path).to eq('foo-project')
+ end
+
+ it 'creates new project with name and path and returns 201' do
+ expect { post api("/projects/user/#{user.id}", admin), path: 'path-project-Foo', name: 'Foo Project' }
+ .to change { Project.count }.by(1)
+ expect(response).to have_http_status(201)
+
+ project = Project.first
+
+ expect(project.name).to eq('Foo Project')
+ expect(project.path).to eq('path-project-Foo')
end
it 'responds with 400 on failure and not project' do
- expect { post api("/projects/user/#{user.id}", admin) }.
- not_to change { Project.count }
+ expect { post api("/projects/user/#{user.id}", admin) }
+ .not_to change { Project.count }
expect(response).to have_http_status(400)
expect(json_response['error']).to eq('name is missing')
@@ -548,7 +665,9 @@ describe API::Projects do
end
describe "POST /projects/:id/uploads" do
- before { project }
+ before do
+ project
+ end
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")
@@ -611,10 +730,13 @@ describe API::Projects do
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['import_status']).to be_present
+ expect(json_response).to include("import_error")
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_jobs']).to be_present
+ expect(json_response['ci_config_path']).to be_nil
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)
@@ -646,7 +768,7 @@ describe API::Projects do
dot_user = create(:user, username: 'dot.user')
project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace)
- get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user)
+ get api("/projects/#{CGI.escape(project.full_path)}", dot_user)
expect(response).to have_http_status(200)
expect(json_response['name']).to eq(project.name)
end
@@ -660,7 +782,8 @@ describe API::Projects do
'name' => user.namespace.name,
'path' => user.namespace.path,
'kind' => user.namespace.kind,
- 'full_path' => user.namespace.full_path
+ 'full_path' => user.namespace.full_path,
+ 'parent_id' => nil
})
end
@@ -678,16 +801,64 @@ describe API::Projects do
expect(json_response).to include 'statistics'
end
+ it "includes import_error if user can admin project" do
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to include("import_error")
+ end
+
+ it "does not include import_error if user cannot admin project" do
+ get api("/projects/#{project.id}", user3)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).not_to include("import_error")
+ end
+
+ context 'links exposure' do
+ it 'exposes related resources full URIs' do
+ get api("/projects/#{project.id}", user)
+
+ links = json_response['_links']
+
+ expect(links['self']).to end_with("/api/v4/projects/#{project.id}")
+ expect(links['issues']).to end_with("/api/v4/projects/#{project.id}/issues")
+ expect(links['merge_requests']).to end_with("/api/v4/projects/#{project.id}/merge_requests")
+ expect(links['repo_branches']).to end_with("/api/v4/projects/#{project.id}/repository/branches")
+ expect(links['labels']).to end_with("/api/v4/projects/#{project.id}/labels")
+ expect(links['events']).to end_with("/api/v4/projects/#{project.id}/events")
+ expect(links['members']).to end_with("/api/v4/projects/#{project.id}/members")
+ end
+
+ it 'filters related URIs when their feature is not enabled' do
+ project = create(:empty_project, :public,
+ :merge_requests_disabled,
+ :issues_disabled,
+ creator_id: user.id,
+ namespace: user.namespace)
+
+ get api("/projects/#{project.id}", user)
+
+ links = json_response['_links']
+
+ expect(links.has_key?('merge_requests')).to be_falsy
+ expect(links.has_key?('issues')).to be_falsy
+ expect(links['self']).to end_with("/api/v4/projects/#{project.id}")
+ end
+ end
+
describe 'permissions' do
context 'all projects' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'contains permission information' do
get api("/projects", user)
expect(response).to have_http_status(200)
- expect(json_response.first['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ 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
@@ -698,8 +869,8 @@ describe API::Projects do
get api("/projects/#{project.id}", user)
expect(response).to have_http_status(200)
- expect(json_response['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ expect(json_response['permissions']['project_access']['access_level'])
+ .to eq(Gitlab::Access::MASTER)
expect(json_response['permissions']['group_access']).to be_nil
end
end
@@ -707,79 +878,23 @@ describe API::Projects do
context 'group project' do
let(:project2) { create(:empty_project, group: create(:group)) }
- before { project2.group.add_owner(user) }
+ before do
+ project2.group.add_owner(user)
+ end
it 'sets the owner and return 200' do
get api("/projects/#{project2.id}", user)
expect(response).to have_http_status(200)
expect(json_response['permissions']['project_access']).to be_nil
- expect(json_response['permissions']['group_access']['access_level']).
- to eq(Gitlab::Access::OWNER)
+ expect(json_response['permissions']['group_access']['access_level'])
+ .to eq(Gitlab::Access::OWNER)
end
end
end
end
end
- describe 'GET /projects/:id/events' do
- shared_examples_for 'project events response' do
- it 'returns the project events' do
- member = create(:user)
- create(:project_member, :developer, user: member, project: project)
- 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", current_user)
-
- expect(response).to have_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
-
- first_event = json_response.first
- expect(first_event['action_name']).to eq('commented on')
- expect(first_event['note']['body']).to eq('What an awesome day!')
-
- last_event = json_response.last
-
- expect(last_event['action_name']).to eq('joined')
- expect(last_event['project_id'].to_i).to eq(project.id)
- expect(last_event['author_username']).to eq(member.username)
- expect(last_event['author']['name']).to eq(member.name)
- end
- end
-
- context 'when unauthenticated' do
- it_behaves_like 'project events response' do
- let(:project) { create(:empty_project, :public) }
- let(:current_user) { nil }
- end
- end
-
- context 'when authenticated' do
- context 'valid request' do
- it_behaves_like 'project events response' do
- let(:current_user) { user }
- end
- end
-
- it 'returns a 404 error if not found' do
- get api('/projects/42/events', user)
-
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('404 Project Not Found')
- end
-
- it 'returns a 404 error if user is not a member' do
- other_user = create(:user)
-
- get api("/projects/#{project.id}/events", other_user)
-
- expect(response).to have_http_status(404)
- end
- end
- end
-
describe 'GET /projects/:id/users' do
shared_examples_for 'project users response' do
it 'returns the project users' do
@@ -831,7 +946,9 @@ describe API::Projects do
end
describe 'GET /projects/:id/snippets' do
- before { snippet }
+ before do
+ snippet
+ end
it 'returns an array of project snippets' do
get api("/projects/#{project.id}/snippets", user)
@@ -888,7 +1005,9 @@ describe API::Projects do
end
describe 'DELETE /projects/:id/snippets/:snippet_id' do
- before { snippet }
+ before do
+ snippet
+ end
it 'deletes existing project snippet' do
expect do
@@ -1083,14 +1202,16 @@ describe API::Projects do
end
describe 'PUT /projects/:id' do
- before { project }
- before { user }
- before { user3 }
- before { user4 }
- before { project3 }
- before { project4 }
- before { project_member2 }
- before { project_member }
+ before do
+ expect(project).to be_persisted
+ expect(user).to be_persisted
+ expect(user3).to be_persisted
+ expect(user4).to be_persisted
+ expect(project3).to be_persisted
+ expect(project4).to be_persisted
+ expect(project_member2).to be_persisted
+ expect(project_member).to be_persisted
+ end
it 'returns 400 when nothing sent' do
project_param = {}
@@ -1440,6 +1561,8 @@ describe API::Projects do
expect(json_response['owner']['id']).to eq(user2.id)
expect(json_response['namespace']['id']).to eq(user2.namespace.id)
expect(json_response['forked_from_project']['id']).to eq(project.id)
+ expect(json_response['import_status']).to eq('scheduled')
+ expect(json_response).to include("import_error")
end
it 'forks if user is admin' do
@@ -1451,6 +1574,8 @@ describe API::Projects do
expect(json_response['owner']['id']).to eq(admin.id)
expect(json_response['namespace']['id']).to eq(admin.namespace.id)
expect(json_response['forked_from_project']['id']).to eq(project.id)
+ expect(json_response['import_status']).to eq('scheduled')
+ expect(json_response).to include("import_error")
end
it 'fails on missing project access for the project to fork' do