mirror of
https://github.com/gitlabhq/gitlabhq.git
synced 2025-08-10 03:00:46 +00:00
217 lines
7.4 KiB
Ruby
217 lines
7.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
RSpec.describe Namespaces::Traversal::Cached, feature_category: :database do
|
|
describe 'callbacks' do
|
|
let_it_be_with_refind(:old_parent) { create(:group) }
|
|
let_it_be_with_refind(:new_parent) { create(:group) }
|
|
let_it_be_with_refind(:group) { create(:group, parent: old_parent) }
|
|
let_it_be_with_refind(:subgroup) { create(:group, parent: group) }
|
|
|
|
context 'when no cached records are present' do
|
|
it 'does nothing' do
|
|
group.parent = new_parent
|
|
|
|
expect { group.save! }.not_to change { Namespaces::Descendants.all.to_a }
|
|
end
|
|
end
|
|
|
|
context 'when the namespace record is UserNamespace' do
|
|
it 'does nothing' do
|
|
# we won't use the optimization for UserNamespace
|
|
namespace = create(:user_namespace)
|
|
cache = create(:namespace_descendants, namespace: namespace)
|
|
|
|
expect { namespace.destroy! }.not_to change { cache.reload.outdated_at }
|
|
end
|
|
end
|
|
|
|
context 'when cached record is present' do
|
|
let!(:cache) { create(:namespace_descendants, namespace: group) }
|
|
|
|
it 'invalidates the cache' do
|
|
expect { group.update!(parent: new_parent) }.to change { cache.reload.outdated_at }.from(nil)
|
|
end
|
|
|
|
it 'does not invalidate the cache of subgroups' do
|
|
subgroup_cache = create(:namespace_descendants, namespace: subgroup)
|
|
|
|
expect { group.update!(parent: new_parent) }.not_to change { subgroup_cache.reload.outdated_at }
|
|
end
|
|
|
|
context 'when a new subgroup is added' do
|
|
it 'invalidates the cache' do
|
|
expect { create(:group, parent: group) }.to change { cache.reload.outdated_at }
|
|
end
|
|
end
|
|
|
|
context 'when a new project is added' do
|
|
it 'invalidates the cache' do
|
|
expect { create(:project, group: group) }.to change { cache.reload.outdated_at }
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when parent group has cached record' do
|
|
it 'invalidates the parent cache' do
|
|
old_parent_cache = create(:namespace_descendants, namespace: old_parent)
|
|
new_parent_cache = create(:namespace_descendants, namespace: new_parent)
|
|
|
|
group.update!(parent: new_parent)
|
|
|
|
expect(old_parent_cache.reload.outdated_at).not_to be_nil
|
|
expect(new_parent_cache.reload.outdated_at).not_to be_nil
|
|
end
|
|
end
|
|
|
|
context 'when group is destroyed' do
|
|
it 'invalidates the cache' do
|
|
subgroup.destroy!
|
|
cache = create(:namespace_descendants, namespace: group)
|
|
|
|
expect { group.destroy! }.to change { cache.reload.outdated_at }.from(nil)
|
|
end
|
|
|
|
context 'when parent group has cached record' do
|
|
it 'invalidates the parent cache' do
|
|
old_parent_cache = create(:namespace_descendants, namespace: old_parent)
|
|
new_parent_cache = create(:namespace_descendants, namespace: new_parent)
|
|
|
|
subgroup.destroy!
|
|
group.destroy!
|
|
|
|
expect(old_parent_cache.reload.outdated_at).not_to be_nil
|
|
expect(new_parent_cache.reload.outdated_at).to be_nil # no change
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'query methods' do
|
|
let_it_be(:group) { create(:group) }
|
|
let_it_be(:subgroup) { create(:group, parent: group) }
|
|
let_it_be(:subsubgroup) { create(:group, parent: subgroup) }
|
|
|
|
let_it_be(:project1) { create(:project, group: group) }
|
|
let_it_be(:project2) { create(:project, group: subsubgroup) }
|
|
let_it_be(:project3) { create(:project, group: subsubgroup, archived: true) }
|
|
|
|
# deliberately making the created record different from the actual
|
|
# data so we can verify that the cached query is running.
|
|
# self_and_descendant_group_ids should be [group.id, subgroup.id, subsubgroup.id]
|
|
# all_project_ids should be [project1.id, project2.id, project3.id]
|
|
# all_unarchived_project_ids should be [project1.id, project2.id]
|
|
let_it_be_with_refind(:namespace_descendants) do
|
|
create(:namespace_descendants,
|
|
:up_to_date,
|
|
namespace: group,
|
|
self_and_descendant_group_ids: [group.id, subgroup.id],
|
|
all_project_ids: [project1.id],
|
|
all_unarchived_project_ids: [project1.id]
|
|
)
|
|
end
|
|
|
|
describe '#self_and_descendant_ids' do
|
|
subject(:ids) { group.self_and_descendant_ids.pluck(:id) }
|
|
|
|
it 'returns the cached values' do
|
|
expect(ids).to eq(namespace_descendants.self_and_descendant_group_ids)
|
|
end
|
|
|
|
context 'when the cache is outdated' do
|
|
it 'returns the values from the uncached self_and_descendant_ids query' do
|
|
namespace_descendants.update!(outdated_at: Time.current)
|
|
|
|
expect(ids.sort).to eq([group.id, subgroup.id, subsubgroup.id])
|
|
end
|
|
end
|
|
|
|
context 'when the group_hierarchy_optimization feature flag is disabled' do
|
|
before do
|
|
stub_feature_flags(group_hierarchy_optimization: false)
|
|
end
|
|
|
|
it 'returns the values from the uncached self_and_descendant_ids query' do
|
|
expect(ids.sort).to eq([group.id, subgroup.id, subsubgroup.id])
|
|
end
|
|
end
|
|
|
|
context 'when the scope is specified' do
|
|
it 'returns uncached values that match the scope' do
|
|
ids = group.self_and_descendant_ids(skope: Namespace).pluck(:id)
|
|
|
|
expect(ids).to contain_exactly(
|
|
group.id, subgroup.id, subsubgroup.id, project1.project_namespace.id, project2.project_namespace.id,
|
|
project3.project_namespace.id
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#all_project_ids' do
|
|
subject(:ids) { group.all_project_ids.pluck(:id) }
|
|
|
|
it 'returns the cached values' do
|
|
expect(ids).to eq(namespace_descendants.all_project_ids)
|
|
end
|
|
|
|
context 'when the cache is outdated' do
|
|
it 'returns the values from the uncached all_project_ids query' do
|
|
namespace_descendants.update!(outdated_at: Time.current)
|
|
|
|
expect(ids.sort).to eq([project1.id, project2.id, project3.id])
|
|
end
|
|
end
|
|
|
|
context 'when the group_hierarchy_optimization feature flag is disabled' do
|
|
before do
|
|
stub_feature_flags(group_hierarchy_optimization: false)
|
|
end
|
|
|
|
it 'returns the values from the uncached all_project_ids query' do
|
|
expect(ids.sort).to eq([project1.id, project2.id, project3.id])
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#all_unarchived_project_ids' do
|
|
subject(:ids) { group.all_unarchived_project_ids.pluck(:id) }
|
|
|
|
it 'returns the cached values' do
|
|
expect(ids).to eq(namespace_descendants.all_unarchived_project_ids)
|
|
end
|
|
|
|
context 'when the cache is outdated' do
|
|
before do
|
|
namespace_descendants.update!(outdated_at: Time.current)
|
|
end
|
|
|
|
it 'returns the values from the uncached all_unarchived_project_ids query' do
|
|
expect(ids).to match_array([project1.id, project2.id])
|
|
end
|
|
|
|
context 'when group is archived' do
|
|
before do
|
|
group.archive
|
|
end
|
|
|
|
it 'excludes all descendants projects' do
|
|
expect(ids).to be_empty
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when the group_hierarchy_optimization feature flag is disabled' do
|
|
before do
|
|
stub_feature_flags(group_hierarchy_optimization: false)
|
|
end
|
|
|
|
it 'returns the values from the uncached all_unarchived_project_ids query' do
|
|
expect(ids).to match_array([project1.id, project2.id])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|