summaryrefslogtreecommitdiff
path: root/spec/requests/api/groups_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests/api/groups_spec.rb')
-rw-r--r--spec/requests/api/groups_spec.rb219
1 files changed, 218 insertions, 1 deletions
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 18feff85482..9a449499576 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -231,6 +231,27 @@ describe API::Groups do
end
end
+ context "when using top_level_only" do
+ let(:top_level_group) { create(:group, name: 'top-level-group') }
+ let(:subgroup) { create(:group, :nested, name: 'subgroup') }
+ let(:response_groups) { json_response.map { |group| group['name'] } }
+
+ before do
+ top_level_group.add_owner(user1)
+ subgroup.add_owner(user1)
+ end
+
+ it "doesn't return subgroups" do
+ get api("/groups", user1), params: { top_level_only: true }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(response_groups).to include(top_level_group.name)
+ expect(response_groups).not_to include(subgroup.name)
+ end
+ end
+
context "when using sorting" do
let(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") }
let(:group4) { create(:group, name: "same-name", path: "y#{group1.path}") }
@@ -415,6 +436,8 @@ describe API::Groups do
it "returns one of user1's groups" do
project = create(:project, namespace: group2, path: 'Foo')
create(:project_group_link, project: project, group: group1)
+ group = create(:group)
+ link = create(:group_group_link, shared_group: group1, shared_with_group: group)
get api("/groups/#{group1.id}", user1)
@@ -439,6 +462,13 @@ describe API::Groups do
expect(json_response['full_path']).to eq(group1.full_path)
expect(json_response['parent_id']).to eq(group1.parent_id)
expect(json_response['created_at']).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_full_path']).to eq(group.full_path)
+ expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
+ expect(json_response['shared_with_groups'][0]).to have_key('expires_at')
expect(json_response['projects']).to be_an Array
expect(json_response['projects'].length).to eq(2)
expect(json_response['shared_projects']).to be_an Array
@@ -505,7 +535,7 @@ describe API::Groups do
.to contain_exactly(projects[:public].id, projects[:internal].id)
end
- it 'avoids N+1 queries' do
+ it 'avoids N+1 queries with project links' do
get api("/groups/#{group1.id}", admin)
control_count = ActiveRecord::QueryRecorder.new do
@@ -518,6 +548,24 @@ describe API::Groups do
get api("/groups/#{group1.id}", admin)
end.not_to exceed_query_limit(control_count)
end
+
+ it 'avoids N+1 queries with shared group links' do
+ # setup at least 1 shared group, so that we record the queries that preload the nested associations too.
+ create(:group_group_link, shared_group: group1, shared_with_group: create(:group))
+
+ control_count = ActiveRecord::QueryRecorder.new do
+ get api("/groups/#{group1.id}", admin)
+ end.count
+
+ # setup "n" more shared groups
+ create(:group_group_link, shared_group: group1, shared_with_group: create(:group))
+ create(:group_group_link, shared_group: group1, shared_with_group: create(:group))
+
+ # test that no of queries for 1 shared group is same as for n shared groups
+ expect do
+ get api("/groups/#{group1.id}", admin)
+ end.not_to exceed_query_limit(control_count)
+ end
end
context "when authenticated as admin" do
@@ -1507,4 +1555,173 @@ describe API::Groups do
group2.add_owner(user1)
end
end
+
+ describe "POST /groups/:id/share" do
+ shared_examples 'shares group with group' do
+ it "shares group with group" do
+ expires_at = 10.days.from_now.to_date
+
+ expect do
+ post api("/groups/#{group.id}/share", user), params: { group_id: shared_with_group.id, group_access: Gitlab::Access::DEVELOPER, expires_at: expires_at }
+ end.to change { group.shared_with_group_links.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:created)
+ 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(shared_with_group.id)
+ expect(json_response['shared_with_groups'][0]['group_name']).to eq(shared_with_group.name)
+ expect(json_response['shared_with_groups'][0]['group_full_path']).to eq(shared_with_group.full_path)
+ expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(Gitlab::Access::DEVELOPER)
+ expect(json_response['shared_with_groups'][0]['expires_at']).to eq(expires_at.to_s)
+ end
+
+ it "returns a 400 error when group id is not given" do
+ post api("/groups/#{group.id}/share", user), params: { group_access: Gitlab::Access::DEVELOPER }
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+
+ it "returns a 400 error when access level is not given" do
+ post api("/groups/#{group.id}/share", user), params: { group_id: shared_with_group.id }
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+
+ it 'returns a 404 error when group does not exist' do
+ post api("/groups/#{group.id}/share", user), params: { group_id: non_existing_record_id, group_access: Gitlab::Access::DEVELOPER }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it "returns a 400 error when wrong params passed" do
+ post api("/groups/#{group.id}/share", user), params: { group_id: shared_with_group.id, group_access: non_existing_record_access_level }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq 'group_access does not have a valid value'
+ end
+
+ it "returns a 409 error when link is not saved" do
+ allow(::Groups::GroupLinks::CreateService).to receive_message_chain(:new, :execute)
+ .and_return({ status: :error, http_status: 409, message: 'error' })
+
+ post api("/groups/#{group.id}/share", user), params: { group_id: shared_with_group.id, group_access: Gitlab::Access::DEVELOPER }
+
+ expect(response).to have_gitlab_http_status(:conflict)
+ end
+ end
+
+ context 'when authenticated as owner' do
+ let(:owner_group) { create(:group) }
+ let(:owner_user) { create(:user) }
+
+ before do
+ owner_group.add_owner(owner_user)
+ end
+
+ it_behaves_like 'shares group with group' do
+ let(:user) { owner_user }
+ let(:group) { owner_group }
+ let(:shared_with_group) { create(:group) }
+ end
+ end
+
+ context 'when the user is not the owner of the group' do
+ let(:group) { create(:group) }
+ let(:user4) { create(:user) }
+ let(:expires_at) { 10.days.from_now.to_date }
+
+ before do
+ group1.add_maintainer(user4)
+ end
+
+ it 'does not create group share' do
+ post api("/groups/#{group1.id}/share", user4), params: { group_id: group.id, group_access: Gitlab::Access::DEVELOPER, expires_at: expires_at }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when authenticated as admin' do
+ it_behaves_like 'shares group with group' do
+ let(:user) { admin }
+ let(:group) { create(:group) }
+ let(:shared_with_group) { create(:group) }
+ end
+ end
+ end
+
+ describe 'DELETE /groups/:id/share/:group_id' do
+ shared_examples 'deletes group share' do
+ it 'deletes a group share' do
+ expect do
+ delete api("/groups/#{shared_group.id}/share/#{shared_with_group.id}", user)
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(shared_group.shared_with_group_links).to be_empty
+ end.to change { shared_group.shared_with_group_links.count }.by(-1)
+ end
+
+ it 'requires the group id to be an integer' do
+ delete api("/groups/#{shared_group.id}/share/foo", user)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+
+ it 'returns a 404 error when group link does not exist' do
+ delete api("/groups/#{shared_group.id}/share/#{non_existing_record_id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns a 404 error when group does not exist' do
+ delete api("/groups/123/share/#{non_existing_record_id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when authenticated as owner' do
+ let(:group_a) { create(:group) }
+
+ before do
+ create(:group_group_link, shared_group: group1, shared_with_group: group_a)
+ end
+
+ it_behaves_like 'deletes group share' do
+ let(:user) { user1 }
+ let(:shared_group) { group1 }
+ let(:shared_with_group) { group_a }
+ end
+ end
+
+ context 'when the user is not the owner of the group' do
+ let(:group_a) { create(:group) }
+ let(:user4) { create(:user) }
+
+ before do
+ group1.add_maintainer(user4)
+ create(:group_group_link, shared_group: group1, shared_with_group: group_a)
+ end
+
+ it 'does not remove group share' do
+ expect do
+ delete api("/groups/#{group1.id}/share/#{group_a.id}", user4)
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end.not_to change { group1.shared_with_group_links }
+ end
+ end
+
+ context 'when authenticated as admin' do
+ let(:group_b) { create(:group) }
+
+ before do
+ create(:group_group_link, shared_group: group2, shared_with_group: group_b)
+ end
+
+ it_behaves_like 'deletes group share' do
+ let(:user) { admin }
+ let(:shared_group) { group2 }
+ let(:shared_with_group) { group_b }
+ end
+ end
+ end
end