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.rb390
1 files changed, 215 insertions, 175 deletions
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c57c2792f87..78e32571d7d 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:project_members).dependent(:delete_all) }
it { is_expected.to have_many(:users).through(:project_members) }
it { is_expected.to have_many(:requesters).dependent(:delete_all) }
- it { is_expected.to have_many(:notes) }
+ it { is_expected.to have_many(:notes).dependent(:destroy) }
it { is_expected.to have_many(:snippets).class_name('ProjectSnippet') }
it { is_expected.to have_many(:deploy_keys_projects) }
it { is_expected.to have_many(:deploy_keys) }
@@ -43,31 +43,31 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_one(:webex_teams_service) }
it { is_expected.to have_one(:packagist_service) }
it { is_expected.to have_one(:pushover_service) }
- it { is_expected.to have_one(:asana_service) }
+ it { is_expected.to have_one(:asana_integration) }
it { is_expected.to have_many(:boards) }
- it { is_expected.to have_one(:campfire_service) }
- it { is_expected.to have_one(:datadog_service) }
- it { is_expected.to have_one(:discord_service) }
- it { is_expected.to have_one(:drone_ci_service) }
+ it { is_expected.to have_one(:campfire_integration) }
+ it { is_expected.to have_one(:datadog_integration) }
+ it { is_expected.to have_one(:discord_integration) }
+ it { is_expected.to have_one(:drone_ci_integration) }
it { is_expected.to have_one(:emails_on_push_service) }
it { is_expected.to have_one(:pipelines_email_service) }
it { is_expected.to have_one(:irker_service) }
it { is_expected.to have_one(:pivotaltracker_service) }
it { is_expected.to have_one(:flowdock_service) }
- it { is_expected.to have_one(:assembla_service) }
+ it { is_expected.to have_one(:assembla_integration) }
it { is_expected.to have_one(:slack_slash_commands_service) }
it { is_expected.to have_one(:mattermost_slash_commands_service) }
- it { is_expected.to have_one(:buildkite_service) }
- it { is_expected.to have_one(:bamboo_service) }
+ it { is_expected.to have_one(:buildkite_integration) }
+ it { is_expected.to have_one(:bamboo_integration) }
it { is_expected.to have_one(:teamcity_service) }
it { is_expected.to have_one(:jira_service) }
it { is_expected.to have_one(:redmine_service) }
it { is_expected.to have_one(:youtrack_service) }
- it { is_expected.to have_one(:custom_issue_tracker_service) }
- it { is_expected.to have_one(:bugzilla_service) }
+ it { is_expected.to have_one(:custom_issue_tracker_integration) }
+ it { is_expected.to have_one(:bugzilla_integration) }
it { is_expected.to have_one(:ewm_service) }
it { is_expected.to have_one(:external_wiki_service) }
- it { is_expected.to have_one(:confluence_service) }
+ it { is_expected.to have_one(:confluence_integration) }
it { is_expected.to have_one(:project_feature) }
it { is_expected.to have_one(:project_repository) }
it { is_expected.to have_one(:container_expiration_policy) }
@@ -472,6 +472,23 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#merge_requests_author_approval' do
+ where(:attribute_value, :return_value) do
+ true | true
+ false | false
+ nil | false
+ end
+
+ with_them do
+ let(:project) { create(:project, merge_requests_author_approval: attribute_value) }
+
+ it 'returns expected value' do
+ expect(project.merge_requests_author_approval).to eq(return_value)
+ expect(project.merge_requests_author_approval?).to eq(return_value)
+ end
+ end
+ end
+
describe '#all_pipelines' do
let_it_be(:project) { create(:project) }
@@ -636,6 +653,16 @@ RSpec.describe Project, factory_default: :keep do
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) }
+
+ context 'when read_container_registry_access_level is disabled' do
+ before do
+ stub_feature_flags(read_container_registry_access_level: false)
+ end
+
+ it { is_expected.not_to delegate_method(:container_registry_enabled?).to(:project_feature) }
+ end
end
describe 'reference methods' do
@@ -967,6 +994,39 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#open_issues_count', :aggregate_failures do
+ let(:project) { build(:project) }
+
+ it 'provides the issue count' do
+ expect(project.open_issues_count).to eq 0
+ end
+
+ it 'invokes the count service with current_user' do
+ user = build(:user)
+ count_service = instance_double(Projects::OpenIssuesCountService)
+ expect(Projects::OpenIssuesCountService).to receive(:new).with(project, user).and_return(count_service)
+ expect(count_service).to receive(:count)
+
+ 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)
+
+ project.open_issues_count
+ end
+ end
+
+ describe '#open_merge_requests_count' do
+ it 'provides the merge request count' do
+ project = build(:project)
+
+ expect(project.open_merge_requests_count).to eq 0
+ end
+ end
+
describe '#issue_exists?' do
let_it_be(:project) { create(:project) }
@@ -1086,7 +1146,7 @@ RSpec.describe Project, factory_default: :keep do
project = create(:redmine_project)
expect(project).to receive(:integrations).once.and_call_original
- 2.times { expect(project.external_issue_tracker).to be_a_kind_of(RedmineService) }
+ 2.times { expect(project.external_issue_tracker).to be_a_kind_of(Integrations::Redmine) }
end
end
@@ -1158,7 +1218,7 @@ RSpec.describe Project, factory_default: :keep do
it 'returns an active external wiki' do
create(:service, project: project, type: 'ExternalWikiService', active: true)
- is_expected.to be_kind_of(ExternalWikiService)
+ is_expected.to be_kind_of(Integrations::ExternalWiki)
end
it 'does not return an inactive external wiki' do
@@ -1584,19 +1644,20 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '.find_by_service_desk_project_key' do
- it 'returns the correct project' do
+ describe '.with_service_desk_key' do
+ it 'returns projects with given key' do
project1 = create(:project)
project2 = create(:project)
create(:service_desk_setting, project: project1, project_key: 'key1')
- create(:service_desk_setting, project: project2, project_key: 'key2')
+ create(:service_desk_setting, project: project2, project_key: 'key1')
+ create(:service_desk_setting, project_key: 'key2')
+ create(:service_desk_setting)
- expect(Project.find_by_service_desk_project_key('key1')).to eq(project1)
- expect(Project.find_by_service_desk_project_key('key2')).to eq(project2)
+ expect(Project.with_service_desk_key('key1')).to contain_exactly(project1, project2)
end
- it 'returns nil if there is no project with the key' do
- expect(Project.find_by_service_desk_project_key('some_key')).to be_nil
+ it 'returns empty if there is no project with the key' do
+ expect(Project.with_service_desk_key('key1')).to be_empty
end
end
@@ -1632,112 +1693,6 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#any_active_runners?' do
- subject { project.any_active_runners? }
-
- context 'shared runners' do
- let(:project) { create(:project, shared_runners_enabled: shared_runners_enabled) }
- let(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
- let(:shared_runner) { create(:ci_runner, :instance) }
-
- context 'for shared runners disabled' do
- let(:shared_runners_enabled) { false }
-
- it 'has no runners available' do
- is_expected.to be_falsey
- end
-
- it 'has a specific runner' do
- specific_runner
-
- is_expected.to be_truthy
- end
-
- it 'has a shared runner, but they are prohibited to use' do
- shared_runner
-
- is_expected.to be_falsey
- end
-
- it 'checks the presence of specific runner' do
- specific_runner
-
- expect(project.any_active_runners? { |runner| runner == specific_runner }).to be_truthy
- end
-
- it 'returns false if match cannot be found' do
- specific_runner
-
- expect(project.any_active_runners? { false }).to be_falsey
- end
- end
-
- context 'for shared runners enabled' do
- let(:shared_runners_enabled) { true }
-
- it 'has a shared runner' do
- shared_runner
-
- is_expected.to be_truthy
- end
-
- it 'checks the presence of shared runner' do
- shared_runner
-
- expect(project.any_active_runners? { |runner| runner == shared_runner }).to be_truthy
- end
-
- it 'returns false if match cannot be found' do
- shared_runner
-
- expect(project.any_active_runners? { false }).to be_falsey
- end
- end
- end
-
- context 'group runners' do
- let(:project) { create(:project, group_runners_enabled: group_runners_enabled) }
- let(:group) { create(:group, projects: [project]) }
- let(:group_runner) { create(:ci_runner, :group, groups: [group]) }
-
- context 'for group runners disabled' do
- let(:group_runners_enabled) { false }
-
- it 'has no runners available' do
- is_expected.to be_falsey
- end
-
- it 'has a group runner, but they are prohibited to use' do
- group_runner
-
- is_expected.to be_falsey
- end
- end
-
- context 'for group runners enabled' do
- let(:group_runners_enabled) { true }
-
- it 'has a group runner' do
- group_runner
-
- is_expected.to be_truthy
- end
-
- it 'checks the presence of group runner' do
- group_runner
-
- expect(project.any_active_runners? { |runner| runner == group_runner }).to be_truthy
- end
-
- it 'returns false if match cannot be found' do
- group_runner
-
- expect(project.any_active_runners? { false }).to be_falsey
- end
- end
- end
- end
-
describe '#any_online_runners?' do
subject { project.any_online_runners? }
@@ -1858,13 +1813,13 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#shared_runners' do
- let!(:runner) { create(:ci_runner, :instance) }
+ shared_examples 'shared_runners' do
+ let_it_be(:runner) { create(:ci_runner, :instance) }
subject { project.shared_runners }
context 'when shared runners are enabled for project' do
- let!(:project) { create(:project, shared_runners_enabled: true) }
+ let(:project) { build_stubbed(:project, shared_runners_enabled: true) }
it "returns a list of shared runners" do
is_expected.to eq([runner])
@@ -1872,7 +1827,7 @@ RSpec.describe Project, factory_default: :keep do
end
context 'when shared runners are disabled for project' do
- let!(:project) { create(:project, shared_runners_enabled: false) }
+ let(:project) { build_stubbed(:project, shared_runners_enabled: false) }
it "returns a empty list" do
is_expected.to be_empty
@@ -1880,6 +1835,16 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#shared_runners' do
+ it_behaves_like 'shared_runners'
+ end
+
+ describe '#available_shared_runners' do
+ it_behaves_like 'shared_runners' do
+ subject { project.available_shared_runners }
+ end
+ end
+
describe '#visibility_level' do
let(:project) { build(:project) }
@@ -2238,13 +2203,13 @@ RSpec.describe Project, factory_default: :keep do
end
context 'with projects on legacy storage' do
- let(:project) { create(:project, :repository, :legacy_storage) }
+ let(:project) { create(:project, :legacy_storage) }
it_behaves_like 'tracks storage location'
end
context 'with projects on hashed storage' do
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project) }
it_behaves_like 'tracks storage location'
end
@@ -2363,35 +2328,55 @@ RSpec.describe Project, factory_default: :keep do
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::DISABLED)
-
- expect(project.container_registry_enabled).to eq(true)
- expect(project.project_feature.container_registry_access_level).to eq(ProjectFeature::DISABLED)
+ project.project_feature.update_column(:container_registry_access_level, ProjectFeature::ENABLED)
project.update!(container_registry_enabled: false)
- expect(project.container_registry_enabled).to eq(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.container_registry_enabled).to eq(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::DISABLED)
-
- expect(project.container_registry_enabled).to eq(true)
- expect(project.project_feature.container_registry_access_level).to eq(ProjectFeature::DISABLED)
+ 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.container_registry_enabled).to eq(true)
- expect(project.project_feature.reload.container_registry_access_level).to eq(ProjectFeature::DISABLED)
+ 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)
+ expect(project.container_registry_enabled?).to eq(false)
+ end
+
+ context 'with read_container_registry_access_level disabled' do
+ before do
+ stub_feature_flags(read_container_registry_access_level: false)
+ end
+
+ it 'reads project.container_registry_enabled' 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(true)
+ expect(project.container_registry_enabled?).to eq(true)
+ end
end
end
@@ -2932,6 +2917,16 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#mark_primary_write_location' do
+ let(:project) { create(:project) }
+
+ it 'marks the location with project ID' do
+ expect(Gitlab::Database::LoadBalancing::Sticking).to receive(:mark_primary_write_location).with(:project, project.id)
+
+ project.mark_primary_write_location
+ end
+ end
+
describe '#mark_stuck_remote_mirrors_as_failed!' do
it 'fails stuck remote mirrors' do
project = create(:project, :repository, :remote_mirror)
@@ -4414,6 +4409,18 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ context 'with export' do
+ let(:project) { create(:project, :with_export) }
+
+ it '#export_file_exists? returns true' do
+ expect(project.export_file_exists?).to be true
+ end
+
+ it '#export_archive_exists? returns false' do
+ expect(project.export_archive_exists?).to be true
+ end
+ end
+
describe '#forks_count' do
it 'returns the number of forks' do
project = build(:project)
@@ -4692,7 +4699,6 @@ RSpec.describe Project, factory_default: :keep do
specify do
expect(subject).to include
[
- { key: 'CI_PROJECT_CONFIG_PATH', value: Ci::Pipeline::DEFAULT_CONFIG_PATH, public: true, masked: false },
{ key: 'CI_CONFIG_PATH', value: Ci::Pipeline::DEFAULT_CONFIG_PATH, public: true, masked: false }
]
end
@@ -4705,7 +4711,6 @@ RSpec.describe Project, factory_default: :keep do
it do
expect(subject).to include
[
- { key: 'CI_PROJECT_CONFIG_PATH', value: 'random.yml', public: true, masked: false },
{ key: 'CI_CONFIG_PATH', value: 'random.yml', public: true, masked: false }
]
end
@@ -5328,7 +5333,7 @@ RSpec.describe Project, factory_default: :keep do
it 'executes services with the specified scope' do
data = 'any data'
- expect_next_found_instance_of(SlackService) do |instance|
+ expect_next_found_instance_of(Integrations::Slack) do |instance|
expect(instance).to receive(:async_execute).with(data).once
end
@@ -5336,7 +5341,7 @@ RSpec.describe Project, factory_default: :keep do
end
it 'does not execute services that don\'t match the specified scope' do
- expect(SlackService).not_to receive(:allocate).and_wrap_original do |method|
+ expect(Integrations::Slack).not_to receive(:allocate).and_wrap_original do |method|
method.call.tap do |instance|
expect(instance).not_to receive(:async_execute)
end
@@ -5379,7 +5384,7 @@ RSpec.describe Project, factory_default: :keep do
it { expect(project.has_active_services?).to be_falsey }
it 'returns true when a matching service exists' do
- create(:custom_issue_tracker_service, push_events: true, merge_requests_events: false, project: project)
+ create(:custom_issue_tracker_integration, push_events: true, merge_requests_events: false, project: project)
expect(project.has_active_services?(:merge_request_hooks)).to be_falsey
expect(project.has_active_services?).to be_truthy
@@ -6307,14 +6312,16 @@ RSpec.describe Project, factory_default: :keep do
let_it_be(:project) { create(:project, group: create(:group, :public)) }
it 'returns a maximum of ten maintainers of the project in recent_sign_in descending order' do
- users = create_list(:user, 12, :with_sign_ins)
+ limit = 2
+ stub_const("Member::ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT", limit)
+ users = create_list(:user, limit + 1, :with_sign_ins)
active_maintainers = users.map do |user|
create(:project_member, :maintainer, user: user, project: project)
end
active_maintainers_in_recent_sign_in_desc_order = project.members_and_requesters
.id_in(active_maintainers)
- .order_recent_sign_in.limit(10)
+ .order_recent_sign_in.limit(limit)
expect(project.access_request_approvers_to_be_notified).to eq(active_maintainers_in_recent_sign_in_desc_order)
end
@@ -6558,6 +6565,14 @@ RSpec.describe Project, factory_default: :keep do
expect(subject).to eq([lfs_object.oid])
end
end
+
+ it 'lfs_objects_projects associations are deleted along with project' do
+ expect { project.delete }.to change(LfsObjectsProject, :count).by(-2)
+ end
+
+ it 'lfs_objects associations are unchanged when the assicated project is removed' do
+ expect { project.delete }.not_to change(LfsObject, :count)
+ end
end
context 'when project has no associated LFS objects' do
@@ -6666,7 +6681,7 @@ RSpec.describe Project, factory_default: :keep do
context 'when project export is completed' do
before do
finish_job(project_export_job)
- allow(project).to receive(:export_file).and_return(double(ImportExportUploader, file: 'exists.zip'))
+ allow(project).to receive(:export_file_exists?).and_return(true)
end
it { expect(project.export_status).to eq :finished }
@@ -6677,7 +6692,7 @@ RSpec.describe Project, factory_default: :keep do
before do
finish_job(project_export_job)
- allow(project).to receive(:export_file).and_return(double(ImportExportUploader, file: 'exists.zip'))
+ allow(project).to receive(:export_file_exists?).and_return(true)
end
it { expect(project.export_status).to eq :regeneration_in_progress }
@@ -6973,7 +6988,7 @@ RSpec.describe Project, factory_default: :keep do
end
describe 'topics' do
- let_it_be(:project) { create(:project, tag_list: 'topic1, topic2, topic3') }
+ let_it_be(:project) { create(: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])
@@ -6983,44 +6998,69 @@ RSpec.describe Project, factory_default: :keep do
expect(project.topics.first.class.name).to eq('ActsAsTaggableOn::Tag')
expect(project.topics.map(&:name)).to match_array(%w[topic1 topic2 topic3])
end
+ end
- context 'aliases' do
- it 'tag_list returns correct string array' do
- expect(project.tag_list).to match_array(%w[topic1 topic2 topic3])
+ shared_examples 'all_runners' do
+ let_it_be_with_refind(:project) { create(:project, group: create(:group)) }
+ let_it_be(:instance_runner) { create(:ci_runner, :instance) }
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [project.group]) }
+ let_it_be(:other_group_runner) { create(:ci_runner, :group) }
+ let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project]) }
+ let_it_be(:other_project_runner) { create(:ci_runner, :project) }
+
+ subject { project.all_runners }
+
+ context 'when shared runners are enabled for project' do
+ before do
+ project.update!(shared_runners_enabled: true)
end
- it 'tags returns correct tag records' do
- expect(project.tags.first.class.name).to eq('ActsAsTaggableOn::Tag')
- expect(project.tags.map(&:name)).to match_array(%w[topic1 topic2 topic3])
+ it 'returns a list with all runners' do
+ is_expected.to match_array([instance_runner, group_runner, project_runner])
end
end
- context 'intermediate state during background migration' do
+ context 'when shared runners are disabled for project' do
before do
- project.taggings.first.update!(context: 'tags')
- project.instance_variable_set("@tag_list", nil)
- project.reload
+ project.update!(shared_runners_enabled: false)
end
- it 'tag_list returns string array including old and new topics' do
- expect(project.tag_list).to match_array(%w[topic1 topic2 topic3])
+ it 'returns a list without shared runners' do
+ is_expected.to match_array([group_runner, project_runner])
+ end
+ end
+
+ context 'when group runners are enabled for project' do
+ before do
+ project.update!(group_runners_enabled: true)
end
- it 'tags returns old and new tag records' do
- expect(project.tags.first.class.name).to eq('ActsAsTaggableOn::Tag')
- expect(project.tags.map(&:name)).to match_array(%w[topic1 topic2 topic3])
- expect(project.taggings.map(&:context)).to match_array(%w[tags topics topics])
+ it 'returns a list with all runners' do
+ is_expected.to match_array([instance_runner, group_runner, project_runner])
end
+ end
- it 'update tag_list adds new topics and removes old topics' do
- project.update!(tag_list: 'topic1, topic2, topic3, topic4')
+ context 'when group runners are disabled for project' do
+ before do
+ project.update!(group_runners_enabled: false)
+ end
- expect(project.tags.map(&:name)).to match_array(%w[topic1 topic2 topic3 topic4])
- expect(project.taggings.map(&:context)).to match_array(%w[topics topics topics topics])
+ it 'returns a list without group runners' do
+ is_expected.to match_array([instance_runner, project_runner])
end
end
end
+ describe '#all_runners' do
+ it_behaves_like 'all_runners'
+ end
+
+ describe '#all_available_runners' do
+ it_behaves_like 'all_runners' do
+ subject { project.all_available_runners }
+ end
+ end
+
def finish_job(export_job)
export_job.start
export_job.finish