summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-10-27 15:13:41 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-10-27 15:13:41 +0000
commitf50cb4f63fae6f02a0b8d9c3a95f29b3b4f79516 (patch)
treeb6bcddc7b76089f91cc6143d141440cb63adfcbd /spec
parent0a559c913e4d333f339fcc03b89bfeade9ca0b93 (diff)
downloadgitlab-ce-f50cb4f63fae6f02a0b8d9c3a95f29b3b4f79516.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/application_controller_spec.rb4
-rw-r--r--spec/lib/gitlab/database/gitlab_schema_spec.rb (renamed from spec/support_specs/database/gitlab_schema_spec.rb)2
-rw-r--r--spec/lib/gitlab/database/load_balancing/configuration_spec.rb73
-rw-r--r--spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb52
-rw-r--r--spec/lib/gitlab/database/load_balancing/rack_middleware_spec.rb16
-rw-r--r--spec/lib/gitlab/database/load_balancing/sticking_spec.rb6
-rw-r--r--spec/lib/gitlab/database/partitioning_spec.rb11
-rw-r--r--spec/lib/gitlab/etag_caching/middleware_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb10
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb10
-rw-r--r--spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb168
-rw-r--r--spec/models/chat_name_spec.rb8
-rw-r--r--spec/models/ci/runner_spec.rb8
-rw-r--r--spec/models/namespace_spec.rb152
-rw-r--r--spec/requests/api/api_spec.rb2
-rw-r--r--spec/requests/api/releases_spec.rb32
-rw-r--r--spec/support/database/gitlab_schema.rb20
-rw-r--r--spec/support/database/gitlab_schemas.yml537
-rw-r--r--spec/support/database/prevent_cross_database_modification.rb2
-rw-r--r--spec/support/database/prevent_cross_joins.rb2
-rw-r--r--spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb52
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