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/project_spec.rb')
-rw-r--r--spec/models/project_spec.rb259
1 files changed, 193 insertions, 66 deletions
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index d8f3a63d221..3989ddc31e8 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe Project, factory_default: :keep do
describe 'associations' do
it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:namespace) }
+ it { is_expected.to belong_to(:project_namespace).class_name('Namespaces::ProjectNamespace').with_foreign_key('project_namespace_id').inverse_of(:project) }
it { is_expected.to belong_to(:creator).class_name('User') }
it { is_expected.to belong_to(:pool_repository) }
it { is_expected.to have_many(:users) }
@@ -137,6 +138,8 @@ RSpec.describe Project, factory_default: :keep do
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') }
+ it { is_expected.to have_many(:pending_builds).class_name('Ci::PendingBuild') }
+ it { is_expected.to have_many(:ci_feature_usages).class_name('Projects::CiFeatureUsage') }
# GitLab Pages
it { is_expected.to have_many(:pages_domains) }
@@ -183,6 +186,20 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ context 'when deleting project' do
+ # using delete rather than destroy due to `delete` skipping AR hooks/callbacks
+ # so it's ensured to work at the DB level. Uses AFTER DELETE trigger.
+ let_it_be(:project) { create(:project) }
+ let_it_be(:project_namespace) { create(:project_namespace, project: project) }
+
+ it 'also deletes the associated ProjectNamespace' do
+ project.delete
+
+ expect { project.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { project_namespace.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
context 'when creating a new project' do
let_it_be(:project) { create(:project) }
@@ -602,6 +619,12 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#membership_locked?' do
+ it 'returns false' do
+ expect(build(:project)).not_to be_membership_locked
+ end
+ end
+
describe '#autoclose_referenced_issues' do
context 'when DB entry is nil' do
let(:project) { build(:project, autoclose_referenced_issues: nil) }
@@ -1051,12 +1074,12 @@ RSpec.describe Project, factory_default: :keep do
project.open_issues_count(user)
end
- it 'invokes the count service with no current_user' do
- count_service = instance_double(Projects::OpenIssuesCountService)
- expect(Projects::OpenIssuesCountService).to receive(:new).with(project, nil).and_return(count_service)
- expect(count_service).to receive(:count)
+ it 'invokes the batch count service with no current_user' do
+ count_service = instance_double(Projects::BatchOpenIssuesCountService)
+ expect(Projects::BatchOpenIssuesCountService).to receive(:new).with([project]).and_return(count_service)
+ expect(count_service).to receive(:refresh_cache_and_retrieve_data).and_return({})
- project.open_issues_count
+ project.open_issues_count.to_s
end
end
@@ -1257,19 +1280,19 @@ RSpec.describe Project, factory_default: :keep do
end
it 'returns an active external wiki' do
- create(:service, project: project, type: 'ExternalWikiService', active: true)
+ create(:external_wiki_integration, project: project, active: true)
is_expected.to be_kind_of(Integrations::ExternalWiki)
end
it 'does not return an inactive external wiki' do
- create(:service, project: project, type: 'ExternalWikiService', active: false)
+ create(:external_wiki_integration, project: project, active: false)
is_expected.to eq(nil)
end
it 'sets Project#has_external_wiki when it is nil' do
- create(:service, project: project, type: 'ExternalWikiService', active: true)
+ create(:external_wiki_integration, project: project, active: true)
project.update_column(:has_external_wiki, nil)
expect { subject }.to change { project.has_external_wiki }.from(nil).to(true)
@@ -1279,36 +1302,40 @@ RSpec.describe Project, factory_default: :keep do
describe '#has_external_wiki' do
let_it_be(:project) { create(:project) }
- def subject
+ def has_external_wiki
project.reload.has_external_wiki
end
- specify { is_expected.to eq(false) }
+ specify { expect(has_external_wiki).to eq(false) }
- context 'when there is an active external wiki service' do
- let!(:service) do
- create(:service, project: project, type: 'ExternalWikiService', active: true)
+ context 'when there is an active external wiki integration' do
+ let(:active) { true }
+
+ let!(:integration) do
+ create(:external_wiki_integration, project: project, active: active)
end
- specify { is_expected.to eq(true) }
+ specify { expect(has_external_wiki).to eq(true) }
it 'becomes false if the external wiki service is destroyed' do
expect do
- Integration.find(service.id).delete
- end.to change { subject }.to(false)
+ Integration.find(integration.id).delete
+ end.to change { has_external_wiki }.to(false)
end
it 'becomes false if the external wiki service becomes inactive' do
expect do
- service.update_column(:active, false)
- end.to change { subject }.to(false)
+ integration.update_column(:active, false)
+ end.to change { has_external_wiki }.to(false)
end
- end
- it 'is false when external wiki service is not active' do
- create(:service, project: project, type: 'ExternalWikiService', active: false)
+ context 'when created as inactive' do
+ let(:active) { false }
- is_expected.to eq(false)
+ it 'is false' do
+ expect(has_external_wiki).to eq(false)
+ end
+ end
end
end
@@ -2536,7 +2563,7 @@ RSpec.describe Project, factory_default: :keep do
end
describe '#uses_default_ci_config?' do
- let(:project) { build(:project)}
+ let(:project) { build(:project) }
it 'has a custom ci config path' do
project.ci_config_path = 'something_custom'
@@ -2557,6 +2584,44 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#uses_external_project_ci_config?' do
+ subject(:uses_external_project_ci_config) { project.uses_external_project_ci_config? }
+
+ let(:project) { build(:project) }
+
+ context 'when ci_config_path is configured with external project' do
+ before do
+ project.ci_config_path = '.gitlab-ci.yml@hello/world'
+ end
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when ci_config_path is nil' do
+ before do
+ project.ci_config_path = nil
+ end
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when ci_config_path is configured with a file in the project' do
+ before do
+ project.ci_config_path = 'hello/world/gitlab-ci.yml'
+ end
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when ci_config_path is configured with remote file' do
+ before do
+ project.ci_config_path = 'https://example.org/file.yml'
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
describe '#latest_successful_build_for_ref' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:pipeline) { create_pipeline(project) }
@@ -3260,6 +3325,16 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#after_change_head_branch_does_not_exist' do
+ let_it_be(:project) { create(:project) }
+
+ it 'adds an error to container if branch does not exist' do
+ expect do
+ project.after_change_head_branch_does_not_exist('unexisted-branch')
+ end.to change { project.errors.size }.from(0).to(1)
+ end
+ end
+
describe '#lfs_objects_for_repository_types' do
let(:project) { create(:project) }
@@ -4496,44 +4571,6 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#legacy_remove_pages' do
- let(:project) { create(:project).tap { |project| project.mark_pages_as_deployed } }
- let(:pages_metadatum) { project.pages_metadatum }
- let(:namespace) { project.namespace }
- let(:pages_path) { project.pages_path }
-
- around do |example|
- FileUtils.mkdir_p(pages_path)
- begin
- example.run
- ensure
- FileUtils.rm_rf(pages_path)
- end
- end
-
- it 'removes the pages directory and marks the project as not having pages deployed' do
- expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project).and_return(true)
- expect(PagesWorker).to receive(:perform_in).with(5.minutes, :remove, namespace.full_path, anything)
-
- expect { project.legacy_remove_pages }.to change { pages_metadatum.reload.deployed }.from(true).to(false)
- end
-
- it 'does nothing if updates on legacy storage are disabled' do
- allow(Settings.pages.local_store).to receive(:enabled).and_return(false)
-
- expect(Gitlab::PagesTransfer).not_to receive(:new)
- expect(PagesWorker).not_to receive(:perform_in)
-
- project.legacy_remove_pages
- end
-
- it 'is run when the project is destroyed' do
- expect(project).to receive(:legacy_remove_pages).and_call_original
-
- expect { project.destroy! }.not_to raise_error
- end
- end
-
describe '#remove_export' do
let(:project) { create(:project, :with_export) }
@@ -7037,6 +7074,15 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#ci_config_external_project' do
+ subject(:ci_config_external_project) { project.ci_config_external_project }
+
+ let(:other_project) { create(:project) }
+ let(:project) { build(:project, ci_config_path: ".gitlab-ci.yml@#{other_project.full_path}") }
+
+ it { is_expected.to eq(other_project) }
+ end
+
describe '#enabled_group_deploy_keys' do
let_it_be(:project) { create(:project) }
@@ -7131,15 +7177,96 @@ RSpec.describe Project, factory_default: :keep do
end
describe 'topics' do
- let_it_be(:project) { create(:project, topic_list: 'topic1, topic2, topic3') }
+ let_it_be(:project) { create(:project, name: 'topic-project', topic_list: 'topic1, topic2, topic3') }
it 'topic_list returns correct string array' do
- expect(project.topic_list).to match_array(%w[topic1 topic2 topic3])
+ expect(project.topic_list).to eq(%w[topic1 topic2 topic3])
+ end
+
+ it 'topics returns correct topic records' do
+ expect(project.topics.first.class.name).to eq('Projects::Topic')
+ expect(project.topics.map(&:name)).to eq(%w[topic1 topic2 topic3])
+ end
+
+ context 'topic_list=' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:topic_list, :expected_result) do
+ ['topicA', 'topicB'] | %w[topicA topicB] # rubocop:disable Style/WordArray, Lint/BinaryOperatorWithIdenticalOperands
+ ['topicB', 'topicA'] | %w[topicB topicA] # rubocop:disable Style/WordArray, Lint/BinaryOperatorWithIdenticalOperands
+ [' topicC ', ' topicD '] | %w[topicC topicD]
+ ['topicE', 'topicF', 'topicE'] | %w[topicE topicF] # rubocop:disable Style/WordArray
+ ['topicE ', 'topicF', ' topicE'] | %w[topicE topicF]
+ 'topicA, topicB' | %w[topicA topicB]
+ 'topicB, topicA' | %w[topicB topicA]
+ ' topicC , topicD ' | %w[topicC topicD]
+ 'topicE, topicF, topicE' | %w[topicE topicF]
+ 'topicE , topicF, topicE' | %w[topicE topicF]
+ end
+
+ with_them do
+ it 'set topics' do
+ project.topic_list = topic_list
+ project.save!
+
+ expect(project.topics.map(&:name)).to eq(expected_result)
+ end
+ end
+
+ it 'set topics if only the order is changed' do
+ project.topic_list = 'topicA, topicB'
+ project.save!
+
+ expect(project.reload.topics.map(&:name)).to eq(%w[topicA topicB])
+
+ project.topic_list = 'topicB, topicA'
+ project.save!
+
+ expect(project.reload.topics.map(&:name)).to eq(%w[topicB topicA])
+ end
+
+ it 'does not persist topics before project is saved' do
+ project.topic_list = 'topicA, topicB'
+
+ expect(project.reload.topics.map(&:name)).to eq(%w[topic1 topic2 topic3])
+ end
+
+ it 'does not update topics if project is not valid' do
+ project.name = nil
+ project.topic_list = 'topicA, topicB'
+
+ expect(project.save).to be_falsy
+ expect(project.reload.topics.map(&:name)).to eq(%w[topic1 topic2 topic3])
+ end
end
- it 'topics returns correct tag records' do
- expect(project.topics.first.class.name).to eq('ActsAsTaggableOn::Tag')
- expect(project.topics.map(&:name)).to match_array(%w[topic1 topic2 topic3])
+ context 'during ExtractProjectTopicsIntoSeparateTable migration' do
+ before do
+ topic_a = ActsAsTaggableOn::Tag.find_or_create_by!(name: 'topicA')
+ topic_b = ActsAsTaggableOn::Tag.find_or_create_by!(name: 'topicB')
+
+ project.reload.topics_acts_as_taggable = [topic_a, topic_b]
+ project.save!
+ project.reload
+ end
+
+ it 'topic_list returns correct string array' do
+ expect(project.topic_list).to eq(%w[topicA topicB topic1 topic2 topic3])
+ end
+
+ it 'topics returns correct topic records' do
+ expect(project.topics.map(&:class)).to eq([ActsAsTaggableOn::Tag, ActsAsTaggableOn::Tag, Projects::Topic, Projects::Topic, Projects::Topic])
+ expect(project.topics.map(&:name)).to eq(%w[topicA topicB topic1 topic2 topic3])
+ end
+
+ it 'topic_list= sets new topics and removes old topics' do
+ project.topic_list = 'new-topic1, new-topic2'
+ project.save!
+ project.reload
+
+ expect(project.topics.map(&:class)).to eq([Projects::Topic, Projects::Topic])
+ expect(project.topics.map(&:name)).to eq(%w[new-topic1 new-topic2])
+ end
end
end