summaryrefslogtreecommitdiff
path: root/spec/models/project_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/project_spec.rb')
-rw-r--r--spec/models/project_spec.rb324
1 files changed, 217 insertions, 107 deletions
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index efa269cdb5c..d8f3a63d221 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Project, factory_default: :keep do
include ProjectForksHelper
include GitHelpers
include ExternalAuthorizationServiceHelpers
+ include ReloadHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:namespace) { create_default(:namespace).freeze }
@@ -86,7 +87,6 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:ci_pipelines) }
it { is_expected.to have_many(:ci_refs) }
it { is_expected.to have_many(:builds) }
- it { is_expected.to have_many(:build_trace_section_names)}
it { is_expected.to have_many(:build_report_results) }
it { is_expected.to have_many(:runner_projects) }
it { is_expected.to have_many(:runners) }
@@ -135,6 +135,8 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:pipeline_artifacts) }
it { is_expected.to have_many(:terraform_states).class_name('Terraform::State').inverse_of(:project) }
it { is_expected.to have_many(:timelogs) }
+ it { is_expected.to have_many(:error_tracking_errors).class_name('ErrorTracking::Error') }
+ it { is_expected.to have_many(:error_tracking_client_keys).class_name('ErrorTracking::ClientKey') }
# GitLab Pages
it { is_expected.to have_many(:pages_domains) }
@@ -317,7 +319,8 @@ RSpec.describe Project, factory_default: :keep do
end
it 'validates presence of project_feature' do
- project = build(:project, project_feature: nil)
+ project = build(:project)
+ project.project_feature = nil
expect(project).not_to be_valid
end
@@ -654,7 +657,6 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
it { is_expected.to delegate_method(:root_ancestor).to(:namespace).with_arguments(allow_nil: true) }
it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) }
- it { is_expected.to delegate_method(:allow_editing_commit_messages?).to(:project_setting) }
it { is_expected.to delegate_method(:container_registry_enabled?).to(:project_feature) }
it { is_expected.to delegate_method(:container_registry_access_level).to(:project_feature) }
@@ -825,8 +827,6 @@ RSpec.describe Project, factory_default: :keep do
end
describe '#merge_method' do
- using RSpec::Parameterized::TableSyntax
-
where(:ff, :rebase, :method) do
true | true | :ff
true | false | :ff
@@ -1485,33 +1485,21 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '.with_active_jira_integrations' do
- it 'returns the correct integrations' do
- active_jira_integration = create(:jira_integration)
- active_service = create(:service, active: true)
-
- expect(described_class.with_active_jira_integrations).to include(active_jira_integration.project)
- expect(described_class.with_active_jira_integrations).not_to include(active_service.project)
- end
- end
-
describe '.with_jira_dvcs_cloud' do
it 'returns the correct project' do
jira_dvcs_cloud_project = create(:project, :jira_dvcs_cloud)
- jira_dvcs_server_project = create(:project, :jira_dvcs_server)
+ create(:project, :jira_dvcs_server)
- expect(described_class.with_jira_dvcs_cloud).to include(jira_dvcs_cloud_project)
- expect(described_class.with_jira_dvcs_cloud).not_to include(jira_dvcs_server_project)
+ expect(described_class.with_jira_dvcs_cloud).to contain_exactly(jira_dvcs_cloud_project)
end
end
describe '.with_jira_dvcs_server' do
it 'returns the correct project' do
jira_dvcs_server_project = create(:project, :jira_dvcs_server)
- jira_dvcs_cloud_project = create(:project, :jira_dvcs_cloud)
+ create(:project, :jira_dvcs_cloud)
- expect(described_class.with_jira_dvcs_server).to include(jira_dvcs_server_project)
- expect(described_class.with_jira_dvcs_server).not_to include(jira_dvcs_cloud_project)
+ expect(described_class.with_jira_dvcs_server).to contain_exactly(jira_dvcs_server_project)
end
end
@@ -1597,15 +1585,39 @@ RSpec.describe Project, factory_default: :keep do
end
describe '.with_integration' do
- before do
- create_list(:prometheus_project, 2)
+ it 'returns the correct projects' do
+ active_confluence_integration = create(:confluence_integration)
+ inactive_confluence_integration = create(:confluence_integration, active: false)
+ create(:bugzilla_integration)
+
+ expect(described_class.with_integration(::Integrations::Confluence)).to contain_exactly(
+ active_confluence_integration.project,
+ inactive_confluence_integration.project
+ )
end
+ end
- let(:integration) { :prometheus_integration }
+ describe '.with_active_integration' do
+ it 'returns the correct projects' do
+ active_confluence_integration = create(:confluence_integration)
+ create(:confluence_integration, active: false)
+ create(:bugzilla_integration, active: true)
- it 'avoids n + 1' do
- expect { described_class.with_integration(integration).map(&integration) }
- .not_to exceed_query_limit(1)
+ expect(described_class.with_active_integration(::Integrations::Confluence)).to contain_exactly(
+ active_confluence_integration.project
+ )
+ end
+ end
+
+ describe '.include_integration' do
+ it 'avoids n + 1', :aggregate_failures do
+ create(:prometheus_integration)
+ run_test = -> { described_class.include_integration(:prometheus_integration).map(&:prometheus_integration) }
+ control_count = ActiveRecord::QueryRecorder.new { run_test.call }
+ create(:prometheus_integration)
+
+ expect(run_test.call.count).to eq(2)
+ expect { run_test.call }.not_to exceed_query_limit(control_count)
end
end
@@ -1938,8 +1950,6 @@ RSpec.describe Project, factory_default: :keep do
end
context 'when set to INTERNAL in application settings' do
- using RSpec::Parameterized::TableSyntax
-
before do
stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
end
@@ -2000,8 +2010,6 @@ RSpec.describe Project, factory_default: :keep do
end
describe '#default_branch_protected?' do
- using RSpec::Parameterized::TableSyntax
-
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, namespace: namespace) }
@@ -2405,43 +2413,24 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#set_container_registry_access_level' do
+ describe '#container_registry_enabled=' do
let_it_be_with_reload(:project) { create(:project) }
it 'updates project_feature', :aggregate_failures do
- # Simulate an existing project that has container_registry enabled
- project.update_column(:container_registry_enabled, true)
- project.project_feature.update_column(:container_registry_access_level, ProjectFeature::ENABLED)
-
project.update!(container_registry_enabled: false)
- expect(project.read_attribute(:container_registry_enabled)).to eq(false)
expect(project.project_feature.container_registry_access_level).to eq(ProjectFeature::DISABLED)
project.update!(container_registry_enabled: true)
- expect(project.read_attribute(:container_registry_enabled)).to eq(true)
expect(project.project_feature.container_registry_access_level).to eq(ProjectFeature::ENABLED)
end
-
- it 'rollsback both projects and project_features row in case of error', :aggregate_failures do
- project.update_column(:container_registry_enabled, true)
- project.project_feature.update_column(:container_registry_access_level, ProjectFeature::ENABLED)
-
- allow(project).to receive(:valid?).and_return(false)
-
- expect { project.update!(container_registry_enabled: false) }.to raise_error(ActiveRecord::RecordInvalid)
-
- expect(project.reload.read_attribute(:container_registry_enabled)).to eq(true)
- expect(project.project_feature.reload.container_registry_access_level).to eq(ProjectFeature::ENABLED)
- end
end
describe '#container_registry_enabled' do
let_it_be_with_reload(:project) { create(:project) }
it 'delegates to project_feature', :aggregate_failures do
- project.update_column(:container_registry_enabled, true)
project.project_feature.update_column(:container_registry_access_level, ProjectFeature::DISABLED)
expect(project.container_registry_enabled).to eq(false)
@@ -2870,6 +2859,36 @@ RSpec.describe Project, factory_default: :keep do
it { expect(project.import?).to be true }
end
+ describe '#github_import?' do
+ let_it_be(:project) { build(:project, import_type: 'github') }
+
+ it { expect(project.github_import?).to be true }
+ end
+
+ describe '#github_enterprise_import?' do
+ let_it_be(:github_com_project) do
+ build(
+ :project,
+ import_type: 'github',
+ import_url: 'https://api.github.com/user/repo'
+ )
+ end
+
+ let_it_be(:github_enterprise_project) do
+ build(
+ :project,
+ import_type: 'github',
+ import_url: 'https://othergithub.net/user/repo'
+ )
+ end
+
+ it { expect(github_com_project.github_import?).to be true }
+ it { expect(github_com_project.github_enterprise_import?).to be false }
+
+ it { expect(github_enterprise_project.github_import?).to be true }
+ it { expect(github_enterprise_project.github_enterprise_import?).to be true }
+ end
+
describe '#remove_import_data' do
let(:import_data) { ProjectImportData.new(data: { 'test' => 'some data' }) }
@@ -2912,10 +2931,6 @@ RSpec.describe Project, factory_default: :keep do
subject { project.has_remote_mirror? }
- before do
- allow_any_instance_of(RemoteMirror).to receive(:refresh_remote)
- end
-
it 'returns true when a remote mirror is enabled' do
is_expected.to be_truthy
end
@@ -2932,10 +2947,6 @@ RSpec.describe Project, factory_default: :keep do
delegate :update_remote_mirrors, to: :project
- before do
- allow_any_instance_of(RemoteMirror).to receive(:refresh_remote)
- end
-
it 'syncs enabled remote mirror' do
expect_any_instance_of(RemoteMirror).to receive(:sync)
@@ -3011,29 +3022,106 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#ancestors_upto' do
- let_it_be(:parent) { create(:group) }
- let_it_be(:child) { create(:group, parent: parent) }
- let_it_be(:child2) { create(:group, parent: child) }
- let_it_be(:project) { create(:project, namespace: child2) }
+ shared_context 'project with group ancestry' do
+ let(:parent) { create(:group) }
+ let(:child) { create(:group, parent: parent) }
+ let(:child2) { create(:group, parent: child) }
+ let(:project) { create(:project, namespace: child2) }
- it 'returns all ancestors when no namespace is given' do
- expect(project.ancestors_upto).to contain_exactly(child2, child, parent)
+ before do
+ reload_models(parent, child, child2)
end
+ end
+
+ shared_context 'project with namespace ancestry' do
+ let(:namespace) { create :namespace }
+ let(:project) { create :project, namespace: namespace }
+ end
+
+ shared_examples 'project with group ancestors' do
+ it 'returns all ancestors' do
+ is_expected.to contain_exactly(child2, child, parent)
+ end
+ end
- it 'includes ancestors upto but excluding the given ancestor' do
- expect(project.ancestors_upto(parent)).to contain_exactly(child2, child)
+ shared_examples 'project with ordered group ancestors' do
+ let(:hierarchy_order) { :desc }
+
+ it 'returns ancestors ordered by descending hierarchy' do
+ is_expected.to eq([parent, child, child2])
end
+ end
- describe 'with hierarchy_order' do
- it 'returns ancestors ordered by descending hierarchy' do
- expect(project.ancestors_upto(hierarchy_order: :desc)).to eq([parent, child, child2])
+ shared_examples '#ancestors' do
+ context 'group ancestory' do
+ include_context 'project with group ancestry'
+
+ it_behaves_like 'project with group ancestors' do
+ subject { project.ancestors }
end
- it 'can be used with upto option' do
- expect(project.ancestors_upto(parent, hierarchy_order: :desc)).to eq([child, child2])
+ it_behaves_like 'project with ordered group ancestors' do
+ subject { project.ancestors(hierarchy_order: hierarchy_order) }
end
end
+
+ context 'namespace ancestry' do
+ include_context 'project with namespace ancestry'
+
+ subject { project.ancestors }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ describe '#ancestors' do
+ context 'with linear_project_ancestors feature flag enabled' do
+ before do
+ stub_feature_flags(linear_project_ancestors: true)
+ end
+
+ include_examples '#ancestors'
+ end
+
+ context 'with linear_project_ancestors feature flag disabled' do
+ before do
+ stub_feature_flags(linear_project_ancestors: false)
+ end
+
+ include_examples '#ancestors'
+ end
+ end
+
+ describe '#ancestors_upto' do
+ context 'group ancestry' do
+ include_context 'project with group ancestry'
+
+ it_behaves_like 'project with group ancestors' do
+ subject { project.ancestors_upto }
+ end
+
+ it_behaves_like 'project with ordered group ancestors' do
+ subject { project.ancestors_upto(hierarchy_order: hierarchy_order) }
+ end
+
+ it 'includes ancestors upto but excluding the given ancestor' do
+ expect(project.ancestors_upto(parent)).to contain_exactly(child2, child)
+ end
+
+ describe 'with hierarchy_order' do
+ it 'can be used with upto option' do
+ expect(project.ancestors_upto(parent, hierarchy_order: :desc)).to eq([child, child2])
+ end
+ end
+ end
+
+ context 'namespace ancestry' do
+ include_context 'project with namespace ancestry'
+
+ subject { project.ancestors_upto }
+
+ it { is_expected.to be_empty }
+ end
end
describe '#root_ancestor' do
@@ -5194,11 +5282,26 @@ RSpec.describe Project, factory_default: :keep do
expect(InternalId).to receive(:flush_records!).with(project: project)
expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:repository_size])
expect(DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id)
- expect(project).to receive(:write_repository_config)
+ expect(AuthorizedProjectUpdate::ProjectRecalculateWorker).to receive(:perform_async).with(project.id)
+ expect(project).to receive(:set_full_path)
project.after_import
end
+ context 'project authorizations refresh' do
+ it 'updates user authorizations' do
+ create(:import_state, :started, project: project)
+
+ member = build(:project_member, project: project)
+ member.importing = true
+ member.save!
+
+ Sidekiq::Testing.inline! { project.after_import }
+
+ expect(member.user.authorized_project?(project)).to be true
+ end
+ end
+
context 'branch protection' do
let_it_be(:namespace) { create(:namespace) }
@@ -5263,25 +5366,25 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#write_repository_config' do
+ describe '#set_full_path' do
let_it_be(:project) { create(:project, :repository) }
it 'writes full path in .git/config when key is missing' do
- project.write_repository_config
+ project.set_full_path
expect(rugged_config['gitlab.fullpath']).to eq project.full_path
end
it 'updates full path in .git/config when key is present' do
- project.write_repository_config(gl_full_path: 'old/path')
+ project.set_full_path(gl_full_path: 'old/path')
- expect { project.write_repository_config }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
+ expect { project.set_full_path }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
end
it 'does not raise an error with an empty repository' do
project = create(:project_empty_repo)
- expect { project.write_repository_config }.not_to raise_error
+ expect { project.set_full_path }.not_to raise_error
end
end
@@ -5911,10 +6014,9 @@ RSpec.describe Project, factory_default: :keep do
end
end
- context 'with an instance-level and template integrations' do
+ context 'with an instance-level integration' do
before do
create(:prometheus_integration, :instance, api_url: 'https://prometheus.instance.com/')
- create(:prometheus_integration, :template, api_url: 'https://prometheus.template.com/')
end
it 'builds the integration from the instance integration' do
@@ -5922,17 +6024,7 @@ RSpec.describe Project, factory_default: :keep do
end
end
- context 'with a template integration and no instance-level' do
- before do
- create(:prometheus_integration, :template, api_url: 'https://prometheus.template.com/')
- end
-
- it 'builds the integration from the template' do
- expect(subject.find_or_initialize_integration('prometheus').api_url).to eq('https://prometheus.template.com/')
- end
- end
-
- context 'without an exisiting integration, or instance-level or template' do
+ context 'without an existing integration or instance-level' do
it 'builds the integration' do
expect(subject.find_or_initialize_integration('prometheus')).to be_a(::Integrations::Prometheus)
expect(subject.find_or_initialize_integration('prometheus').api_url).to be_nil
@@ -6834,28 +6926,46 @@ RSpec.describe Project, factory_default: :keep do
end
describe '#package_already_taken?' do
- let(:namespace) { create(:namespace) }
- let(:project) { create(:project, :public, namespace: namespace) }
- let!(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo") }
+ let_it_be(:namespace) { create(:namespace, path: 'test') }
+ let_it_be(:project) { create(:project, :public, namespace: namespace) }
+ let_it_be(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo", version: '1.2.3') }
- context 'no package exists with the same name' do
- it 'returns false' do
- result = project.package_already_taken?("@#{namespace.path}/bar")
- expect(result).to be false
+ subject { project.package_already_taken?(package_name, package_version, package_type: :npm) }
+
+ context 'within the package project' do
+ where(:package_name, :package_version, :expected_result) do
+ '@test/bar' | '1.2.3' | false
+ '@test/bar' | '5.5.5' | false
+ '@test/foo' | '1.2.3' | false
+ '@test/foo' | '5.5.5' | false
end
- it 'returns false if it is the project that the package belongs to' do
- result = project.package_already_taken?("@#{namespace.path}/foo")
- expect(result).to be false
+ with_them do
+ it { is_expected.to eq expected_result}
end
end
- context 'a package already exists with the same name' do
- let(:alt_project) { create(:project, :public, namespace: namespace) }
+ context 'within a different project' do
+ let_it_be(:alt_project) { create(:project, :public, namespace: namespace) }
- it 'returns true' do
- result = alt_project.package_already_taken?("@#{namespace.path}/foo")
- expect(result).to be true
+ subject { alt_project.package_already_taken?(package_name, package_version, package_type: :npm) }
+
+ where(:package_name, :package_version, :expected_result) do
+ '@test/bar' | '1.2.3' | false
+ '@test/bar' | '5.5.5' | false
+ '@test/foo' | '1.2.3' | true
+ '@test/foo' | '5.5.5' | false
+ end
+
+ with_them do
+ it { is_expected.to eq expected_result}
+ end
+
+ context 'for a different package type' do
+ it 'returns false' do
+ result = alt_project.package_already_taken?(package.name, package.version, package_type: :nuget)
+ expect(result).to be false
+ end
end
end
end