diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-24 00:09:27 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-24 00:09:27 +0300 |
commit | 17bb9dd270c78fad45851c6cc6ec6e6fdb3d23bf (patch) | |
tree | aa7235893811d97055b3fc750d139a039ae95b0a /spec/lib | |
parent | abd2c6b32aabff4654b6be9cb98b59dcd3193fc4 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/lib')
15 files changed, 456 insertions, 163 deletions
diff --git a/spec/lib/gitlab/background_migration/backfill_project_import_level_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_import_level_spec.rb deleted file mode 100644 index 73661a3da1f..00000000000 --- a/spec/lib/gitlab/background_migration/backfill_project_import_level_spec.rb +++ /dev/null @@ -1,122 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -# rubocop:disable Layout/HashAlignment -RSpec.describe Gitlab::BackgroundMigration::BackfillProjectImportLevel do - let(:migration) do - described_class.new( - start_id: table(:namespaces).minimum(:id), - end_id: table(:namespaces).maximum(:id), - batch_table: :namespaces, - batch_column: :id, - sub_batch_size: 2, - pause_ms: 0, - connection: ApplicationRecord.connection - ) - end - # rubocop:enable Layout/HashAlignment - - let(:namespaces_table) { table(:namespaces) } - let(:namespace_settings_table) { table(:namespace_settings) } - - let!(:user_namespace) do - namespaces_table.create!( - name: 'user_namespace', - path: 'user_namespace', - type: 'User', - project_creation_level: 100 - ) - end - - let!(:group_namespace_nil) do - namespaces_table.create!( - name: 'group_namespace_nil', - path: 'group_namespace_nil', - type: 'Group', - project_creation_level: nil - ) - end - - let!(:group_namespace_0) do - namespaces_table.create!( - name: 'group_namespace_0', - path: 'group_namespace_0', - type: 'Group', - project_creation_level: 0 - ) - end - - let!(:group_namespace_1) do - namespaces_table.create!( - name: 'group_namespace_1', - path: 'group_namespace_1', - type: 'Group', - project_creation_level: 1 - ) - end - - let!(:group_namespace_2) do - namespaces_table.create!( - name: 'group_namespace_2', - path: 'group_namespace_2', - type: 'Group', - project_creation_level: 2 - ) - end - - let!(:group_namespace_9999) do - namespaces_table.create!( - name: 'group_namespace_9999', - path: 'group_namespace_9999', - type: 'Group', - project_creation_level: 9999 - ) - end - - subject(:perform_migration) { migration.perform } - - before do - namespace_settings_table.create!(namespace_id: user_namespace.id) - namespace_settings_table.create!(namespace_id: group_namespace_nil.id) - namespace_settings_table.create!(namespace_id: group_namespace_0.id) - namespace_settings_table.create!(namespace_id: group_namespace_1.id) - namespace_settings_table.create!(namespace_id: group_namespace_2.id) - namespace_settings_table.create!(namespace_id: group_namespace_9999.id) - end - - describe 'Groups' do - using RSpec::Parameterized::TableSyntax - - where(:namespace_id, :prev_level, :new_level) do - lazy { group_namespace_0.id } | ::Gitlab::Access::OWNER | ::Gitlab::Access::NO_ACCESS - lazy { group_namespace_1.id } | ::Gitlab::Access::OWNER | ::Gitlab::Access::MAINTAINER - lazy { group_namespace_2.id } | ::Gitlab::Access::OWNER | ::Gitlab::Access::DEVELOPER - end - - with_them do - it 'backfills the correct project_import_level of Group namespaces' do - expect { perform_migration } - .to change { namespace_settings_table.find_by(namespace_id: namespace_id).project_import_level } - .from(prev_level).to(new_level) - end - end - - it 'does not update `User` namespaces or values outside range' do - expect { perform_migration } - .not_to change { namespace_settings_table.find_by(namespace_id: user_namespace.id).project_import_level } - - expect { perform_migration } - .not_to change { namespace_settings_table.find_by(namespace_id: group_namespace_9999.id).project_import_level } - end - - it 'maintains default import_level if creation_level is nil' do - project_import_level = namespace_settings_table.find_by(namespace_id: group_namespace_nil.id).project_import_level - - expect { perform_migration } - .not_to change { project_import_level } - - expect(project_import_level).to eq(::Gitlab::Access::OWNER) - end - end -end diff --git a/spec/lib/gitlab/ci/config/external/file/base_spec.rb b/spec/lib/gitlab/ci/config/external/file/base_spec.rb index bcfab620bd9..6100974bbe8 100644 --- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb +++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb @@ -145,7 +145,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base, feature_category: :pipe it 'surfaces interpolation errors' do expect(valid?).to be_falsy expect(file.errors) - .to include('`some-location.yml`: interpolation interrupted by errors, unknown interpolation key: `abcd`') + .to include('`some-location.yml`: unknown interpolation key: `abcd`') end end diff --git a/spec/lib/gitlab/ci/config/interpolation/text_interpolator_spec.rb b/spec/lib/gitlab/ci/config/interpolation/text_interpolator_spec.rb index 8655d3fb0b7..2e59b6982dd 100644 --- a/spec/lib/gitlab/ci/config/interpolation/text_interpolator_spec.rb +++ b/spec/lib/gitlab/ci/config/interpolation/text_interpolator_spec.rb @@ -34,17 +34,6 @@ RSpec.describe Gitlab::Ci::Config::Interpolation::TextInterpolator, feature_cate end end - context 'when the header has an error while being parsed' do - let(:header) { ::Gitlab::Config::Loader::Yaml.new('_!@malformedyaml:&') } - - it 'surfaces the error' do - interpolator.interpolate! - - expect(interpolator).not_to be_valid - expect(interpolator.error_message).to eq('Invalid configuration format') - end - end - context 'when spec header is missing but inputs are specified' do let(:documents) { ::Gitlab::Ci::Config::Yaml::Documents.new([content]) } diff --git a/spec/lib/gitlab/ci/config/yaml/documents_spec.rb b/spec/lib/gitlab/ci/config/yaml/documents_spec.rb index babdea6623b..424fa4858a4 100644 --- a/spec/lib/gitlab/ci/config/yaml/documents_spec.rb +++ b/spec/lib/gitlab/ci/config/yaml/documents_spec.rb @@ -5,24 +5,6 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Config::Yaml::Documents, feature_category: :pipeline_composition do let(:documents) { described_class.new(yaml_documents) } - describe '#valid?' do - context 'when there are no errors' do - let(:yaml_documents) { [::Gitlab::Config::Loader::Yaml.new('job:')] } - - it 'returns true' do - expect(documents).to be_valid - end - end - - context 'when there are errors' do - let(:yaml_documents) { [::Gitlab::Config::Loader::Yaml.new('_!@malformedyaml:&')] } - - it 'returns false' do - expect(documents).not_to be_valid - end - end - end - describe '#header' do context 'when there are at least 2 documents and the first document has a `spec` keyword' do let(:yaml_documents) { [::Gitlab::Config::Loader::Yaml.new('spec:'), ::Gitlab::Config::Loader::Yaml.new('job:')] } diff --git a/spec/lib/gitlab/ci/config/yaml/loader_spec.rb b/spec/lib/gitlab/ci/config/yaml/loader_spec.rb index 684da1df43b..045cdf37037 100644 --- a/spec/lib/gitlab/ci/config/yaml/loader_spec.rb +++ b/spec/lib/gitlab/ci/config/yaml/loader_spec.rb @@ -7,6 +7,7 @@ RSpec.describe ::Gitlab::Ci::Config::Yaml::Loader, feature_category: :pipeline_c let_it_be(:project) { create(:project) } let(:inputs) { { test_input: 'hello test' } } + let(:variables) { [] } let(:yaml) do <<~YAML @@ -21,7 +22,7 @@ RSpec.describe ::Gitlab::Ci::Config::Yaml::Loader, feature_category: :pipeline_c YAML end - subject(:result) { described_class.new(yaml, inputs: inputs).load } + subject(:result) { described_class.new(yaml, inputs: inputs, variables: variables).load } it 'loads and interpolates CI config YAML' do expected_config = { test_job: { script: ['echo "hello test"'] } } @@ -57,6 +58,240 @@ RSpec.describe ::Gitlab::Ci::Config::Yaml::Loader, feature_category: :pipeline_c expect(result.error).to eq('`test_input` input: required value has not been provided') end end + + context 'when interpolating into a YAML key' do + let(:yaml) do + <<~YAML + --- + spec: + inputs: + test_input: + --- + "$[[ inputs.test_input ]]_job": + script: + - echo "test" + YAML + end + + it 'loads and interpolates CI config YAML' do + expected_config = { 'hello test_job': { script: ['echo "test"'] } } + + expect(result).to be_valid + expect(result).to be_interpolated + expect(result.content).to eq(expected_config) + end + end + + context 'when interpolating values of different types' do + let(:inputs) do + { + test_boolean: true, + test_number: 8, + test_string: 'test' + } + end + + let(:yaml) do + <<~YAML + --- + spec: + inputs: + test_string: + type: string + test_boolean: + type: boolean + test_number: + type: number + --- + "$[[ inputs.test_string ]]_job": + allow_failure: $[[ inputs.test_boolean ]] + parallel: $[[ inputs.test_number ]] + YAML + end + + it 'loads and interpolates CI config YAML' do + expected_config = { test_job: { allow_failure: true, parallel: 8 } } + + expect(result).to be_valid + expect(result).to be_interpolated + expect(result.content).to eq(expected_config) + end + end + + context 'when interpolating and expanding variables' do + let(:inputs) { { test_input: '$TEST_VAR' } } + + let(:variables) do + Gitlab::Ci::Variables::Collection.new([ + { key: 'TEST_VAR', value: 'test variable', masked: false } + ]) + end + + let(:yaml) do + <<~YAML + --- + spec: + inputs: + test_input: + --- + "test_job": + script: + - echo "$[[ inputs.test_input | expand_vars ]]" + YAML + end + + it 'loads and interpolates CI config YAML' do + expected_config = { test_job: { script: ['echo "test variable"'] } } + + expect(result).to be_valid + expect(result).to be_interpolated + expect(result.content).to eq(expected_config) + end + end + + context 'when using !reference' do + let(:yaml) do + <<~YAML + --- + spec: + inputs: + test_input: + job_name: + default: .example_ref + --- + .example_ref: + script: + - echo "$[[ inputs.test_input ]]" + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + + build_job: + script: echo "build" + rules: + - !reference ["$[[ inputs.job_name ]]", "rules"] + + test_job: + script: + - !reference [.example_ref, script] + YAML + end + + it 'loads and interpolates CI config YAML' do + expect(result).to be_valid + expect(result).to be_interpolated + expect(result.content).to include('.example_ref': { + rules: [{ if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' }], + script: ['echo "hello test"'] + }) + expect(result.content.dig(:build_job, :rules).first.data[:seq]).to eq(['.example_ref', 'rules']) + expect(result.content).to include( + test_job: { script: [an_instance_of(::Gitlab::Ci::Config::Yaml::Tags::Reference)] } + ) + end + end + + context 'when there are too many interpolation blocks' do + let(:inputs) { { first_input: 'first', second_input: 'second' } } + + let(:yaml) do + <<~YAML + --- + spec: + inputs: + first_input: + second_input: + --- + test_job: + script: + - echo "$[[ inputs.first_input ]]" + - echo "$[[ inputs.second_input ]]" + YAML + end + + it 'returns an error result' do + stub_const('::Gitlab::Ci::Config::Interpolation::TextTemplate::MAX_BLOCKS', 1) + + expect(result).not_to be_valid + expect(result.error).to eq('too many interpolation blocks') + end + end + + context 'when a block is invalid' do + let(:yaml) do + <<~YAML + --- + spec: + inputs: + test_input: + --- + test_job: + script: + - echo "$[[ inputs.test_input | expand_vars | truncate(0,1) ]]" + YAML + end + + it 'returns an error result' do + stub_const('::Gitlab::Ci::Config::Interpolation::Block::MAX_FUNCTIONS', 1) + + expect(result).not_to be_valid + expect(result.error).to eq('too many functions in interpolation block') + end + end + + context 'when the YAML file is too large' do + it 'returns an error result' do + stub_application_setting(ci_max_total_yaml_size_bytes: 1) + + expect(result).not_to be_valid + expect(result.error).to eq('config too large') + end + end + + context 'when given an empty YAML file' do + let(:inputs) { {} } + let(:yaml) { '' } + + it 'returns an error result' do + expect(result).not_to be_valid + expect(result.error).to eq('Invalid configuration format') + end + end + + context 'when ci_text_interpolation is disabled' do + before do + stub_feature_flags(ci_text_interpolation: false) + end + + it 'loads and interpolates CI config YAML' do + expected_config = { test_job: { script: ['echo "hello test"'] } } + + expect(result).to be_valid + expect(result).to be_interpolated + expect(result.content).to eq(expected_config) + end + + context 'when hash interpolation fails' do + let(:yaml) do + <<~YAML + --- + spec: + inputs: + test_input: + --- + test_job: + script: + - echo "$[[ inputs.test_input | expand_vars | truncate(0,1) ]]" + YAML + end + + it 'returns an error result' do + stub_const('::Gitlab::Ci::Config::Interpolation::Block::MAX_FUNCTIONS', 1) + + expect(result).not_to be_valid + expect(result.error).to eq('interpolation interrupted by errors, too many functions in interpolation block') + end + end + end end describe '#load_uninterpolated_yaml' do diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb index 4017076d29f..967cd1693a9 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb @@ -56,6 +56,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External, feature_category let(:validation_service_url) { 'https://validation-service.external/' } before do + stub_feature_flags(external_pipeline_validation_migration: false) stub_env('EXTERNAL_VALIDATION_SERVICE_URL', validation_service_url) allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('correlation-id') end @@ -84,6 +85,42 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External, feature_category end end + context 'when :external_pipeline_validation_migration feature flag is enabled' do + let(:migrated_validation_service_url) { 'https://runway.validation-service.external/' } + + before do + stub_feature_flags(external_pipeline_validation_migration: project) + end + + context 'when EXTERNAL_VALIDATION_SERVICE_RUNWAY_URL is NOT present' do + before do + stub_env('EXTERNAL_VALIDATION_SERVICE_RUNWAY_URL', nil) + end + + it 'fallbacks to existing validation service URL' do + expect(::Gitlab::HTTP).to receive(:post) do |url, _params| + expect(url).to eq(validation_service_url) + end + + perform! + end + end + + context 'when EXTERNAL_VALIDATION_SERVICE_RUNWAY_URL is present' do + before do + stub_env('EXTERNAL_VALIDATION_SERVICE_RUNWAY_URL', migrated_validation_service_url) + end + + it 'uses migrated validation service URL' do + expect(::Gitlab::HTTP).to receive(:post) do |url, _params| + expect(url).to eq(migrated_validation_service_url) + end + + perform! + end + end + end + it 'respects the defined payload schema' do expect(::Gitlab::HTTP).to receive(:post) do |_url, params| expect(params[:body]).to match_schema('/external_validation') diff --git a/spec/lib/gitlab/database/query_analyzer_spec.rb b/spec/lib/gitlab/database/query_analyzer_spec.rb index 0b849063562..20599bb89b6 100644 --- a/spec/lib/gitlab/database/query_analyzer_spec.rb +++ b/spec/lib/gitlab/database/query_analyzer_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Gitlab::Database::QueryAnalyzer, query_analyzers: false do + using RSpec::Parameterized::TableSyntax + let(:analyzer) { double(:query_analyzer) } let(:user_analyzer) { double(:user_query_analyzer) } let(:disabled_analyzer) { double(:disabled_query_analyzer) } @@ -181,12 +183,34 @@ RSpec.describe Gitlab::Database::QueryAnalyzer, query_analyzers: false do expect { process_sql("SELECT 1 FROM projects") }.not_to raise_error end - def process_sql(sql) + def process_sql(sql, event_name = 'load') described_class.instance.within do ApplicationRecord.load_balancer.read_write do |connection| - described_class.instance.send(:process_sql, sql, connection) + described_class.instance.send(:process_sql, sql, connection, event_name) end end end end + + describe '#normalize_event_name' do + where(:event, :parsed_event) do + 'Project Load' | 'load' + 'Namespaces::UserNamespace Create' | 'create' + 'Project Update' | 'update' + 'Project Destroy' | 'destroy' + 'Project Pluck' | 'pluck' + 'Project Insert' | 'insert' + 'Project Delete All' | 'delete_all' + 'Project Exists?' | 'exists?' + nil | '' + 'TRANSACTION' | 'transaction' + 'SCHEMA' | 'schema' + end + + with_them do + it 'parses event name correctly' do + expect(described_class.instance.send(:normalize_event_name, event)).to eq(parsed_event) + end + end + end end diff --git a/spec/lib/gitlab/database/query_analyzers/ci/partitioning_id_analyzer_spec.rb b/spec/lib/gitlab/database/query_analyzers/ci/partitioning_id_analyzer_spec.rb index 0fe19041b6d..0c1c694a3e3 100644 --- a/spec/lib/gitlab/database/query_analyzers/ci/partitioning_id_analyzer_spec.rb +++ b/spec/lib/gitlab/database/query_analyzers/ci/partitioning_id_analyzer_spec.rb @@ -115,7 +115,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::Ci::PartitioningIdAnalyzer, que def process_sql(model, sql) Gitlab::Database::QueryAnalyzer.instance.within do # Skip load balancer and retrieve connection assigned to model - Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection) + Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection, 'load') end end end diff --git a/spec/lib/gitlab/database/query_analyzers/ci/partitioning_routing_analyzer_spec.rb b/spec/lib/gitlab/database/query_analyzers/ci/partitioning_routing_analyzer_spec.rb index 1f86c2ccbb0..8b053fa0291 100644 --- a/spec/lib/gitlab/database/query_analyzers/ci/partitioning_routing_analyzer_spec.rb +++ b/spec/lib/gitlab/database/query_analyzers/ci/partitioning_routing_analyzer_spec.rb @@ -64,7 +64,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::Ci::PartitioningRoutingAnalyzer def process_sql(model, sql) Gitlab::Database::QueryAnalyzer.instance.within do # Skip load balancer and retrieve connection assigned to model - Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection) + Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection, 'load') end end end diff --git a/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb index 1909e134e66..fb00fbe27ba 100644 --- a/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb +++ b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb @@ -99,7 +99,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::GitlabSchemasMetrics, query_ana def process_sql(model, sql) Gitlab::Database::QueryAnalyzer.instance.within do # Skip load balancer and retrieve connection assigned to model - Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection) + Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection, 'load') end end end diff --git a/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb index 0664508fa8d..6a36db1870a 100644 --- a/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb +++ b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb @@ -97,7 +97,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection def process_sql(model, sql) Gitlab::Database::QueryAnalyzer.instance.within([analyzer]) do # Skip load balancer and retrieve connection assigned to model - Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection) + Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection, 'load') end end end diff --git a/spec/lib/gitlab/database/query_analyzers/log_large_in_lists_spec.rb b/spec/lib/gitlab/database/query_analyzers/log_large_in_lists_spec.rb new file mode 100644 index 00000000000..5646c3ff3b6 --- /dev/null +++ b/spec/lib/gitlab/database/query_analyzers/log_large_in_lists_spec.rb @@ -0,0 +1,148 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::QueryAnalyzers::LogLargeInLists, query_analyzers: false, feature_category: :database do + let(:analyzer) { described_class } + let(:fixture) { fixture_file("gitlab/database/query_analyzers/#{file}") } + let(:sql) { fixture.gsub('%IN_LIST%', arguments) } + + # Reduce the in list size to 5 to help with testing + # Reduce the min query size to 50 to help with testing + before do + stub_const("#{described_class}::IN_SIZE_LIMIT", 5) + stub_const("#{described_class}::MIN_QUERY_SIZE", 50) + allow(analyzer).to receive(:backtrace).and_return([]) + allow(analyzer).to receive(:suppressed?).and_return(true) # bypass suppressed? method to avoid false positives + end + + after do + # Clears analyzers list after each test to reload the state of `enabled?` method + Thread.current[:query_analyzer_enabled_analyzers] = [] + end + + context 'when feature flag is enabled' do + before do + stub_feature_flags(log_large_in_list_queries: true) + Gitlab::Database::QueryAnalyzer.instance.begin!([analyzer]) + end + + context 'when conditions are satisfied for logging' do + where(:file, :arguments, :result, :event_name) do + [ + [ + 'small_query_with_in_list.txt', + '1, 2, 3, 4, 5, 6', + { message: 'large_in_list_found', matches: 1, in_list_size: "6", stacktrace: [], event_name: 'load' }, + 'load' + ], + [ + 'small_query_with_in_list.txt', + '1,2,3,4,5,6', + { message: 'large_in_list_found', matches: 1, in_list_size: "6", stacktrace: [], event_name: 'pluck' }, + 'pluck' + ], + [ + 'small_query_with_in_list.txt', + 'SELECT id FROM projects where id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)', + { message: 'large_in_list_found', matches: 1, in_list_size: "10", stacktrace: [], event_name: 'load' }, + 'load' + ], + [ + 'large_query_with_in_list.txt', + '1,2,3,4,5,6', + { message: 'large_in_list_found', matches: 1, in_list_size: "6", stacktrace: [], event_name: 'load' }, + 'load' + ], + [ + 'large_query_with_in_list.txt', + '1, 2, 3, 4, 5, 6', + { message: 'large_in_list_found', matches: 1, in_list_size: "6", stacktrace: [], event_name: 'pluck' }, + 'pluck' + ], + [ + 'large_query_with_in_list.txt', + 'SELECT id FROM projects where id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)', + { message: 'large_in_list_found', matches: 1, in_list_size: "10", stacktrace: [], event_name: 'load' }, + 'load' + ] + ] + end + + with_them do + it 'logs all the occurrences' do + expect(Gitlab::AppLogger).to receive(:warn).with(result) + + process_sql(sql, event_name) + end + end + end + + context 'when conditions are not satisfied for logging' do + where(:file, :arguments, :event_name) do + [ + ['small_query_with_in_list.txt', '1, 2, 3, 4, 5', 'load'], + ['small_query_with_in_list.txt', '$1, $2, $3, $4, $5', 'load'], + ['small_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN (1, 2, 3, 4, 5)', 'load'], + ['small_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN (SELECT id FROM namespaces)', 'load'], + ['small_query_with_in_list.txt', '1, 2, 3, 4, 5', 'schema'], + ['large_query_with_in_list.txt', '1, 2, 3, 4, 5', 'load'], + ['large_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN (1, 2, 3, 4, 5)', 'load'], + ['large_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN (SELECT id FROM namespaces)', 'load'], + ['large_query_with_in_list.txt', '1, 2, 3, 4, 5', 'schema'], + ['small_query_without_in_list.txt', '', 'load'], + ['small_query_without_in_list.txt', '', 'schema'] + ] + end + + with_them do + it 'skips logging the occurrences' do + expect(Gitlab::AppLogger).not_to receive(:warn) + + process_sql(sql, event_name) + end + end + end + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(log_large_in_list_queries: false) + Gitlab::Database::QueryAnalyzer.instance.begin!([analyzer]) + end + + where(:file, :arguments, :event_name) do + [ + ['small_query_with_in_list.txt', '1, 2, 3, 4, 5, 6', 'load'], + ['small_query_with_in_list.txt', '$1, $2, $3, $4, $5, $6', 'load'], + ['small_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN (1, 2, 3, 4, 5, 6)', 'load'], + ['small_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN (1, 2, 3, 4, 5, 6)', 'load'], + ['small_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN (SELECT id FROM namespaces)', 'load'], + ['small_query_with_in_list.txt', '1, 2, 3, 4, 5, 6', 'schema'], + ['large_query_with_in_list.txt', '1, 2, 3, 4, 5, 6', 'load'], + ['large_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8)', 'load'], + ['large_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN ($1, $2, $3, $4, $5, $6, $7)', 'load'], + ['large_query_with_in_list.txt', 'SELECT id FROM projects WHERE id IN (SELECT id FROM namespaces)', 'load'], + ['large_query_with_in_list.txt', '1, 2, 3, 4, 5, 6', 'schema'], + ['small_query_without_in_list.txt', '', 'load'], + ['small_query_without_in_list.txt', '', 'schema'] + ] + end + + with_them do + it 'skips logging the occurrences' do + expect(Gitlab::AppLogger).not_to receive(:warn) + + process_sql(sql, event_name) + end + end + end + + private + + def process_sql(sql, event_name) + Gitlab::Database::QueryAnalyzer.instance.within do + Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, ActiveRecord::Base.connection, event_name) + end + end +end diff --git a/spec/lib/gitlab/database/query_analyzers/prevent_set_operator_mismatch_spec.rb b/spec/lib/gitlab/database/query_analyzers/prevent_set_operator_mismatch_spec.rb index 7fcdc59b691..00b16faab01 100644 --- a/spec/lib/gitlab/database/query_analyzers/prevent_set_operator_mismatch_spec.rb +++ b/spec/lib/gitlab/database/query_analyzers/prevent_set_operator_mismatch_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::PreventSetOperatorMismatch, que def process_sql(sql, model = ApplicationRecord) Gitlab::Database::QueryAnalyzer.instance.within([analyzer]) do # Skip load balancer and retrieve connection assigned to model - Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection) + Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection, 'load') end end diff --git a/spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb b/spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb index b90f60e0301..8054743c9a9 100644 --- a/spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb +++ b/spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb @@ -180,7 +180,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas, yield if block_given? # Skip load balancer and retrieve connection assigned to model - Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection) + Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection, 'load') end end end diff --git a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb index 4184c674823..844c3b54587 100644 --- a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb +++ b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb @@ -167,9 +167,9 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter, feature_category: :cloud describe '#start' do it "doesn't start running server" do - expect_any_instance_of(::WEBrick::HTTPServer).not_to receive(:start) + expect(::WEBrick::HTTPServer).not_to receive(:new) - expect { exporter.start }.not_to change { exporter.thread? } + exporter.start end end |