Files
openproject/spec/controllers/members_controller_spec.rb
T

484 lines
15 KiB
Ruby

# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require "spec_helper"
RSpec.describe MembersController do
shared_let(:admin) { create(:admin) }
shared_let(:project) { create(:project, identifier: "pet_project") }
let(:user) { create(:user) }
let(:role) { create(:project_role) }
let(:member) do
create(:member, project:,
user:,
roles: [role])
end
before { login_as(admin) }
describe "#autocomplete_for_member" do
let(:params) { { "project_id" => project.identifier.to_s, "q" => query } }
let(:query) { "" }
let(:json_response) { response.parsed_body }
let(:global_permissions) { [] }
let(:project_permissions) { [] }
let(:user) do
create(:user,
member_with_permissions: { project => project_permissions },
global_permissions: global_permissions)
end
subject { post(:autocomplete_for_member, xhr: true, params:, format: :json) }
before do
login_as(user)
end
describe "WHEN the user is authorized to view all users WHEN a project is provided" do
let(:project_permissions) { [:manage_members] }
let(:global_permissions) { [:view_all_principals] }
it "is success" do
subject
expect(response).to be_successful
end
context "when the user is not authorized to see email addresses" do
it "returns id, name and href" do
subject
expect(json_response).to be_an(Array)
expect(json_response).to include(
{
"id" => admin.id,
"name" => admin.name,
"href" => "/api/v3/users/#{admin.id}"
}
)
end
context "when searching email addresses" do
let(:query) { admin.mail }
it "does not return matches from emails" do
subject
expect(json_response).to be_empty
end
end
end
context "when the user is authorized to see email addresses" do
let(:global_permissions) { %i[view_all_principals view_user_email] }
it "returns id, name, email and href" do
subject
expect(json_response).to be_an(Array)
expect(json_response).to include(
{
"id" => admin.id,
"name" => admin.name,
"email" => admin.mail,
"href" => "/api/v3/users/#{admin.id}"
}
)
end
context "when searching email addresses" do
let(:query) { admin.mail }
it "returns matches from emails" do
subject
expect(json_response).to include(
{
"id" => admin.id,
"name" => admin.name,
"email" => admin.mail,
"href" => "/api/v3/users/#{admin.id}"
}
)
end
end
end
end
describe "WHEN the user has manage_members but no view_all_users (reduced visibility)" do
let(:project_permissions) { [:manage_members] }
let!(:other_project) { create(:project) }
let!(:other_user) { create(:user, member_with_permissions: { other_project => %i[view_project] }) }
let!(:user) do
create(:user,
global_permissions: global_permissions,
member_with_permissions: { project => project_permissions, other_project => %i[view_project] })
end
context "when the user is not authorized to see email addresses" do
it "returns only users visible through shared projects" do
subject
expect(json_response).to be_an(Array)
expect(json_response).to include(
{
"id" => other_user.id,
"name" => other_user.name,
"href" => "/api/v3/users/#{other_user.id}"
}
)
expect(json_response).not_to include(
{
"id" => admin.id,
"name" => admin.name,
"href" => "/api/v3/users/#{admin.id}"
}
)
end
context "when searching email addresses" do
let(:query) { other_user.mail }
it "does not return matches from emails" do
subject
expect(json_response).to be_empty
end
end
end
context "when the user is authorized to see email addresses" do
let(:global_permissions) { [:view_user_email] }
it "returns id, name, email and href for visible users" do
subject
expect(json_response).to be_an(Array)
expect(json_response).to include(
{
"id" => other_user.id,
"name" => other_user.name,
"email" => other_user.mail,
"href" => "/api/v3/users/#{other_user.id}"
}
)
end
context "when searching email addresses" do
let(:query) { other_user.mail }
it "returns matches from emails for visible users" do
subject
expect(json_response).to include(
{
"id" => other_user.id,
"name" => other_user.name,
"email" => other_user.mail,
"href" => "/api/v3/users/#{other_user.id}"
}
)
end
end
end
end
describe "WHEN the user is not authorized" do
it "is forbidden" do
subject
expect(response).to have_http_status(:forbidden)
end
end
end
describe "#index" do
let(:role) { create(:project_role, permissions: [:manage_members]) }
let!(:member) { create(:member, project:, user:, roles: [role]) }
let!(:visible_group) { create(:group, members: [user]) }
let!(:hidden_group) { create(:group) }
before { login_as(user) }
it "only includes groups the user is a member of in the filter options" do
get :index, params: { project_id: project.id }
expect(response).to be_successful
groups = assigns(:members_filter_options)[:groups]
expect(groups).to include(visible_group)
expect(groups).not_to include(hidden_group)
end
end
describe "#create with reduced visibility" do
let(:project_permissions) { %i[manage_members invite_members_by_email] }
let!(:other_project) { create(:project) }
let!(:other_user) { create(:user, member_with_permissions: { other_project => %i[view_project] }) }
let!(:user) do
create(:user, member_with_permissions: { project => project_permissions, other_project => %i[view_project] })
end
before do
login_as(user)
end
context "when inviting by email an existing user who is not visible" do
let!(:hidden_user) { create(:user, mail: "hidden@example.com") }
let(:params) do
{
project_id: project.id,
member: {
role_ids: [role.id],
user_ids: [hidden_user.mail]
}
}
end
it "adds the existing user as a member instead of creating a new invitation" do
expect { post :create, params: }
.to change(Member, :count).by(1)
.and change(User, :count).by(0)
expect(response).to redirect_to "/projects/pet_project/members?status=all"
# The hidden user should now be a member of the project
hidden_user.reload
expect(hidden_user).to be_member_of(project)
# No invitation email should be sent since the user already exists
expect(ActionMailer::Base.deliveries).to be_empty
end
end
context "when adding by direct user ID a user who is not visible" do
let!(:hidden_user) { create(:user) }
let(:params) do
{
project_id: project.id,
member: {
role_ids: [role.id],
user_ids: [hidden_user.id]
}
}
end
it "does not add the hidden user as a member" do
expect { post :create, params: }
.to change(Member, :count).by(0)
hidden_user.reload
expect(hidden_user).not_to be_member_of(project)
end
end
end
describe "#create" do
render_views
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:user4) { create(:user) }
context "for single member" do
let(:action) do
post :create,
params: {
project_id: project.id,
member: { role_ids: [role.id], user_id: user2.id }
}
end
it "adds a member" do
expect { action }.to change(Member, :count).by(1)
expect(response).to redirect_to "/projects/pet_project/members?status=all"
expect(user2).to be_member_of(project)
end
end
context "for multiple members" do
let(:action) do
post :create,
params: {
project_id: project.id,
member: { role_ids: [role.id], user_ids: [user2.id, user3.id, user4.id] }
}
end
it "adds all members" do
expect { action }.to change(Member, :count).by(3)
expect(response).to redirect_to "/projects/pet_project/members?status=all"
expect(user2).to be_member_of(project)
expect(user3).to be_member_of(project)
expect(user4).to be_member_of(project)
end
end
context "with yet-to-be-invited emails" do
let(:emails) { ["h.wurst@openproject.com", "1277551@openproject.com"] }
let(:params) do
{
project_id: project.id,
member: {
role_ids: [role.id],
user_ids: [emails.first] + [user2.id, user3.id] + [emails.last]
}
}
end
let(:invited_users) { User.where(mail: emails).to_a }
let(:users) { invited_users + [user2, user3] }
let(:original_member_count) { Member.count }
before do
original_member_count
perform_enqueued_jobs do
post :create, params:
end
end
it "redirects to the members list" do
expect(response).to redirect_to "/projects/pet_project/members?status=all"
end
it "adds members" do
expect(users.size).to eq 4 # 2 emails, 2 existing users
expect(users).to all be_member_of(project)
expect(Member.count).to eq (original_member_count + users.size)
end
it "invites new users" do
mails = ActionMailer::Base.deliveries
expect(invited_users.size).to eq 2
expect(mails.size).to eq invited_users.size
expect(mails.map(&:to).flatten).to eq invited_users.map(&:mail)
mails.each do |mail|
expect(mail.subject).to include "account activation"
end
end
end
context "with a failed save" do
let(:invalid_params) do
{ project_id: project.id,
member: { role_ids: [],
user_ids: [user2.id, user3.id, user4.id] } }
end
before do
post :create, params: invalid_params
end
it "does not redirect to the members index" do
expect(response).not_to redirect_to "/projects/pet_project/members"
end
it "shows an error message" do
expect(response.body).to include "Roles need to be assigned."
end
end
end
describe "#destroy_by_principal" do
let(:action) do
delete :destroy_by_principal, params: { project_id: project.id, principal_id: user.id, **more_params }
end
let(:role) { create(:project_role, permissions: %i[manage_members share_work_packages]) }
let!(:project_role_member) do
create(:member, project:,
user:,
roles: [role])
end
let(:work_package_role) { create(:view_work_package_role) }
let!(:work_packages_shares) do
Array.new(2) do
create(:member,
project:,
roles: [work_package_role],
entity: create(:work_package, project:),
principal: user)
end
end
before do
allow(User).to receive(:current).and_return(user)
end
context "when requested to delete only project role member" do
let(:more_params) { { project: "" } }
it "destroys the project role member" do
expect { action }.to change(Member, :count).by(-1)
expect(response).to redirect_to "/projects/pet_project/members"
expect(user).not_to be_member_of(project)
end
end
context "when requested to delete only work packages shares" do
let(:more_params) { { work_package_shares_role_id: "all" } }
it "destroys the project role member" do
expect { action }.to change(Member, :count).by(-2)
expect(response).to redirect_to "/projects/pet_project/members"
expect(user).to be_member_of(project)
end
end
context "when requested to delete both project role member and work packages shares" do
let(:more_params) { { project: "", work_package_shares_role_id: "all" } }
it "destroys the project role member" do
expect { action }.to change(Member, :count).by(-3)
expect(response).to redirect_to "/projects/pet_project/members"
expect(user).not_to be_member_of(project)
end
end
end
describe "#update" do
let(:action) do
post :update,
params: {
project_id: project.id,
id: member.id,
member: { role_ids: [role2.id], user_id: user.id }
}
end
let(:role2) { create(:project_role) }
before do
member
end
it "updates the member" do
expect { action }.not_to change(Member, :count)
expect(response).to redirect_to "/projects/pet_project/members"
end
end
end