diff options
Diffstat (limited to 'spec/models/concerns')
-rw-r--r-- | spec/models/concerns/bulk_insert_safe_spec.rb | 36 | ||||
-rw-r--r-- | spec/models/concerns/cascading_namespace_setting_attribute_spec.rb | 36 | ||||
-rw-r--r-- | spec/models/concerns/chronic_duration_attribute_spec.rb | 3 | ||||
-rw-r--r-- | spec/models/concerns/ci/maskable_spec.rb | 2 | ||||
-rw-r--r-- | spec/models/concerns/cron_schedulable_spec.rb | 17 | ||||
-rw-r--r-- | spec/models/concerns/has_integrations_spec.rb | 33 | ||||
-rw-r--r-- | spec/models/concerns/has_timelogs_report_spec.rb | 22 | ||||
-rw-r--r-- | spec/models/concerns/noteable_spec.rb | 2 | ||||
-rw-r--r-- | spec/models/concerns/routable_spec.rb | 74 | ||||
-rw-r--r-- | spec/models/concerns/sidebars/container_with_html_options_spec.rb | 21 | ||||
-rw-r--r-- | spec/models/concerns/sidebars/positionable_list_spec.rb | 59 |
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 |