gitlab-org--gitlab-foss/spec/controllers/groups_controller_spec.rb
Mario de la Ossa 39afba0659
Always use CTE for IssuableFinder counts
Since the CTE is faster than a subquery and the only reason we're using
a subquery is that the CTE can't handle sorting by certain attributes,
let's use the CTE always (when the feature flag is enabled) when
counting, since we can ignore ordering if we just want a count of
results.
2019-02-28 11:25:57 -06:00

636 lines
20 KiB
Ruby

require 'spec_helper'
describe GroupsController do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:group) { create(:group, :public) }
let(:project) { create(:project, namespace: group) }
let!(:group_member) { create(:group_member, group: group, user: user) }
let!(:owner) { group.add_owner(create(:user)).user }
let!(:maintainer) { group.add_maintainer(create(:user)).user }
let!(:developer) { group.add_developer(create(:user)).user }
let!(:guest) { group.add_guest(create(:user)).user }
shared_examples 'member with ability to create subgroups' do
it 'renders the new page' do
sign_in(member)
get :new, params: { parent_id: group.id }
expect(response).to render_template(:new)
end
end
shared_examples 'member without ability to create subgroups' do
it 'renders the 404 page' do
sign_in(member)
get :new, params: { parent_id: group.id }
expect(response).not_to render_template(:new)
expect(response.status).to eq(404)
end
end
describe 'GET #show' do
before do
sign_in(user)
project
end
context 'as atom' do
it 'assigns events for all the projects in the group' do
create(:event, project: project)
get :show, params: { id: group.to_param }, format: :atom
expect(assigns(:events)).not_to be_empty
end
end
end
describe 'GET edit' do
it 'sets the badge API endpoint' do
sign_in(owner)
get :edit, params: { id: group.to_param }
expect(assigns(:badge_api_endpoint)).not_to be_nil
end
end
describe 'GET #new' do
context 'when creating subgroups', :nested_groups do
[true, false].each do |can_create_group_status|
context "and can_create_group is #{can_create_group_status}" do
before do
User.where(id: [admin, owner, maintainer, developer, guest]).update_all(can_create_group: can_create_group_status)
end
[:admin, :owner].each do |member_type|
context "and logged in as #{member_type.capitalize}" do
it_behaves_like 'member with ability to create subgroups' do
let(:member) { send(member_type) }
end
end
end
[:guest, :developer, :maintainer].each do |member_type|
context "and logged in as #{member_type.capitalize}" do
it_behaves_like 'member without ability to create subgroups' do
let(:member) { send(member_type) }
end
end
end
end
end
end
end
describe 'GET #activity' do
render_views
before do
sign_in(user)
project
end
context 'as json' do
it 'includes all projects in event feed' do
3.times do
project = create(:project, group: group)
create(:event, project: project)
end
get :activity, params: { id: group.to_param }, format: :json
expect(response).to have_gitlab_http_status(200)
expect(json_response['count']).to eq(3)
expect(assigns(:projects).limit_value).to be_nil
end
end
end
describe 'POST #create' do
context 'when creating subgroups', :nested_groups do
[true, false].each do |can_create_group_status|
context "and can_create_group is #{can_create_group_status}" do
context 'and logged in as Owner' do
it 'creates the subgroup' do
owner.update_attribute(:can_create_group, can_create_group_status)
sign_in(owner)
post :create, params: { group: { parent_id: group.id, path: 'subgroup' } }
expect(response).to be_redirect
expect(response.body).to match(%r{http://test.host/#{group.path}/subgroup})
end
end
context 'and logged in as Developer' do
it 'renders the new template' do
developer.update_attribute(:can_create_group, can_create_group_status)
sign_in(developer)
previous_group_count = Group.count
post :create, params: { group: { parent_id: group.id, path: 'subgroup' } }
expect(response).to render_template(:new)
expect(Group.count).to eq(previous_group_count)
end
end
end
end
end
context 'when creating a top level group' do
before do
sign_in(developer)
end
context 'and can_create_group is enabled' do
before do
developer.update_attribute(:can_create_group, true)
end
it 'creates the Group' do
original_group_count = Group.count
post :create, params: { group: { path: 'subgroup' } }
expect(Group.count).to eq(original_group_count + 1)
expect(response).to be_redirect
end
end
context 'and can_create_group is disabled' do
before do
developer.update_attribute(:can_create_group, false)
end
it 'does not create the Group' do
original_group_count = Group.count
post :create, params: { group: { path: 'subgroup' } }
expect(Group.count).to eq(original_group_count)
expect(response).to render_template(:new)
end
end
end
end
describe 'GET #index' do
context 'as a user' do
it 'redirects to Groups Dashboard' do
sign_in(user)
get :index
expect(response).to redirect_to(dashboard_groups_path)
end
end
context 'as a guest' do
it 'redirects to Explore Groups' do
get :index
expect(response).to redirect_to(explore_groups_path)
end
end
end
describe 'GET #issues' do
let(:issue_1) { create(:issue, project: project, title: 'foo') }
let(:issue_2) { create(:issue, project: project, title: 'bar') }
before do
create_list(:award_emoji, 3, awardable: issue_2)
create_list(:award_emoji, 2, awardable: issue_1)
create_list(:award_emoji, 2, :downvote, awardable: issue_2)
sign_in(user)
end
context 'sorting by votes' do
it 'sorts most popular issues' do
get :issues, params: { id: group.to_param, sort: 'upvotes_desc' }
expect(assigns(:issues)).to eq [issue_2, issue_1]
end
it 'sorts least popular issues' do
get :issues, params: { id: group.to_param, sort: 'downvotes_desc' }
expect(assigns(:issues)).to eq [issue_2, issue_1]
end
end
context 'searching' do
before do
stub_feature_flags(attempt_group_search_optimizations: true)
end
it 'works with popularity sort' do
get :issues, params: { id: group.to_param, search: 'foo', sort: 'popularity' }
expect(assigns(:issues)).to eq([issue_1])
end
it 'works with priority sort' do
get :issues, params: { id: group.to_param, search: 'foo', sort: 'priority' }
expect(assigns(:issues)).to eq([issue_1])
end
it 'works with label priority sort' do
get :issues, params: { id: group.to_param, search: 'foo', sort: 'label_priority' }
expect(assigns(:issues)).to eq([issue_1])
end
end
end
describe 'GET #merge_requests' do
let(:merge_request_1) { create(:merge_request, source_project: project) }
let(:merge_request_2) { create(:merge_request, :simple, source_project: project) }
before do
create_list(:award_emoji, 3, awardable: merge_request_2)
create_list(:award_emoji, 2, awardable: merge_request_1)
create_list(:award_emoji, 2, :downvote, awardable: merge_request_2)
sign_in(user)
end
context 'sorting by votes' do
it 'sorts most popular merge requests' do
get :merge_requests, params: { id: group.to_param, sort: 'upvotes_desc' }
expect(assigns(:merge_requests)).to eq [merge_request_2, merge_request_1]
end
it 'sorts least popular merge requests' do
get :merge_requests, params: { id: group.to_param, sort: 'downvotes_desc' }
expect(assigns(:merge_requests)).to eq [merge_request_2, merge_request_1]
end
end
end
describe 'DELETE #destroy' do
context 'as another user' do
it 'returns 404' do
sign_in(create(:user))
delete :destroy, params: { id: group.to_param }
expect(response.status).to eq(404)
end
end
context 'as the group owner' do
before do
sign_in(user)
end
it 'schedules a group destroy' do
Sidekiq::Testing.fake! do
expect { delete :destroy, params: { id: group.to_param } }.to change(GroupDestroyWorker.jobs, :size).by(1)
end
end
it 'redirects to the root path' do
delete :destroy, params: { id: group.to_param }
expect(response).to redirect_to(root_path)
end
end
end
describe 'PUT update' do
before do
sign_in(user)
end
it 'updates the path successfully' do
post :update, params: { id: group.to_param, group: { path: 'new_path' } }
expect(response).to have_gitlab_http_status(302)
expect(controller).to set_flash[:notice]
end
it 'does not update the path on error' do
allow_any_instance_of(Group).to receive(:move_dir).and_raise(Gitlab::UpdatePathError)
post :update, params: { id: group.to_param, group: { path: 'new_path' } }
expect(assigns(:group).errors).not_to be_empty
expect(assigns(:group).path).not_to eq('new_path')
end
end
describe '#ensure_canonical_path' do
before do
sign_in(user)
end
context 'for a GET request' do
context 'when requesting groups at the root path' do
before do
allow(request).to receive(:original_fullpath).and_return("/#{group_full_path}")
get :show, params: { id: group_full_path }
end
context 'when requesting the canonical path with different casing' do
let(:group_full_path) { group.to_param.upcase }
it 'redirects to the correct casing' do
expect(response).to redirect_to(group)
expect(controller).not_to set_flash[:notice]
end
end
context 'when requesting a redirected path' do
let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
let(:group_full_path) { redirect_route.path }
it 'redirects to the canonical path' do
expect(response).to redirect_to(group)
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
context 'when the old group path is a substring of the scheme or host' do
let(:redirect_route) { group.redirect_routes.create(path: 'http') }
it 'does not modify the requested host' do
expect(response).to redirect_to(group)
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
end
context 'when the old group path is substring of groups' do
# I.e. /groups/oups should not become /grfoo/oups
let(:redirect_route) { group.redirect_routes.create(path: 'oups') }
it 'does not modify the /groups part of the path' do
expect(response).to redirect_to(group)
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
end
end
end
context 'when requesting groups under the /groups path' do
context 'when requesting the canonical path' do
context 'non-show path' do
context 'with exactly matching casing' do
it 'does not redirect' do
get :issues, params: { id: group.to_param }
expect(response).not_to have_gitlab_http_status(301)
end
end
context 'with different casing' do
it 'redirects to the correct casing' do
get :issues, params: { id: group.to_param.upcase }
expect(response).to redirect_to(issues_group_path(group.to_param))
expect(controller).not_to set_flash[:notice]
end
end
end
context 'show path' do
context 'with exactly matching casing' do
it 'does not redirect' do
get :show, params: { id: group.to_param }
expect(response).not_to have_gitlab_http_status(301)
end
end
context 'with different casing' do
it 'redirects to the correct casing at the root path' do
get :show, params: { id: group.to_param.upcase }
expect(response).to redirect_to(group)
expect(controller).not_to set_flash[:notice]
end
end
end
end
context 'when requesting a redirected path' do
let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
it 'redirects to the canonical path' do
get :issues, params: { id: redirect_route.path }
expect(response).to redirect_to(issues_group_path(group.to_param))
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
context 'when the old group path is a substring of the scheme or host' do
let(:redirect_route) { group.redirect_routes.create(path: 'http') }
it 'does not modify the requested host' do
get :issues, params: { id: redirect_route.path }
expect(response).to redirect_to(issues_group_path(group.to_param))
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
end
context 'when the old group path is substring of groups' do
# I.e. /groups/oups should not become /grfoo/oups
let(:redirect_route) { group.redirect_routes.create(path: 'oups') }
it 'does not modify the /groups part of the path' do
get :issues, params: { id: redirect_route.path }
expect(response).to redirect_to(issues_group_path(group.to_param))
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
end
context 'when the old group path is substring of groups plus the new path' do
# I.e. /groups/oups/oup should not become /grfoos
let(:redirect_route) { group.redirect_routes.create(path: 'oups/oup') }
it 'does not modify the /groups part of the path' do
get :issues, params: { id: redirect_route.path }
expect(response).to redirect_to(issues_group_path(group.to_param))
expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group))
end
end
end
end
context 'for a POST request' do
context 'when requesting the canonical path with different casing' do
it 'does not 404' do
post :update, params: { id: group.to_param.upcase, group: { path: 'new_path' } }
expect(response).not_to have_gitlab_http_status(404)
end
it 'does not redirect to the correct casing' do
post :update, params: { id: group.to_param.upcase, group: { path: 'new_path' } }
expect(response).not_to have_gitlab_http_status(301)
end
end
context 'when requesting a redirected path' do
let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
it 'returns not found' do
post :update, params: { id: redirect_route.path, group: { path: 'new_path' } }
expect(response).to have_gitlab_http_status(404)
end
end
end
context 'for a DELETE request' do
context 'when requesting the canonical path with different casing' do
it 'does not 404' do
delete :destroy, params: { id: group.to_param.upcase }
expect(response).not_to have_gitlab_http_status(404)
end
it 'does not redirect to the correct casing' do
delete :destroy, params: { id: group.to_param.upcase }
expect(response).not_to have_gitlab_http_status(301)
end
end
context 'when requesting a redirected path' do
let(:redirect_route) { group.redirect_routes.create(path: 'old-path') }
it 'returns not found' do
delete :destroy, params: { id: redirect_route.path }
expect(response).to have_gitlab_http_status(404)
end
end
end
end
def group_moved_message(redirect_route, group)
"Group '#{redirect_route.path}' was moved to '#{group.full_path}'. Please update any links and bookmarks that may still have the old path."
end
end
describe 'PUT transfer', :postgresql do
before do
sign_in(user)
end
context 'when transferring to a subgroup goes right' do
let(:new_parent_group) { create(:group, :public) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
let!(:new_parent_group_member) { create(:group_member, :owner, group: new_parent_group, user: user) }
before do
put :transfer,
params: {
id: group.to_param,
new_parent_group_id: new_parent_group.id
}
end
it 'should return a notice' do
expect(flash[:notice]).to eq("Group '#{group.name}' was successfully transferred.")
end
it 'should redirect to the new path' do
expect(response).to redirect_to("/#{new_parent_group.path}/#{group.path}")
end
end
context 'when converting to a root group goes right' do
let(:group) { create(:group, :public, :nested) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
before do
put :transfer,
params: {
id: group.to_param,
new_parent_group_id: ''
}
end
it 'should return a notice' do
expect(flash[:notice]).to eq("Group '#{group.name}' was successfully transferred.")
end
it 'should redirect to the new path' do
expect(response).to redirect_to("/#{group.path}")
end
end
context 'When the transfer goes wrong' do
let(:new_parent_group) { create(:group, :public) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
let!(:new_parent_group_member) { create(:group_member, :owner, group: new_parent_group, user: user) }
before do
allow_any_instance_of(::Groups::TransferService).to receive(:proceed_to_transfer).and_raise(Gitlab::UpdatePathError, 'namespace directory cannot be moved')
put :transfer,
params: {
id: group.to_param,
new_parent_group_id: new_parent_group.id
}
end
it 'should return an alert' do
expect(flash[:alert]).to eq "Transfer failed: namespace directory cannot be moved"
end
it 'should redirect to the current path' do
expect(response).to render_template(:edit)
end
end
context 'when the user is not allowed to transfer the group' do
let(:new_parent_group) { create(:group, :public) }
let!(:group_member) { create(:group_member, :guest, group: group, user: user) }
let!(:new_parent_group_member) { create(:group_member, :guest, group: new_parent_group, user: user) }
before do
put :transfer,
params: {
id: group.to_param,
new_parent_group_id: new_parent_group.id
}
end
it 'should be denied' do
expect(response).to have_gitlab_http_status(404)
end
end
end
context 'token authentication' do
it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do
before do
default_params.merge!(id: group)
end
end
it_behaves_like 'authenticates sessionless user', :issues, :atom, public: true do
before do
default_params.merge!(id: group, author_id: user.id)
end
end
it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics, public: true do
before do
default_params.merge!(id: group)
end
end
end
end