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/namespace_spec.rb')
-rw-r--r--spec/models/namespace_spec.rb412
1 files changed, 290 insertions, 122 deletions
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index a0698ac30f5..89df24d75c9 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -31,7 +31,6 @@ RSpec.describe Namespace, feature_category: :subgroups do
it { is_expected.to have_many :pending_builds }
it { is_expected.to have_one :namespace_route }
it { is_expected.to have_many :namespace_members }
- it { is_expected.to have_many :member_roles }
it { is_expected.to have_one :cluster_enabled_grant }
it { is_expected.to have_many(:work_items) }
it { is_expected.to have_many :achievements }
@@ -173,15 +172,24 @@ RSpec.describe Namespace, feature_category: :subgroups do
# rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
where(:namespace_type, :path, :valid) do
- ref(:project_sti_name) | 'j' | true
- ref(:project_sti_name) | 'path.' | true
- ref(:project_sti_name) | 'blob' | false
- ref(:group_sti_name) | 'j' | false
- ref(:group_sti_name) | 'path.' | false
- ref(:group_sti_name) | 'blob' | true
- ref(:user_sti_name) | 'j' | false
- ref(:user_sti_name) | 'path.' | false
- ref(:user_sti_name) | 'blob' | true
+ ref(:project_sti_name) | 'j' | true
+ ref(:project_sti_name) | 'path.' | false
+ ref(:project_sti_name) | '.path' | false
+ ref(:project_sti_name) | 'path.git' | false
+ ref(:project_sti_name) | 'namespace__path' | false
+ ref(:project_sti_name) | 'blob' | false
+ ref(:group_sti_name) | 'j' | false
+ ref(:group_sti_name) | 'path.' | false
+ ref(:group_sti_name) | '.path' | false
+ ref(:group_sti_name) | 'path.git' | false
+ ref(:group_sti_name) | 'namespace__path' | false
+ ref(:group_sti_name) | 'blob' | true
+ ref(:user_sti_name) | 'j' | false
+ ref(:user_sti_name) | 'path.' | false
+ ref(:user_sti_name) | '.path' | false
+ ref(:user_sti_name) | 'path.git' | false
+ ref(:user_sti_name) | 'namespace__path' | false
+ ref(:user_sti_name) | 'blob' | true
end
# rubocop:enable Lint/BinaryOperatorWithIdenticalOperands
@@ -193,6 +201,26 @@ RSpec.describe Namespace, feature_category: :subgroups do
expect(namespace.valid?).to be(valid)
end
end
+
+ context 'when path starts or ends with a special character' do
+ it 'does not raise validation error for path for existing namespaces' do
+ parent.update_attribute(:path, '_path_')
+
+ expect { parent.update!(name: 'Foo') }.not_to raise_error
+ end
+ end
+
+ context 'when restrict_special_characters_in_namespace_path feature flag is disabled' do
+ before do
+ stub_feature_flags(restrict_special_characters_in_namespace_path: false)
+ end
+
+ it 'allows special character at the start or end of project namespace path' do
+ namespace = build(:namespace, type: project_sti_name, parent: parent, path: '_path_')
+
+ expect(namespace).to be_valid
+ end
+ end
end
describe '1 char path length' do
@@ -237,6 +265,117 @@ RSpec.describe Namespace, feature_category: :subgroups do
end
end
+ describe "ReferencePatternValidation" do
+ subject { described_class.reference_pattern }
+
+ it { is_expected.to match("@group1") }
+ it { is_expected.to match("@group1/group2/group3") }
+ it { is_expected.to match("@1234/1234/1234") }
+ it { is_expected.to match("@.q-w_e") }
+ end
+
+ describe '#to_reference_base' do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:user_namespace) { user.namespace }
+
+ let_it_be(:parent) { create(:group) }
+ let_it_be(:group) { create(:group, parent: parent) }
+ let_it_be(:another_group) { create(:group) }
+
+ let_it_be(:project) { create(:project, namespace: group) }
+ let_it_be(:project_namespace) { project.project_namespace }
+
+ let_it_be(:another_namespace_project) { create(:project) }
+ let_it_be(:another_namespace_project_namespace) { another_namespace_project.project_namespace }
+
+ # testing references with namespace being: group, project namespace and user namespace
+ where(:namespace, :full, :from, :result) do
+ ref(:parent) | false | nil | nil
+ ref(:parent) | true | nil | lazy { parent.full_path }
+ ref(:parent) | false | ref(:group) | lazy { parent.path }
+ ref(:parent) | true | ref(:group) | lazy { parent.full_path }
+ ref(:parent) | false | ref(:parent) | nil
+ ref(:parent) | true | ref(:parent) | lazy { parent.full_path }
+ ref(:parent) | false | ref(:project) | lazy { parent.path }
+ ref(:parent) | true | ref(:project) | lazy { parent.full_path }
+ ref(:parent) | false | ref(:project_namespace) | lazy { parent.path }
+ ref(:parent) | true | ref(:project_namespace) | lazy { parent.full_path }
+ ref(:parent) | false | ref(:another_group) | lazy { parent.full_path }
+ ref(:parent) | true | ref(:another_group) | lazy { parent.full_path }
+ ref(:parent) | false | ref(:another_namespace_project) | lazy { parent.full_path }
+ ref(:parent) | true | ref(:another_namespace_project) | lazy { parent.full_path }
+ ref(:parent) | false | ref(:another_namespace_project_namespace) | lazy { parent.full_path }
+ ref(:parent) | true | ref(:another_namespace_project_namespace) | lazy { parent.full_path }
+ ref(:parent) | false | ref(:user_namespace) | lazy { parent.full_path }
+ ref(:parent) | true | ref(:user_namespace) | lazy { parent.full_path }
+
+ ref(:group) | false | nil | nil
+ ref(:group) | true | nil | lazy { group.full_path }
+ ref(:group) | false | ref(:group) | nil
+ ref(:group) | true | ref(:group) | lazy { group.full_path }
+ ref(:group) | false | ref(:parent) | lazy { group.path }
+ ref(:group) | true | ref(:parent) | lazy { group.full_path }
+ ref(:group) | false | ref(:project) | lazy { group.path }
+ ref(:group) | true | ref(:project) | lazy { group.full_path }
+ ref(:group) | false | ref(:project_namespace) | lazy { group.path }
+ ref(:group) | true | ref(:project_namespace) | lazy { group.full_path }
+ ref(:group) | false | ref(:another_group) | lazy { group.full_path }
+ ref(:group) | true | ref(:another_group) | lazy { group.full_path }
+ ref(:group) | false | ref(:another_namespace_project) | lazy { group.full_path }
+ ref(:group) | true | ref(:another_namespace_project) | lazy { group.full_path }
+ ref(:group) | false | ref(:another_namespace_project_namespace) | lazy { group.full_path }
+ ref(:group) | true | ref(:another_namespace_project_namespace) | lazy { group.full_path }
+ ref(:group) | false | ref(:user_namespace) | lazy { group.full_path }
+ ref(:group) | true | ref(:user_namespace) | lazy { group.full_path }
+
+ ref(:project_namespace) | false | nil | nil
+ ref(:project_namespace) | true | nil | lazy { project_namespace.full_path }
+ ref(:project_namespace) | false | ref(:group) | lazy { project_namespace.path }
+ ref(:project_namespace) | true | ref(:group) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | false | ref(:parent) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | true | ref(:parent) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | false | ref(:project) | nil
+ ref(:project_namespace) | true | ref(:project) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | false | ref(:project_namespace) | nil
+ ref(:project_namespace) | true | ref(:project_namespace) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | false | ref(:another_group) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | true | ref(:another_group) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | false | ref(:another_namespace_project) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | true | ref(:another_namespace_project) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | false | ref(:another_namespace_project_namespace) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | true | ref(:another_namespace_project_namespace) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | false | ref(:user_namespace) | lazy { project_namespace.full_path }
+ ref(:project_namespace) | true | ref(:user_namespace) | lazy { project_namespace.full_path }
+
+ ref(:user_namespace) | false | nil | nil
+ ref(:user_namespace) | true | nil | lazy { user_namespace.full_path }
+ ref(:user_namespace) | false | ref(:user_namespace) | nil
+ ref(:user_namespace) | true | ref(:user_namespace) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | false | ref(:group) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | true | ref(:group) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | false | ref(:parent) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | true | ref(:parent) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | false | ref(:project) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | true | ref(:project) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | false | ref(:project_namespace) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | true | ref(:project_namespace) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | false | ref(:another_group) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | true | ref(:another_group) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | false | ref(:another_namespace_project) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | true | ref(:another_namespace_project) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | false | ref(:another_namespace_project_namespace) | lazy { user_namespace.full_path }
+ ref(:user_namespace) | true | ref(:another_namespace_project_namespace) | lazy { user_namespace.full_path }
+ end
+
+ with_them do
+ it 'returns correct path' do
+ expect(namespace.to_reference_base(from, full: full)).to eq(result)
+ end
+ end
+ end
+
describe 'handling STI', :aggregate_failures do
let(:namespace_type) { nil }
let(:parent) { nil }
@@ -436,6 +575,14 @@ RSpec.describe Namespace, feature_category: :subgroups do
end
end
+ context 'when parent is nil' do
+ let(:namespace) { build(:group, parent: nil) }
+
+ it 'returns []' do
+ expect(namespace.traversal_ids).to eq []
+ end
+ end
+
context 'when made a child group' do
let!(:namespace) { create(:group) }
let!(:parent_namespace) { create(:group, children: [namespace]) }
@@ -458,6 +605,17 @@ RSpec.describe Namespace, feature_category: :subgroups do
expect(namespace.root_ancestor).to eq new_root
end
end
+
+ context 'within a transaction' do
+ # We would like traversal_ids to be defined within a transaction, but it's not possible yet.
+ # This spec exists to assert that the behavior is known.
+ it 'is not defined yet' do
+ Namespace.transaction do
+ group = create(:group)
+ expect(group.traversal_ids).to be_empty
+ end
+ end
+ end
end
context 'traversal scopes' do
@@ -542,17 +700,6 @@ RSpec.describe Namespace, feature_category: :subgroups do
it { expect(child.traversal_ids).to eq [parent.id, child.id] }
it { expect(parent.sync_events.count).to eq 1 }
it { expect(child.sync_events.count).to eq 1 }
-
- context 'when set_traversal_ids_on_save feature flag is disabled' do
- before do
- stub_feature_flags(set_traversal_ids_on_save: false)
- end
-
- it 'only sets traversal_ids on reload' do
- expect { parent.reload }.to change(parent, :traversal_ids).from([]).to([parent.id])
- expect { child.reload }.to change(child, :traversal_ids).from([]).to([parent.id, child.id])
- end
- end
end
context 'traversal_ids on update' do
@@ -565,18 +712,6 @@ RSpec.describe Namespace, feature_category: :subgroups do
it 'sets the traversal_ids attribute' do
expect { subject }.to change { namespace1.traversal_ids }.from([namespace1.id]).to([namespace2.id, namespace1.id])
end
-
- context 'when set_traversal_ids_on_save feature flag is disabled' do
- before do
- stub_feature_flags(set_traversal_ids_on_save: false)
- end
-
- it 'sets traversal_ids after reload' do
- subject
-
- expect { namespace1.reload }.to change(namespace1, :traversal_ids).from([]).to([namespace2.id, namespace1.id])
- end
- end
end
it 'creates a Namespaces::SyncEvent using triggers' do
@@ -677,24 +812,41 @@ RSpec.describe Namespace, feature_category: :subgroups do
describe '#any_project_has_container_registry_tags?' do
subject { namespace.any_project_has_container_registry_tags? }
- let!(:project_without_registry) { create(:project, namespace: namespace) }
+ let(:project) { create(:project, namespace: namespace) }
- context 'without tags' do
- it { is_expected.to be_falsey }
+ it 'returns true if there is a project with container registry tags' do
+ expect(namespace).to receive(:first_project_with_container_registry_tags).and_return(project)
+
+ expect(subject).to be_truthy
end
- context 'with tags' do
- before do
- repositories = create_list(:container_repository, 3)
- create(:project, namespace: namespace, container_repositories: repositories)
+ it 'returns false if there is no project with container registry tags' do
+ expect(namespace).to receive(:first_project_with_container_registry_tags).and_return(nil)
+ expect(subject).to be_falsey
+ end
+ end
+
+ describe '#first_project_with_container_registry_tags' do
+ let(:container_repository) { create(:container_repository) }
+ let!(:project) { create(:project, namespace: namespace, container_repositories: [container_repository]) }
+
+ context 'when Gitlab API is not supported' do
+ before do
stub_container_registry_config(enabled: true)
+ allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(false)
end
- it 'finds tags' do
+ it 'returns the project' do
stub_container_registry_tags(repository: :any, tags: ['tag'])
- is_expected.to be_truthy
+ expect(namespace.first_project_with_container_registry_tags).to eq(project)
+ end
+
+ it 'returns no project' do
+ stub_container_registry_tags(repository: :any, tags: nil)
+
+ expect(namespace.first_project_with_container_registry_tags).to be_nil
end
it 'does not cause N+1 query in fetching registries' do
@@ -704,29 +856,52 @@ RSpec.describe Namespace, feature_category: :subgroups do
other_repositories = create_list(:container_repository, 2)
create(:project, namespace: namespace, container_repositories: other_repositories)
- expect { namespace.any_project_has_container_registry_tags? }.not_to exceed_query_limit(control_count + 1)
+ expect { namespace.first_project_with_container_registry_tags }.not_to exceed_query_limit(control_count + 1)
end
end
- end
- describe '#first_project_with_container_registry_tags' do
- let(:container_repository) { create(:container_repository) }
- let!(:project) { create(:project, namespace: namespace, container_repositories: [container_repository]) }
+ context 'when Gitlab API is supported' do
+ before do
+ allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(true)
+ stub_container_registry_config(enabled: true, api_url: 'http://container-registry', key: 'spec/fixtures/x509_certificate_pk.key')
+ end
- before do
- stub_container_registry_config(enabled: true)
- end
+ it 'calls and returns GitlabApiClient.one_project_with_container_registry_tag' do
+ expect(ContainerRegistry::GitlabApiClient)
+ .to receive(:one_project_with_container_registry_tag)
+ .with(namespace.full_path)
+ .and_return(project)
- it 'returns the project' do
- stub_container_registry_tags(repository: :any, tags: ['tag'])
+ expect(namespace.first_project_with_container_registry_tags).to eq(project)
+ end
- expect(namespace.first_project_with_container_registry_tags).to eq(project)
- end
+ context 'when the feature flag use_sub_repositories_api is disabled' do
+ before do
+ stub_feature_flags(use_sub_repositories_api: false)
+ end
+
+ it 'returns the project' do
+ stub_container_registry_tags(repository: :any, tags: ['tag'])
+
+ expect(namespace.first_project_with_container_registry_tags).to eq(project)
+ end
+
+ it 'returns no project' do
+ stub_container_registry_tags(repository: :any, tags: nil)
+
+ expect(namespace.first_project_with_container_registry_tags).to be_nil
+ end
- it 'returns no project' do
- stub_container_registry_tags(repository: :any, tags: nil)
+ it 'does not cause N+1 query in fetching registries' do
+ stub_container_registry_tags(repository: :any, tags: [])
+ control_count = ActiveRecord::QueryRecorder.new { namespace.any_project_has_container_registry_tags? }.count
- expect(namespace.first_project_with_container_registry_tags).to be_nil
+ other_repositories = create_list(:container_repository, 2)
+ create(:project, namespace: namespace, container_repositories: other_repositories)
+
+ expect { namespace.first_project_with_container_registry_tags }.not_to exceed_query_limit(control_count + 1)
+ end
+ end
end
end
@@ -755,6 +930,7 @@ RSpec.describe Namespace, feature_category: :subgroups do
with_them do
before do
+ allow(ContainerRegistry::GitlabApiClient).to receive(:one_project_with_container_registry_tag).and_return(nil)
stub_container_registry_config(enabled: true, api_url: 'http://container-registry', key: 'spec/fixtures/x509_certificate_pk.key')
allow(Gitlab).to receive(:com?).and_return(true)
allow(ContainerRegistry::GitlabApiClient).to receive(:supports_gitlab_api?).and_return(gitlab_api_supported)
@@ -975,43 +1151,6 @@ RSpec.describe Namespace, feature_category: :subgroups do
end
end
- describe '.find_by_pages_host' do
- it 'finds namespace by GitLab Pages host and is case-insensitive' do
- namespace = create(:namespace, name: 'topNAMEspace', path: 'topNAMEspace')
- create(:namespace, name: 'annother_namespace')
- host = "TopNamespace.#{Settings.pages.host.upcase}"
-
- expect(described_class.find_by_pages_host(host)).to eq(namespace)
- end
-
- context 'when there is non-top-level group with searched name' do
- before do
- create(:group, :nested, path: 'pages')
- end
-
- it 'ignores this group' do
- host = "pages.#{Settings.pages.host.upcase}"
-
- expect(described_class.find_by_pages_host(host)).to be_nil
- end
-
- it 'finds right top level group' do
- group = create(:group, path: 'pages')
-
- host = "pages.#{Settings.pages.host.upcase}"
-
- expect(described_class.find_by_pages_host(host)).to eq(group)
- end
- end
-
- it "returns no result if the provided host is not subdomain of the Pages host" do
- create(:namespace, name: 'namespace.io')
- host = "namespace.io"
-
- expect(described_class.find_by_pages_host(host)).to eq(nil)
- end
- end
-
describe '.top_most' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:group) { create(:group) }
@@ -1037,6 +1176,7 @@ RSpec.describe Namespace, feature_category: :subgroups do
allow(namespace).to receive(:path_was).and_return(namespace.path)
allow(namespace).to receive(:path).and_return('new_path')
+ allow(namespace).to receive(:first_project_with_container_registry_tags).and_return(project)
end
it 'raises an error about not movable project' do
@@ -1917,6 +2057,62 @@ RSpec.describe Namespace, feature_category: :subgroups do
expect(very_deep_nested_group.root_ancestor).to eq(root_group)
end
end
+
+ context 'when parent is changed' do
+ let(:group) { create(:group) }
+ let(:new_parent) { create(:group) }
+
+ shared_examples 'updates root_ancestor' do
+ it do
+ expect { subject }.to change { group.root_ancestor }.from(group).to(new_parent)
+ end
+ end
+
+ context 'by object' do
+ subject { group.parent = new_parent }
+
+ include_examples 'updates root_ancestor'
+ end
+
+ context 'by id' do
+ subject { group.parent_id = new_parent.id }
+
+ include_examples 'updates root_ancestor'
+ end
+ end
+
+ context 'within a transaction' do
+ context 'with a persisted parent' do
+ let(:parent) { create(:group) }
+
+ it do
+ Namespace.transaction do
+ group = create(:group, parent: parent)
+ expect(group.root_ancestor).to eq parent
+ end
+ end
+ end
+
+ context 'with a non-persisted parent' do
+ let(:parent) { build(:group) }
+
+ it do
+ Namespace.transaction do
+ group = create(:group, parent: parent)
+ expect(group.root_ancestor).to eq parent
+ end
+ end
+ end
+
+ context 'without a parent' do
+ it do
+ Namespace.transaction do
+ group = create(:group)
+ expect(group.root_ancestor).to eq group
+ end
+ end
+ end
+ end
end
describe '#full_path_before_last_save' do
@@ -2105,34 +2301,6 @@ RSpec.describe Namespace, feature_category: :subgroups do
end
end
- describe '#pages_virtual_domain' do
- let(:project) { create(:project, namespace: namespace) }
- let(:virtual_domain) { namespace.pages_virtual_domain }
-
- before do
- project.mark_pages_as_deployed
- project.update_pages_deployment!(create(:pages_deployment, project: project))
- end
-
- it 'returns the virual domain' do
- expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain)
- expect(virtual_domain.lookup_paths).not_to be_empty
- expect(virtual_domain.cache_key).to match(/pages_domain_for_namespace_#{namespace.root_ancestor.id}_/)
- end
-
- context 'when :cache_pages_domain_api is disabled' do
- before do
- stub_feature_flags(cache_pages_domain_api: false)
- end
-
- it 'returns the virual domain' do
- expect(virtual_domain).to be_an_instance_of(Pages::VirtualDomain)
- expect(virtual_domain.lookup_paths).not_to be_empty
- expect(virtual_domain.cache_key).to be_nil
- end
- end
- end
-
describe '#any_project_with_pages_deployed?' do
it 'returns true if any project nested under the group has pages deployed' do
parent_1 = create(:group) # Three projects, one with pages