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/concerns')
-rw-r--r--spec/models/concerns/ci/partitionable_spec.rb84
-rw-r--r--spec/models/concerns/disables_sti_spec.rb15
-rw-r--r--spec/models/concerns/enums/sbom_spec.rb62
-rw-r--r--spec/models/concerns/ignorable_columns_spec.rb18
-rw-r--r--spec/models/concerns/pg_full_text_searchable_spec.rb14
-rw-r--r--spec/models/concerns/routable_spec.rb139
-rw-r--r--spec/models/concerns/transitionable_spec.rb17
-rw-r--r--spec/models/concerns/triggerable_hooks_spec.rb2
-rw-r--r--spec/models/concerns/vulnerability_finding_helpers_spec.rb27
9 files changed, 298 insertions, 80 deletions
diff --git a/spec/models/concerns/ci/partitionable_spec.rb b/spec/models/concerns/ci/partitionable_spec.rb
index 6daafc78cff..735b81f54bc 100644
--- a/spec/models/concerns/ci/partitionable_spec.rb
+++ b/spec/models/concerns/ci/partitionable_spec.rb
@@ -5,6 +5,12 @@ require 'spec_helper'
RSpec.describe Ci::Partitionable do
let(:ci_model) { Class.new(Ci::ApplicationRecord) }
+ around do |ex|
+ Gitlab::Database::SharedModel.using_connection(ci_model.connection) do
+ ex.run
+ end
+ end
+
describe 'partitionable models inclusion' do
subject { ci_model.include(described_class) }
@@ -61,10 +67,58 @@ RSpec.describe Ci::Partitionable do
context 'when partitioned is true' do
let(:partitioned) { true }
+ let(:partitioning_strategy) { ci_model.partitioning_strategy }
it { expect(ci_model.ancestors).to include(PartitionedTable) }
- it { expect(ci_model.partitioning_strategy).to be_a(Gitlab::Database::Partitioning::CiSlidingListStrategy) }
- it { expect(ci_model.partitioning_strategy.partitioning_key).to eq(:partition_id) }
+ it { expect(partitioning_strategy).to be_a(Gitlab::Database::Partitioning::CiSlidingListStrategy) }
+ it { expect(partitioning_strategy.partitioning_key).to eq(:partition_id) }
+
+ describe 'next_partition_if callback' do
+ let(:active_partition) { partitioning_strategy.active_partition }
+
+ let(:table_options) do
+ {
+ primary_key: [:id, :partition_id],
+ options: 'PARTITION BY LIST (partition_id)',
+ if_not_exists: false
+ }
+ end
+
+ before do
+ ci_model.connection.create_table(:_test_table_name, **table_options) do |t|
+ t.bigserial :id, null: false
+ t.bigint :partition_id, null: false
+ end
+
+ ci_model.table_name = :_test_table_name
+ end
+
+ subject(:value) { partitioning_strategy.next_partition_if.call(active_partition) }
+
+ context 'without any existing partitions' do
+ it { is_expected.to eq(true) }
+ end
+
+ context 'with initial partition attached' do
+ before do
+ ci_model.connection.execute(<<~SQL)
+ CREATE TABLE IF NOT EXISTS _test_table_name_100 PARTITION OF _test_table_name FOR VALUES IN (100);
+ SQL
+ end
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'with an existing partition for partition_id = 101' do
+ before do
+ ci_model.connection.execute(<<~SQL)
+ CREATE TABLE IF NOT EXISTS _test_table_name_101 PARTITION OF _test_table_name FOR VALUES IN (101);
+ SQL
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
end
context 'when partitioned is false' do
@@ -74,4 +128,30 @@ RSpec.describe Ci::Partitionable do
it { expect(ci_model).not_to respond_to(:partitioning_strategy) }
end
end
+
+ describe '.in_partition' do
+ before do
+ stub_const("#{described_class}::Testing::PARTITIONABLE_MODELS", [ci_model.name])
+ ci_model.table_name = :p_ci_builds
+ ci_model.include(described_class)
+ end
+
+ subject(:scope_values) { ci_model.in_partition(value).where_values_hash }
+
+ context 'with integer parameters' do
+ let(:value) { 101 }
+
+ it 'adds a partition_id filter' do
+ expect(scope_values).to include('partition_id' => 101)
+ end
+ end
+
+ context 'with partitionable records' do
+ let(:value) { build_stubbed(:ci_pipeline, partition_id: 101) }
+
+ it 'adds a partition_id filter' do
+ expect(scope_values).to include('partition_id' => 101)
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/disables_sti_spec.rb b/spec/models/concerns/disables_sti_spec.rb
new file mode 100644
index 00000000000..07eea635289
--- /dev/null
+++ b/spec/models/concerns/disables_sti_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe DisablesSti, feature_category: :shared do
+ describe '.allow_legacy_sti_class' do
+ it 'is nil by default' do
+ expect(ApplicationRecord.allow_legacy_sti_class).to eq(nil)
+ end
+
+ it 'is true on legacy models' do
+ expect(PersonalSnippet.allow_legacy_sti_class).to eq(true)
+ end
+ end
+end
diff --git a/spec/models/concerns/enums/sbom_spec.rb b/spec/models/concerns/enums/sbom_spec.rb
index e2f56cc637d..3bbdf619a8c 100644
--- a/spec/models/concerns/enums/sbom_spec.rb
+++ b/spec/models/concerns/enums/sbom_spec.rb
@@ -3,9 +3,9 @@
require "spec_helper"
RSpec.describe Enums::Sbom, feature_category: :dependency_management do
- describe '.purl_types' do
- using RSpec::Parameterized::TableSyntax
+ using RSpec::Parameterized::TableSyntax
+ describe '.purl_types' do
subject(:actual_purl_type) { described_class.purl_types[package_manager] }
where(:given_package_manager, :expected_purl_type) do
@@ -35,5 +35,63 @@ RSpec.describe Enums::Sbom, feature_category: :dependency_management do
expect(actual_purl_type).to eql(expected_purl_type)
end
end
+
+ it 'contains all of the dependency scanning and container scanning purl types' do
+ expect(described_class::DEPENDENCY_SCANNING_PURL_TYPES + described_class::CONTAINER_SCANNING_PURL_TYPES)
+ .to eql(described_class::PURL_TYPES.keys)
+ end
+ end
+
+ describe '.dependency_scanning_purl_type?' do
+ where(:purl_type, :expected) do
+ :composer | false
+ 'composer' | true
+ 'conan' | true
+ 'gem' | true
+ 'golang' | true
+ 'maven' | true
+ 'npm' | true
+ 'nuget' | true
+ 'pypi' | true
+ 'unknown' | false
+ 'apk' | false
+ 'rpm' | false
+ 'deb' | false
+ 'wolfi' | false
+ end
+
+ with_them do
+ it 'returns true if the purl_type is for dependency_scanning' do
+ actual = described_class.dependency_scanning_purl_type?(purl_type)
+ expect(actual).to eql(expected)
+ end
+ end
+ end
+
+ describe '.container_scanning_purl_type?' do
+ where(:purl_type, :expected) do
+ 'composer' | false
+ 'conan' | false
+ 'gem' | false
+ 'golang' | false
+ 'maven' | false
+ 'npm' | false
+ 'nuget' | false
+ 'pypi' | false
+ 'unknown' | false
+ :apk | false
+ 'apk' | true
+ 'rpm' | true
+ 'deb' | true
+ 'cbl-mariner' | true
+ 'wolfi' | true
+ end
+
+ with_them do
+ it 'returns true if the purl_type is for container_scanning' do
+ actual = described_class.container_scanning_purl_type?(purl_type)
+ expect(actual).to eql(expected)
+ end
+ end
end
end
diff --git a/spec/models/concerns/ignorable_columns_spec.rb b/spec/models/concerns/ignorable_columns_spec.rb
index 339f06f9c45..44dc0bb6da6 100644
--- a/spec/models/concerns/ignorable_columns_spec.rb
+++ b/spec/models/concerns/ignorable_columns_spec.rb
@@ -27,6 +27,12 @@ RSpec.describe IgnorableColumns do
expect { subject.ignore_columns(:name, remove_after: nil, remove_with: 12.6) }.to raise_error(ArgumentError, /Please indicate/)
end
+ it 'allows setting remove_never: true and not setting other remove options' do
+ expect do
+ subject.ignore_columns(%i[name created_at], remove_never: true)
+ end.to change { subject.ignored_columns }.from([]).to(%w[name created_at])
+ end
+
it 'requires remove_after attribute to be set' do
expect { subject.ignore_columns(:name, remove_after: "not a date", remove_with: 12.6) }.to raise_error(ArgumentError, /Please indicate/)
end
@@ -73,9 +79,11 @@ RSpec.describe IgnorableColumns do
end
describe IgnorableColumns::ColumnIgnore do
- subject { described_class.new(remove_after, remove_with) }
+ subject { described_class.new(remove_after, remove_with, remove_never) }
+ let(:remove_after) { nil }
let(:remove_with) { double }
+ let(:remove_never) { false }
describe '#safe_to_remove?' do
context 'after remove_after date has passed' do
@@ -93,6 +101,14 @@ RSpec.describe IgnorableColumns do
expect(subject.safe_to_remove?).to be_falsey
end
end
+
+ context 'with remove_never: true' do
+ let(:remove_never) { true }
+
+ it 'is false' do
+ expect(subject.safe_to_remove?).to be_falsey
+ end
+ end
end
end
end
diff --git a/spec/models/concerns/pg_full_text_searchable_spec.rb b/spec/models/concerns/pg_full_text_searchable_spec.rb
index 7da48489e12..f3289408643 100644
--- a/spec/models/concerns/pg_full_text_searchable_spec.rb
+++ b/spec/models/concerns/pg_full_text_searchable_spec.rb
@@ -87,6 +87,12 @@ RSpec.describe PgFullTextSearchable, feature_category: :global_search do
[english, with_accent, japanese].each(&:update_search_data!)
end
+ it 'builds a search query using `search_vector` from the search_data table' do
+ sql = model_class.pg_full_text_search('test').to_sql
+
+ expect(sql).to include('"issue_search_data"."search_vector" @@ to_tsquery')
+ end
+
it 'searches across all fields' do
expect(model_class.pg_full_text_search('title english')).to contain_exactly(english, japanese)
end
@@ -158,6 +164,14 @@ RSpec.describe PgFullTextSearchable, feature_category: :global_search do
end
end
+ describe '.pg_full_text_search_in_model' do
+ it 'builds a search query using `search_vector` from the model table' do
+ sql = model_class.pg_full_text_search_in_model('test').to_sql
+
+ expect(sql).to include('"issues"."search_vector" @@ to_tsquery')
+ end
+ end
+
describe '#update_search_data!' do
let(:model) { model_class.create!(project: project, namespace: project.project_namespace, title: 'title', description: 'description') }
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index 7e324812b97..e71392f7bbc 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.shared_examples 'routable resource' do
- shared_examples_for '.find_by_full_path' do |has_cross_join: false|
+ shared_examples_for '.find_by_full_path' do
it 'finds records by their full path' do
expect(described_class.find_by_full_path(record.full_path)).to eq(record)
expect(described_class.find_by_full_path(record.full_path.upcase)).to eq(record)
@@ -46,22 +46,98 @@ RSpec.shared_examples 'routable resource' do
end
end
- if has_cross_join
- it 'has a cross-join' do
- expect(Gitlab::Database).to receive(:allow_cross_joins_across_databases)
+ it 'does not have cross-join' do
+ expect(Gitlab::Database).not_to receive(:allow_cross_joins_across_databases)
- described_class.find_by_full_path(record.full_path)
+ described_class.find_by_full_path(record.full_path)
+ end
+ end
+
+ it_behaves_like '.find_by_full_path', :aggregate_failures
+
+ shared_examples_for '.where_full_path_in' do
+ context 'without any paths' do
+ it 'returns an empty relation' do
+ expect(described_class.where_full_path_in([])).to eq([])
+ end
+ end
+
+ context 'without any valid paths' do
+ it 'returns an empty relation' do
+ expect(described_class.where_full_path_in(%w[unknown])).to eq([])
+ end
+ end
+
+ context 'with valid paths' do
+ it 'returns the entities matching the paths' do
+ result = described_class.where_full_path_in([record.full_path, record_2.full_path])
+
+ expect(result).to contain_exactly(record, record_2)
+ end
+
+ it 'returns entities regardless of the casing of paths' do
+ result = described_class.where_full_path_in([record.full_path.upcase, record_2.full_path.upcase])
+
+ expect(result).to contain_exactly(record, record_2)
+ end
+ end
+
+ context 'on the usage of `use_includes` parameter' do
+ let_it_be(:klass) { record.class.to_s.downcase }
+ let_it_be(:record_3) { create(:"#{klass}") }
+ let_it_be(:record_4) { create(:"#{klass}") }
+
+ context 'when use_includes: true' do
+ it 'includes route information when loading records' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ described_class.where_full_path_in([record.full_path, record_2.full_path], use_includes: true)
+ .map(&:route)
+ end
+
+ expect do
+ described_class.where_full_path_in(
+ [
+ record.full_path,
+ record_2.full_path,
+ record_3.full_path,
+ record_4.full_path
+ ], use_includes: true)
+ .map(&:route)
+ end.to issue_same_number_of_queries_as(control_count)
+ end
end
- else
- it 'does not have cross-join' do
- expect(Gitlab::Database).not_to receive(:allow_cross_joins_across_databases)
- described_class.find_by_full_path(record.full_path)
+ context 'when use_includes: false' do
+ it 'does not include route information when loading records' do
+ control_count = ActiveRecord::QueryRecorder.new do
+ described_class.where_full_path_in([record.full_path, record_2.full_path], use_includes: false)
+ .map(&:route)
+ end
+
+ expect do
+ described_class.where_full_path_in(
+ [
+ record.full_path,
+ record_2.full_path,
+ record_3.full_path,
+ record_4.full_path
+ ], use_includes: false)
+ .map(&:route)
+ end.not_to issue_same_number_of_queries_as(control_count)
+ end
end
end
end
- it_behaves_like '.find_by_full_path', :aggregate_failures
+ it_behaves_like '.where_full_path_in', :aggregate_failures
+
+ context 'when the `optimize_where_full_path_in` feature flag is turned OFF' do
+ before do
+ stub_feature_flags(optimize_where_full_path_in: false)
+ end
+
+ it_behaves_like '.where_full_path_in', :aggregate_failures
+ end
end
RSpec.shared_examples 'routable resource with parent' do
@@ -105,10 +181,12 @@ RSpec.describe Group, 'Routable', :with_clean_rails_cache, feature_category: :gr
it_behaves_like 'routable resource' do
let_it_be(:record) { group }
+ let_it_be(:record_2) { nested_group }
end
it_behaves_like 'routable resource with parent' do
let_it_be(:record) { nested_group }
+ let_it_be(:record_2) { group }
end
describe 'Validations' do
@@ -169,34 +247,6 @@ RSpec.describe Group, 'Routable', :with_clean_rails_cache, feature_category: :gr
expect(group.route.namespace).to eq(group)
end
- describe '.where_full_path_in' do
- context 'without any paths' do
- it 'returns an empty relation' do
- expect(described_class.where_full_path_in([])).to eq([])
- end
- end
-
- context 'without any valid paths' do
- it 'returns an empty relation' do
- expect(described_class.where_full_path_in(%w[unknown])).to eq([])
- end
- end
-
- context 'with valid paths' do
- it 'returns the projects matching the paths' do
- result = described_class.where_full_path_in([group.to_param, nested_group.to_param])
-
- expect(result).to contain_exactly(group, nested_group)
- end
-
- it 'returns projects regardless of the casing of paths' do
- result = described_class.where_full_path_in([group.to_param.upcase, nested_group.to_param.upcase])
-
- expect(result).to contain_exactly(group, nested_group)
- end
- end
- end
-
describe '#parent_loaded?' do
before do
group.parent = create(:group)
@@ -232,9 +282,11 @@ end
RSpec.describe Project, 'Routable', :with_clean_rails_cache, feature_category: :groups_and_projects do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, namespace: namespace) }
+ let_it_be(:project_2) { create(:project) }
it_behaves_like 'routable resource with parent' do
let_it_be(:record) { project }
+ let_it_be(:record_2) { project_2 }
end
it 'creates route with namespace referencing project namespace' do
@@ -252,6 +304,17 @@ RSpec.describe Project, 'Routable', :with_clean_rails_cache, feature_category: :
expect(record).to be_nil
end
end
+
+ describe '.where_full_path_in' do
+ it 'does not return records if the sources are different, but the IDs match' do
+ group = create(:group, id: 1992)
+ project = create(:project, id: 1992)
+
+ records = described_class.where(id: project.id).where_full_path_in([group.full_path])
+
+ expect(records).to be_empty
+ end
+ end
end
RSpec.describe Namespaces::ProjectNamespace, 'Routable', :with_clean_rails_cache, feature_category: :groups_and_projects do
diff --git a/spec/models/concerns/transitionable_spec.rb b/spec/models/concerns/transitionable_spec.rb
index b80d363ef78..c8cba1ae226 100644
--- a/spec/models/concerns/transitionable_spec.rb
+++ b/spec/models/concerns/transitionable_spec.rb
@@ -22,19 +22,16 @@ RSpec.describe Transitionable, feature_category: :code_review_workflow do
let(:object) { klass.new(transitioning) }
describe '#transitioning?' do
- where(:transitioning, :feature_flag, :result) do
- true | true | true
- false | false | false
- true | false | false
- false | true | false
+ context "when trasnitioning" do
+ let(:transitioning) { true }
+
+ it { expect(object.transitioning?).to eq(true) }
end
- with_them do
- before do
- stub_feature_flags(skip_validations_during_transitions: feature_flag)
- end
+ context "when not trasnitioning" do
+ let(:transitioning) { false }
- it { expect(object.transitioning?).to eq(result) }
+ it { expect(object.transitioning?).to eq(false) }
end
end
end
diff --git a/spec/models/concerns/triggerable_hooks_spec.rb b/spec/models/concerns/triggerable_hooks_spec.rb
index 28cda269458..c209d6476f3 100644
--- a/spec/models/concerns/triggerable_hooks_spec.rb
+++ b/spec/models/concerns/triggerable_hooks_spec.rb
@@ -10,6 +10,8 @@ RSpec.describe TriggerableHooks do
include TriggerableHooks # rubocop:disable RSpec/DescribedClass
triggerable_hooks [:push_hooks]
+ self.allow_legacy_sti_class = true
+
scope :executable, -> { all }
end
end
diff --git a/spec/models/concerns/vulnerability_finding_helpers_spec.rb b/spec/models/concerns/vulnerability_finding_helpers_spec.rb
deleted file mode 100644
index 023ecccb520..00000000000
--- a/spec/models/concerns/vulnerability_finding_helpers_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe VulnerabilityFindingHelpers do
- let(:cls) do
- Class.new do
- include VulnerabilityFindingHelpers
-
- attr_accessor :report_type
-
- def initialize(report_type)
- @report_type = report_type
- end
- end
- end
-
- describe '#requires_manual_resolution?' do
- it 'returns false if the finding does not require manual resolution' do
- expect(cls.new('sast').requires_manual_resolution?).to eq(false)
- end
-
- it 'returns true when the finding requires manual resolution' do
- expect(cls.new('secret_detection').requires_manual_resolution?).to eq(true)
- end
- end
-end