390 lines
16 KiB
Ruby
390 lines
16 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
RSpec.describe ProjectAuthorizations::Changes, feature_category: :groups_and_projects do
|
|
describe '.apply!' do
|
|
subject(:apply_project_authorization_changes) { project_authorization_changes.apply! }
|
|
|
|
shared_examples_for 'does not log any detail' do
|
|
it 'does not log any detail' do
|
|
expect(Gitlab::AppLogger).not_to receive(:info)
|
|
|
|
apply_project_authorization_changes
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'logs the detail' do |batch_size:|
|
|
it 'logs the detail' do
|
|
expect(Gitlab::AppLogger).to receive(:info).with(
|
|
entire_size: 3,
|
|
message: 'Project authorizations refresh performed with delay',
|
|
total_delay: (3 / batch_size.to_f).ceil * ProjectAuthorizations::Changes::SLEEP_DELAY,
|
|
**Gitlab::ApplicationContext.current
|
|
)
|
|
|
|
apply_project_authorization_changes
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'publishes AuthorizationsChangedEvent' do
|
|
it 'publishes a AuthorizationsChangedEvent event with project id' do
|
|
project_ids.each do |project_id|
|
|
project_data = { project_id: project_id }
|
|
project_event = instance_double('::ProjectAuthorizations::AuthorizationsChangedEvent', data: project_data)
|
|
|
|
allow(::ProjectAuthorizations::AuthorizationsChangedEvent).to receive(:new)
|
|
.with(data: project_data)
|
|
.and_return(project_event)
|
|
|
|
allow(::Gitlab::EventStore).to receive(:publish)
|
|
expect(::Gitlab::EventStore).to receive(:publish).with(project_event)
|
|
end
|
|
|
|
apply_project_authorization_changes
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'publishes AuthorizationsRemovedEvent' do
|
|
it 'publishes a AuthorizationsRemovedEvent event with project id' do
|
|
project_ids.each do |project_id|
|
|
project_data = { project_id: project_id, user_ids: user_ids }
|
|
project_event = instance_double('::ProjectAuthorizations::AuthorizationsRemovedEvent', data: project_data)
|
|
|
|
allow(::ProjectAuthorizations::AuthorizationsRemovedEvent).to receive(:new)
|
|
.with(data: project_data)
|
|
.and_return(project_event)
|
|
|
|
allow(::Gitlab::EventStore).to receive(:publish)
|
|
expect(::Gitlab::EventStore).to receive(:publish).with(project_event)
|
|
end
|
|
|
|
apply_project_authorization_changes
|
|
end
|
|
|
|
context 'when feature flag "user_approval_rules_removal" is disabled' do
|
|
before do
|
|
stub_feature_flags(user_approval_rules_removal: false)
|
|
end
|
|
|
|
it 'does not publish a AuthorizationsRemovedEvent event' do
|
|
expect(::Gitlab::EventStore).not_to(
|
|
receive(:publish).with(an_instance_of(::ProjectAuthorizations::AuthorizationsRemovedEvent))
|
|
)
|
|
|
|
apply_project_authorization_changes
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'does not publish AuthorizationsChangedEvent' do
|
|
it 'does not publish a AuthorizationsChangedEvent event' do
|
|
expect(::Gitlab::EventStore).not_to receive(:publish)
|
|
.with(an_instance_of(::ProjectAuthorizations::AuthorizationsChangedEvent))
|
|
|
|
apply_project_authorization_changes
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'does not publish AuthorizationsRemovedEvent' do
|
|
it 'does not publish a AuthorizationsRemovedEvent event' do
|
|
expect(::Gitlab::EventStore).not_to receive(:publish)
|
|
.with(an_instance_of(::ProjectAuthorizations::AuthorizationsRemovedEvent))
|
|
|
|
apply_project_authorization_changes
|
|
end
|
|
end
|
|
|
|
context 'when new authorizations should be added' do
|
|
let_it_be(:user) { create(:user) }
|
|
let_it_be(:project_1) { create(:project) }
|
|
let_it_be(:project_2) { create(:project) }
|
|
let_it_be(:project_3) { create(:project) }
|
|
let(:project_ids) { [project_1.id, project_2.id, project_3.id] }
|
|
|
|
let(:authorizations_to_add) do
|
|
[
|
|
{ user_id: user.id, project_id: project_1.id, access_level: Gitlab::Access::MAINTAINER },
|
|
{ user_id: user.id, project_id: project_2.id, access_level: Gitlab::Access::MAINTAINER },
|
|
{ user_id: user.id, project_id: project_3.id, access_level: Gitlab::Access::MAINTAINER }
|
|
]
|
|
end
|
|
|
|
let(:project_authorization_changes) do
|
|
ProjectAuthorizations::Changes.new do |changes|
|
|
changes.add(authorizations_to_add)
|
|
end
|
|
end
|
|
|
|
before do
|
|
# Configure as if a replica database is enabled
|
|
allow(::Gitlab::Database::LoadBalancing).to receive(:primary_only?).and_return(false)
|
|
end
|
|
|
|
shared_examples_for 'inserts the rows in batches, as per the `per_batch` size, without a delay between batches' do
|
|
specify do
|
|
expect(project_authorization_changes).not_to receive(:sleep)
|
|
|
|
apply_project_authorization_changes
|
|
|
|
expect(user.project_authorizations.pluck(:user_id, :project_id,
|
|
:access_level, :is_unique)).to match_array(authorizations_to_add.map(&:values))
|
|
end
|
|
end
|
|
|
|
context 'when the total number of records to be inserted is greater than the batch size' do
|
|
before do
|
|
stub_const("#{described_class}::BATCH_SIZE", 2)
|
|
end
|
|
|
|
it 'inserts the rows in batches, as per the `per_batch` size, with a delay between each batch' do
|
|
expect(ProjectAuthorization).to receive(:insert_all).twice.and_call_original
|
|
expect(project_authorization_changes).to receive(:sleep).twice
|
|
|
|
apply_project_authorization_changes
|
|
|
|
expect(user.project_authorizations.pluck(:user_id, :project_id,
|
|
:access_level, :is_unique)).to match_array(authorizations_to_add.map(&:values))
|
|
end
|
|
|
|
it 'writes is_unique' do
|
|
apply_project_authorization_changes
|
|
|
|
expect(user.project_authorizations.pluck(:is_unique)).to all(be(true))
|
|
end
|
|
|
|
it_behaves_like 'logs the detail', batch_size: 2
|
|
it_behaves_like 'publishes AuthorizationsChangedEvent'
|
|
it_behaves_like 'does not publish AuthorizationsRemovedEvent'
|
|
|
|
context 'when the GitLab installation does not have a replica database configured' do
|
|
before do
|
|
# Configure as if a replica database is not enabled
|
|
allow(::Gitlab::Database::LoadBalancing).to receive(:primary_only?).and_return(true)
|
|
end
|
|
|
|
it_behaves_like 'inserts the rows in batches, as per the `per_batch` size, without a delay between batches'
|
|
it_behaves_like 'does not log any detail'
|
|
it_behaves_like 'publishes AuthorizationsChangedEvent'
|
|
it_behaves_like 'does not publish AuthorizationsRemovedEvent'
|
|
end
|
|
end
|
|
|
|
context 'when the total number of records to be inserted is less than the batch size' do
|
|
before do
|
|
stub_const("#{described_class}::BATCH_SIZE", 5)
|
|
end
|
|
|
|
it_behaves_like 'inserts the rows in batches, as per the `per_batch` size, without a delay between batches'
|
|
it_behaves_like 'does not log any detail'
|
|
it_behaves_like 'publishes AuthorizationsChangedEvent'
|
|
it_behaves_like 'does not publish AuthorizationsRemovedEvent'
|
|
end
|
|
end
|
|
|
|
context 'when authorizations should be deleted for a project' do
|
|
let_it_be(:project) { create(:project) }
|
|
let_it_be(:user_1) { create(:user) }
|
|
let_it_be(:user_2) { create(:user) }
|
|
let_it_be(:user_3) { create(:user) }
|
|
let_it_be(:user_4) { create(:user) }
|
|
|
|
let(:user_ids) { [user_1.id, user_2.id, user_3.id] }
|
|
let(:project_ids) { [project.id] }
|
|
|
|
let(:project_authorization_changes) do
|
|
ProjectAuthorizations::Changes.new do |changes|
|
|
changes.remove_users_in_project(project, user_ids)
|
|
end
|
|
end
|
|
|
|
before do
|
|
# Configure as if a replica database is enabled
|
|
allow(::Gitlab::Database::LoadBalancing).to receive(:primary_only?).and_return(false)
|
|
end
|
|
|
|
before_all do
|
|
create(:project_authorization, user: user_1, project: project)
|
|
create(:project_authorization, user: user_2, project: project)
|
|
create(:project_authorization, user: user_3, project: project)
|
|
create(:project_authorization, user: user_4, project: project)
|
|
end
|
|
|
|
shared_examples_for 'removes project authorizations of the users in the current project, without a delay' do
|
|
specify do
|
|
expect(project_authorization_changes).not_to receive(:sleep)
|
|
|
|
apply_project_authorization_changes
|
|
|
|
expect(project.project_authorizations.pluck(:user_id)).not_to include(*user_ids)
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'does not removes project authorizations of the users in the current project' do
|
|
it 'does not delete any project authorization' do
|
|
expect { apply_project_authorization_changes }.not_to change { project.project_authorizations.count }
|
|
end
|
|
end
|
|
|
|
context 'when the total number of records to be removed is greater than the batch size' do
|
|
before do
|
|
stub_const("#{described_class}::BATCH_SIZE", 2)
|
|
end
|
|
|
|
it 'removes project authorizations of the users in the current project, with a delay' do
|
|
expect(project_authorization_changes).to receive(:sleep).twice
|
|
|
|
apply_project_authorization_changes
|
|
|
|
expect(project.project_authorizations.pluck(:user_id)).not_to include(*user_ids)
|
|
end
|
|
|
|
it_behaves_like 'logs the detail', batch_size: 2
|
|
it_behaves_like 'publishes AuthorizationsChangedEvent'
|
|
it_behaves_like 'publishes AuthorizationsRemovedEvent'
|
|
|
|
context 'when the GitLab installation does not have a replica database configured' do
|
|
before do
|
|
# Configure as if a replica database is not enabled
|
|
allow(::Gitlab::Database::LoadBalancing).to receive(:primary_only?).and_return(true)
|
|
end
|
|
|
|
it_behaves_like 'removes project authorizations of the users in the current project, without a delay'
|
|
it_behaves_like 'does not log any detail'
|
|
it_behaves_like 'publishes AuthorizationsChangedEvent'
|
|
it_behaves_like 'publishes AuthorizationsRemovedEvent'
|
|
end
|
|
end
|
|
|
|
context 'when the total number of records to be removed is less than the batch size' do
|
|
before do
|
|
stub_const("#{described_class}::BATCH_SIZE", 5)
|
|
end
|
|
|
|
it_behaves_like 'removes project authorizations of the users in the current project, without a delay'
|
|
it_behaves_like 'does not log any detail'
|
|
it_behaves_like 'publishes AuthorizationsChangedEvent'
|
|
it_behaves_like 'publishes AuthorizationsRemovedEvent'
|
|
end
|
|
|
|
context 'when the user_ids list is empty' do
|
|
let(:user_ids) { [] }
|
|
|
|
it_behaves_like 'does not removes project authorizations of the users in the current project'
|
|
it_behaves_like 'does not publish AuthorizationsChangedEvent'
|
|
it_behaves_like 'does not publish AuthorizationsRemovedEvent'
|
|
end
|
|
|
|
context 'when the user_ids list is nil' do
|
|
let(:user_ids) { nil }
|
|
|
|
it_behaves_like 'does not removes project authorizations of the users in the current project'
|
|
it_behaves_like 'does not publish AuthorizationsChangedEvent'
|
|
it_behaves_like 'does not publish AuthorizationsRemovedEvent'
|
|
end
|
|
end
|
|
|
|
describe 'when authorizations should be deleted for an user' do
|
|
let_it_be(:user) { create(:user) }
|
|
let_it_be(:project_1) { create(:project) }
|
|
let_it_be(:project_2) { create(:project) }
|
|
let_it_be(:project_3) { create(:project) }
|
|
let_it_be(:project_4) { create(:project) }
|
|
|
|
let(:project_ids) { [project_1.id, project_2.id, project_3.id] }
|
|
let(:user_ids) { [user.id] }
|
|
|
|
let(:project_authorization_changes) do
|
|
ProjectAuthorizations::Changes.new do |changes|
|
|
changes.remove_projects_for_user(user, project_ids)
|
|
end
|
|
end
|
|
|
|
before do
|
|
# Configure as if a replica database is enabled
|
|
allow(::Gitlab::Database::LoadBalancing).to receive(:primary_only?).and_return(false)
|
|
end
|
|
|
|
before_all do
|
|
create(:project_authorization, user: user, project: project_1)
|
|
create(:project_authorization, user: user, project: project_2)
|
|
create(:project_authorization, user: user, project: project_3)
|
|
create(:project_authorization, user: user, project: project_4)
|
|
end
|
|
|
|
shared_examples_for 'removes project authorizations of projects from the current user, without a delay' do
|
|
specify do
|
|
expect(project_authorization_changes).not_to receive(:sleep)
|
|
|
|
apply_project_authorization_changes
|
|
|
|
expect(user.project_authorizations.pluck(:project_id)).not_to include(*project_ids)
|
|
end
|
|
end
|
|
|
|
shared_examples_for 'does not removes any project authorizations from the current user' do
|
|
it 'does not delete any project authorization' do
|
|
expect { apply_project_authorization_changes }.not_to change { user.project_authorizations.count }
|
|
end
|
|
end
|
|
|
|
context 'when the total number of records to be removed is greater than the batch size' do
|
|
before do
|
|
stub_const("#{described_class}::BATCH_SIZE", 2)
|
|
end
|
|
|
|
it 'removes the project authorizations of projects from the current user, with a delay between each batch' do
|
|
expect(project_authorization_changes).to receive(:sleep).twice
|
|
|
|
apply_project_authorization_changes
|
|
|
|
expect(user.project_authorizations.pluck(:project_id)).not_to include(*project_ids)
|
|
end
|
|
|
|
it_behaves_like 'logs the detail', batch_size: 2
|
|
it_behaves_like 'publishes AuthorizationsChangedEvent'
|
|
it_behaves_like 'publishes AuthorizationsRemovedEvent'
|
|
|
|
context 'when the GitLab installation does not have a replica database configured' do
|
|
before do
|
|
# Configure as if a replica database is not enabled
|
|
allow(::Gitlab::Database::LoadBalancing).to receive(:primary_only?).and_return(true)
|
|
end
|
|
|
|
it_behaves_like 'removes project authorizations of projects from the current user, without a delay'
|
|
it_behaves_like 'does not log any detail'
|
|
it_behaves_like 'publishes AuthorizationsChangedEvent'
|
|
it_behaves_like 'publishes AuthorizationsRemovedEvent'
|
|
end
|
|
end
|
|
|
|
context 'when the total number of records to be removed is less than the batch size' do
|
|
before do
|
|
stub_const("#{described_class}::BATCH_SIZE", 5)
|
|
end
|
|
|
|
it_behaves_like 'removes project authorizations of projects from the current user, without a delay'
|
|
it_behaves_like 'does not log any detail'
|
|
it_behaves_like 'publishes AuthorizationsChangedEvent'
|
|
it_behaves_like 'publishes AuthorizationsRemovedEvent'
|
|
end
|
|
|
|
context 'when the project_ids list is empty' do
|
|
let(:project_ids) { [] }
|
|
|
|
it_behaves_like 'does not removes any project authorizations from the current user'
|
|
it_behaves_like 'does not publish AuthorizationsChangedEvent'
|
|
it_behaves_like 'does not publish AuthorizationsRemovedEvent'
|
|
end
|
|
|
|
context 'when the user_ids list is nil' do
|
|
let(:project_ids) { nil }
|
|
|
|
it_behaves_like 'does not removes any project authorizations from the current user'
|
|
it_behaves_like 'does not publish AuthorizationsChangedEvent'
|
|
it_behaves_like 'does not publish AuthorizationsRemovedEvent'
|
|
end
|
|
end
|
|
end
|
|
end
|