diff options
Diffstat (limited to 'spec/controllers/projects/project_members_controller_spec.rb')
-rw-r--r-- | spec/controllers/projects/project_members_controller_spec.rb | 762 |
1 files changed, 387 insertions, 375 deletions
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index fb27fe58cd9..ab33195eb83 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -8,183 +8,168 @@ RSpec.describe Projects::ProjectMembersController do let_it_be(:sub_group) { create(:group, parent: group) } let_it_be(:project, reload: true) { create(:project, :public) } - before do - travel_to DateTime.new(2019, 4, 1) - end + shared_examples_for 'controller actions' do + before do + travel_to DateTime.new(2019, 4, 1) + end - after do - travel_back - end + after do + travel_back + end - describe 'GET index' do - it 'has the project_members address with a 200 status code' do - get :index, params: { namespace_id: project.namespace, project_id: project } + describe 'GET index' do + it 'has the project_members address with a 200 status code' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(response).to have_gitlab_http_status(:ok) - end + expect(response).to have_gitlab_http_status(:ok) + end - context 'project members' do - context 'when project belongs to group' do - let_it_be(:user_in_group) { create(:user) } - let_it_be(:project_in_group) { create(:project, :public, group: group) } + context 'project members' do + context 'when project belongs to group' do + let_it_be(:user_in_group) { create(:user) } + let_it_be(:project_in_group) { create(:project, :public, group: group) } - before do - group.add_owner(user_in_group) - project_in_group.add_maintainer(user) - sign_in(user) - end + before do + group.add_owner(user_in_group) + project_in_group.add_maintainer(user) + sign_in(user) + end - it 'lists inherited project members by default' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group } + it 'lists inherited project members by default' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id) - end + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id) + end - it 'lists direct project members only' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' } + it 'lists direct project members only' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id) - end + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id) + end - it 'lists inherited project members only' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' } + it 'lists inherited project members only' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id) + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id) + end end - end - context 'when project belongs to a sub-group' do - let_it_be(:user_in_group) { create(:user) } - let_it_be(:project_in_group) { create(:project, :public, group: sub_group) } + context 'when project belongs to a sub-group' do + let_it_be(:user_in_group) { create(:user) } + let_it_be(:project_in_group) { create(:project, :public, group: sub_group) } - before do - group.add_owner(user_in_group) - project_in_group.add_maintainer(user) - sign_in(user) - end + before do + group.add_owner(user_in_group) + project_in_group.add_maintainer(user) + sign_in(user) + end - it 'lists inherited project members by default' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group } + it 'lists inherited project members by default' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id) - end + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id) + end - it 'lists direct project members only' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' } + it 'lists direct project members only' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id) - end + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id) + end - it 'lists inherited project members only' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' } + it 'lists inherited project members only' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id) + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id) + end end - end - context 'when invited project members are present' do - let!(:invited_member) { create(:project_member, :invited, project: project) } + context 'when invited project members are present' do + let!(:invited_member) { create(:project_member, :invited, project: project) } - before do - project.add_maintainer(user) - sign_in(user) - end + before do + project.add_maintainer(user) + sign_in(user) + end - it 'excludes the invited members from project members list' do - get :index, params: { namespace_id: project.namespace, project_id: project } + it 'excludes the invited members from project members list' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:project_members).map(&:invite_email)).not_to contain_exactly(invited_member.invite_email) + expect(assigns(:project_members).map(&:invite_email)).not_to contain_exactly(invited_member.invite_email) + end end end - end - - context 'invited members' do - let_it_be(:invited_member) { create(:project_member, :invited, project: project) } - before do - sign_in(user) - end + context 'invited members' do + let_it_be(:invited_member) { create(:project_member, :invited, project: project) } - context 'when user has `admin_project_member` permissions' do before do - project.add_maintainer(user) + sign_in(user) end - it 'lists invited members' do - get :index, params: { namespace_id: project.namespace, project_id: project } + context 'when user has `admin_project_member` permissions' do + before do + project.add_maintainer(user) + end + + it 'lists invited members' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:invited_members).map(&:invite_email)).to contain_exactly(invited_member.invite_email) + expect(assigns(:invited_members).map(&:invite_email)).to contain_exactly(invited_member.invite_email) + end end - end - context 'when user does not have `admin_project_member` permissions' do - it 'does not list invited members' do - get :index, params: { namespace_id: project.namespace, project_id: project } + context 'when user does not have `admin_project_member` permissions' do + it 'does not list invited members' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:invited_members)).to be_nil + expect(assigns(:invited_members)).to be_nil + end end end - end - context 'access requests' do - let_it_be(:access_requester_user) { create(:user) } - - before do - project.request_access(access_requester_user) - sign_in(user) - end + context 'access requests' do + let_it_be(:access_requester_user) { create(:user) } - context 'when user has `admin_project_member` permissions' do before do - project.add_maintainer(user) + project.request_access(access_requester_user) + sign_in(user) end - it 'lists access requests' do - get :index, params: { namespace_id: project.namespace, project_id: project } + context 'when user has `admin_project_member` permissions' do + before do + project.add_maintainer(user) + end + + it 'lists access requests' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:requesters).map(&:user_id)).to contain_exactly(access_requester_user.id) + expect(assigns(:requesters).map(&:user_id)).to contain_exactly(access_requester_user.id) + end end - end - context 'when user does not have `admin_project_member` permissions' do - it 'does not list access requests' do - get :index, params: { namespace_id: project.namespace, project_id: project } + context 'when user does not have `admin_project_member` permissions' do + it 'does not list access requests' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:requesters)).to be_nil + expect(assigns(:requesters)).to be_nil + end end end end - end - - describe 'PUT update' do - let_it_be(:requester) { create(:project_member, :access_request, project: project) } - - before do - project.add_maintainer(user) - sign_in(user) - end - context 'access level' do - Gitlab::Access.options.each do |label, value| - it "can change the access level to #{label}" do - params = { - project_member: { access_level: value }, - namespace_id: project.namespace, - project_id: project, - id: requester - } + describe 'PUT update' do + let_it_be(:requester) { create(:project_member, :access_request, project: project) } - put :update, params: params, xhr: true - - expect(requester.reload.human_access).to eq(label) - end + before do + project.add_maintainer(user) + sign_in(user) end - describe 'managing project direct owners' do - context 'when a Maintainer tries to elevate another user to OWNER' do - it 'does not allow the operation' do + context 'access level' do + Gitlab::Access.options.each do |label, value| + it "can change the access level to #{label}" do params = { - project_member: { access_level: Gitlab::Access::OWNER }, + project_member: { access_level: value }, namespace_id: project.namespace, project_id: project, id: requester @@ -192,368 +177,395 @@ RSpec.describe Projects::ProjectMembersController do put :update, params: params, xhr: true - expect(response).to have_gitlab_http_status(:forbidden) + expect(requester.reload.human_access).to eq(label) end end - context 'when a user with OWNER access tries to elevate another user to OWNER' do - # inherited owner role via personal project association - let(:user) { project.first_owner } + describe 'managing project direct owners' do + context 'when a Maintainer tries to elevate another user to OWNER' do + it 'does not allow the operation' do + params = { + project_member: { access_level: Gitlab::Access::OWNER }, + namespace_id: project.namespace, + project_id: project, + id: requester + } - before do - sign_in(user) + put :update, params: params, xhr: true + + expect(response).to have_gitlab_http_status(:forbidden) + end end - it 'returns success' do - params = { - project_member: { access_level: Gitlab::Access::OWNER }, - namespace_id: project.namespace, - project_id: project, - id: requester - } + context 'when a user with OWNER access tries to elevate another user to OWNER' do + # inherited owner role via personal project association + let(:user) { project.first_owner } - put :update, params: params, xhr: true + before do + sign_in(user) + end + + it 'returns success' do + params = { + project_member: { access_level: Gitlab::Access::OWNER }, + namespace_id: project.namespace, + project_id: project, + id: requester + } + + put :update, params: params, xhr: true - expect(response).to have_gitlab_http_status(:ok) - expect(requester.reload.access_level).to eq(Gitlab::Access::OWNER) + expect(response).to have_gitlab_http_status(:ok) + expect(requester.reload.access_level).to eq(Gitlab::Access::OWNER) + end end end end - end - context 'access expiry date' do - subject do - put :update, xhr: true, params: { - project_member: { - expires_at: expires_at - }, - namespace_id: project.namespace, - project_id: project, - id: requester - } - end + context 'access expiry date' do + subject do + put :update, xhr: true, params: { + project_member: { + expires_at: expires_at + }, + namespace_id: project.namespace, + project_id: project, + id: requester + } + end - context 'when set to a date in the past' do - let(:expires_at) { 2.days.ago } + context 'when set to a date in the past' do + let(:expires_at) { 2.days.ago } - it 'does not update the member' do - subject + it 'does not update the member' do + subject - expect(requester.reload.expires_at).not_to eq(expires_at.to_date) - end + expect(requester.reload.expires_at).not_to eq(expires_at.to_date) + end - it 'returns error status' do - subject + it 'returns error status' do + subject - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end - it 'returns error message' do - subject + it 'returns error message' do + subject - expect(json_response).to eq({ 'message' => 'Expires at cannot be a date in the past' }) + expect(json_response).to eq({ 'message' => 'Expires at cannot be a date in the past' }) + end end - end - context 'when set to a date in the future' do - let(:expires_at) { 5.days.from_now } + context 'when set to a date in the future' do + let(:expires_at) { 5.days.from_now } - it 'updates the member' do - subject + it 'updates the member' do + subject - expect(requester.reload.expires_at).to eq(expires_at.to_date) + expect(requester.reload.expires_at).to eq(expires_at.to_date) + end end end - end - context 'expiration date' do - let(:expiry_date) { 1.month.from_now.to_date } + context 'expiration date' do + let(:expiry_date) { 1.month.from_now.to_date } - before do - travel_to Time.now.utc.beginning_of_day - - put( - :update, - params: { - project_member: { expires_at: expiry_date }, - namespace_id: project.namespace, - project_id: project, - id: requester - }, - format: :json - ) - end + before do + travel_to Time.now.utc.beginning_of_day + + put( + :update, + params: { + project_member: { expires_at: expiry_date }, + namespace_id: project.namespace, + project_id: project, + id: requester + }, + format: :json + ) + end - context 'when `expires_at` is set' do - it 'returns correct json response' do - expect(json_response).to eq({ - "expires_soon" => false, - "expires_at_formatted" => expiry_date.to_time.in_time_zone.to_s(:medium) - }) + context 'when `expires_at` is set' do + it 'returns correct json response' do + expect(json_response).to eq({ + "expires_soon" => false, + "expires_at_formatted" => expiry_date.to_time.in_time_zone.to_s(:medium) + }) + end end - end - context 'when `expires_at` is not set' do - let(:expiry_date) { nil } + context 'when `expires_at` is not set' do + let(:expiry_date) { nil } - it 'returns empty json response' do - expect(json_response).to be_empty + it 'returns empty json response' do + expect(json_response).to be_empty + end end end end - end - describe 'DELETE destroy' do - let_it_be(:member) { create(:project_member, :developer, project: project) } + describe 'DELETE destroy' do + let_it_be(:member) { create(:project_member, :developer, project: project) } - before do - sign_in(user) - end + before do + sign_in(user) + end - context 'when member is not found' do - it 'returns 404' do - delete :destroy, params: { - namespace_id: project.namespace, - project_id: project, - id: 42 - } + context 'when member is not found' do + it 'returns 404' do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: 42 + } - expect(response).to have_gitlab_http_status(:not_found) + expect(response).to have_gitlab_http_status(:not_found) + end end - end - context 'when member is found' do - context 'when user does not have enough rights' do - context 'when user does not have rights to manage other members' do - before do - project.add_developer(user) + context 'when member is found' do + context 'when user does not have enough rights' do + context 'when user does not have rights to manage other members' do + before do + project.add_developer(user) + end + + it 'returns 404', :aggregate_failures do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to have_gitlab_http_status(:not_found) + expect(project.members).to include member + end end - it 'returns 404', :aggregate_failures do - delete :destroy, params: { - namespace_id: project.namespace, - project_id: project, - id: member - } + context 'when user does not have rights to manage Owner members' do + let_it_be(:member) { create(:project_member, project: project, access_level: Gitlab::Access::OWNER) } - expect(response).to have_gitlab_http_status(:not_found) - expect(project.members).to include member + before do + project.add_maintainer(user) + end + + it 'returns 403', :aggregate_failures do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to have_gitlab_http_status(:forbidden) + expect(project.members).to include member + end end end - context 'when user does not have rights to manage Owner members' do - let_it_be(:member) { create(:project_member, project: project, access_level: Gitlab::Access::OWNER) } - + context 'when user has enough rights' do before do project.add_maintainer(user) end - it 'returns 403', :aggregate_failures do + it '[HTML] removes user from members', :aggregate_failures do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to redirect_to( + project_project_members_path(project) + ) + expect(project.members).not_to include member + end + + it '[JS] removes user from members', :aggregate_failures do delete :destroy, params: { namespace_id: project.namespace, project_id: project, id: member - } + }, xhr: true - expect(response).to have_gitlab_http_status(:forbidden) - expect(project.members).to include member + expect(response).to be_successful + expect(project.members).not_to include member end end end - - context 'when user has enough rights' do - before do - project.add_maintainer(user) - end - - it '[HTML] removes user from members', :aggregate_failures do - delete :destroy, params: { - namespace_id: project.namespace, - project_id: project, - id: member - } - - expect(response).to redirect_to( - project_project_members_path(project) - ) - expect(project.members).not_to include member - end - - it '[JS] removes user from members', :aggregate_failures do - delete :destroy, params: { - namespace_id: project.namespace, - project_id: project, - id: member - }, xhr: true - - expect(response).to be_successful - expect(project.members).not_to include member - end - end - end - end - - describe 'DELETE leave' do - before do - sign_in(user) end - context 'when member is not found' do - it 'returns 404' do - delete :leave, params: { - namespace_id: project.namespace, - project_id: project - } - - expect(response).to have_gitlab_http_status(:not_found) + describe 'DELETE leave' do + before do + sign_in(user) end - end - - context 'when member is found' do - context 'and is not an owner' do - before do - project.add_developer(user) - end - it 'removes user from members', :aggregate_failures do + context 'when member is not found' do + it 'returns 404' do delete :leave, params: { namespace_id: project.namespace, project_id: project } - expect(controller).to set_flash.to "You left the \"#{project.human_name}\" project." - expect(response).to redirect_to(dashboard_projects_path) - expect(project.users).not_to include user + expect(response).to have_gitlab_http_status(:not_found) end end - context 'and is an owner' do - let(:project) { create(:project, namespace: user.namespace) } + context 'when member is found' do + context 'and is not an owner' do + before do + project.add_developer(user) + end + + it 'removes user from members', :aggregate_failures do + delete :leave, params: { + namespace_id: project.namespace, + project_id: project + } - before do - project.add_maintainer(user) + expect(controller).to set_flash.to "You left the \"#{project.human_name}\" project." + expect(response).to redirect_to(dashboard_projects_path) + expect(project.users).not_to include user + end end - it 'cannot remove themselves from the project' do - delete :leave, params: { - namespace_id: project.namespace, - project_id: project - } + context 'and is an owner' do + let(:project) { create(:project, namespace: user.namespace) } - expect(response).to have_gitlab_http_status(:forbidden) - end - end + before do + project.add_maintainer(user) + end - context 'and is a requester' do - before do - project.request_access(user) + it 'cannot remove themselves from the project' do + delete :leave, params: { + namespace_id: project.namespace, + project_id: project + } + + expect(response).to have_gitlab_http_status(:forbidden) + end end - it 'removes user from members', :aggregate_failures do - delete :leave, params: { - namespace_id: project.namespace, - project_id: project - } + context 'and is a requester' do + before do + project.request_access(user) + end + + it 'removes user from members', :aggregate_failures do + delete :leave, params: { + namespace_id: project.namespace, + project_id: project + } - expect(controller).to set_flash.to 'Your access request to the project has been withdrawn.' - expect(response).to redirect_to(project_path(project)) - expect(project.requesters).to be_empty - expect(project.users).not_to include user + expect(controller).to set_flash.to 'Your access request to the project has been withdrawn.' + expect(response).to redirect_to(project_path(project)) + expect(project.requesters).to be_empty + expect(project.users).not_to include user + end end end end - end - - describe 'POST request_access' do - before do - sign_in(user) - end - it 'creates a new ProjectMember that is not a team member', :aggregate_failures do - post :request_access, params: { - namespace_id: project.namespace, - project_id: project - } - - expect(controller).to set_flash.to 'Your request for access has been queued for review.' - expect(response).to redirect_to( - project_path(project) - ) - expect(project.requesters.exists?(user_id: user)).to be_truthy - expect(project.users).not_to include user - end - end + describe 'POST request_access' do + before do + sign_in(user) + end - describe 'POST approve' do - let_it_be(:member) { create(:project_member, :access_request, project: project) } + it 'creates a new ProjectMember that is not a team member', :aggregate_failures do + post :request_access, params: { + namespace_id: project.namespace, + project_id: project + } - before do - sign_in(user) + expect(controller).to set_flash.to 'Your request for access has been queued for review.' + expect(response).to redirect_to( + project_path(project) + ) + expect(project.requesters.exists?(user_id: user)).to be_truthy + expect(project.users).not_to include user + end end - context 'when member is not found' do - it 'returns 404' do - post :approve_access_request, params: { - namespace_id: project.namespace, - project_id: project, - id: 42 - } + describe 'POST approve' do + let_it_be(:member) { create(:project_member, :access_request, project: project) } - expect(response).to have_gitlab_http_status(:not_found) + before do + sign_in(user) end - end - - context 'when member is found' do - context 'when user does not have rights to manage other members' do - before do - project.add_developer(user) - end - it 'returns 404', :aggregate_failures do + context 'when member is not found' do + it 'returns 404' do post :approve_access_request, params: { namespace_id: project.namespace, project_id: project, - id: member + id: 42 } expect(response).to have_gitlab_http_status(:not_found) - expect(project.members).not_to include member end end - context 'when user has enough rights' do - before do - project.add_maintainer(user) + context 'when member is found' do + context 'when user does not have rights to manage other members' do + before do + project.add_developer(user) + end + + it 'returns 404', :aggregate_failures do + post :approve_access_request, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to have_gitlab_http_status(:not_found) + expect(project.members).not_to include member + end end - it 'adds user to members', :aggregate_failures do - post :approve_access_request, params: { - namespace_id: project.namespace, - project_id: project, - id: member - } + context 'when user has enough rights' do + before do + project.add_maintainer(user) + end - expect(response).to redirect_to( - project_project_members_path(project) - ) - expect(project.members).to include member + it 'adds user to members', :aggregate_failures do + post :approve_access_request, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to redirect_to( + project_project_members_path(project) + ) + expect(project.members).to include member + end end end end - end - describe 'POST resend_invite' do - let_it_be(:member) { create(:project_member, project: project) } + describe 'POST resend_invite' do + let_it_be(:member) { create(:project_member, project: project) } - before do - project.add_maintainer(user) - sign_in(user) + before do + project.add_maintainer(user) + sign_in(user) + end + + it 'is successful' do + post :resend_invite, params: { namespace_id: project.namespace, project_id: project, id: member } + + expect(response).to have_gitlab_http_status(:found) + end end + end - it 'is successful' do - post :resend_invite, params: { namespace_id: project.namespace, project_id: project, id: member } + it_behaves_like 'controller actions' - expect(response).to have_gitlab_http_status(:found) + context 'when project_members_index_by_project_namespace feature flag is disabled' do + before do + stub_feature_flags(project_members_index_by_project_namespace: false) end + + it_behaves_like 'controller actions' end end |