gitlab-org--gitlab-foss/spec/models/members/group_member_spec.rb

282 lines
9.5 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GroupMember do
context 'scopes' do
let_it_be(:user_1) { create(:user) }
let_it_be(:user_2) { create(:user) }
it 'counts users by group ID' do
group_1 = create(:group)
group_2 = create(:group)
group_1.add_owner(user_1)
group_1.add_owner(user_2)
group_2.add_owner(user_1)
expect(described_class.count_users_by_group_id).to eq(group_1.id => 2,
group_2.id => 1)
end
describe '.of_ldap_type' do
it 'returns ldap type users' do
group_member = create(:group_member, :ldap)
expect(described_class.of_ldap_type).to eq([group_member])
end
end
describe '.with_user' do
it 'returns requested user' do
group_member = create(:group_member, user: user_2)
create(:group_member, user: user_1)
expect(described_class.with_user(user_2)).to eq([group_member])
end
end
end
describe 'delegations' do
it { is_expected.to delegate_method(:update_two_factor_requirement).to(:user).allow_nil }
end
describe '.access_level_roles' do
it 'returns Gitlab::Access.options_with_owner' do
expect(described_class.access_level_roles).to eq(Gitlab::Access.options_with_owner)
end
end
it_behaves_like 'members notifications', :group
describe '#namespace_id' do
subject { build(:group_member, source_id: 1).namespace_id }
it { is_expected.to eq 1 }
end
describe '#real_source_type' do
subject { create(:group_member).real_source_type }
it { is_expected.to eq 'Group' }
end
describe '#update_two_factor_requirement' do
it 'is called after creation and deletion' do
user = build :user
group_member = build :group_member, user: user
expect(user).to receive(:update_two_factor_requirement)
group_member.save!
expect(user).to receive(:update_two_factor_requirement)
group_member.destroy!
end
end
describe '#destroy' do
context 'for an orphaned member' do
let!(:orphaned_group_member) do
create(:group_member).tap { |member| member.update_column(:user_id, nil) }
end
it 'does not raise an error' do
expect { orphaned_group_member.destroy! }.not_to raise_error
end
end
end
describe '#after_accept_invite' do
it 'calls #update_two_factor_requirement' do
email = 'foo@email.com'
user = build(:user, email: email)
group = create(:group, require_two_factor_authentication: true)
group_member = create(:group_member, group: group, invite_token: '1234', invite_email: email)
expect(user).to receive(:require_two_factor_authentication_from_group).and_call_original
group_member.accept_invite!(user)
expect(user.require_two_factor_authentication_from_group).to be_truthy
end
end
context 'access levels' do
context 'with parent group' do
it_behaves_like 'inherited access level as a member of entity' do
let(:entity) { create(:group, parent: parent_entity) }
end
end
context 'with parent group and a sub subgroup' do
it_behaves_like 'inherited access level as a member of entity' do
let(:subgroup) { create(:group, parent: parent_entity) }
let(:entity) { create(:group, parent: subgroup) }
end
context 'when only the subgroup has the member' do
it_behaves_like 'inherited access level as a member of entity' do
let(:parent_entity) { create(:group, parent: create(:group)) }
let(:entity) { create(:group, parent: parent_entity) }
end
end
end
end
context 'when group member expiration date is updated' do
let_it_be(:group_member) { create(:group_member) }
it 'emails the user that their group membership expiry has changed' do
expect_next_instance_of(NotificationService) do |notification|
allow(notification).to receive(:updated_group_member_expiration).with(group_member)
end
group_member.update!(expires_at: 5.days.from_now)
end
end
describe 'refresh_member_authorized_projects' do
context 'when importing' do
it 'does not refresh' do
expect(UserProjectAccessChangedService).not_to receive(:new)
member = build(:group_member)
member.importing = true
member.save!
end
end
end
context 'authorization refresh on addition/updation/deletion' do
let_it_be(:group) { create(:group) }
let_it_be(:project_a) { create(:project, group: group) }
let_it_be(:project_b) { create(:project, group: group) }
let_it_be(:project_c) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
let_it_be(:affected_project_ids) { Project.id_in([project_a, project_b, project_c]).ids }
before do
stub_const(
"#{described_class.name}::THRESHOLD_FOR_REFRESHING_AUTHORIZATIONS_VIA_PROJECTS",
affected_project_ids.size - 1)
end
shared_examples_for 'calls UserProjectAccessChangedService to recalculate authorizations' do
it 'calls UserProjectAccessChangedService to recalculate authorizations' do
expect_next_instance_of(UserProjectAccessChangedService, user.id) do |service|
expect(service).to receive(:execute).with(blocking: blocking)
end
action
end
end
shared_examples_for 'tries to update permissions via refreshing authorizations for the affected projects' do
context 'when the number of affected projects exceeds the set threshold' do
it 'updates permissions via refreshing authorizations for the affected projects asynchronously' do
expect_next_instance_of(
AuthorizedProjectUpdate::ProjectAccessChangedService, affected_project_ids
) do |service|
expect(service).to receive(:execute).with(blocking: false)
end
action
end
it 'calls AuthorizedProjectUpdate::UserRefreshFromReplicaWorker with a delay as a safety net' do
expect(AuthorizedProjectUpdate::UserRefreshFromReplicaWorker).to(
receive(:bulk_perform_in)
.with(1.hour,
[[user.id]],
batch_delay: 30.seconds, batch_size: 100)
)
action
end
end
context 'when the number of affected projects does not exceed the set threshold' do
before do
stub_const(
"#{described_class.name}::THRESHOLD_FOR_REFRESHING_AUTHORIZATIONS_VIA_PROJECTS",
affected_project_ids.size + 1)
end
it_behaves_like 'calls UserProjectAccessChangedService to recalculate authorizations'
end
end
context 'on create' do
let(:action) { group.add_user(user, Gitlab::Access::GUEST) }
let(:blocking) { true }
it 'changes access level', :sidekiq_inline do
expect { action }.to change { user.can?(:guest_access, project_a) }.from(false).to(true)
.and change { user.can?(:guest_access, project_b) }.from(false).to(true)
.and change { user.can?(:guest_access, project_c) }.from(false).to(true)
end
it_behaves_like 'tries to update permissions via refreshing authorizations for the affected projects'
context 'when the feature flag `refresh_authorizations_via_affected_projects_on_group_membership` is disabled' do
before do
stub_feature_flags(refresh_authorizations_via_affected_projects_on_group_membership: false)
end
it_behaves_like 'calls UserProjectAccessChangedService to recalculate authorizations'
end
end
context 'on update' do
before do
group.add_user(user, Gitlab::Access::GUEST)
end
let(:action) { group.members.find_by(user: user).update!(access_level: Gitlab::Access::DEVELOPER) }
let(:blocking) { true }
it 'changes access level', :sidekiq_inline do
expect { action }.to change { user.can?(:developer_access, project_a) }.from(false).to(true)
.and change { user.can?(:developer_access, project_b) }.from(false).to(true)
.and change { user.can?(:developer_access, project_c) }.from(false).to(true)
end
it_behaves_like 'tries to update permissions via refreshing authorizations for the affected projects'
context 'when the feature flag `refresh_authorizations_via_affected_projects_on_group_membership` is disabled' do
before do
stub_feature_flags(refresh_authorizations_via_affected_projects_on_group_membership: false)
end
it_behaves_like 'calls UserProjectAccessChangedService to recalculate authorizations'
end
end
context 'on destroy' do
before do
group.add_user(user, Gitlab::Access::GUEST)
end
let(:action) { group.members.find_by(user: user).destroy! }
let(:blocking) { false }
it 'changes access level', :sidekiq_inline do
expect { action }.to change { user.can?(:guest_access, project_a) }.from(true).to(false)
.and change { user.can?(:guest_access, project_b) }.from(true).to(false)
.and change { user.can?(:guest_access, project_c) }.from(true).to(false)
end
it_behaves_like 'tries to update permissions via refreshing authorizations for the affected projects'
context 'when the feature flag `refresh_authorizations_via_affected_projects_on_group_membership` is disabled' do
before do
stub_feature_flags(refresh_authorizations_via_affected_projects_on_group_membership: false)
end
it_behaves_like 'calls UserProjectAccessChangedService to recalculate authorizations'
end
end
end
end