Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/group_spec.rb')
-rw-r--r--spec/models/group_spec.rb407
1 files changed, 268 insertions, 139 deletions
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index ddeab16908d..96ef36a5b75 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -683,160 +683,126 @@ RSpec.describe Group, feature_category: :groups_and_projects do
context 'traversal queries' do
let_it_be(:group, reload: true) { create(:group, :nested) }
- context 'recursive' do
- before do
- stub_feature_flags(use_traversal_ids: false)
- end
-
- it_behaves_like 'namespace traversal'
+ it_behaves_like 'namespace traversal'
- describe '#self_and_descendants' do
- it { expect(group.self_and_descendants.to_sql).not_to include 'traversal_ids @>' }
- end
+ describe '#self_and_descendants' do
+ it { expect(group.self_and_descendants.to_sql).to include 'traversal_ids @>' }
+ end
- describe '#self_and_descendant_ids' do
- it { expect(group.self_and_descendant_ids.to_sql).not_to include 'traversal_ids @>' }
- end
+ describe '#self_and_descendant_ids' do
+ it { expect(group.self_and_descendant_ids.to_sql).to include 'traversal_ids @>' }
+ end
- describe '#descendants' do
- it { expect(group.descendants.to_sql).not_to include 'traversal_ids @>' }
- end
+ describe '#descendants' do
+ it { expect(group.descendants.to_sql).to include 'traversal_ids @>' }
+ end
- describe '#self_and_hierarchy' do
- it { expect(group.self_and_hierarchy.to_sql).not_to include 'traversal_ids @>' }
- end
+ describe '#self_and_hierarchy' do
+ it { expect(group.self_and_hierarchy.to_sql).to include 'traversal_ids @>' }
+ end
- describe '#ancestors' do
- it { expect(group.ancestors.to_sql).not_to include 'traversal_ids <@' }
- end
+ describe '#ancestors' do
+ it { expect(group.ancestors.to_sql).to include "\"namespaces\".\"id\" = #{group.parent_id}" }
- describe '.shortest_traversal_ids_prefixes' do
- it { expect { described_class.shortest_traversal_ids_prefixes }.to raise_error /Feature not supported since the `:use_traversal_ids` is disabled/ }
+ it 'hierarchy order' do
+ expect(group.ancestors(hierarchy_order: :asc).to_sql).to include 'ORDER BY "depth" ASC'
end
end
- context 'linear' do
- it_behaves_like 'namespace traversal'
+ describe '#ancestors_upto' do
+ it { expect(group.ancestors_upto.to_sql).to include "WITH ORDINALITY" }
+ end
- describe '#self_and_descendants' do
- it { expect(group.self_and_descendants.to_sql).to include 'traversal_ids @>' }
- end
+ describe '.shortest_traversal_ids_prefixes' do
+ subject { filter.shortest_traversal_ids_prefixes }
- describe '#self_and_descendant_ids' do
- it { expect(group.self_and_descendant_ids.to_sql).to include 'traversal_ids @>' }
- end
+ context 'for many top-level namespaces' do
+ let!(:top_level_groups) { create_list(:group, 4) }
- describe '#descendants' do
- it { expect(group.descendants.to_sql).to include 'traversal_ids @>' }
- end
+ context 'when querying all groups' do
+ let(:filter) { described_class.id_in(top_level_groups) }
- describe '#self_and_hierarchy' do
- it { expect(group.self_and_hierarchy.to_sql).to include 'traversal_ids @>' }
- end
+ it "returns all traversal_ids" do
+ is_expected.to contain_exactly(
+ *top_level_groups.map { |group| [group.id] }
+ )
+ end
+ end
- describe '#ancestors' do
- it { expect(group.ancestors.to_sql).to include "\"namespaces\".\"id\" = #{group.parent_id}" }
+ context 'when querying selected groups' do
+ let(:filter) { described_class.id_in(top_level_groups.first) }
- it 'hierarchy order' do
- expect(group.ancestors(hierarchy_order: :asc).to_sql).to include 'ORDER BY "depth" ASC'
+ it "returns only a selected traversal_ids" do
+ is_expected.to contain_exactly([top_level_groups.first.id])
+ end
end
end
- describe '#ancestors_upto' do
- it { expect(group.ancestors_upto.to_sql).to include "WITH ORDINALITY" }
- end
+ context 'for namespace hierarchy' do
+ let!(:group_a) { create(:group) }
+ let!(:group_a_sub_1) { create(:group, parent: group_a) }
+ let!(:group_a_sub_2) { create(:group, parent: group_a) }
+ let!(:group_b) { create(:group) }
+ let!(:group_b_sub_1) { create(:group, parent: group_b) }
+ let!(:group_c) { create(:group) }
- describe '.shortest_traversal_ids_prefixes' do
- subject { filter.shortest_traversal_ids_prefixes }
+ context 'when querying all groups' do
+ let(:filter) { described_class.id_in([group_a, group_a_sub_1, group_a_sub_2, group_b, group_b_sub_1, group_c]) }
- context 'for many top-level namespaces' do
- let!(:top_level_groups) { create_list(:group, 4) }
-
- context 'when querying all groups' do
- let(:filter) { described_class.id_in(top_level_groups) }
-
- it "returns all traversal_ids" do
- is_expected.to contain_exactly(
- *top_level_groups.map { |group| [group.id] }
- )
- end
- end
-
- context 'when querying selected groups' do
- let(:filter) { described_class.id_in(top_level_groups.first) }
-
- it "returns only a selected traversal_ids" do
- is_expected.to contain_exactly([top_level_groups.first.id])
- end
+ it 'returns only shortest prefixes of top-level groups' do
+ is_expected.to contain_exactly(
+ [group_a.id],
+ [group_b.id],
+ [group_c.id]
+ )
end
end
- context 'for namespace hierarchy' do
- let!(:group_a) { create(:group) }
- let!(:group_a_sub_1) { create(:group, parent: group_a) }
- let!(:group_a_sub_2) { create(:group, parent: group_a) }
- let!(:group_b) { create(:group) }
- let!(:group_b_sub_1) { create(:group, parent: group_b) }
- let!(:group_c) { create(:group) }
+ context 'when sub-group is reparented' do
+ let(:filter) { described_class.id_in([group_b_sub_1, group_c]) }
- context 'when querying all groups' do
- let(:filter) { described_class.id_in([group_a, group_a_sub_1, group_a_sub_2, group_b, group_b_sub_1, group_c]) }
-
- it 'returns only shortest prefixes of top-level groups' do
- is_expected.to contain_exactly(
- [group_a.id],
- [group_b.id],
- [group_c.id]
- )
- end
+ before do
+ group_b_sub_1.update!(parent: group_c)
end
- context 'when sub-group is reparented' do
- let(:filter) { described_class.id_in([group_b_sub_1, group_c]) }
-
- before do
- group_b_sub_1.update!(parent: group_c)
- end
-
- it 'returns a proper shortest prefix of a new group' do
- is_expected.to contain_exactly(
- [group_c.id]
- )
- end
+ it 'returns a proper shortest prefix of a new group' do
+ is_expected.to contain_exactly(
+ [group_c.id]
+ )
end
+ end
- context 'when querying sub-groups' do
- let(:filter) { described_class.id_in([group_a_sub_1, group_b_sub_1, group_c]) }
+ context 'when querying sub-groups' do
+ let(:filter) { described_class.id_in([group_a_sub_1, group_b_sub_1, group_c]) }
- it 'returns sub-groups as they are shortest prefixes' do
- is_expected.to contain_exactly(
- [group_a.id, group_a_sub_1.id],
- [group_b.id, group_b_sub_1.id],
- [group_c.id]
- )
- end
+ it 'returns sub-groups as they are shortest prefixes' do
+ is_expected.to contain_exactly(
+ [group_a.id, group_a_sub_1.id],
+ [group_b.id, group_b_sub_1.id],
+ [group_c.id]
+ )
end
+ end
- context 'when querying group and sub-group of this group' do
- let(:filter) { described_class.id_in([group_a, group_a_sub_1, group_c]) }
+ context 'when querying group and sub-group of this group' do
+ let(:filter) { described_class.id_in([group_a, group_a_sub_1, group_c]) }
- it 'returns parent groups as this contains all sub-groups' do
- is_expected.to contain_exactly(
- [group_a.id],
- [group_c.id]
- )
- end
+ it 'returns parent groups as this contains all sub-groups' do
+ is_expected.to contain_exactly(
+ [group_a.id],
+ [group_c.id]
+ )
end
end
end
+ end
- context 'when project namespace exists in the group' do
- let!(:project) { create(:project, group: group) }
- let!(:project_namespace) { project.project_namespace }
+ context 'when project namespace exists in the group' do
+ let!(:project) { create(:project, group: group) }
+ let!(:project_namespace) { project.project_namespace }
- it 'filters out project namespace' do
- expect(group.descendants.find_by_id(project_namespace.id)).to be_nil
- end
+ it 'filters out project namespace' do
+ expect(group.descendants.find_by_id(project_namespace.id)).to be_nil
end
end
end
@@ -921,6 +887,143 @@ RSpec.describe Group, feature_category: :groups_and_projects do
end
end
+ describe '.sort_by_attribute' do
+ before do
+ group.destroy!
+ end
+
+ let!(:group_1) { create(:group, name: 'Y group') }
+ let!(:group_2) { create(:group, name: 'J group', created_at: 2.days.ago, updated_at: 1.day.ago) }
+ let!(:group_3) { create(:group, name: 'A group') }
+ let!(:group_4) { create(:group, name: 'F group', created_at: 1.day.ago, updated_at: 1.day.ago) }
+
+ subject { described_class.with_statistics.with_route.sort_by_attribute(sort) }
+
+ context 'when sort by is not provided (id desc by default)' do
+ let(:sort) { nil }
+
+ it { is_expected.to eq([group_1, group_2, group_3, group_4]) }
+ end
+
+ context 'when sort by name_asc' do
+ let(:sort) { 'name_asc' }
+
+ it { is_expected.to eq([group_3, group_4, group_2, group_1]) }
+ end
+
+ context 'when sort by name_desc' do
+ let(:sort) { 'name_desc' }
+
+ it { is_expected.to eq([group_1, group_2, group_4, group_3]) }
+ end
+
+ context 'when sort by recently_created' do
+ let(:sort) { 'created_desc' }
+
+ it { is_expected.to eq([group_3, group_1, group_4, group_2]) }
+ end
+
+ context 'when sort by oldest_created' do
+ let(:sort) { 'created_asc' }
+
+ it { is_expected.to eq([group_2, group_4, group_1, group_3]) }
+ end
+
+ context 'when sort by latest_activity' do
+ let(:sort) { 'latest_activity_desc' }
+
+ it { is_expected.to eq([group_1, group_2, group_3, group_4]) }
+ end
+
+ context 'when sort by oldest_activity' do
+ let(:sort) { 'latest_activity_asc' }
+
+ it { is_expected.to eq([group_1, group_2, group_3, group_4]) }
+ end
+
+ context 'when sort by storage_size_desc' do
+ let!(:project_1) do
+ create(:project,
+ namespace: group_1,
+ statistics: build(
+ :project_statistics,
+ namespace: group_1,
+ repository_size: 2178370,
+ storage_size: 1278370,
+ wiki_size: 505,
+ lfs_objects_size: 202,
+ build_artifacts_size: 303,
+ pipeline_artifacts_size: 707,
+ packages_size: 404,
+ snippets_size: 605,
+ uploads_size: 808
+ )
+ )
+ end
+
+ let!(:project_2) do
+ create(:project,
+ namespace: group_2,
+ statistics: build(
+ :project_statistics,
+ namespace: group_2,
+ repository_size: 3178370,
+ storage_size: 3178370,
+ wiki_size: 505,
+ lfs_objects_size: 202,
+ build_artifacts_size: 303,
+ pipeline_artifacts_size: 707,
+ packages_size: 404,
+ snippets_size: 605,
+ uploads_size: 808
+ )
+ )
+ end
+
+ let!(:project_3) do
+ create(:project,
+ namespace: group_3,
+ statistics: build(
+ :project_statistics,
+ namespace: group_3,
+ repository_size: 1278370,
+ storage_size: 1178370,
+ wiki_size: 505,
+ lfs_objects_size: 202,
+ build_artifacts_size: 303,
+ pipeline_artifacts_size: 707,
+ packages_size: 404,
+ snippets_size: 605,
+ uploads_size: 808
+ )
+ )
+ end
+
+ let!(:project_4) do
+ create(:project,
+ namespace: group_4,
+ statistics: build(
+ :project_statistics,
+ namespace: group_4,
+ repository_size: 2178370,
+ storage_size: 2278370,
+ wiki_size: 505,
+ lfs_objects_size: 202,
+ build_artifacts_size: 303,
+ pipeline_artifacts_size: 707,
+ packages_size: 404,
+ snippets_size: 605,
+ uploads_size: 808
+ )
+ )
+ end
+
+ let(:sort) { 'storage_size_desc' }
+
+ it { is_expected.to eq([group_2, group_4, group_1, group_3]) }
+ end
+ end
+
describe 'scopes' do
let_it_be(:private_group) { create(:group, :private) }
let_it_be(:internal_group) { create(:group, :internal) }
@@ -1152,21 +1255,6 @@ RSpec.describe Group, feature_category: :groups_and_projects do
expect(group.group_members.developers.map(&:user)).to include(user)
expect(group.group_members.guests.map(&:user)).not_to include(user)
end
-
- context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do
- let!(:project) { create(:project, group: group) }
-
- before do
- group.add_members([create(:user)], :developer, tasks_to_be_done: %w(ci code), tasks_project_id: project.id)
- end
-
- it 'creates a member_task with the correct attributes', :aggregate_failures do
- member = group.group_members.last
-
- expect(member.tasks_to_be_done).to match_array([:ci, :code])
- expect(member.member_task.project).to eq(project)
- end
- end
end
describe '#avatar_type' do
@@ -1340,6 +1428,11 @@ RSpec.describe Group, feature_category: :groups_and_projects do
group.add_member(user, GroupMember::OWNER)
end
+ before do
+ # Add an invite to the group, which should be filtered out
+ create(:group_member, :invited, source: group)
+ end
+
it 'returns the member-owners' do
expect(group.member_owners_excluding_project_bots).to contain_exactly(member_owner)
end
@@ -1367,6 +1460,16 @@ RSpec.describe Group, feature_category: :groups_and_projects do
it 'returns only direct member-owners' do
expect(group.member_owners_excluding_project_bots).to contain_exactly(member_owner)
end
+
+ context 'when there is an invite in the linked group' do
+ before do
+ create(:group_member, :invited, source: subgroup)
+ end
+
+ it 'returns only direct member-owners' do
+ expect(group.member_owners_excluding_project_bots).to contain_exactly(member_owner)
+ end
+ end
end
end
@@ -1382,6 +1485,31 @@ RSpec.describe Group, feature_category: :groups_and_projects do
it 'returns member-owners including parents' do
expect(subgroup.member_owners_excluding_project_bots).to contain_exactly(member_owner, member_owner_2)
end
+
+ context 'with group sharing' do
+ let_it_be(:invited_group) { create(:group) }
+
+ let!(:invited_group_owner) { invited_group.add_member(user, GroupMember::OWNER) }
+
+ before do
+ create(:group_group_link, :owner, shared_group: subgroup, shared_with_group: invited_group)
+ end
+
+ it 'returns member-owners including parents, and member-owners of the invited group' do
+ expect(subgroup.member_owners_excluding_project_bots).to contain_exactly(member_owner, member_owner_2, invited_group_owner)
+ end
+
+ context 'when there is an invite in the linked group' do
+ before do
+ # Add an invite to this group, which should be filtered out
+ create(:group_member, :invited, source: invited_group)
+ end
+
+ it 'returns member-owners including parents, and member-owners of the invited group' do
+ expect(subgroup.member_owners_excluding_project_bots).to contain_exactly(member_owner, member_owner_2, invited_group_owner)
+ end
+ end
+ end
end
end
@@ -1561,6 +1689,14 @@ RSpec.describe Group, feature_category: :groups_and_projects do
it 'returns correct access level' do
expect(group.max_member_access_for_user(group_user)).to eq(Gitlab::Access::OWNER)
end
+
+ context 'when user is not active' do
+ let_it_be(:group_user) { create(:user, :deactivated) }
+
+ it 'returns NO_ACCESS' do
+ expect(group.max_member_access_for_user(group_user)).to eq(Gitlab::Access::NO_ACCESS)
+ end
+ end
end
context 'when user is nil' do
@@ -3320,13 +3456,6 @@ RSpec.describe Group, feature_category: :groups_and_projects do
end
end
- describe '#content_editor_on_issues_feature_flag_enabled?' do
- it_behaves_like 'checks self and root ancestor feature flag' do
- let(:feature_flag) { :content_editor_on_issues }
- let(:feature_flag_method) { :content_editor_on_issues_feature_flag_enabled? }
- end
- end
-
describe '#work_items_feature_flag_enabled?' do
it_behaves_like 'checks self and root ancestor feature flag' do
let(:feature_flag) { :work_items }