summaryrefslogtreecommitdiff
path: root/spec/models/concerns
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
commit4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch)
tree5423a1c7516cffe36384133ade12572cf709398d /spec/models/concerns
parente570267f2f6b326480d284e0164a6464ba4081bc (diff)
downloadgitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/models/concerns')
-rw-r--r--spec/models/concerns/bulk_insert_safe_spec.rb36
-rw-r--r--spec/models/concerns/cascading_namespace_setting_attribute_spec.rb36
-rw-r--r--spec/models/concerns/chronic_duration_attribute_spec.rb3
-rw-r--r--spec/models/concerns/ci/maskable_spec.rb2
-rw-r--r--spec/models/concerns/cron_schedulable_spec.rb17
-rw-r--r--spec/models/concerns/has_integrations_spec.rb33
-rw-r--r--spec/models/concerns/has_timelogs_report_spec.rb22
-rw-r--r--spec/models/concerns/noteable_spec.rb2
-rw-r--r--spec/models/concerns/routable_spec.rb74
-rw-r--r--spec/models/concerns/sidebars/container_with_html_options_spec.rb21
-rw-r--r--spec/models/concerns/sidebars/positionable_list_spec.rb59
11 files changed, 202 insertions, 103 deletions
diff --git a/spec/models/concerns/bulk_insert_safe_spec.rb b/spec/models/concerns/bulk_insert_safe_spec.rb
index e40b0cf11ff..ca6df506ee8 100644
--- a/spec/models/concerns/bulk_insert_safe_spec.rb
+++ b/spec/models/concerns/bulk_insert_safe_spec.rb
@@ -5,6 +5,10 @@ require 'spec_helper'
RSpec.describe BulkInsertSafe do
before(:all) do
ActiveRecord::Schema.define do
+ create_table :bulk_insert_parent_items, force: true do |t|
+ t.string :name, null: false
+ end
+
create_table :bulk_insert_items, force: true do |t|
t.string :name, null: true
t.integer :enum_value, null: false
@@ -12,6 +16,7 @@ RSpec.describe BulkInsertSafe do
t.string :encrypted_secret_value_iv, null: false
t.binary :sha_value, null: false, limit: 20
t.jsonb :jsonb_value, null: false
+ t.belongs_to :bulk_insert_parent_item, foreign_key: true, null: true
t.index :name, unique: true
end
@@ -21,9 +26,23 @@ RSpec.describe BulkInsertSafe do
after(:all) do
ActiveRecord::Schema.define do
drop_table :bulk_insert_items, force: true
+ drop_table :bulk_insert_parent_items, force: true
end
end
+ BulkInsertParentItem = Class.new(ActiveRecord::Base) do
+ self.table_name = :bulk_insert_parent_items
+ self.inheritance_column = :_type_disabled
+
+ def self.name
+ table_name.singularize.camelcase
+ end
+ end
+
+ let_it_be(:bulk_insert_parent_item) do
+ BulkInsertParentItem.create!(name: 'parent')
+ end
+
let_it_be(:bulk_insert_item_class) do
Class.new(ActiveRecord::Base) do
self.table_name = 'bulk_insert_items'
@@ -33,6 +52,8 @@ RSpec.describe BulkInsertSafe do
validates :name, :enum_value, :secret_value, :sha_value, :jsonb_value, presence: true
+ belongs_to :bulk_insert_parent_item
+
sha_attribute :sha_value
enum enum_value: { case_1: 1 }
@@ -51,8 +72,8 @@ RSpec.describe BulkInsertSafe do
'BulkInsertItem'
end
- def self.valid_list(count)
- Array.new(count) { |n| new(name: "item-#{n}", secret_value: 'my-secret') }
+ def self.valid_list(count, bulk_insert_parent_item: nil)
+ Array.new(count) { |n| new(name: "item-#{n}", secret_value: 'my-secret', bulk_insert_parent_item: bulk_insert_parent_item) }
end
def self.invalid_list(count)
@@ -117,6 +138,14 @@ RSpec.describe BulkInsertSafe do
bulk_insert_item_class.bulk_insert!(items, batch_size: 5)
end
+ it 'inserts items with belongs_to association' do
+ items = bulk_insert_item_class.valid_list(10, bulk_insert_parent_item: bulk_insert_parent_item)
+
+ bulk_insert_item_class.bulk_insert!(items, batch_size: 5)
+
+ expect(bulk_insert_item_class.last(items.size).map(&:bulk_insert_parent_item)).to eq([bulk_insert_parent_item] * 10)
+ end
+
it 'items can be properly fetched from database' do
items = bulk_insert_item_class.valid_list(10)
@@ -129,8 +158,7 @@ RSpec.describe BulkInsertSafe do
it 'rolls back the transaction when any item is invalid' do
# second batch is bad
- all_items = bulk_insert_item_class.valid_list(10) +
- bulk_insert_item_class.invalid_list(10)
+ all_items = bulk_insert_item_class.valid_list(10) + bulk_insert_item_class.invalid_list(10)
expect do
bulk_insert_item_class.bulk_insert!(all_items, batch_size: 2) rescue nil
diff --git a/spec/models/concerns/cascading_namespace_setting_attribute_spec.rb b/spec/models/concerns/cascading_namespace_setting_attribute_spec.rb
index ddff9ce32b4..02cd8557231 100644
--- a/spec/models/concerns/cascading_namespace_setting_attribute_spec.rb
+++ b/spec/models/concerns/cascading_namespace_setting_attribute_spec.rb
@@ -142,7 +142,7 @@ RSpec.describe NamespaceSetting, 'CascadingNamespaceSettingAttribute' do
end
it 'does not allow the local value to be saved' do
- subgroup_settings.delayed_project_removal = nil
+ subgroup_settings.delayed_project_removal = false
expect { subgroup_settings.save! }
.to raise_error(ActiveRecord::RecordInvalid, /Delayed project removal cannot be changed because it is locked by an ancestor/)
@@ -164,6 +164,19 @@ RSpec.describe NamespaceSetting, 'CascadingNamespaceSettingAttribute' do
end
end
+ describe '#delayed_project_removal=' do
+ before do
+ subgroup_settings.update!(delayed_project_removal: nil)
+ group_settings.update!(delayed_project_removal: true)
+ end
+
+ it 'does not save the value locally when it matches the cascaded value' do
+ subgroup_settings.update!(delayed_project_removal: true)
+
+ expect(subgroup_settings.read_attribute(:delayed_project_removal)).to eq(nil)
+ end
+ end
+
describe '#delayed_project_removal_locked?' do
shared_examples 'not locked' do
it 'is not locked by an ancestor' do
@@ -189,6 +202,20 @@ RSpec.describe NamespaceSetting, 'CascadingNamespaceSettingAttribute' do
it_behaves_like 'not locked'
end
+ context 'when attribute is locked by self' do
+ before do
+ subgroup_settings.update!(lock_delayed_project_removal: true)
+ end
+
+ it 'is not locked by default' do
+ expect(subgroup_settings.delayed_project_removal_locked?).to eq(false)
+ end
+
+ it 'is locked when including self' do
+ expect(subgroup_settings.delayed_project_removal_locked?(include_self: true)).to eq(true)
+ end
+ end
+
context 'when parent does not lock the attribute' do
it_behaves_like 'not locked'
end
@@ -277,6 +304,13 @@ RSpec.describe NamespaceSetting, 'CascadingNamespaceSettingAttribute' do
expect { subgroup_settings.save! }
.to raise_error(ActiveRecord::RecordInvalid, /Delayed project removal cannot be nil when locking the attribute/)
end
+
+ it 'copies the cascaded value when locking the attribute if the local value is nil', :aggregate_failures do
+ subgroup_settings.delayed_project_removal = nil
+ subgroup_settings.lock_delayed_project_removal = true
+
+ expect(subgroup_settings.read_attribute(:delayed_project_removal)).to eq(false)
+ end
end
context 'when application settings locks the attribute' do
diff --git a/spec/models/concerns/chronic_duration_attribute_spec.rb b/spec/models/concerns/chronic_duration_attribute_spec.rb
index e6dbf403b63..00e28e19bd5 100644
--- a/spec/models/concerns/chronic_duration_attribute_spec.rb
+++ b/spec/models/concerns/chronic_duration_attribute_spec.rb
@@ -56,8 +56,7 @@ RSpec.shared_examples 'ChronicDurationAttribute writer' do
subject.send("#{virtual_field}=", '-10m')
expect(subject.valid?).to be_falsey
- expect(subject.errors&.messages)
- .to include(base: ['Maximum job timeout has a value which could not be accepted'])
+ expect(subject.errors.added?(:base, 'Maximum job timeout has a value which could not be accepted')).to be true
end
end
diff --git a/spec/models/concerns/ci/maskable_spec.rb b/spec/models/concerns/ci/maskable_spec.rb
index 840a08b6060..2b13fc21fe8 100644
--- a/spec/models/concerns/ci/maskable_spec.rb
+++ b/spec/models/concerns/ci/maskable_spec.rb
@@ -66,7 +66,7 @@ RSpec.describe Ci::Maskable do
end
it 'matches valid strings' do
- expect(subject.match?('Hello+World_123/@:-.')).to eq(true)
+ expect(subject.match?('Hello+World_123/@:-~.')).to eq(true)
end
end
diff --git a/spec/models/concerns/cron_schedulable_spec.rb b/spec/models/concerns/cron_schedulable_spec.rb
new file mode 100644
index 00000000000..39c3d5e55d3
--- /dev/null
+++ b/spec/models/concerns/cron_schedulable_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe CronSchedulable do
+ let(:ideal_next_run_at) { schedule.send(:ideal_next_run_from, Time.zone.now) }
+ let(:cron_worker_next_run_at) { schedule.send(:cron_worker_next_run_from, Time.zone.now) }
+
+ context 'for ci_pipeline_schedule' do
+ let(:schedule) { create(:ci_pipeline_schedule, :every_minute) }
+ let(:schedule_1) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') }
+ let(:schedule_2) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') }
+ let(:new_cron) { '0 0 1 1 *' }
+
+ it_behaves_like 'handles set_next_run_at'
+ end
+end
diff --git a/spec/models/concerns/has_integrations_spec.rb b/spec/models/concerns/has_integrations_spec.rb
new file mode 100644
index 00000000000..6e55a1c8b01
--- /dev/null
+++ b/spec/models/concerns/has_integrations_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe HasIntegrations do
+ let_it_be(:project_1) { create(:project) }
+ let_it_be(:project_2) { create(:project) }
+ let_it_be(:project_3) { create(:project) }
+ let_it_be(:project_4) { create(:project) }
+ let_it_be(:instance_integration) { create(:jira_service, :instance) }
+
+ before do
+ create(:jira_service, project: project_1, inherit_from_id: instance_integration.id)
+ create(:jira_service, project: project_2, inherit_from_id: nil)
+ create(:jira_service, group: create(:group), project: nil, inherit_from_id: nil)
+ create(:jira_service, project: project_3, inherit_from_id: nil)
+ create(:slack_service, project: project_4, inherit_from_id: nil)
+ end
+
+ describe '.with_custom_integration_for' do
+ it 'returns projects with custom integrations' do
+ # We use pagination to verify that the group is excluded from the query
+ expect(Project.with_custom_integration_for(instance_integration, 0, 2)).to contain_exactly(project_2, project_3)
+ expect(Project.with_custom_integration_for(instance_integration)).to contain_exactly(project_2, project_3)
+ end
+ end
+
+ describe '.without_integration' do
+ it 'returns projects without integration' do
+ expect(Project.without_integration(instance_integration)).to contain_exactly(project_4)
+ end
+ end
+end
diff --git a/spec/models/concerns/has_timelogs_report_spec.rb b/spec/models/concerns/has_timelogs_report_spec.rb
index f694fc350ee..f0dca47fae1 100644
--- a/spec/models/concerns/has_timelogs_report_spec.rb
+++ b/spec/models/concerns/has_timelogs_report_spec.rb
@@ -3,16 +3,20 @@
require 'spec_helper'
RSpec.describe HasTimelogsReport do
- let(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
+
let(:group) { create(:group) }
- let(:issue) { create(:issue, project: create(:project, :public, group: group)) }
+ let(:project) { create(:project, :public, group: group) }
+ let(:issue1) { create(:issue, project: project) }
+ let(:merge_request1) { create(:merge_request, source_project: project) }
describe '#timelogs' do
- let!(:timelog1) { create_timelog(15.days.ago) }
- let!(:timelog2) { create_timelog(10.days.ago) }
- let!(:timelog3) { create_timelog(5.days.ago) }
- let(:start_time) { 20.days.ago }
- let(:end_time) { 8.days.ago }
+ let_it_be(:start_time) { 20.days.ago }
+ let_it_be(:end_time) { 8.days.ago }
+
+ let!(:timelog1) { create_timelog(15.days.ago, issue: issue1) }
+ let!(:timelog2) { create_timelog(10.days.ago, merge_request: merge_request1) }
+ let!(:timelog3) { create_timelog(5.days.ago, issue: issue1) }
before do
group.add_developer(user)
@@ -45,7 +49,7 @@ RSpec.describe HasTimelogsReport do
end
end
- def create_timelog(time)
- create(:timelog, issue: issue, user: user, spent_at: time)
+ def create_timelog(time, issue: nil, merge_request: nil)
+ create(:timelog, issue: issue, merge_request: merge_request, user: user, spent_at: time)
end
end
diff --git a/spec/models/concerns/noteable_spec.rb b/spec/models/concerns/noteable_spec.rb
index a7117af81a2..38766d8decd 100644
--- a/spec/models/concerns/noteable_spec.rb
+++ b/spec/models/concerns/noteable_spec.rb
@@ -288,7 +288,7 @@ RSpec.describe Noteable do
end
before do
- MergeRequests::MergeToRefService.new(merge_request.project, merge_request.author).execute(merge_request)
+ MergeRequests::MergeToRefService.new(project: merge_request.project, current_user: merge_request.author).execute(merge_request)
Discussions::CaptureDiffNotePositionsService.new(merge_request).execute
end
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index 6ab87053258..0a433a8cf4f 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -62,7 +62,7 @@ RSpec.describe Routable do
end
end
-RSpec.describe Group, 'Routable' do
+RSpec.describe Group, 'Routable', :with_clean_rails_cache do
let_it_be_with_reload(:group) { create(:group, name: 'foo') }
let_it_be(:nested_group) { create(:group, parent: group) }
@@ -165,19 +165,63 @@ RSpec.describe Group, 'Routable' do
end
end
+ describe '#parent_loaded?' do
+ before do
+ group.parent = create(:group)
+ group.save!
+
+ group.reload
+ end
+
+ it 'is false when the parent is not loaded' do
+ expect(group.parent_loaded?).to be_falsey
+ end
+
+ it 'is true when the parent is loaded' do
+ group.parent
+
+ expect(group.parent_loaded?).to be_truthy
+ end
+ end
+
+ describe '#route_loaded?' do
+ it 'is false when the route is not loaded' do
+ expect(group.route_loaded?).to be_falsey
+ end
+
+ it 'is true when the route is loaded' do
+ group.route
+
+ expect(group.route_loaded?).to be_truthy
+ end
+ end
+
describe '#full_path' do
it { expect(group.full_path).to eq(group.path) }
it { expect(nested_group.full_path).to eq("#{group.full_path}/#{nested_group.path}") }
+
+ it 'hits the cache when not preloaded' do
+ forcibly_hit_cached_lookup(nested_group, :full_path)
+
+ expect(nested_group.full_path).to eq("#{group.full_path}/#{nested_group.path}")
+ end
end
describe '#full_name' do
it { expect(group.full_name).to eq(group.name) }
it { expect(nested_group.full_name).to eq("#{group.name} / #{nested_group.name}") }
+
+ it 'hits the cache when not preloaded' do
+ forcibly_hit_cached_lookup(nested_group, :full_name)
+
+ expect(nested_group.full_name).to eq("#{group.name} / #{nested_group.name}")
+ end
end
end
-RSpec.describe Project, 'Routable' do
- let_it_be(:project) { create(:project) }
+RSpec.describe Project, 'Routable', :with_clean_rails_cache do
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project) { create(:project, namespace: namespace) }
it_behaves_like '.find_by_full_path' do
let_it_be(:record) { project }
@@ -192,10 +236,30 @@ RSpec.describe Project, 'Routable' do
end
describe '#full_path' do
- it { expect(project.full_path).to eq "#{project.namespace.full_path}/#{project.path}" }
+ it { expect(project.full_path).to eq "#{namespace.full_path}/#{project.path}" }
+
+ it 'hits the cache when not preloaded' do
+ forcibly_hit_cached_lookup(project, :full_path)
+
+ expect(project.full_path).to eq("#{namespace.full_path}/#{project.path}")
+ end
end
describe '#full_name' do
- it { expect(project.full_name).to eq "#{project.namespace.human_name} / #{project.name}" }
+ it { expect(project.full_name).to eq "#{namespace.human_name} / #{project.name}" }
+
+ it 'hits the cache when not preloaded' do
+ forcibly_hit_cached_lookup(project, :full_name)
+
+ expect(project.full_name).to eq("#{namespace.human_name} / #{project.name}")
+ end
end
end
+
+def forcibly_hit_cached_lookup(record, method)
+ stub_feature_flags(cached_route_lookups: true)
+ expect(record).to receive(:persisted?).and_return(true)
+ expect(record).to receive(:route_loaded?).and_return(false)
+ expect(record).to receive(:parent_loaded?).and_return(false)
+ expect(Gitlab::Cache).to receive(:fetch_once).with([record.cache_key, method]).and_call_original
+end
diff --git a/spec/models/concerns/sidebars/container_with_html_options_spec.rb b/spec/models/concerns/sidebars/container_with_html_options_spec.rb
deleted file mode 100644
index cc83fc84113..00000000000
--- a/spec/models/concerns/sidebars/container_with_html_options_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Sidebars::ContainerWithHtmlOptions do
- subject do
- Class.new do
- include Sidebars::ContainerWithHtmlOptions
-
- def title
- 'Foo'
- end
- end.new
- end
-
- describe '#container_html_options' do
- it 'includes by default aria-label attribute' do
- expect(subject.container_html_options).to eq(aria: { label: 'Foo' })
- end
- end
-end
diff --git a/spec/models/concerns/sidebars/positionable_list_spec.rb b/spec/models/concerns/sidebars/positionable_list_spec.rb
deleted file mode 100644
index 231aa5295dd..00000000000
--- a/spec/models/concerns/sidebars/positionable_list_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Sidebars::PositionableList do
- subject do
- Class.new do
- include Sidebars::PositionableList
- end.new
- end
-
- describe '#add_element' do
- it 'adds the element to the last position of the list' do
- list = [1, 2]
-
- subject.add_element(list, 3)
-
- expect(list).to eq([1, 2, 3])
- end
- end
-
- describe '#insert_element_before' do
- let(:user) { build(:user) }
- let(:list) { [1, user] }
-
- it 'adds element before the specific element class' do
- subject.insert_element_before(list, User, 2)
-
- expect(list).to eq [1, 2, user]
- end
-
- context 'when reference element does not exist' do
- it 'adds the element to the top of the list' do
- subject.insert_element_before(list, Project, 2)
-
- expect(list).to eq [2, 1, user]
- end
- end
- end
-
- describe '#insert_element_after' do
- let(:user) { build(:user) }
- let(:list) { [1, user] }
-
- it 'adds element after the specific element class' do
- subject.insert_element_after(list, Integer, 2)
-
- expect(list).to eq [1, 2, user]
- end
-
- context 'when reference element does not exist' do
- it 'adds the element to the end of the list' do
- subject.insert_element_after(list, Project, 2)
-
- expect(list).to eq [1, user, 2]
- end
- end
- end
-end