-
由 Vijay Hawoldar 创作于
In order to efficiently query for namespace settings by last_dormant_member_review_at and remove_dormant_members, a composite index can be used which will be more performant Changelog: changed
由 Vijay Hawoldar 创作于In order to efficiently query for namespace settings by last_dormant_member_review_at and remove_dormant_members, a composite index can be used which will be more performant Changelog: changed
代码所有者
将用户和群组指定为特定文件更改的核准人。 了解更多。
namespace_setting_spec.rb 23.33 KiB
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe NamespaceSetting, feature_category: :groups_and_projects, type: :model do
using RSpec::Parameterized::TableSyntax
let(:group) { create(:group) }
let(:setting) { group.namespace_settings }
describe 'enums' do
it 'defines an enum for enabled_git_access_protocol' do
is_expected.to define_enum_for(
:enabled_git_access_protocol
).with_values([:all, :ssh, :http]).with_suffix
end
end
describe 'scopes' do
describe '.requiring_dormant_member_review', :freeze_time do
it 'returns settings with feature enabled requiring review' do
setting_never_reviewed = create(:namespace_settings, remove_dormant_members: true, last_dormant_member_review_at: nil)
setting_reviewed_19_hours_ago = create(:namespace_settings, remove_dormant_members: true, last_dormant_member_review_at: 19.hours.ago)
create(:namespace_settings, last_dormant_member_review_at: 18.hours.ago) # exactly 18 hours ago
create(:namespace_settings, remove_dormant_members: false) # feature is disabled
expect(described_class.requiring_dormant_member_review(3))
.to contain_exactly(setting_never_reviewed, setting_reviewed_19_hours_ago)
end
end
end
describe 'validations' do
subject(:settings) { group.namespace_settings }
it { is_expected.to validate_presence_of(:unique_project_download_limit) }
it { is_expected.to validate_presence_of(:unique_project_download_limit_interval_in_seconds) }
it {
is_expected.to validate_numericality_of(:unique_project_download_limit)
.only_integer
.is_greater_than_or_equal_to(0)
.is_less_than_or_equal_to(10_000)
}
it {
is_expected.to validate_numericality_of(:unique_project_download_limit_interval_in_seconds)
.only_integer
.is_greater_than_or_equal_to(0)
.is_less_than_or_equal_to(10.days.to_i)
}
describe 'experiment features' do
let(:attr) { :experiment_features_enabled }
subject(:settings) { group.namespace_settings }
before do
allow(subject).to receive(:experiment_settings_allowed?).and_return(true)
end
it 'allows changing experiment_features_enabled' do
subject[attr] = !subject[attr]
expect(subject).to be_valid
end
context 'when experiment settings are not allowed' do
before do
allow(subject).to receive(:experiment_settings_allowed?).and_return(false)
end
it 'does not allow changing experiment_features_enabled' do
subject[attr] = !subject[attr]
expect(subject).not_to be_valid
expect(subject.errors[attr].first).to include("Experiment features' settings not allowed.")
end
end
end
describe 'unique_project_download_limit_allowlist', feature_category: :insider_threat do
let_it_be(:user) { create(:user) }
let(:attr) { :unique_project_download_limit_allowlist }
it { is_expected.to allow_value([]).for(attr) }
it { is_expected.to allow_value([user.username]).for(attr) }
it { is_expected.not_to allow_value(nil).for(attr) }
it { is_expected.not_to allow_value(['unknown_user']).for(attr) }
context 'when maximum length is exceeded' do
it 'is not valid' do
subject.unique_project_download_limit_allowlist = generate_list(:username, 101)
expect(subject).not_to be_valid
expect(subject.errors[attr]).to include("exceeds maximum length (100 usernames)")
end
end
end
describe 'unique_project_download_limit_alertlist', feature_category: :insider_threat do
let_it_be(:user) { create(:user) }
let(:attr) { :unique_project_download_limit_alertlist }
it { is_expected.to allow_value([]).for(attr) }
it { is_expected.to allow_value([user.id]).for(attr) }
it { is_expected.to allow_value(nil).for(attr) }
it { is_expected.not_to allow_value([non_existing_record_id]).for(attr) }
context 'when maximum length is exceeded' do
it 'is not valid' do
subject.unique_project_download_limit_alertlist = Array.new(101)
expect(subject).not_to be_valid
expect(subject.errors[attr]).to include('exceeds maximum length (100 user ids)')
end
context 'when empty' do
let(:active_user) { create(:user) }
let(:inactive_user) { create(:user, :deactivated) }
before do
group.add_owner(active_user)
group.add_owner(inactive_user)
end
it 'returns the user ids of the active group owners' do
expect(subject.unique_project_download_limit_alertlist).to contain_exactly(active_user.id)
end
end
context 'when not empty' do
let(:alerted_user_ids) { [1, 2] }
before do
subject.update_attribute(:unique_project_download_limit_alertlist, alerted_user_ids)
end
it 'returns the set user ids' do
expect(subject.unique_project_download_limit_alertlist).to eq(alerted_user_ids)
end
end
end
end
describe 'new_user_signups_cap', :saas do
# rubocop:disable Rails/SaveBang -- Testing validations
describe 'validations' do
context 'when seat_control is user_cap' do
it 'is invalid when set to nil' do
settings.update(seat_control: :user_cap, new_user_signups_cap: nil)
expect(settings.errors.messages[:new_user_signups_cap]).to eq(["is not a number"])
end
it 'must be a positive number' do
settings.update(seat_control: :user_cap, new_user_signups_cap: -1)
expect(settings.errors.messages[:new_user_signups_cap]).to eq(["must be greater than or equal to 0"])
end
it 'must be an integer' do
settings.update(seat_control: :user_cap, new_user_signups_cap: 2.5)
expect(settings.errors.messages[:new_user_signups_cap]).to eq(["must be an integer"])
end
it 'is valid when it is a positive integer' do
settings.update(seat_control: :user_cap, new_user_signups_cap: 1)
expect(settings).to be_valid
end
end
context 'when seat_control is off' do
it 'can be nil' do
settings.update(seat_control: :off, new_user_signups_cap: nil)
expect(settings).to be_valid
end
it 'can be set' do
settings.update(seat_control: :off, new_user_signups_cap: 4)
expect(settings).to be_valid
end
end
end
# rubocop:enable Rails/SaveBang
it 'will be set to nil when seat_control is off' do
settings.update!(seat_control: :off, new_user_signups_cap: 5)
expect(settings.new_user_signups_cap).to be_nil
end
it 'is unchanged when seat_control is user_cap' do
settings.update!(seat_control: :user_cap, new_user_signups_cap: 5)
expect(settings.new_user_signups_cap).to eq(5)
end
end
describe 'remove_dormant_members' do
let(:subgroup) { create(:group, :nested) }
it { expect(subgroup.namespace_settings).to validate_inclusion_of(:remove_dormant_members).in_array([false]) }
end
describe 'remove_dormant_members_period' do
it do
expect(settings).to validate_numericality_of(:remove_dormant_members_period)
.only_integer
.is_greater_than_or_equal_to(90)
.is_less_than_or_equal_to(1827)
end
end
end
describe '.duo_features_set' do
let_it_be(:setting_1) { create(:namespace_settings, duo_features_enabled: true) }
let_it_be(:setting_2) { create(:namespace_settings, duo_features_enabled: false) }
subject { described_class.duo_features_set(true) }
it { is_expected.to contain_exactly(setting_1) }
end
describe '#prevent_forking_outside_group?' do
context 'with feature available' do
before do
stub_licensed_features(group_forking_protection: true)
end
context 'group with no associated saml provider' do
before do
setting.update!(prevent_forking_outside_group: true)
end
it 'returns namespace setting' do
expect(setting.prevent_forking_outside_group?).to eq(true)
end
end
context 'group with associated saml provider' do
before do
stub_licensed_features(group_saml: true, group_forking_protection: true)
end
context 'when it is configured to true on saml level' do
before do
setting.update!(prevent_forking_outside_group: true)
create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: true, group: group)
end
it 'returns true' do
expect(setting.prevent_forking_outside_group?).to eq(true)
end
end
context 'when it is configured to false on saml level' do
before do
create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: false, group: group)
end
it 'returns false' do
expect(setting.prevent_forking_outside_group?).to eq(false)
end
context 'when setting is configured on namespace level' do
before do
setting.update!(prevent_forking_outside_group: true)
end
it 'returns namespace setting' do
expect(setting.prevent_forking_outside_group?).to eq(true)
end
end
end
end
end
context 'without feature available' do
before do
setting.update!(prevent_forking_outside_group: true)
end
it 'returns false' do
expect(setting.prevent_forking_outside_group?).to be_falsey
end
context 'when saml setting is available' do
before do
stub_licensed_features(group_saml: true)
end
context 'when it is configured to true on saml level' do
before do
create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: true, group: group)
end
it 'returns true' do
expect(setting.prevent_forking_outside_group?).to eq(true)
end
end
context 'when it is configured to false on saml level' do
before do
create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: false, group: group)
end
it 'returns false' do
expect(setting.prevent_forking_outside_group?).to eq(false)
end
end
end
end
end
describe '#user_cap_enabled?', feature_category: :consumables_cost_management do
where(:seat_control, :new_user_signups_cap, :root_namespace, :expectation) do
:off | nil | false | false
:off | nil | true | false
:off | 10 | false | false
:off | 10 | true | false
:user_cap | 10 | false | false
:user_cap | 10 | true | true
end
with_them do
let(:setting) { build(:namespace_settings, seat_control: seat_control, new_user_signups_cap: new_user_signups_cap) }
let(:group) { build(:group, namespace_settings: setting) }
before do
allow(group).to receive(:root?).and_return(root_namespace)
end
it 'returns the expected response' do
expect(setting.user_cap_enabled?).to be expectation
end
end
end
describe '#duo_availability' do
using RSpec::Parameterized::TableSyntax
where(:duo_features_enabled, :duo_features_enabled_locked, :expectation) do
true | false | :default_on
false | false | :default_off
false | true | :never_on
end
with_them do
before do
setting.duo_features_enabled = duo_features_enabled
allow(setting).to receive(:duo_features_enabled_locked?).with(include_self: true).and_return(duo_features_enabled_locked)
end
it 'returns the expected response' do
expect(setting.duo_availability).to eq(expectation)
end
end
context 'when value is "never_on"' do
it 'sets experiment_features_enabled to false' do
setting.duo_availability = "never_on"
expect(setting.experiment_features_enabled).to be false
end
end
end
describe '#duo_availability=' do
using RSpec::Parameterized::TableSyntax
where(:duo_availability, :duo_features_enabled_expectation, :lock_duo_features_enabled_expectation) do
"default_on" | true | false
"default_off" | false | false
"never_on" | false | true
end
with_them do
before do
setting.duo_availability = duo_availability
end
it 'returns the expected response' do
expect(setting.duo_features_enabled).to be duo_features_enabled_expectation
expect(setting.lock_duo_features_enabled).to be lock_duo_features_enabled_expectation
end
end
end
context 'validating new_user_signup_cap' do
using RSpec::Parameterized::TableSyntax
where(:feature_available, :seat_control_old, :old_value, :seat_control_new, :new_value, :expectation) do
true | :off | nil | :user_cap | 10 | true
true | :user_cap | 0 | :user_cap | 10 | true
true | :user_cap | 0 | :user_cap | 0 | true
false | :off | nil | :user_cap | 10 | false
false | :user_cap | 10 | :user_cap | 10 | true
end
with_them do
let(:setting) { build(:namespace_settings, seat_control: seat_control_old, new_user_signups_cap: old_value) }
let(:group) { create(:group, namespace_settings: setting) }
before do
allow(group).to receive(:user_cap_available?).and_return feature_available
setting.seat_control = seat_control_new
setting.new_user_signups_cap = new_value
end
it 'returns the expected response' do
expect(setting.valid?).to be expectation
expect(setting.errors.messages[:new_user_signups_cap]).to include("cannot be enabled") unless expectation
end
end
context 'when enabling the setting' do
let(:feature_available) { true }
before do
allow(group).to receive(:user_cap_available?).and_return feature_available
setting.seat_control = :user_cap
setting.new_user_signups_cap = 10
end
shared_examples 'user cap is not available' do
it 'is invalid' do
expect(setting.valid?).to be false
expect(setting.errors.messages[:new_user_signups_cap]).to include("cannot be enabled")
end
end
context 'when the group is a subgroup' do
before do
group.parent = build(:group)
end
it_behaves_like 'user cap is not available'
end
context 'when the group is shared externally' do
before do
create(:group_group_link, shared_group: group)
end
it_behaves_like 'user cap is not available'
end
context 'when the namespace is a user' do
let(:user) { create(:user) }
let(:setting) { user.namespace.namespace_settings }
it_behaves_like 'user cap is not available'
end
end
end
context 'hooks related to group user cap update' do
let(:group) { create(:group) }
let(:settings) { group.namespace_settings }
before do
allow(group).to receive(:root?).and_return(true)
allow(group).to receive(:user_cap_available?).and_return(true)
group.namespace_settings.update!(seat_control: seat_control, new_user_signups_cap: user_cap)
end
context 'when setting a user cap' do
let(:seat_control) { :off }
let(:user_cap) { nil }
it 'also sets share_with_group_lock and prevent_sharing_groups_outside_hierarchy to true' do
expect(group.new_user_signups_cap).to be_nil
expect(group.share_with_group_lock).to be_falsey
expect(settings.prevent_sharing_groups_outside_hierarchy).to be_falsey
settings.update!(seat_control: :user_cap, new_user_signups_cap: 10)
group.reload
expect(group.new_user_signups_cap).to eq(10)
expect(group.share_with_group_lock).to be_truthy
expect(settings.reload.prevent_sharing_groups_outside_hierarchy).to be_truthy
end
it 'has share_with_group_lock and prevent_sharing_groups_outside_hierarchy returning true for descendent groups' do
descendent = create(:group, parent: group)
desc_settings = descendent.namespace_settings
expect(descendent.share_with_group_lock).to be_falsey
expect(desc_settings.prevent_sharing_groups_outside_hierarchy).to be_falsey
settings.update!(seat_control: :user_cap, new_user_signups_cap: 10)
expect(descendent.reload.share_with_group_lock).to be_truthy
expect(desc_settings.reload.prevent_sharing_groups_outside_hierarchy).to be_truthy
end
end
context 'when removing a user cap from namespace settings' do
let(:seat_control) { :user_cap }
let(:user_cap) { 10 }
it 'leaves share_with_group_lock and prevent_sharing_groups_outside_hierarchy set to true to the related group' do
expect(group.share_with_group_lock).to be_truthy
expect(settings.prevent_sharing_groups_outside_hierarchy).to be_truthy
settings.update!(seat_control: :off, new_user_signups_cap: nil)
expect(group.reload.share_with_group_lock).to be_truthy
expect(settings.reload.prevent_sharing_groups_outside_hierarchy).to be_truthy
end
end
end
describe '#prevent_sharing_groups_outside_hierarchy' do
let_it_be_with_refind(:group) { create(:group) }
let(:settings) { group.namespace_settings }
it 'becomes enabled when block seat overages becomes enabled', :saas do
expect(settings.prevent_sharing_groups_outside_hierarchy).to eq(false)
settings.update!(seat_control: :block_overages)
expect(settings.reload.prevent_sharing_groups_outside_hierarchy).to eq(true)
end
context 'when block seat overages is already enabled for the group', :saas do
before do
group.namespace_settings.update!(seat_control: :block_overages)
end
it 'cannot be disabled' do
settings.update!(prevent_sharing_groups_outside_hierarchy: false)
expect(settings.reload.prevent_sharing_groups_outside_hierarchy).to eq(true)
end
end
end
describe '.allowed_namespace_settings_params' do
it 'includes attributes used for limiting unique project downloads' do
expect(described_class.allowed_namespace_settings_params).to include(
*%i[
unique_project_download_limit
unique_project_download_limit_interval_in_seconds
unique_project_download_limit_allowlist
])
end
end
describe '.cascading_with_parent_namespace' do
context "when calling .cascading_with_parent_namespace" do
it 'create three instance methods for attribute' do
described_class.cascading_with_parent_namespace("any_configuration")
expect(described_class.instance_methods).to include(
:any_configuration_of_parent_group, :any_configuration_locked?, :any_configuration?)
end
end
context 'three configurations of MR checks' do
let_it_be_with_reload(:group) { create(:group) }
let_it_be_with_reload(:subgroup) { create(:group, parent: group) }
let_it_be_with_reload(:subsubgroup) { create(:group, parent: subgroup) }
shared_examples '[configuration](inherit_group_setting: bool) and [configuration]_locked?' do |attribute|
where(:group_attr, :subgroup_attr, :subsubgroup_attr, :group_with_inherit_attr?, :group_without_inherit_attr?, :group_locked?, :subgroup_with_inherit_attr?, :subgroup_without_inherit_attr?, :subgroup_locked?, :subsubgroup_with_inherit_attr?, :subsubgroup_without_inherit_attr?, :subsubgroup_locked?) do
true | true | true | true | true | false | true | true | true | true | true | true
true | true | false | true | true | false | true | true | true | true | false | true
true | false | false | true | true | false | true | false | true | true | false | true
false | true | true | false | false | false | true | true | false | true | true | true
false | true | false | false | false | false | true | true | false | true | false | true
false | false | false | false | false | false | false | false | false | false | false | false
end
with_them do
before do
group.namespace_settings.update!(attribute => group_attr)
subgroup.namespace_settings.update!(attribute => subgroup_attr)
subsubgroup.namespace_settings.update!(attribute => subsubgroup_attr)
end
it 'returns correct value' do
expect(group.namespace_settings.public_send("#{attribute}?", inherit_group_setting: true)).to eq(group_with_inherit_attr?)
expect(group.namespace_settings.public_send("#{attribute}?", inherit_group_setting: false)).to eq(group_without_inherit_attr?)
expect(group.namespace_settings.public_send("#{attribute}_locked?")).to eq(group_locked?)
expect(subgroup.namespace_settings.public_send("#{attribute}?", inherit_group_setting: true)).to eq(subgroup_with_inherit_attr?)
expect(subgroup.namespace_settings.public_send("#{attribute}?", inherit_group_setting: false)).to eq(subgroup_without_inherit_attr?)
expect(subgroup.namespace_settings.public_send("#{attribute}_locked?")).to eq(subgroup_locked?)
expect(subsubgroup.namespace_settings.public_send("#{attribute}?", inherit_group_setting: true)).to eq(subsubgroup_with_inherit_attr?)
expect(subsubgroup.namespace_settings.public_send("#{attribute}?", inherit_group_setting: false)).to eq(subsubgroup_without_inherit_attr?)
expect(subsubgroup.namespace_settings.public_send("#{attribute}_locked?")).to eq(subsubgroup_locked?)
end
end
end
it_behaves_like '[configuration](inherit_group_setting: bool) and [configuration]_locked?', :only_allow_merge_if_pipeline_succeeds
it_behaves_like '[configuration](inherit_group_setting: bool) and [configuration]_locked?', :allow_merge_on_skipped_pipeline
it_behaves_like '[configuration](inherit_group_setting: bool) and [configuration]_locked?', :only_allow_merge_if_all_discussions_are_resolved
end
end
describe '.experiment_settings_allowed?' do
subject { group.namespace_settings.experiment_settings_allowed? }
context 'when namespace is root' do
let_it_be(:group) { create(:group) }
it { is_expected.to be true }
end
context 'when namespace is subgroup' do
let_it_be(:group) { create(:group, :nested) }
it { is_expected.to be false }
end
end
describe '#duo_features_enabled' do
it_behaves_like 'a cascading namespace setting boolean attribute', settings_attribute_name: :duo_features_enabled
end
describe '#enterprise_users_extensions_marketplace_enabled=' do
subject { setting.enterprise_users_extensions_marketplace_opt_in_status }
where(:value, :expected_opt_in_status) do
true | 'enabled'
false | 'disabled'
0 | 'disabled'
1 | 'enabled'
end
with_them do
before do
setting.update!(enterprise_users_extensions_marketplace_opt_in_status: 'unset')
setting.enterprise_users_extensions_marketplace_enabled = value
end
it { is_expected.to eq(expected_opt_in_status) }
end
end
end