diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-27 15:13:41 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-27 15:13:41 +0000 |
commit | f50cb4f63fae6f02a0b8d9c3a95f29b3b4f79516 (patch) | |
tree | b6bcddc7b76089f91cc6143d141440cb63adfcbd /spec | |
parent | 0a559c913e4d333f339fcc03b89bfeade9ca0b93 (diff) | |
download | gitlab-ce-f50cb4f63fae6f02a0b8d9c3a95f29b3b4f79516.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
21 files changed, 514 insertions, 655 deletions
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index c4f93de5c23..e623c1ab940 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -923,7 +923,7 @@ RSpec.describe ApplicationController do describe '#set_current_context' do controller(described_class) do - feature_category :issue_tracking + feature_category :team_planning def index Gitlab::ApplicationContext.with_raw_context do |context| @@ -977,7 +977,7 @@ RSpec.describe ApplicationController do it 'sets the feature_category as defined in the controller' do get :index, format: :json - expect(json_response['meta.feature_category']).to eq('issue_tracking') + expect(json_response['meta.feature_category']).to eq('team_planning') end it 'assigns the context to a variable for logging' do diff --git a/spec/support_specs/database/gitlab_schema_spec.rb b/spec/lib/gitlab/database/gitlab_schema_spec.rb index 693071bfd60..02a9f6eef7b 100644 --- a/spec/support_specs/database/gitlab_schema_spec.rb +++ b/spec/lib/gitlab/database/gitlab_schema_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe Database::GitlabSchema do +RSpec.describe Gitlab::Database::GitlabSchema do it 'matches all the tables in the database', :aggregate_failures do # These tables do not need a gitlab_schema excluded_tables = %w(ar_internal_metadata schema_migrations) diff --git a/spec/lib/gitlab/database/load_balancing/configuration_spec.rb b/spec/lib/gitlab/database/load_balancing/configuration_spec.rb index 3e5249a3dea..435da8b6ae2 100644 --- a/spec/lib/gitlab/database/load_balancing/configuration_spec.rb +++ b/spec/lib/gitlab/database/load_balancing/configuration_spec.rb @@ -3,17 +3,12 @@ require 'spec_helper' RSpec.describe Gitlab::Database::LoadBalancing::Configuration do - let(:model) do - config = ActiveRecord::DatabaseConfigurations::HashConfig - .new('main', 'test', configuration_hash) - - double(:model, connection_db_config: config) - end + let(:configuration_hash) { {} } + let(:db_config) { ActiveRecord::DatabaseConfigurations::HashConfig.new('test', 'ci', configuration_hash) } + let(:model) { double(:model, connection_db_config: db_config) } describe '.for_model' do context 'when load balancing is not configured' do - let(:configuration_hash) { {} } - it 'uses the default settings' do config = described_class.for_model(model) @@ -105,6 +100,14 @@ RSpec.describe Gitlab::Database::LoadBalancing::Configuration do expect(config.pool_size).to eq(4) end end + + it 'calls reuse_primary_connection!' do + expect_next_instance_of(described_class) do |subject| + expect(subject).to receive(:reuse_primary_connection!).and_call_original + end + + described_class.for_model(model) + end end describe '#load_balancing_enabled?' do @@ -180,4 +183,58 @@ RSpec.describe Gitlab::Database::LoadBalancing::Configuration do end end end + + describe '#db_config_name' do + let(:config) { described_class.new(model) } + + subject { config.db_config_name } + + it 'returns connection name as symbol' do + is_expected.to eq(:ci) + end + end + + describe '#replica_db_config' do + let(:model) { double(:model, connection_db_config: db_config, connection_specification_name: 'Ci::CiDatabaseRecord') } + let(:config) { described_class.for_model(model) } + + it 'returns exactly db_config' do + expect(config.replica_db_config).to eq(db_config) + end + + context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=main' do + it 'does not change replica_db_config' do + stub_env('GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci', 'main') + + expect(config.replica_db_config).to eq(db_config) + end + end + end + + describe 'reuse_primary_connection!' do + let(:model) { double(:model, connection_db_config: db_config, connection_specification_name: 'Ci::CiDatabaseRecord') } + let(:config) { described_class.for_model(model) } + + context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_* not configured' do + it 'the primary connection uses default specification' do + expect(config.primary_connection_specification_name).to eq('Ci::CiDatabaseRecord') + end + end + + context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=main' do + it 'the primary connection uses main connection' do + stub_env('GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci', 'main') + + expect(config.primary_connection_specification_name).to eq('ActiveRecord::Base') + end + end + + context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=unknown' do + it 'raises exception' do + stub_env('GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci', 'unknown') + + expect { config.reuse_primary_connection! }.to raise_error /Invalid value for/ + end + end + end end diff --git a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb index 78d75d69e15..269a1f872dc 100644 --- a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb +++ b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb @@ -4,10 +4,11 @@ require 'spec_helper' RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do let(:conflict_error) { Class.new(RuntimeError) } - let(:db_host) { ActiveRecord::Base.connection_pool.db_config.host } + let(:model) { ActiveRecord::Base } + let(:db_host) { model.connection_pool.db_config.host } let(:config) do Gitlab::Database::LoadBalancing::Configuration - .new(ActiveRecord::Base, [db_host, db_host]) + .new(model, [db_host, db_host]) end let(:lb) { described_class.new(config) } @@ -452,4 +453,51 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do expect(lb.send(:get_write_location, double(select_all: []))).to be_nil end end + + describe 'primary connection re-use', :reestablished_active_record_base do + let(:model) { Ci::CiDatabaseRecord } + + before do + # fake additional Database + model.establish_connection( + ActiveRecord::DatabaseConfigurations::HashConfig.new(Rails.env, 'ci', ActiveRecord::Base.connection_db_config.configuration_hash) + ) + end + + describe '#read' do + it 'returns ci replica connection' do + expect { |b| lb.read(&b) }.to yield_with_args do |args| + expect(args.pool.db_config.name).to eq('ci_replica') + end + end + + context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=main' do + it 'returns ci replica connection' do + stub_env('GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci', 'main') + + expect { |b| lb.read(&b) }.to yield_with_args do |args| + expect(args.pool.db_config.name).to eq('ci_replica') + end + end + end + end + + describe '#read_write' do + it 'returns Ci::CiDatabaseRecord connection' do + expect { |b| lb.read_write(&b) }.to yield_with_args do |args| + expect(args.pool.db_config.name).to eq('ci') + end + end + + context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=main' do + it 'returns ActiveRecord::Base connection' do + stub_env('GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci', 'main') + + expect { |b| lb.read_write(&b) }.to yield_with_args do |args| + expect(args.pool.db_config.name).to eq('main') + end + end + end + end + end end diff --git a/spec/lib/gitlab/database/load_balancing/rack_middleware_spec.rb b/spec/lib/gitlab/database/load_balancing/rack_middleware_spec.rb index af7e2a4b167..b768d4ecea3 100644 --- a/spec/lib/gitlab/database/load_balancing/rack_middleware_spec.rb +++ b/spec/lib/gitlab/database/load_balancing/rack_middleware_spec.rb @@ -6,12 +6,12 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do let(:app) { double(:app) } let(:middleware) { described_class.new(app) } let(:warden_user) { double(:warden, user: double(:user, id: 42)) } - let(:single_sticking_object) { Set.new([[ActiveRecord::Base, :user, 42]]) } + let(:single_sticking_object) { Set.new([[ActiveRecord::Base.sticking, :user, 42]]) } let(:multiple_sticking_objects) do Set.new([ - [ActiveRecord::Base, :user, 42], - [ActiveRecord::Base, :runner, '123456789'], - [ActiveRecord::Base, :runner, '1234'] + [ActiveRecord::Base.sticking, :user, 42], + [ActiveRecord::Base.sticking, :runner, '123456789'], + [ActiveRecord::Base.sticking, :runner, '1234'] ]) end @@ -162,7 +162,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do it 'returns the warden user if present' do env = { 'warden' => warden_user } ids = Gitlab::Database::LoadBalancing.base_models.map do |model| - [model, :user, 42] + [model.sticking, :user, 42] end expect(middleware.sticking_namespaces(env)).to eq(ids) @@ -181,9 +181,9 @@ RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do env = { described_class::STICK_OBJECT => multiple_sticking_objects } expect(middleware.sticking_namespaces(env)).to eq([ - [ActiveRecord::Base, :user, 42], - [ActiveRecord::Base, :runner, '123456789'], - [ActiveRecord::Base, :runner, '1234'] + [ActiveRecord::Base.sticking, :user, 42], + [ActiveRecord::Base.sticking, :runner, '123456789'], + [ActiveRecord::Base.sticking, :runner, '1234'] ]) end end diff --git a/spec/lib/gitlab/database/load_balancing/sticking_spec.rb b/spec/lib/gitlab/database/load_balancing/sticking_spec.rb index 8ceda52ee85..9d0937f004c 100644 --- a/spec/lib/gitlab/database/load_balancing/sticking_spec.rb +++ b/spec/lib/gitlab/database/load_balancing/sticking_spec.rb @@ -22,7 +22,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do sticking.stick_or_unstick_request(env, :user, 42) expect(env[Gitlab::Database::LoadBalancing::RackMiddleware::STICK_OBJECT].to_a) - .to eq([[ActiveRecord::Base, :user, 42]]) + .to eq([[sticking, :user, 42]]) end it 'sticks or unsticks multiple objects and updates the Rack environment' do @@ -42,8 +42,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::Sticking, :redis do sticking.stick_or_unstick_request(env, :runner, '123456789') expect(env[Gitlab::Database::LoadBalancing::RackMiddleware::STICK_OBJECT].to_a).to eq([ - [ActiveRecord::Base, :user, 42], - [ActiveRecord::Base, :runner, '123456789'] + [sticking, :user, 42], + [sticking, :runner, '123456789'] ]) end end diff --git a/spec/lib/gitlab/database/partitioning_spec.rb b/spec/lib/gitlab/database/partitioning_spec.rb index 6841af8f69d..ca01667aed2 100644 --- a/spec/lib/gitlab/database/partitioning_spec.rb +++ b/spec/lib/gitlab/database/partitioning_spec.rb @@ -45,8 +45,13 @@ RSpec.describe Gitlab::Database::Partitioning do let(:model) { double('model') } it 'manages partitions for each registered model' do + registered_for_sync = described_class.__send__(:registered_for_sync) + + allow(described_class).to receive(:registered_for_sync) + .and_return(registered_for_sync) + expect(Gitlab::Database::EachDatabase).to receive(:each_model_connection) - .with(described_class.registered_models) + .with(registered_for_sync) .and_yield(model) expect(partition_manager_class).to receive(:new).with(model).and_return(partition_manager) @@ -71,7 +76,7 @@ RSpec.describe Gitlab::Database::Partitioning do end expect(Gitlab::Database::EachDatabase).to receive(:each_model_connection) - .with(described_class.registered_models) + .with(described_class.__send__(:registered_models)) .and_yield(model1) .and_yield(model2) @@ -123,7 +128,7 @@ RSpec.describe Gitlab::Database::Partitioning do context 'ensure that the registered models have partitioning strategy' do it 'fails when partitioning_strategy is not specified for the model' do - expect(described_class.registered_models).to all(respond_to(:partitioning_strategy)) + expect(described_class.__send__(:registered_models)).to all(respond_to(:partitioning_strategy)) end end end diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb index c4da89e5f5c..982c0d911bc 100644 --- a/spec/lib/gitlab/etag_caching/middleware_spec.rb +++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb @@ -174,7 +174,7 @@ RSpec.describe Gitlab::EtagCaching::Middleware, :clean_gitlab_redis_shared_state it "pushes route's feature category to the context" do expect(Gitlab::ApplicationContext).to receive(:push).with( - feature_category: 'issue_tracking' + feature_category: 'team_planning' ) _, _, _ = middleware.call(build_request(path, if_none_match)) diff --git a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb index 1145bda3570..3396de9b12c 100644 --- a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb +++ b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb @@ -116,14 +116,14 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do context 'application context' do context 'when a context is present' do before do - ::Gitlab::ApplicationContext.push(feature_category: 'issue_tracking', caller_id: 'IssuesController#show') + ::Gitlab::ApplicationContext.push(feature_category: 'team_planning', caller_id: 'IssuesController#show') end it 'adds the feature category to the labels for required metrics' do - expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'issue_tracking') + expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'team_planning') expect(described_class).not_to receive(:http_health_requests_total) expect(Gitlab::Metrics::RailsSlis.request_apdex) - .to receive(:increment).with(labels: { feature_category: 'issue_tracking', endpoint_id: 'IssuesController#show', request_urgency: :default }, success: true) + .to receive(:increment).with(labels: { feature_category: 'team_planning', endpoint_id: 'IssuesController#show', request_urgency: :default }, success: true) subject.call(env) end @@ -141,12 +141,12 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do context 'when application raises an exception when the feature category context is present' do before do - ::Gitlab::ApplicationContext.push(feature_category: 'issue_tracking') + ::Gitlab::ApplicationContext.push(feature_category: 'team_planning') allow(app).to receive(:call).and_raise(StandardError) end it 'adds the feature category to the labels for http_requests_total' do - expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: 'undefined', feature_category: 'issue_tracking') + expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: 'undefined', feature_category: 'team_planning') expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex) expect { subject.call(env) }.to raise_error(StandardError) diff --git a/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb b/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb index 92a11c83a4a..b9a13fd697e 100644 --- a/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb @@ -11,7 +11,7 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do include ApplicationWorker - feature_category :issue_tracking + feature_category :team_planning def self.job_for_args(args) jobs.find { |job| job['args'] == args } @@ -78,8 +78,8 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do job1 = TestWithContextWorker.job_for_args(['job1', 1, 2, 3]) job2 = TestWithContextWorker.job_for_args(['job2', 1, 2, 3]) - expect(job1['meta.feature_category']).to eq('issue_tracking') - expect(job2['meta.feature_category']).to eq('issue_tracking') + expect(job1['meta.feature_category']).to eq('team_planning') + expect(job2['meta.feature_category']).to eq('team_planning') end it 'takes the feature category from the caller if the worker is not owned' do @@ -116,8 +116,8 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do job1 = TestWithContextWorker.job_for_args(['job1', 1, 2, 3]) job2 = TestWithContextWorker.job_for_args(['job2', 1, 2, 3]) - expect(job1['meta.feature_category']).to eq('issue_tracking') - expect(job2['meta.feature_category']).to eq('issue_tracking') + expect(job1['meta.feature_category']).to eq('team_planning') + expect(job2['meta.feature_category']).to eq('team_planning') end it 'takes the feature category from the caller if the worker is not owned' do diff --git a/spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb b/spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb new file mode 100644 index 00000000000..95c5be2fc30 --- /dev/null +++ b/spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb @@ -0,0 +1,168 @@ +# frozen_string_literal: true +require 'spec_helper' +require_migration!('schedule_remove_duplicate_vulnerabilities_findings3') + +RSpec.describe ScheduleRemoveDuplicateVulnerabilitiesFindings3, :migration do + let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') } + let(:users) { table(:users) } + let(:user) { create_user! } + let(:project) { table(:projects).create!(id: 14219619, namespace_id: namespace.id) } + let(:scanners) { table(:vulnerability_scanners) } + let!(:scanner1) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') } + let!(:scanner2) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') } + let!(:scanner3) { scanners.create!(project_id: project.id, external_id: 'test 3', name: 'test scanner 3') } + let!(:unrelated_scanner) { scanners.create!(project_id: project.id, external_id: 'unreleated_scanner', name: 'unrelated scanner') } + let(:vulnerabilities) { table(:vulnerabilities) } + let(:vulnerability_findings) { table(:vulnerability_occurrences) } + let(:vulnerability_identifiers) { table(:vulnerability_identifiers) } + let(:vulnerability_identifier) do + vulnerability_identifiers.create!( + id: 1244459, + project_id: project.id, + external_type: 'vulnerability-identifier', + external_id: 'vulnerability-identifier', + fingerprint: '0a203e8cd5260a1948edbedc76c7cb91ad6a2e45', + name: 'vulnerability identifier') + end + + let!(:vulnerability_for_first_duplicate) do + create_vulnerability!( + project_id: project.id, + author_id: user.id + ) + end + + let!(:first_finding_duplicate) do + create_finding!( + id: 5606961, + uuid: "bd95c085-71aa-51d7-9bb6-08ae669c262e", + vulnerability_id: vulnerability_for_first_duplicate.id, + report_type: 0, + location_fingerprint: '00049d5119c2cb3bfb3d1ee1f6e031fe925aed75', + primary_identifier_id: vulnerability_identifier.id, + scanner_id: scanner1.id, + project_id: project.id + ) + end + + let!(:vulnerability_for_second_duplicate) do + create_vulnerability!( + project_id: project.id, + author_id: user.id + ) + end + + let!(:second_finding_duplicate) do + create_finding!( + id: 8765432, + uuid: "5b714f58-1176-5b26-8fd5-e11dfcb031b5", + vulnerability_id: vulnerability_for_second_duplicate.id, + report_type: 0, + location_fingerprint: '00049d5119c2cb3bfb3d1ee1f6e031fe925aed75', + primary_identifier_id: vulnerability_identifier.id, + scanner_id: scanner2.id, + project_id: project.id + ) + end + + let!(:vulnerability_for_third_duplicate) do + create_vulnerability!( + project_id: project.id, + author_id: user.id + ) + end + + let!(:third_finding_duplicate) do + create_finding!( + id: 8832995, + uuid: "cfe435fa-b25b-5199-a56d-7b007cc9e2d4", + vulnerability_id: vulnerability_for_third_duplicate.id, + report_type: 0, + location_fingerprint: '00049d5119c2cb3bfb3d1ee1f6e031fe925aed75', + primary_identifier_id: vulnerability_identifier.id, + scanner_id: scanner3.id, + project_id: project.id + ) + end + + let!(:unrelated_finding) do + create_finding!( + id: 9999999, + uuid: "unreleated_finding", + vulnerability_id: nil, + report_type: 1, + location_fingerprint: 'random_location_fingerprint', + primary_identifier_id: vulnerability_identifier.id, + scanner_id: unrelated_scanner.id, + project_id: project.id + ) + end + + before do + stub_const("#{described_class}::BATCH_SIZE", 1) + end + + around do |example| + freeze_time { Sidekiq::Testing.fake! { example.run } } + end + + it 'schedules background migration' do + migrate! + + expect(BackgroundMigrationWorker.jobs.size).to eq(4) + expect(described_class::MIGRATION).to be_scheduled_migration(first_finding_duplicate.id, first_finding_duplicate.id) + expect(described_class::MIGRATION).to be_scheduled_migration(second_finding_duplicate.id, second_finding_duplicate.id) + expect(described_class::MIGRATION).to be_scheduled_migration(third_finding_duplicate.id, third_finding_duplicate.id) + expect(described_class::MIGRATION).to be_scheduled_migration(unrelated_finding.id, unrelated_finding.id) + end + + private + + def create_vulnerability!(project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0) + vulnerabilities.create!( + project_id: project_id, + author_id: author_id, + title: title, + severity: severity, + confidence: confidence, + report_type: report_type + ) + end + + # rubocop:disable Metrics/ParameterLists + def create_finding!( + id: nil, + vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:, + name: "test", severity: 7, confidence: 7, report_type: 0, + project_fingerprint: '123qweasdzxc', location_fingerprint: 'test', + metadata_version: 'test', raw_metadata: 'test', uuid: 'test') + vulnerability_findings.create!({ + id: id, + vulnerability_id: vulnerability_id, + project_id: project_id, + name: name, + severity: severity, + confidence: confidence, + report_type: report_type, + project_fingerprint: project_fingerprint, + scanner_id: scanner_id, + primary_identifier_id: vulnerability_identifier.id, + location_fingerprint: location_fingerprint, + metadata_version: metadata_version, + raw_metadata: raw_metadata, + uuid: uuid + }.compact) + end + # rubocop:enable Metrics/ParameterLists + + def create_user!(name: "Example User", email: "user@example.com", user_type: nil, created_at: Time.zone.now, confirmed_at: Time.zone.now) + users.create!( + name: name, + email: email, + username: name, + projects_limit: 0, + user_type: user_type, + confirmed_at: confirmed_at + ) + end +end diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb index 9ed00003ac1..67e0f98d147 100644 --- a/spec/models/chat_name_spec.rb +++ b/spec/models/chat_name_spec.rb @@ -43,4 +43,12 @@ RSpec.describe ChatName do expect(subject.last_used_at).to eq(time) end end + + it_behaves_like 'it has loose foreign keys' do + let(:factory_name) { :chat_name } + + before do + Ci::PipelineChatData # ensure that the referenced model is loaded + end + end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index c44f57ee137..1b679496437 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -5,6 +5,14 @@ require 'spec_helper' RSpec.describe Ci::Runner do it_behaves_like 'having unique enum values' + it_behaves_like 'it has loose foreign keys' do + let(:factory_name) { :ci_runner } + + before do + Clusters::Applications::Runner # ensure that the referenced model is loaded + end + end + describe 'groups association' do # Due to other assoctions such as projects this whole spec is allowed to # generate cross-database queries. So we have this temporary spec to diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 351d170a0a1..75e46bb7873 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -40,6 +40,30 @@ RSpec.describe Namespace do end end + shared_examples 'validations called by different namespace types' do |method| + using RSpec::Parameterized::TableSyntax + + where(:namespace_type, :call_validation) do + :namespace | true + :group | true + :user_namespace | true + :project_namespace | false + end + + with_them do + it 'conditionally runs given validation' do + namespace = build(namespace_type) + if call_validation + expect(namespace).to receive(method) + else + expect(namespace).not_to receive(method) + end + + namespace.valid? + end + end + end + describe 'validations' do it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_length_of(:name).is_at_most(255) } @@ -112,14 +136,20 @@ RSpec.describe Namespace do end end - it 'does not allow too deep nesting' do - ancestors = (1..21).to_a - group = build(:group) + describe '#nesting_level_allowed' do + context 'for a group' do + it 'does not allow too deep nesting' do + ancestors = (1..21).to_a + group = build(:group) - allow(group).to receive(:ancestors).and_return(ancestors) + allow(group).to receive(:ancestors).and_return(ancestors) - expect(group).not_to be_valid - expect(group.errors[:parent_id].first).to eq('has too deep level of nesting') + expect(group).not_to be_valid + expect(group.errors[:parent_id].first).to eq('has too deep level of nesting') + end + end + + it_behaves_like 'validations called by different namespace types', :nesting_level_allowed end describe 'reserved path validation' do @@ -1855,87 +1885,95 @@ RSpec.describe Namespace do end context 'with a parent' do - context 'when parent has shared runners disabled' do - let(:parent) { create(:group, :shared_runners_disabled) } - let(:group) { build(:group, shared_runners_enabled: true, parent_id: parent.id) } - - it 'is invalid' do - expect(group).to be_invalid - expect(group.errors[:shared_runners_enabled]).to include('cannot be enabled because parent group has shared Runners disabled') + context 'when namespace is a group' do + context 'when parent has shared runners disabled' do + let(:parent) { create(:group, :shared_runners_disabled) } + let(:group) { build(:group, shared_runners_enabled: true, parent_id: parent.id) } + + it 'is invalid' do + expect(group).to be_invalid + expect(group.errors[:shared_runners_enabled]).to include('cannot be enabled because parent group has shared Runners disabled') + end end - end - context 'when parent has shared runners disabled but allows override' do - let(:parent) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners) } - let(:group) { build(:group, shared_runners_enabled: true, parent_id: parent.id) } + context 'when parent has shared runners disabled but allows override' do + let(:parent) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners) } + let(:group) { build(:group, shared_runners_enabled: true, parent_id: parent.id) } - it 'is valid' do - expect(group).to be_valid + it 'is valid' do + expect(group).to be_valid + end end - end - context 'when parent has shared runners enabled' do - let(:parent) { create(:group, shared_runners_enabled: true) } - let(:group) { build(:group, shared_runners_enabled: true, parent_id: parent.id) } + context 'when parent has shared runners enabled' do + let(:parent) { create(:group, shared_runners_enabled: true) } + let(:group) { build(:group, shared_runners_enabled: true, parent_id: parent.id) } - it 'is valid' do - expect(group).to be_valid + it 'is valid' do + expect(group).to be_valid + end end end end + + it_behaves_like 'validations called by different namespace types', :changing_shared_runners_enabled_is_allowed end describe 'validation #changing_allow_descendants_override_disabled_shared_runners_is_allowed' do - context 'without a parent' do - context 'with shared runners disabled' do - let(:namespace) { build(:namespace, :allow_descendants_override_disabled_shared_runners, :shared_runners_disabled) } + context 'when namespace is a group' do + context 'without a parent' do + context 'with shared runners disabled' do + let(:namespace) { build(:group, :allow_descendants_override_disabled_shared_runners, :shared_runners_disabled) } - it 'is valid' do - expect(namespace).to be_valid + it 'is valid' do + expect(namespace).to be_valid + end end - end - context 'with shared runners enabled' do - let(:namespace) { create(:namespace) } + context 'with shared runners enabled' do + let(:namespace) { create(:namespace) } - it 'is invalid' do - namespace.allow_descendants_override_disabled_shared_runners = true + it 'is invalid' do + namespace.allow_descendants_override_disabled_shared_runners = true - expect(namespace).to be_invalid - expect(namespace.errors[:allow_descendants_override_disabled_shared_runners]).to include('cannot be changed if shared runners are enabled') + expect(namespace).to be_invalid + expect(namespace.errors[:allow_descendants_override_disabled_shared_runners]).to include('cannot be changed if shared runners are enabled') + end end end - end - context 'with a parent' do - context 'when parent does not allow shared runners' do - let(:parent) { create(:group, :shared_runners_disabled) } - let(:group) { build(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners, parent_id: parent.id) } + context 'with a parent' do + context 'when parent does not allow shared runners' do + let(:parent) { create(:group, :shared_runners_disabled) } + let(:group) { build(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners, parent_id: parent.id) } - it 'is invalid' do - expect(group).to be_invalid - expect(group.errors[:allow_descendants_override_disabled_shared_runners]).to include('cannot be enabled because parent group does not allow it') + it 'is invalid' do + expect(group).to be_invalid + expect(group.errors[:allow_descendants_override_disabled_shared_runners]).to include('cannot be enabled because parent group does not allow it') + end end - end - context 'when parent allows shared runners and setting to true' do - let(:parent) { create(:group, shared_runners_enabled: true) } - let(:group) { build(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners, parent_id: parent.id) } + context 'when parent allows shared runners and setting to true' do + let(:parent) { create(:group, shared_runners_enabled: true) } + let(:group) { build(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners, parent_id: parent.id) } - it 'is valid' do - expect(group).to be_valid + it 'is valid' do + expect(group).to be_valid + end end - end - context 'when parent allows shared runners and setting to false' do - let(:parent) { create(:group, shared_runners_enabled: true) } - let(:group) { build(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false, parent_id: parent.id) } + context 'when parent allows shared runners and setting to false' do + let(:parent) { create(:group, shared_runners_enabled: true) } + let(:group) { build(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false, parent_id: parent.id) } - it 'is valid' do - expect(group).to be_valid + it 'is valid' do + expect(group).to be_valid + end end end end + + it_behaves_like 'validations called by different namespace types', :changing_allow_descendants_override_disabled_shared_runners_is_allowed end describe '#root?' do diff --git a/spec/requests/api/api_spec.rb b/spec/requests/api/api_spec.rb index 95eb503c6bc..d57ecce3149 100644 --- a/spec/requests/api/api_spec.rb +++ b/spec/requests/api/api_spec.rb @@ -116,7 +116,7 @@ RSpec.describe API::API do 'meta.root_namespace' => project.namespace.full_path, 'meta.user' => user.username, 'meta.client_id' => a_string_matching(%r{\Auser/.+}), - 'meta.feature_category' => 'issue_tracking', + 'meta.feature_category' => 'team_planning', 'route' => '/api/:version/projects/:id/issues') end diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb index 90b03a480a8..cb9b6a072b1 100644 --- a/spec/requests/api/releases_spec.rb +++ b/spec/requests/api/releases_spec.rb @@ -42,6 +42,14 @@ RSpec.describe API::Releases do expect(response).to have_gitlab_http_status(:ok) end + it 'returns 200 HTTP status when using JOB-TOKEN auth' do + job = create(:ci_build, :running, project: project, user: maintainer) + + get api("/projects/#{project.id}/releases"), params: { job_token: job.token } + + expect(response).to have_gitlab_http_status(:ok) + end + it 'returns releases ordered by released_at' do get api("/projects/#{project.id}/releases", maintainer) @@ -316,6 +324,14 @@ RSpec.describe API::Releases do expect(response).to have_gitlab_http_status(:ok) end + it 'returns 200 HTTP status when using JOB-TOKEN auth' do + job = create(:ci_build, :running, project: project, user: maintainer) + + get api("/projects/#{project.id}/releases/v0.1"), params: { job_token: job.token } + + expect(response).to have_gitlab_http_status(:ok) + end + it 'returns a release entry' do get api("/projects/#{project.id}/releases/v0.1", maintainer) @@ -1008,6 +1024,14 @@ RSpec.describe API::Releases do expect(response).to have_gitlab_http_status(:ok) end + it 'accepts the request when using JOB-TOKEN auth' do + job = create(:ci_build, :running, project: project, user: maintainer) + + put api("/projects/#{project.id}/releases/v0.1"), params: params.merge(job_token: job.token) + + expect(response).to have_gitlab_http_status(:ok) + end + it 'updates the description' do put api("/projects/#{project.id}/releases/v0.1", maintainer), params: params @@ -1220,6 +1244,14 @@ RSpec.describe API::Releases do expect(response).to have_gitlab_http_status(:ok) end + it 'accepts the request when using JOB-TOKEN auth' do + job = create(:ci_build, :running, project: project, user: maintainer) + + delete api("/projects/#{project.id}/releases/v0.1"), params: { job_token: job.token } + + expect(response).to have_gitlab_http_status(:ok) + end + it 'destroys the release' do expect do delete api("/projects/#{project.id}/releases/v0.1", maintainer) diff --git a/spec/support/database/gitlab_schema.rb b/spec/support/database/gitlab_schema.rb deleted file mode 100644 index 22f997e70e1..00000000000 --- a/spec/support/database/gitlab_schema.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -# This module gathes information about table to schema mapping -# to understand table affinity -module Database - module GitlabSchema - def self.table_schemas(tables) - tables.map { |table| table_schema(table) }.to_set - end - - def self.table_schema(name) - # When undefined it's best to return a unique name so that we don't incorrectly assume that 2 undefined schemas belong on the same database - tables_to_schema[name] || :"undefined_#{name}" - end - - def self.tables_to_schema - @tables_to_schema ||= YAML.load_file(Rails.root.join('spec/support/database/gitlab_schemas.yml')) - end - end -end diff --git a/spec/support/database/gitlab_schemas.yml b/spec/support/database/gitlab_schemas.yml deleted file mode 100644 index d46f2803c93..00000000000 --- a/spec/support/database/gitlab_schemas.yml +++ /dev/null @@ -1,537 +0,0 @@ -abuse_reports: :gitlab_main -agent_group_authorizations: :gitlab_main -agent_project_authorizations: :gitlab_main -alert_management_alert_assignees: :gitlab_main -alert_management_alerts: :gitlab_main -alert_management_alert_user_mentions: :gitlab_main -alert_management_http_integrations: :gitlab_main -allowed_email_domains: :gitlab_main -analytics_cycle_analytics_group_stages: :gitlab_main -analytics_cycle_analytics_group_value_streams: :gitlab_main -analytics_cycle_analytics_issue_stage_events: :gitlab_main -analytics_cycle_analytics_merge_request_stage_events: :gitlab_main -analytics_cycle_analytics_project_stages: :gitlab_main -analytics_cycle_analytics_project_value_streams: :gitlab_main -analytics_cycle_analytics_stage_event_hashes: :gitlab_main -analytics_devops_adoption_segments: :gitlab_main -analytics_devops_adoption_snapshots: :gitlab_main -analytics_language_trend_repository_languages: :gitlab_main -analytics_usage_trends_measurements: :gitlab_main -appearances: :gitlab_main -application_settings: :gitlab_main -application_setting_terms: :gitlab_main -approval_merge_request_rules_approved_approvers: :gitlab_main -approval_merge_request_rules: :gitlab_main -approval_merge_request_rules_groups: :gitlab_main -approval_merge_request_rule_sources: :gitlab_main -approval_merge_request_rules_users: :gitlab_main -approval_project_rules: :gitlab_main -approval_project_rules_groups: :gitlab_main -approval_project_rules_protected_branches: :gitlab_main -approval_project_rules_users: :gitlab_main -approvals: :gitlab_main -approver_groups: :gitlab_main -approvers: :gitlab_main -atlassian_identities: :gitlab_main -audit_events_external_audit_event_destinations: :gitlab_main -audit_events: :gitlab_main -authentication_events: :gitlab_main -award_emoji: :gitlab_main -aws_roles: :gitlab_main -background_migration_jobs: :gitlab_main -badges: :gitlab_main -banned_users: :gitlab_main -batched_background_migration_jobs: :gitlab_main -batched_background_migrations: :gitlab_main -board_assignees: :gitlab_main -board_group_recent_visits: :gitlab_main -board_labels: :gitlab_main -board_project_recent_visits: :gitlab_main -boards_epic_board_labels: :gitlab_main -boards_epic_board_positions: :gitlab_main -boards_epic_board_recent_visits: :gitlab_main -boards_epic_boards: :gitlab_main -boards_epic_lists: :gitlab_main -boards_epic_list_user_preferences: :gitlab_main -boards_epic_user_preferences: :gitlab_main -boards: :gitlab_main -board_user_preferences: :gitlab_main -broadcast_messages: :gitlab_main -bulk_import_configurations: :gitlab_main -bulk_import_entities: :gitlab_main -bulk_import_exports: :gitlab_main -bulk_import_export_uploads: :gitlab_main -bulk_import_failures: :gitlab_main -bulk_imports: :gitlab_main -bulk_import_trackers: :gitlab_main -chat_names: :gitlab_main -chat_teams: :gitlab_main -ci_build_needs: :gitlab_ci -ci_build_pending_states: :gitlab_ci -ci_build_report_results: :gitlab_ci -ci_builds: :gitlab_ci -ci_builds_metadata: :gitlab_ci -ci_builds_runner_session: :gitlab_ci -ci_build_trace_chunks: :gitlab_ci -ci_build_trace_metadata: :gitlab_ci -ci_daily_build_group_report_results: :gitlab_ci -ci_deleted_objects: :gitlab_ci -ci_freeze_periods: :gitlab_ci -ci_group_variables: :gitlab_ci -ci_instance_variables: :gitlab_ci -ci_job_artifacts: :gitlab_ci -ci_job_token_project_scope_links: :gitlab_ci -ci_job_variables: :gitlab_ci -ci_minutes_additional_packs: :gitlab_ci -ci_namespace_monthly_usages: :gitlab_ci -ci_pending_builds: :gitlab_ci -ci_pipeline_artifacts: :gitlab_ci -ci_pipeline_chat_data: :gitlab_ci -ci_pipeline_messages: :gitlab_ci -ci_pipeline_schedules: :gitlab_ci -ci_pipeline_schedule_variables: :gitlab_ci -ci_pipelines_config: :gitlab_ci -ci_pipelines: :gitlab_ci -ci_pipeline_variables: :gitlab_ci -ci_platform_metrics: :gitlab_ci -ci_project_monthly_usages: :gitlab_ci -ci_refs: :gitlab_ci -ci_resource_groups: :gitlab_ci -ci_resources: :gitlab_ci -ci_runner_namespaces: :gitlab_ci -ci_runner_projects: :gitlab_ci -ci_runners: :gitlab_ci -ci_running_builds: :gitlab_ci -ci_sources_pipelines: :gitlab_ci -ci_sources_projects: :gitlab_ci -ci_stages: :gitlab_ci -ci_subscriptions_projects: :gitlab_ci -ci_trigger_requests: :gitlab_ci -ci_triggers: :gitlab_ci -ci_unit_test_failures: :gitlab_ci -ci_unit_tests: :gitlab_ci -ci_variables: :gitlab_ci -cluster_agents: :gitlab_main -cluster_agent_tokens: :gitlab_main -cluster_groups: :gitlab_main -cluster_platforms_kubernetes: :gitlab_main -cluster_projects: :gitlab_main -cluster_providers_aws: :gitlab_main -cluster_providers_gcp: :gitlab_main -clusters_applications_cert_managers: :gitlab_main -clusters_applications_cilium: :gitlab_main -clusters_applications_crossplane: :gitlab_main -clusters_applications_elastic_stacks: :gitlab_main -clusters_applications_helm: :gitlab_main -clusters_applications_ingress: :gitlab_main -clusters_applications_jupyter: :gitlab_main -clusters_applications_knative: :gitlab_main -clusters_applications_prometheus: :gitlab_main -clusters_applications_runners: :gitlab_main -clusters: :gitlab_main -clusters_integration_elasticstack: :gitlab_main -clusters_integration_prometheus: :gitlab_main -clusters_kubernetes_namespaces: :gitlab_main -commit_user_mentions: :gitlab_main -compliance_management_frameworks: :gitlab_main -container_expiration_policies: :gitlab_main -container_repositories: :gitlab_main -conversational_development_index_metrics: :gitlab_main -coverage_fuzzing_corpuses: :gitlab_main -csv_issue_imports: :gitlab_main -custom_emoji: :gitlab_main -customer_relations_contacts: :gitlab_main -customer_relations_organizations: :gitlab_main -dast_profile_schedules: :gitlab_main -dast_profiles: :gitlab_main -dast_profiles_pipelines: :gitlab_main -dast_scanner_profiles_builds: :gitlab_main -dast_scanner_profiles: :gitlab_main -dast_site_profiles_builds: :gitlab_main -dast_site_profile_secret_variables: :gitlab_main -dast_site_profiles: :gitlab_main -dast_site_profiles_pipelines: :gitlab_main -dast_sites: :gitlab_main -dast_site_tokens: :gitlab_main -dast_site_validations: :gitlab_main -dep_ci_build_trace_section_names: :gitlab_main -dep_ci_build_trace_sections: :gitlab_main -dependency_proxy_blobs: :gitlab_main -dependency_proxy_group_settings: :gitlab_main -dependency_proxy_image_ttl_group_policies: :gitlab_main -dependency_proxy_manifests: :gitlab_main -deploy_keys_projects: :gitlab_main -deployment_clusters: :gitlab_main -deployment_merge_requests: :gitlab_main -deployments: :gitlab_main -deploy_tokens: :gitlab_main -description_versions: :gitlab_main -design_management_designs: :gitlab_main -design_management_designs_versions: :gitlab_main -design_management_versions: :gitlab_main -design_user_mentions: :gitlab_main -detached_partitions: :gitlab_main -diff_note_positions: :gitlab_main -dora_daily_metrics: :gitlab_main -draft_notes: :gitlab_main -elastic_index_settings: :gitlab_main -elastic_reindexing_slices: :gitlab_main -elastic_reindexing_subtasks: :gitlab_main -elastic_reindexing_tasks: :gitlab_main -elasticsearch_indexed_namespaces: :gitlab_main -elasticsearch_indexed_projects: :gitlab_main -emails: :gitlab_main -environments: :gitlab_main -epic_issues: :gitlab_main -epic_metrics: :gitlab_main -epics: :gitlab_main -epic_user_mentions: :gitlab_main -error_tracking_client_keys: :gitlab_main -error_tracking_error_events: :gitlab_main -error_tracking_errors: :gitlab_main -events: :gitlab_main -evidences: :gitlab_main -experiments: :gitlab_main -experiment_subjects: :gitlab_main -experiment_users: :gitlab_main -external_approval_rules: :gitlab_main -external_approval_rules_protected_branches: :gitlab_main -external_pull_requests: :gitlab_main -external_status_checks: :gitlab_main -external_status_checks_protected_branches: :gitlab_main -feature_gates: :gitlab_main -features: :gitlab_main -fork_network_members: :gitlab_main -fork_networks: :gitlab_main -geo_cache_invalidation_events: :gitlab_main -geo_container_repository_updated_events: :gitlab_main -geo_event_log: :gitlab_main -geo_events: :gitlab_main -geo_hashed_storage_attachments_events: :gitlab_main -geo_hashed_storage_migrated_events: :gitlab_main -geo_job_artifact_deleted_events: :gitlab_main -geo_lfs_object_deleted_events: :gitlab_main -geo_node_namespace_links: :gitlab_main -geo_nodes: :gitlab_main -geo_node_statuses: :gitlab_main -geo_repositories_changed_events: :gitlab_main -geo_repository_created_events: :gitlab_main -geo_repository_deleted_events: :gitlab_main -geo_repository_renamed_events: :gitlab_main -geo_repository_updated_events: :gitlab_main -geo_reset_checksum_events: :gitlab_main -geo_upload_deleted_events: :gitlab_main -gitlab_subscription_histories: :gitlab_main -gitlab_subscriptions: :gitlab_main -gpg_keys: :gitlab_main -gpg_key_subkeys: :gitlab_main -gpg_signatures: :gitlab_main -grafana_integrations: :gitlab_main -group_custom_attributes: :gitlab_main -group_deletion_schedules: :gitlab_main -group_deploy_keys: :gitlab_main -group_deploy_keys_groups: :gitlab_main -group_deploy_tokens: :gitlab_main -group_group_links: :gitlab_main -group_import_states: :gitlab_main -group_merge_request_approval_settings: :gitlab_main -group_repository_storage_moves: :gitlab_main -group_wiki_repositories: :gitlab_main -historical_data: :gitlab_main -identities: :gitlab_main -import_export_uploads: :gitlab_main -import_failures: :gitlab_main -incident_management_escalation_policies: :gitlab_main -incident_management_escalation_rules: :gitlab_main -incident_management_issuable_escalation_statuses: :gitlab_main -incident_management_oncall_participants: :gitlab_main -incident_management_oncall_rotations: :gitlab_main -incident_management_oncall_schedules: :gitlab_main -incident_management_oncall_shifts: :gitlab_main -incident_management_pending_alert_escalations: :gitlab_main -incident_management_pending_issue_escalations: :gitlab_main -index_statuses: :gitlab_main -in_product_marketing_emails: :gitlab_main -insights: :gitlab_main -integrations: :gitlab_main -internal_ids: :gitlab_main -ip_restrictions: :gitlab_main -issuable_metric_images: :gitlab_main -issuable_severities: :gitlab_main -issuable_slas: :gitlab_main -issue_assignees: :gitlab_main -issue_customer_relations_contacts: :gitlab_main -issue_email_participants: :gitlab_main -issue_links: :gitlab_main -issue_metrics: :gitlab_main -issues: :gitlab_main -issues_prometheus_alert_events: :gitlab_main -issues_self_managed_prometheus_alert_events: :gitlab_main -issue_tracker_data: :gitlab_main -issue_user_mentions: :gitlab_main -iterations_cadences: :gitlab_main -jira_connect_installations: :gitlab_main -jira_connect_subscriptions: :gitlab_main -jira_imports: :gitlab_main -jira_tracker_data: :gitlab_main -keys: :gitlab_main -label_links: :gitlab_main -label_priorities: :gitlab_main -labels: :gitlab_main -ldap_group_links: :gitlab_main -lfs_file_locks: :gitlab_main -lfs_objects: :gitlab_main -lfs_objects_projects: :gitlab_main -licenses: :gitlab_main -lists: :gitlab_main -list_user_preferences: :gitlab_main -loose_foreign_keys_deleted_records: :gitlab_main -member_tasks: :gitlab_main -members: :gitlab_main -merge_request_assignees: :gitlab_main -merge_request_blocks: :gitlab_main -merge_request_cleanup_schedules: :gitlab_main -merge_request_context_commit_diff_files: :gitlab_main -merge_request_context_commits: :gitlab_main -merge_request_diff_commits: :gitlab_main -merge_request_diff_commit_users: :gitlab_main -merge_request_diff_details: :gitlab_main -merge_request_diff_files: :gitlab_main -merge_request_diffs: :gitlab_main -merge_request_metrics: :gitlab_main -merge_request_reviewers: :gitlab_main -merge_requests_closing_issues: :gitlab_main -merge_requests: :gitlab_main -merge_request_user_mentions: :gitlab_main -merge_trains: :gitlab_main -metrics_dashboard_annotations: :gitlab_main -metrics_users_starred_dashboards: :gitlab_main -milestone_releases: :gitlab_main -milestones: :gitlab_main -namespace_admin_notes: :gitlab_main -namespace_aggregation_schedules: :gitlab_main -namespace_limits: :gitlab_main -namespace_package_settings: :gitlab_main -namespace_root_storage_statistics: :gitlab_main -namespace_settings: :gitlab_main -namespaces: :gitlab_main -namespace_statistics: :gitlab_main -note_diff_files: :gitlab_main -notes: :gitlab_main -notification_settings: :gitlab_main -oauth_access_grants: :gitlab_main -oauth_access_tokens: :gitlab_main -oauth_applications: :gitlab_main -oauth_openid_requests: :gitlab_main -onboarding_progresses: :gitlab_main -open_project_tracker_data: :gitlab_main -operations_feature_flags_clients: :gitlab_main -operations_feature_flag_scopes: :gitlab_main -operations_feature_flags: :gitlab_main -operations_feature_flags_issues: :gitlab_main -operations_scopes: :gitlab_main -operations_strategies: :gitlab_main -operations_strategies_user_lists: :gitlab_main -operations_user_lists: :gitlab_main -packages_build_infos: :gitlab_main -packages_composer_cache_files: :gitlab_main -packages_composer_metadata: :gitlab_main -packages_conan_file_metadata: :gitlab_main -packages_conan_metadata: :gitlab_main -packages_debian_file_metadata: :gitlab_main -packages_debian_group_architectures: :gitlab_main -packages_debian_group_component_files: :gitlab_main -packages_debian_group_components: :gitlab_main -packages_debian_group_distribution_keys: :gitlab_main -packages_debian_group_distributions: :gitlab_main -packages_debian_project_architectures: :gitlab_main -packages_debian_project_component_files: :gitlab_main -packages_debian_project_components: :gitlab_main -packages_debian_project_distribution_keys: :gitlab_main -packages_debian_project_distributions: :gitlab_main -packages_debian_publications: :gitlab_main -packages_dependencies: :gitlab_main -packages_dependency_links: :gitlab_main -packages_events: :gitlab_main -packages_helm_file_metadata: :gitlab_main -packages_maven_metadata: :gitlab_main -packages_nuget_dependency_link_metadata: :gitlab_main -packages_nuget_metadata: :gitlab_main -packages_package_file_build_infos: :gitlab_main -packages_package_files: :gitlab_main -packages_packages: :gitlab_main -packages_pypi_metadata: :gitlab_main -packages_rubygems_metadata: :gitlab_main -packages_tags: :gitlab_main -pages_deployments: :gitlab_main -pages_domain_acme_orders: :gitlab_main -pages_domains: :gitlab_main -partitioned_foreign_keys: :gitlab_main -path_locks: :gitlab_main -personal_access_tokens: :gitlab_main -plan_limits: :gitlab_main -plans: :gitlab_main -pool_repositories: :gitlab_main -postgres_async_indexes: :gitlab_main -postgres_reindex_actions: :gitlab_main -product_analytics_events_experimental: :gitlab_main -programming_languages: :gitlab_main -project_access_tokens: :gitlab_main -project_alerting_settings: :gitlab_main -project_aliases: :gitlab_main -project_authorizations: :gitlab_main -project_auto_devops: :gitlab_main -project_ci_cd_settings: :gitlab_main -project_ci_feature_usages: :gitlab_main -project_compliance_framework_settings: :gitlab_main -project_custom_attributes: :gitlab_main -project_daily_statistics: :gitlab_main -project_deploy_tokens: :gitlab_main -project_error_tracking_settings: :gitlab_main -project_export_jobs: :gitlab_main -project_features: :gitlab_main -project_feature_usages: :gitlab_main -project_group_links: :gitlab_main -project_import_data: :gitlab_main -project_incident_management_settings: :gitlab_main -project_metrics_settings: :gitlab_main -project_mirror_data: :gitlab_main -project_pages_metadata: :gitlab_main -project_repositories: :gitlab_main -project_repository_states: :gitlab_main -project_repository_storage_moves: :gitlab_main -project_security_settings: :gitlab_main -project_settings: :gitlab_main -projects: :gitlab_main -project_statistics: :gitlab_main -project_topics: :gitlab_main -project_tracing_settings: :gitlab_main -prometheus_alert_events: :gitlab_main -prometheus_alerts: :gitlab_main -prometheus_metrics: :gitlab_main -protected_branches: :gitlab_main -protected_branch_merge_access_levels: :gitlab_main -protected_branch_push_access_levels: :gitlab_main -protected_branch_unprotect_access_levels: :gitlab_main -protected_environment_deploy_access_levels: :gitlab_main -protected_environments: :gitlab_main -protected_tag_create_access_levels: :gitlab_main -protected_tags: :gitlab_main -push_event_payloads: :gitlab_main -push_rules: :gitlab_main -raw_usage_data: :gitlab_main -redirect_routes: :gitlab_main -release_links: :gitlab_main -releases: :gitlab_main -remote_mirrors: :gitlab_main -repository_languages: :gitlab_main -required_code_owners_sections: :gitlab_main -requirements: :gitlab_main -requirements_management_test_reports: :gitlab_main -resource_iteration_events: :gitlab_main -resource_label_events: :gitlab_main -resource_milestone_events: :gitlab_main -resource_state_events: :gitlab_main -resource_weight_events: :gitlab_main -reviews: :gitlab_main -routes: :gitlab_main -saml_group_links: :gitlab_main -saml_providers: :gitlab_main -scim_identities: :gitlab_main -scim_oauth_access_tokens: :gitlab_main -security_findings: :gitlab_main -security_orchestration_policy_configurations: :gitlab_main -security_orchestration_policy_rule_schedules: :gitlab_main -security_scans: :gitlab_main -self_managed_prometheus_alert_events: :gitlab_main -sent_notifications: :gitlab_main -sentry_issues: :gitlab_main -serverless_domain_cluster: :gitlab_main -service_desk_settings: :gitlab_main -shards: :gitlab_main -slack_integrations: :gitlab_main -smartcard_identities: :gitlab_main -snippet_repositories: :gitlab_main -snippet_repository_storage_moves: :gitlab_main -snippets: :gitlab_main -snippet_statistics: :gitlab_main -snippet_user_mentions: :gitlab_main -software_license_policies: :gitlab_main -software_licenses: :gitlab_main -spam_logs: :gitlab_main -sprints: :gitlab_main -status_check_responses: :gitlab_main -status_page_published_incidents: :gitlab_main -status_page_settings: :gitlab_main -subscriptions: :gitlab_main -suggestions: :gitlab_main -system_note_metadata: :gitlab_main -taggings: :gitlab_ci -tags: :gitlab_ci -term_agreements: :gitlab_main -terraform_states: :gitlab_main -terraform_state_versions: :gitlab_main -timelogs: :gitlab_main -todos: :gitlab_main -token_with_ivs: :gitlab_main -topics: :gitlab_main -trending_projects: :gitlab_main -u2f_registrations: :gitlab_main -upcoming_reconciliations: :gitlab_main -uploads: :gitlab_main -user_agent_details: :gitlab_main -user_callouts: :gitlab_main -user_canonical_emails: :gitlab_main -user_credit_card_validations: :gitlab_main -user_custom_attributes: :gitlab_main -user_details: :gitlab_main -user_follow_users: :gitlab_main -user_group_callouts: :gitlab_main -user_highest_roles: :gitlab_main -user_interacted_projects: :gitlab_main -user_permission_export_uploads: :gitlab_main -user_preferences: :gitlab_main -users: :gitlab_main -users_ops_dashboard_projects: :gitlab_main -users_security_dashboard_projects: :gitlab_main -users_star_projects: :gitlab_main -users_statistics: :gitlab_main -user_statuses: :gitlab_main -user_synced_attributes_metadata: :gitlab_main -verification_codes: :gitlab_main -vulnerabilities: :gitlab_main -vulnerability_exports: :gitlab_main -vulnerability_external_issue_links: :gitlab_main -vulnerability_feedback: :gitlab_main -vulnerability_finding_evidence_assets: :gitlab_main -vulnerability_finding_evidence_headers: :gitlab_main -vulnerability_finding_evidence_requests: :gitlab_main -vulnerability_finding_evidence_responses: :gitlab_main -vulnerability_finding_evidences: :gitlab_main -vulnerability_finding_evidence_sources: :gitlab_main -vulnerability_finding_evidence_supporting_messages: :gitlab_main -vulnerability_finding_links: :gitlab_main -vulnerability_finding_signatures: :gitlab_main -vulnerability_findings_remediations: :gitlab_main -vulnerability_flags: :gitlab_main -vulnerability_historical_statistics: :gitlab_main -vulnerability_identifiers: :gitlab_main -vulnerability_issue_links: :gitlab_main -vulnerability_occurrence_identifiers: :gitlab_main -vulnerability_occurrence_pipelines: :gitlab_main -vulnerability_occurrences: :gitlab_main -vulnerability_remediations: :gitlab_main -vulnerability_scanners: :gitlab_main -vulnerability_statistics: :gitlab_main -vulnerability_user_mentions: :gitlab_main -webauthn_registrations: :gitlab_main -web_hook_logs: :gitlab_main -web_hooks: :gitlab_main -wiki_page_meta: :gitlab_main -wiki_page_slugs: :gitlab_main -work_item_types: :gitlab_main -x509_certificates: :gitlab_main -x509_commit_signatures: :gitlab_main -x509_issuers: :gitlab_main -zentao_tracker_data: :gitlab_main -zoom_meetings: :gitlab_main diff --git a/spec/support/database/prevent_cross_database_modification.rb b/spec/support/database/prevent_cross_database_modification.rb index 229f5026044..09a87f92072 100644 --- a/spec/support/database/prevent_cross_database_modification.rb +++ b/spec/support/database/prevent_cross_database_modification.rb @@ -100,7 +100,7 @@ module Database cross_database_context[:modified_tables_by_db][database].merge(tables) all_tables = cross_database_context[:modified_tables_by_db].values.map(&:to_a).flatten - schemas = Database::GitlabSchema.table_schemas(all_tables) + schemas = ::Gitlab::Database::GitlabSchema.table_schemas(all_tables) if schemas.many? message = "Cross-database data modification of '#{schemas.to_a.join(", ")}' were detected within " \ diff --git a/spec/support/database/prevent_cross_joins.rb b/spec/support/database/prevent_cross_joins.rb index b3d2b966815..e69374fbc70 100644 --- a/spec/support/database/prevent_cross_joins.rb +++ b/spec/support/database/prevent_cross_joins.rb @@ -35,7 +35,7 @@ module Database # https://github.com/pganalyze/pg_query/issues/209 tables = PgQuery.parse(sql).tables - schemas = Database::GitlabSchema.table_schemas(tables) + schemas = ::Gitlab::Database::GitlabSchema.table_schemas(tables) if schemas.include?(:gitlab_ci) && schemas.include?(:gitlab_main) Thread.current[:has_cross_join_exception] = true diff --git a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb new file mode 100644 index 00000000000..7ccd9533811 --- /dev/null +++ b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'it has loose foreign keys' do + let(:factory_name) { nil } + let(:table_name) { described_class.table_name } + let(:connection) { described_class.connection } + + it 'includes the LooseForeignKey module' do + expect(described_class.ancestors).to include(LooseForeignKey) + end + + it 'responds to #loose_foreign_key_definitions' do + expect(described_class).to respond_to(:loose_foreign_key_definitions) + end + + it 'has at least one loose foreign key definition' do + expect(described_class.loose_foreign_key_definitions.size).to be > 0 + end + + it 'has the deletion trigger present' do + sql = <<-SQL + SELECT trigger_name + FROM information_schema.triggers + WHERE event_object_table = '#{table_name}' + SQL + + triggers = connection.execute(sql) + + expected_trigger_name = "#{table_name}_loose_fk_trigger" + expect(triggers.pluck('trigger_name')).to include(expected_trigger_name) + end + + it 'records record deletions' do + model = create(factory_name) # rubocop: disable Rails/SaveBang + model.destroy! + + deleted_record = LooseForeignKeys::DeletedRecord.find_by(fully_qualified_table_name: "#{connection.current_schema}.#{table_name}", primary_key_value: model.id) + + expect(deleted_record).not_to be_nil + end + + it 'cleans up record deletions' do + model = create(factory_name) # rubocop: disable Rails/SaveBang + + expect { model.destroy! }.to change { LooseForeignKeys::DeletedRecord.count }.by(1) + + LooseForeignKeys::ProcessDeletedRecordsService.new(connection: connection).execute + + expect(LooseForeignKeys::DeletedRecord.status_pending.count).to be(0) + expect(LooseForeignKeys::DeletedRecord.status_processed.count).to be(1) + end +end |