diff --git a/spec/linters/migration/no_model_in_specs.rb b/spec/linters/migration/no_model_in_specs.rb index 4b8fb31f182..d2aa6d28a28 100644 --- a/spec/linters/migration/no_model_in_specs.rb +++ b/spec/linters/migration/no_model_in_specs.rb @@ -6,6 +6,25 @@ class NoModelInSpecs < RuboCop::Cop::Base 'Use raw Sequel operations (e.g. db[:table].insert) instead. ' \ 'See spec/migrations/Readme.md for details.'.freeze + def on_new_investigation + @model_let_names = Set.new + return unless processed_source.ast + + processed_source.ast.each_descendant(:block) do |node| + next unless %i[let let!].include?(node.send_node&.method_name) + + let_name_node = node.send_node.first_argument + next unless let_name_node&.sym_type? + + body = node.body + next unless body + + # let(:foo) { SomeModel } or let(:foo) { VCAP::CloudController::SomeModel } + inner = body.begin_type? ? body.children.last : body + @model_let_names << let_name_node.value if model_const_node?(inner) + end + end + def on_send(node) add_offense(node) if model_receiver?(node.receiver) end @@ -14,9 +33,20 @@ def on_send(node) def model_receiver?(receiver) return false unless receiver - return false unless receiver.const_type? - name = receiver.const_name.to_s + return model_const_node?(receiver) if receiver.const_type? + return @model_let_names.include?(receiver.method_name) if receiver.send_type? + + false + end + + def model_const_node?(node) + return false unless node&.const_type? + + model_class_name?(node.const_name.to_s) + end + + def model_class_name?(name) name.end_with?('Model') && name != 'Sequel::Model' end end diff --git a/spec/linters/migration/no_model_in_specs_spec.rb b/spec/linters/migration/no_model_in_specs_spec.rb index d77c864e674..601e2677a8c 100644 --- a/spec/linters/migration/no_model_in_specs_spec.rb +++ b/spec/linters/migration/no_model_in_specs_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'rubocop' require 'rubocop/rspec/cop_helper' require 'rubocop/config' @@ -71,4 +70,31 @@ expect(result.size).to eq(0) end + + it 'registers an offense for model usage via let variable' do + result = inspect_source(<<~RUBY) + let(:annotation) { VCAP::CloudController::IsolationSegmentAnnotationModel } + annotation.create(resource_guid: 'x', key_name: 'k', value: 'v') + RUBY + + expect(result.size).to eq(1) + end + + it 'registers an offense for model usage via let! variable' do + result = inspect_source(<<~RUBY) + let!(:label) { VCAP::CloudController::IsolationSegmentLabelModel } + label.where(resource_guid: 'x').count + RUBY + + expect(result.size).to eq(1) + end + + it 'does not register an offense for let variable not holding a model' do + result = inspect_source(<<~RUBY) + let(:guid) { SecureRandom.uuid } + guid.upcase + RUBY + + expect(result.size).to eq(0) + end end diff --git a/spec/migration_spec_helper.rb b/spec/migration_spec_helper.rb new file mode 100644 index 00000000000..bb393472bb4 --- /dev/null +++ b/spec/migration_spec_helper.rb @@ -0,0 +1,100 @@ +require 'rubygems' +require 'bundler/setup' +require 'tmpdir' +require 'fileutils' + +if ENV['COVERAGE'] + require 'simplecov' + SimpleCov.start +end + +$LOAD_PATH.push(File.expand_path(File.join(__dir__, '..', 'app'))) +$LOAD_PATH.push(File.expand_path(File.join(__dir__, '..', 'lib'))) + +ENV['BOOTSNAP_CACHE_DIR'] ||= File.expand_path('../tmp/bootsnap-cache', __dir__) +require 'bootsnap/setup' +require 'active_support/all' +require 'steno/steno' +require 'sequel/plugins/microsecond_timestamp_precision' + +module VCAP + module CloudController + # Stub Config so migrations that call Config.config&.get(...) don't fail. + # The safe-navigation operator (&.) means nil is fine here. + class Config + def self.config + nil + end + + def get(*_args); end + end + end +end + +require 'cloud_controller/db' +Sequel.default_timezone = :utc +require 'cloud_controller/db_migrator' +require 'cloud_controller/database_parts_parser' +require 'support/bootstrap/db_connection_string' +require 'support/table_truncator' +require 'support/referential_integrity' +require 'support/matchers/be_a_guid' +require 'support/matchers/have_queried_db_times' +require 'support/and_record_arguments' + +# Establish DB connection (sets Sequel::Model.db) without loading CC models. +connection_string = DbConnectionString.new.to_s +db_config = { + database: VCAP::CloudController::DatabasePartsParser.database_parts_from_connection(connection_string), + pool_timeout: 10, + read_timeout: 3600, + connection_validation_timeout: 3600, + max_connections: 42 +} +VCAP::CloudController::DB.connect(db_config, Logger.new(nil)) +Sequel.extension :migration + +# Lightweight truncation that skips seed re-seeding (migration specs don't use seeds). +module MigrationSpecTruncation + WRITE_REGEX = /\b(?:INSERT INTO|UPDATE|DELETE FROM|TRUNCATE TABLE|TRUNCATE)\s+(\S+)/i + + def self.cleanly(db) + tables_written = Set.new + + logger = Object.new + %i[info warn debug error fatal].each do |level| + logger.define_singleton_method(level) { |msg| MigrationSpecTruncation.capture(msg, tables_written) } + end + + db.loggers << logger + begin + yield + ensure + db.loggers.delete(logger) + all_tables = TableTruncator.isolated_tables(db) + tables = tables_written.to_a & all_tables + TableTruncator.new(db, tables).truncate_tables unless tables.empty? + end + end + + def self.capture(msg, tables_written) + return unless msg =~ WRITE_REGEX + + target = ::Regexp.last_match(1).delete('`"') + tables_written << target.to_sym unless target.include?('.') + end +end + +RSpec.configure do |config| + config.before(:all, type: :migration) do + skip 'Skipped due to NO_DB_MIGRATION env variable being set' if ENV['NO_DB_MIGRATION'] + end + + config.around(type: :migration) do |example| + if example.metadata[:isolation] == :truncation + MigrationSpecTruncation.cleanly(Sequel::Model.db) { example.run } + else + Sequel::Model.db.transaction(rollback: :always, auto_savepoint: true) { example.run } + end + end +end diff --git a/spec/migrations/20190712210940_backfill_status_for_deployments_spec.rb b/spec/migrations/20190712210940_backfill_status_for_deployments_spec.rb index 6263038b80f..155fc3a758b 100644 --- a/spec/migrations/20190712210940_backfill_status_for_deployments_spec.rb +++ b/spec/migrations/20190712210940_backfill_status_for_deployments_spec.rb @@ -1,6 +1,7 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'backfill status_value for deployments', isolation: :truncation, type: :migration do + let(:db) { Sequel::Model.db } let(:tmp_migrations_dir) { Dir.mktmpdir } before do @@ -10,119 +11,62 @@ ) end - let(:app) { create(:app_model) } - it 'backfills status_value based on deployment state' do - # Create all deployment variations - deployment_deployed = VCAP::CloudController::DeploymentModel.create( - guid: 'with-state-deployed', - state: VCAP::CloudController::DeploymentModel::DEPLOYED_STATE, - app: app, - original_web_process_instance_count: 1 - ) - - deployment_canceled = VCAP::CloudController::DeploymentModel.create( - guid: 'with-state-canceled', - state: VCAP::CloudController::DeploymentModel::CANCELED_STATE, - app: app, - original_web_process_instance_count: 1 - ) - - deployment_failed = VCAP::CloudController::DeploymentModel.create( - guid: 'with-state-failed', - state: 'FAILED', - app: app, - original_web_process_instance_count: 1 - ) - - deployment_deploying = VCAP::CloudController::DeploymentModel.create( - guid: 'with-state-deploying', - state: VCAP::CloudController::DeploymentModel::DEPLOYING_STATE, - app: app, - original_web_process_instance_count: 1 - ) - - deployment_canceling = VCAP::CloudController::DeploymentModel.create( - guid: 'with-state-canceling', - state: VCAP::CloudController::DeploymentModel::CANCELING_STATE, - app: app, - original_web_process_instance_count: 1 - ) - - deployment_failing = VCAP::CloudController::DeploymentModel.create( - guid: 'with-state-failing', - state: 'FAILING', - app: app, - original_web_process_instance_count: 1 - ) - - deployment_with_existing_status = VCAP::CloudController::DeploymentModel.create( - guid: 'with-existing-status', - state: VCAP::CloudController::DeploymentModel::DEPLOYED_STATE, - status_value: 'foo', - status_reason: 'bar', - app: app, - original_web_process_instance_count: 1 - ) - - deployment_failing_with_reason = VCAP::CloudController::DeploymentModel.create( - guid: 'failing-with-reason', - state: 'FAILING', - status_value: 'foo', - status_reason: 'bar', - app: app, - original_web_process_instance_count: 1 - ) - - # Run migration once - Sequel::Migrator.run(VCAP::CloudController::DeploymentModel.db, tmp_migrations_dir, table: :my_fake_table) - - # Test: DEPLOYED state -> FINALIZED status - deployment = VCAP::CloudController::DeploymentModel.where(guid: deployment_deployed.guid).first - expect(deployment.state).to eq(VCAP::CloudController::DeploymentModel::DEPLOYED_STATE) - expect(deployment.status_value).to eq(VCAP::CloudController::DeploymentModel::FINALIZED_STATUS_VALUE) - expect(deployment.status_reason).to be_nil - - # Test: CANCELED state -> FINALIZED status - deployment = VCAP::CloudController::DeploymentModel.where(guid: deployment_canceled.guid).first - expect(deployment.state).to eq(VCAP::CloudController::DeploymentModel::CANCELED_STATE) - expect(deployment.status_value).to eq(VCAP::CloudController::DeploymentModel::FINALIZED_STATUS_VALUE) - expect(deployment.status_reason).to be_nil - - # Test: FAILED state -> DEPLOYED state + FINALIZED status - deployment = VCAP::CloudController::DeploymentModel.where(guid: deployment_failed.guid).first - expect(deployment.state).to eq(VCAP::CloudController::DeploymentModel::DEPLOYED_STATE) - expect(deployment.status_value).to eq(VCAP::CloudController::DeploymentModel::FINALIZED_STATUS_VALUE) - expect(deployment.status_reason).to be_nil - - # Test: DEPLOYING state -> DEPLOYING status - deployment = VCAP::CloudController::DeploymentModel.where(guid: deployment_deploying.guid).first - expect(deployment.state).to eq(VCAP::CloudController::DeploymentModel::DEPLOYING_STATE) - expect(deployment.status_value).to eq('DEPLOYING') - expect(deployment.status_reason).to be_nil - - # Test: CANCELING state -> DEPLOYING status - deployment = VCAP::CloudController::DeploymentModel.where(guid: deployment_canceling.guid).first - expect(deployment.state).to eq(VCAP::CloudController::DeploymentModel::CANCELING_STATE) - expect(deployment.status_value).to eq('DEPLOYING') - expect(deployment.status_reason).to be_nil - - # Test: FAILING state -> DEPLOYING state + DEPLOYING status - deployment = VCAP::CloudController::DeploymentModel.where(guid: deployment_failing.guid).first - expect(deployment.state).to eq(VCAP::CloudController::DeploymentModel::DEPLOYING_STATE) - expect(deployment.status_value).to eq('DEPLOYING') - expect(deployment.status_reason).to be_nil - - # Test: existing status_value is not reset - deployment = VCAP::CloudController::DeploymentModel.where(guid: deployment_with_existing_status.guid).first - expect(deployment.state).to eq(VCAP::CloudController::DeploymentModel::DEPLOYED_STATE) - expect(deployment.status_value).to eq('foo') - expect(deployment.status_reason).to eq('bar') - - # Test: existing status_reason is preserved - deployment = VCAP::CloudController::DeploymentModel.where(guid: deployment_failing_with_reason.guid).first - expect(deployment.state).to eq(VCAP::CloudController::DeploymentModel::DEPLOYING_STATE) - expect(deployment.status_value).to eq('DEPLOYING') - expect(deployment.status_reason).to eq('bar') + now = Time.now.utc + app_guid = SecureRandom.uuid + db[:apps].insert(guid: app_guid, name: 'app', created_at: now, updated_at: now) + + db[:deployments].insert(guid: 'with-state-deployed', app_guid: app_guid, state: 'DEPLOYED', original_web_process_instance_count: 1, created_at: now, updated_at: now) + db[:deployments].insert(guid: 'with-state-canceled', app_guid: app_guid, state: 'CANCELED', original_web_process_instance_count: 1, created_at: now, updated_at: now) + db[:deployments].insert(guid: 'with-state-failed', app_guid: app_guid, state: 'FAILED', original_web_process_instance_count: 1, created_at: now, updated_at: now) + db[:deployments].insert(guid: 'with-state-deploying', app_guid: app_guid, state: 'DEPLOYING', original_web_process_instance_count: 1, created_at: now, updated_at: now) + db[:deployments].insert(guid: 'with-state-canceling', app_guid: app_guid, state: 'CANCELING', original_web_process_instance_count: 1, created_at: now, updated_at: now) + db[:deployments].insert(guid: 'with-state-failing', app_guid: app_guid, state: 'FAILING', original_web_process_instance_count: 1, created_at: now, updated_at: now) + db[:deployments].insert(guid: 'with-existing-status', app_guid: app_guid, state: 'DEPLOYED', status_value: 'foo', status_reason: 'bar', original_web_process_instance_count: 1, + created_at: now, updated_at: now) + db[:deployments].insert(guid: 'failing-with-reason', app_guid: app_guid, state: 'FAILING', status_value: 'foo', status_reason: 'bar', original_web_process_instance_count: 1, + created_at: now, updated_at: now) + + Sequel::Migrator.run(db, tmp_migrations_dir, table: :my_fake_table) + + deployment = db[:deployments].where(guid: 'with-state-deployed').first + expect(deployment[:state]).to eq('DEPLOYED') + expect(deployment[:status_value]).to eq('FINALIZED') + expect(deployment[:status_reason]).to be_nil + + deployment = db[:deployments].where(guid: 'with-state-canceled').first + expect(deployment[:state]).to eq('CANCELED') + expect(deployment[:status_value]).to eq('FINALIZED') + expect(deployment[:status_reason]).to be_nil + + deployment = db[:deployments].where(guid: 'with-state-failed').first + expect(deployment[:state]).to eq('DEPLOYED') + expect(deployment[:status_value]).to eq('FINALIZED') + expect(deployment[:status_reason]).to be_nil + + deployment = db[:deployments].where(guid: 'with-state-deploying').first + expect(deployment[:state]).to eq('DEPLOYING') + expect(deployment[:status_value]).to eq('DEPLOYING') + expect(deployment[:status_reason]).to be_nil + + deployment = db[:deployments].where(guid: 'with-state-canceling').first + expect(deployment[:state]).to eq('CANCELING') + expect(deployment[:status_value]).to eq('DEPLOYING') + expect(deployment[:status_reason]).to be_nil + + deployment = db[:deployments].where(guid: 'with-state-failing').first + expect(deployment[:state]).to eq('DEPLOYING') + expect(deployment[:status_value]).to eq('DEPLOYING') + expect(deployment[:status_reason]).to be_nil + + deployment = db[:deployments].where(guid: 'with-existing-status').first + expect(deployment[:state]).to eq('DEPLOYED') + expect(deployment[:status_value]).to eq('foo') + expect(deployment[:status_reason]).to eq('bar') + + deployment = db[:deployments].where(guid: 'failing-with-reason').first + expect(deployment[:state]).to eq('DEPLOYING') + expect(deployment[:status_value]).to eq('DEPLOYING') + expect(deployment[:status_reason]).to eq('bar') end end diff --git a/spec/migrations/20191014212939_add_guid_and_timestamps_to_roles_join_tables_spec.rb b/spec/migrations/20191014212939_add_guid_and_timestamps_to_roles_join_tables_spec.rb deleted file mode 100644 index 85744c6af9d..00000000000 --- a/spec/migrations/20191014212939_add_guid_and_timestamps_to_roles_join_tables_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'spec_helper' - -RSpec.describe 'add role_guid and timestamps to roles join tables', isolation: :truncation, type: :migration do - let(:db) { Sequel::Model.db } - let(:user) { create(:user) } - let(:space) { create(:space) } - - org_roles = %w[ - organizations_auditors - organizations_billing_managers - organizations_managers - organizations_users - ] - space_roles = %w[ - spaces_auditors - spaces_developers - spaces_managers - ] - - before do - space_roles.each do |s_role| - db[s_role.to_sym].insert({ - user_id: user.id, - space_id: space.id - }) - end - - org_roles.each do |o_role| - db[o_role.to_sym].insert({ - user_id: user.id, - organization_id: space.organization.id - }) - end - end - - roles = org_roles + space_roles - - roles.each do |role_table| - context role_table do - it "adds the columns to the #{role_table} table" do - role = db[role_table.to_sym].first(user_id: user.id) - - expect(role[:role_guid]).to be_nil - expect(role[:created_at]).to be_a(Time) - expect(role[:updated_at]).to be_a(Time) - end - end - end -end diff --git a/spec/migrations/20191218001006_fill_guid_and_timestamps_for_spaces_auditors_spec.rb b/spec/migrations/20191218001006_fill_guid_and_timestamps_for_spaces_auditors_spec.rb index dcaa10d7c52..42b2b32194e 100644 --- a/spec/migrations/20191218001006_fill_guid_and_timestamps_for_spaces_auditors_spec.rb +++ b/spec/migrations/20191218001006_fill_guid_and_timestamps_for_spaces_auditors_spec.rb @@ -1,30 +1,38 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'fill role_guid and timestamps for spaces_auditors table', isolation: :truncation, type: :migration do let(:role_table) { :spaces_auditors } let(:filename) { '20191218001006_fill_guid_and_timestamps_for_spaces_auditors.rb' } let(:db) { Sequel::Model.db } - let(:user) { create(:user) } - let(:user_2) { create(:user) } - let(:user_3) { create(:user) } - let(:space) { create(:space) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", + non_basic_services_allowed: false, total_services: -1, memory_limit: 0, total_routes: -1) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", + quota_definition_id: quota_def_id) + end + let(:space_id) do + db[:spaces].insert(guid: SecureRandom.uuid, name: "space-#{SecureRandom.uuid}", organization_id: org_id) + end + let(:user_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_2_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_3_id) { db[:users].insert(guid: SecureRandom.uuid) } let(:tmp_migrations_dir) { Dir.mktmpdir } before do FileUtils.cp(File.join(DBMigrator::SEQUEL_MIGRATIONS, filename), tmp_migrations_dir) - [user, user_2].each do |user| - db[role_table].insert({ user_id: user.id, space_id: space.id }) - end - - db[role_table].insert({ user_id: user_3.id, space_id: space.id, role_guid: 'existing-role-guid' }) + db[role_table].insert({ user_id: user_id, space_id: space_id }) + db[role_table].insert({ user_id: user_2_id, space_id: space_id }) + db[role_table].insert({ user_id: user_3_id, space_id: space_id, role_guid: 'existing-role-guid' }) end it 'fills in columns of the spaces_auditors table' do Sequel::Migrator.run(db, tmp_migrations_dir, table: :my_fake_table) - role = db[role_table].first(user_id: user.id) - role_2 = db[role_table].first(user_id: user_2.id) - role_3 = db[role_table].first(user_id: user_3.id) + role = db[role_table].first(user_id: user_id) + role_2 = db[role_table].first(user_id: user_2_id) + role_3 = db[role_table].first(user_id: user_3_id) expect(role[:role_guid]).to be_a_guid expect(role_2[:role_guid]).to be_a_guid diff --git a/spec/migrations/20191218001010_fill_guid_and_timestamps_for_spaces_managers_spec.rb b/spec/migrations/20191218001010_fill_guid_and_timestamps_for_spaces_managers_spec.rb index 7a663f496b7..8641724d958 100644 --- a/spec/migrations/20191218001010_fill_guid_and_timestamps_for_spaces_managers_spec.rb +++ b/spec/migrations/20191218001010_fill_guid_and_timestamps_for_spaces_managers_spec.rb @@ -1,30 +1,38 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'fill role_guid and timestamps for spaces_managers table', isolation: :truncation, type: :migration do let(:role_table) { :spaces_managers } let(:filename) { '20191218001010_fill_guid_and_timestamps_for_spaces_managers.rb' } let(:db) { Sequel::Model.db } - let(:user) { create(:user) } - let(:user_2) { create(:user) } - let(:user_3) { create(:user) } - let(:space) { create(:space) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", + non_basic_services_allowed: false, total_services: -1, memory_limit: 0, total_routes: -1) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", + quota_definition_id: quota_def_id) + end + let(:space_id) do + db[:spaces].insert(guid: SecureRandom.uuid, name: "space-#{SecureRandom.uuid}", organization_id: org_id) + end + let(:user_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_2_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_3_id) { db[:users].insert(guid: SecureRandom.uuid) } let(:tmp_migrations_dir) { Dir.mktmpdir } before do FileUtils.cp(File.join(DBMigrator::SEQUEL_MIGRATIONS, filename), tmp_migrations_dir) - [user, user_2].each do |user| - db[role_table].insert({ user_id: user.id, space_id: space.id }) - end - - db[role_table].insert({ user_id: user_3.id, space_id: space.id, role_guid: 'existing-role-guid' }) + db[role_table].insert({ user_id: user_id, space_id: space_id }) + db[role_table].insert({ user_id: user_2_id, space_id: space_id }) + db[role_table].insert({ user_id: user_3_id, space_id: space_id, role_guid: 'existing-role-guid' }) end it 'fills in columns of the spaces_managers table' do Sequel::Migrator.run(db, tmp_migrations_dir, table: :my_fake_table) - role = db[role_table].first(user_id: user.id) - role_2 = db[role_table].first(user_id: user_2.id) - role_3 = db[role_table].first(user_id: user_3.id) + role = db[role_table].first(user_id: user_id) + role_2 = db[role_table].first(user_id: user_2_id) + role_3 = db[role_table].first(user_id: user_3_id) expect(role[:role_guid]).to be_a_guid expect(role_2[:role_guid]).to be_a_guid diff --git a/spec/migrations/20191218001015_fill_guid_and_timestamps_for_spaces_developers_spec.rb b/spec/migrations/20191218001015_fill_guid_and_timestamps_for_spaces_developers_spec.rb index 5d6d7011b6a..8bf65b0a9c7 100644 --- a/spec/migrations/20191218001015_fill_guid_and_timestamps_for_spaces_developers_spec.rb +++ b/spec/migrations/20191218001015_fill_guid_and_timestamps_for_spaces_developers_spec.rb @@ -1,30 +1,38 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'fill role_guid and timestamps for spaces_developers table', isolation: :truncation, type: :migration do let(:role_table) { :spaces_developers } let(:filename) { '20191218001015_fill_guid_and_timestamps_for_spaces_developers.rb' } let(:db) { Sequel::Model.db } - let(:user) { create(:user) } - let(:user_2) { create(:user) } - let(:user_3) { create(:user) } - let(:space) { create(:space) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", + non_basic_services_allowed: false, total_services: -1, memory_limit: 0, total_routes: -1) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", + quota_definition_id: quota_def_id) + end + let(:space_id) do + db[:spaces].insert(guid: SecureRandom.uuid, name: "space-#{SecureRandom.uuid}", organization_id: org_id) + end + let(:user_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_2_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_3_id) { db[:users].insert(guid: SecureRandom.uuid) } let(:tmp_migrations_dir) { Dir.mktmpdir } before do FileUtils.cp(File.join(DBMigrator::SEQUEL_MIGRATIONS, filename), tmp_migrations_dir) - [user, user_2].each do |user| - db[role_table].insert({ user_id: user.id, space_id: space.id }) - end - - db[role_table].insert({ user_id: user_3.id, space_id: space.id, role_guid: 'existing-role-guid' }) + db[role_table].insert({ user_id: user_id, space_id: space_id }) + db[role_table].insert({ user_id: user_2_id, space_id: space_id }) + db[role_table].insert({ user_id: user_3_id, space_id: space_id, role_guid: 'existing-role-guid' }) end it 'fills in columns of the spaces_developers table' do Sequel::Migrator.run(db, tmp_migrations_dir, table: :my_fake_table) - role = db[role_table].first(user_id: user.id) - role_2 = db[role_table].first(user_id: user_2.id) - role_3 = db[role_table].first(user_id: user_3.id) + role = db[role_table].first(user_id: user_id) + role_2 = db[role_table].first(user_id: user_2_id) + role_3 = db[role_table].first(user_id: user_3_id) expect(role[:role_guid]).to be_a_guid expect(role_2[:role_guid]).to be_a_guid diff --git a/spec/migrations/20191218001019_fill_guid_and_timestamps_for_organizations_auditors_spec.rb b/spec/migrations/20191218001019_fill_guid_and_timestamps_for_organizations_auditors_spec.rb index 14a33572f67..e2d97844181 100644 --- a/spec/migrations/20191218001019_fill_guid_and_timestamps_for_organizations_auditors_spec.rb +++ b/spec/migrations/20191218001019_fill_guid_and_timestamps_for_organizations_auditors_spec.rb @@ -1,30 +1,35 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'fill role_guid and timestamps for organizations_auditors table', isolation: :truncation, type: :migration do let(:role_table) { :organizations_auditors } let(:filename) { '20191218001019_fill_guid_and_timestamps_for_organizations_auditors.rb' } let(:db) { Sequel::Model.db } - let(:user) { create(:user) } - let(:user_2) { create(:user) } - let(:user_3) { create(:user) } - let(:organization) { create(:organization) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", + non_basic_services_allowed: false, total_services: -1, memory_limit: 0, total_routes: -1) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", + quota_definition_id: quota_def_id) + end + let(:user_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_2_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_3_id) { db[:users].insert(guid: SecureRandom.uuid) } let(:tmp_migrations_dir) { Dir.mktmpdir } before do FileUtils.cp(File.join(DBMigrator::SEQUEL_MIGRATIONS, filename), tmp_migrations_dir) - [user, user_2].each do |user| - db[role_table].insert({ user_id: user.id, organization_id: organization.id }) - end - - db[role_table].insert({ user_id: user_3.id, organization_id: organization.id, role_guid: 'existing-role-guid' }) + db[role_table].insert({ user_id: user_id, organization_id: org_id }) + db[role_table].insert({ user_id: user_2_id, organization_id: org_id }) + db[role_table].insert({ user_id: user_3_id, organization_id: org_id, role_guid: 'existing-role-guid' }) end it 'fills in columns of the organizations_auditors table' do Sequel::Migrator.run(db, tmp_migrations_dir, table: :my_fake_table) - role = db[role_table].first(user_id: user.id) - role_2 = db[role_table].first(user_id: user_2.id) - role_3 = db[role_table].first(user_id: user_3.id) + role = db[role_table].first(user_id: user_id) + role_2 = db[role_table].first(user_id: user_2_id) + role_3 = db[role_table].first(user_id: user_3_id) expect(role[:role_guid]).to be_a_guid expect(role_2[:role_guid]).to be_a_guid diff --git a/spec/migrations/20191218001024_fill_guid_and_timestamps_for_organizations_billing_managers_spec.rb b/spec/migrations/20191218001024_fill_guid_and_timestamps_for_organizations_billing_managers_spec.rb index bded86d358f..b33ed85f2af 100644 --- a/spec/migrations/20191218001024_fill_guid_and_timestamps_for_organizations_billing_managers_spec.rb +++ b/spec/migrations/20191218001024_fill_guid_and_timestamps_for_organizations_billing_managers_spec.rb @@ -1,30 +1,35 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'fill role_guid and timestamps for organizations_billing_managers table', isolation: :truncation, type: :migration do let(:role_table) { :organizations_billing_managers } let(:filename) { '20191218001024_fill_guid_and_timestamps_for_organizations_billing_managers.rb' } let(:db) { Sequel::Model.db } - let(:user) { create(:user) } - let(:user_2) { create(:user) } - let(:user_3) { create(:user) } - let(:organization) { create(:organization) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", + non_basic_services_allowed: false, total_services: -1, memory_limit: 0, total_routes: -1) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", + quota_definition_id: quota_def_id) + end + let(:user_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_2_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_3_id) { db[:users].insert(guid: SecureRandom.uuid) } let(:tmp_migrations_dir) { Dir.mktmpdir } before do FileUtils.cp(File.join(DBMigrator::SEQUEL_MIGRATIONS, filename), tmp_migrations_dir) - [user, user_2].each do |user| - db[role_table].insert({ user_id: user.id, organization_id: organization.id }) - end - - db[role_table].insert({ user_id: user_3.id, organization_id: organization.id, role_guid: 'existing-role-guid' }) + db[role_table].insert({ user_id: user_id, organization_id: org_id }) + db[role_table].insert({ user_id: user_2_id, organization_id: org_id }) + db[role_table].insert({ user_id: user_3_id, organization_id: org_id, role_guid: 'existing-role-guid' }) end it 'fills in columns of the organizations_billing_managers table' do Sequel::Migrator.run(db, tmp_migrations_dir, table: :my_fake_table) - role = db[role_table].first(user_id: user.id) - role_2 = db[role_table].first(user_id: user_2.id) - role_3 = db[role_table].first(user_id: user_3.id) + role = db[role_table].first(user_id: user_id) + role_2 = db[role_table].first(user_id: user_2_id) + role_3 = db[role_table].first(user_id: user_3_id) expect(role[:role_guid]).to be_a_guid expect(role_2[:role_guid]).to be_a_guid diff --git a/spec/migrations/20191218001028_fill_guid_and_timestamps_for_organizations_managers_spec.rb b/spec/migrations/20191218001028_fill_guid_and_timestamps_for_organizations_managers_spec.rb index 5a3ad3496ef..d0c5b6f74cf 100644 --- a/spec/migrations/20191218001028_fill_guid_and_timestamps_for_organizations_managers_spec.rb +++ b/spec/migrations/20191218001028_fill_guid_and_timestamps_for_organizations_managers_spec.rb @@ -1,30 +1,35 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'fill role_guid and timestamps for organizations_managers table', isolation: :truncation, type: :migration do let(:role_table) { :organizations_managers } let(:filename) { '20191218001028_fill_guid_and_timestamps_for_organizations_managers.rb' } let(:db) { Sequel::Model.db } - let(:user) { create(:user) } - let(:user_2) { create(:user) } - let(:user_3) { create(:user) } - let(:organization) { create(:organization) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", + non_basic_services_allowed: false, total_services: -1, memory_limit: 0, total_routes: -1) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", + quota_definition_id: quota_def_id) + end + let(:user_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_2_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_3_id) { db[:users].insert(guid: SecureRandom.uuid) } let(:tmp_migrations_dir) { Dir.mktmpdir } before do FileUtils.cp(File.join(DBMigrator::SEQUEL_MIGRATIONS, filename), tmp_migrations_dir) - [user, user_2].each do |user| - db[role_table].insert({ user_id: user.id, organization_id: organization.id }) - end - - db[role_table].insert({ user_id: user_3.id, organization_id: organization.id, role_guid: 'existing-role-guid' }) + db[role_table].insert({ user_id: user_id, organization_id: org_id }) + db[role_table].insert({ user_id: user_2_id, organization_id: org_id }) + db[role_table].insert({ user_id: user_3_id, organization_id: org_id, role_guid: 'existing-role-guid' }) end it 'fills in columns of the organizations_managers table' do Sequel::Migrator.run(db, tmp_migrations_dir, table: :my_fake_table) - role = db[role_table].first(user_id: user.id) - role_2 = db[role_table].first(user_id: user_2.id) - role_3 = db[role_table].first(user_id: user_3.id) + role = db[role_table].first(user_id: user_id) + role_2 = db[role_table].first(user_id: user_2_id) + role_3 = db[role_table].first(user_id: user_3_id) expect(role[:role_guid]).to be_a_guid expect(role_2[:role_guid]).to be_a_guid diff --git a/spec/migrations/20191218001034_fill_guid_and_timestamps_for_organizations_users_spec.rb b/spec/migrations/20191218001034_fill_guid_and_timestamps_for_organizations_users_spec.rb index d2a17858482..e3c825ac64d 100644 --- a/spec/migrations/20191218001034_fill_guid_and_timestamps_for_organizations_users_spec.rb +++ b/spec/migrations/20191218001034_fill_guid_and_timestamps_for_organizations_users_spec.rb @@ -1,30 +1,35 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'fill role_guid and timestamps for organizations_users table', isolation: :truncation, type: :migration do let(:role_table) { :organizations_users } let(:filename) { '20191218001034_fill_guid_and_timestamps_for_organizations_users.rb' } let(:db) { Sequel::Model.db } - let(:user) { create(:user) } - let(:user_2) { create(:user) } - let(:user_3) { create(:user) } - let(:organization) { create(:organization) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", + non_basic_services_allowed: false, total_services: -1, memory_limit: 0, total_routes: -1) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", + quota_definition_id: quota_def_id) + end + let(:user_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_2_id) { db[:users].insert(guid: SecureRandom.uuid) } + let(:user_3_id) { db[:users].insert(guid: SecureRandom.uuid) } let(:tmp_migrations_dir) { Dir.mktmpdir } before do FileUtils.cp(File.join(DBMigrator::SEQUEL_MIGRATIONS, filename), tmp_migrations_dir) - [user, user_2].each do |user| - db[role_table].insert({ user_id: user.id, organization_id: organization.id }) - end - - db[role_table].insert({ user_id: user_3.id, organization_id: organization.id, role_guid: 'existing-role-guid' }) + db[role_table].insert({ user_id: user_id, organization_id: org_id }) + db[role_table].insert({ user_id: user_2_id, organization_id: org_id }) + db[role_table].insert({ user_id: user_3_id, organization_id: org_id, role_guid: 'existing-role-guid' }) end it 'fills in columns of the organizations_users table' do Sequel::Migrator.run(db, tmp_migrations_dir, table: :my_fake_table) - role = db[role_table].first(user_id: user.id) - role_2 = db[role_table].first(user_id: user_2.id) - role_3 = db[role_table].first(user_id: user_3.id) + role = db[role_table].first(user_id: user_id) + role_2 = db[role_table].first(user_id: user_2_id) + role_3 = db[role_table].first(user_id: user_3_id) expect(role[:role_guid]).to be_a_guid expect(role_2[:role_guid]).to be_a_guid diff --git a/spec/migrations/20220818142407_add_unique_index_to_service_instance_operations_service_instance_id_spec.rb b/spec/migrations/20220818142407_add_unique_index_to_service_instance_operations_service_instance_id_spec.rb index b4f8e34e624..6e103a06782 100644 --- a/spec/migrations/20220818142407_add_unique_index_to_service_instance_operations_service_instance_id_spec.rb +++ b/spec/migrations/20220818142407_add_unique_index_to_service_instance_operations_service_instance_id_spec.rb @@ -1,62 +1,77 @@ -require 'spec_helper' +require 'migration_spec_helper' +require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add unique index on service_instance_id to service_instance_operations', isolation: :truncation, type: :migration do let(:filename) { '20220818142407_add_unique_index_to_service_instance_operations_service_instance_id.rb' } let(:tmp_migrations_dir) { Dir.mktmpdir } let(:db) { Sequel::Model.db } - let(:service_instance) { create(:service_instance) } + + def insert_quota_def + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", + non_basic_services_allowed: false, total_services: -1, memory_limit: 0, total_routes: -1) + end + + def insert_space + org_id = db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", quota_definition_id: insert_quota_def) + db[:spaces].insert(guid: SecureRandom.uuid, name: "space-#{SecureRandom.uuid}", organization_id: org_id) + end + + def insert_service_instance(space_id) + db[:service_instances].insert(guid: SecureRandom.uuid, name: "si-#{SecureRandom.uuid}", space_id: space_id) + end + + def insert_operation(service_instance_id: nil, updated_at: Time.now.utc) + now = Time.now.utc + db[:service_instance_operations].insert(guid: SecureRandom.uuid, type: 'create', state: 'succeeded', + service_instance_id: service_instance_id, updated_at: updated_at, created_at: now) + end before do FileUtils.cp(File.join(DBMigrator::SEQUEL_MIGRATIONS, filename), tmp_migrations_dir) - # Override the 'allow_manual_update' option of 'Sequel::Plugins::Timestamps' for 'ServiceInstanceOperation'. - VCAP::CloudController::ServiceInstanceOperation.instance_exec do - @allow_manual_timestamp_update = true - end - # Revert the given migration, i.e. remove the uniqueness constraint. Sequel::Migrator.run(db, tmp_migrations_dir, target: 0, allow_missing_migration_files: true) end it 'removes duplicate service instance operations' do + space_id = insert_space + # Two operations that do not reference a service instance (i.e. service_instance_id is nil); # none of them should be removed. - create(:service_instance_operation) - create(:service_instance_operation) + insert_operation + insert_operation # Two operations that each reference a different service instance (the 'normal' situation); # none of them should be removed. - si1 = create(:service_instance) - create(:service_instance_operation, service_instance_id: si1.id) - si2 = create(:service_instance) - create(:service_instance_operation, service_instance_id: si2.id) + si1_id = insert_service_instance(space_id) + insert_operation(service_instance_id: si1_id) + si2_id = insert_service_instance(space_id) + insert_operation(service_instance_id: si2_id) # Three operations that reference the same service instance and have the same 'updated_at' value; # the one with the highest 'id' should be kept (o3). - si3 = create(:service_instance) - o1 = create(:service_instance_operation, service_instance_id: si3.id) - o2 = create(:service_instance_operation, service_instance_id: si3.id) - o3 = create(:service_instance_operation, service_instance_id: si3.id) - o2.update({ updated_at: o1.updated_at }) - o3.update({ updated_at: o1.updated_at }) + now = Time.now.utc + si3_id = insert_service_instance(space_id) + o1_id = insert_operation(service_instance_id: si3_id, updated_at: now) + o2_id = insert_operation(service_instance_id: si3_id, updated_at: now) + insert_operation(service_instance_id: si3_id, updated_at: now) # Three operations that reference the same service instance and have different 'updated_at' values; # the one with the newest 'updated_at' value should be kept (o5). - si4 = create(:service_instance) - o4 = create(:service_instance_operation, service_instance_id: si4.id) - o5 = create(:service_instance_operation, service_instance_id: si4.id) - o6 = create(:service_instance_operation, service_instance_id: si4.id) - o5.update({ updated_at: o4.updated_at + 1 }) - o6.update({ updated_at: o4.updated_at - 1 }) + base_time = Time.now.utc + si4_id = insert_service_instance(space_id) + o4_id = insert_operation(service_instance_id: si4_id, updated_at: base_time) + insert_operation(service_instance_id: si4_id, updated_at: base_time + 1) + o6_id = insert_operation(service_instance_id: si4_id, updated_at: base_time - 1) - expect(VCAP::CloudController::ServiceInstanceOperation.count).to eq(10) + expect(db[:service_instance_operations].count).to eq(10) Sequel::Migrator.run(db, tmp_migrations_dir, allow_missing_migration_files: true) - expect(VCAP::CloudController::ServiceInstanceOperation.count).to eq(6) - expect { o1.reload }.to raise_error(Sequel::NoExistingObject) - expect { o2.reload }.to raise_error(Sequel::NoExistingObject) - expect { o4.reload }.to raise_error(Sequel::NoExistingObject) - expect { o6.reload }.to raise_error(Sequel::NoExistingObject) + expect(db[:service_instance_operations].count).to eq(6) + expect(db[:service_instance_operations].where(id: o1_id)).to be_empty + expect(db[:service_instance_operations].where(id: o2_id)).to be_empty + expect(db[:service_instance_operations].where(id: o4_id)).to be_empty + expect(db[:service_instance_operations].where(id: o6_id)).to be_empty end end diff --git a/spec/migrations/20231016094900_microsecond_timestamp_msql_asg_update_spec.rb b/spec/migrations/20231016094900_microsecond_timestamp_msql_asg_update_spec.rb index 7ccd9d1432e..bf2586722d6 100644 --- a/spec/migrations/20231016094900_microsecond_timestamp_msql_asg_update_spec.rb +++ b/spec/migrations/20231016094900_microsecond_timestamp_msql_asg_update_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to enable microsecond precision on asg last updated table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20231113105256_add_service_plan_id_index_spec.rb b/spec/migrations/20231113105256_add_service_plan_id_index_spec.rb index 85a620897a7..e50150690e1 100644 --- a/spec/migrations/20231113105256_add_service_plan_id_index_spec.rb +++ b/spec/migrations/20231113105256_add_service_plan_id_index_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add the service_plan_id index', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20231205143526_remove_deployments_with_degenerate_spec.rb b/spec/migrations/20231205143526_remove_deployments_with_degenerate_spec.rb index 2597a83adf9..c8017df8753 100644 --- a/spec/migrations/20231205143526_remove_deployments_with_degenerate_spec.rb +++ b/spec/migrations/20231205143526_remove_deployments_with_degenerate_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to clean up degenerate records from deployments records', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20231221123000_rename_annotations_key_column_spec.rb b/spec/migrations/20231221123000_rename_annotations_key_column_spec.rb index 561ec6d6342..9aab8c6daec 100644 --- a/spec/migrations/20231221123000_rename_annotations_key_column_spec.rb +++ b/spec/migrations/20231221123000_rename_annotations_key_column_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to streamline changes to annotation_key_prefix', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20240102150000_add_annotation_label_uniqueness_spec.rb b/spec/migrations/20240102150000_add_annotation_label_uniqueness_spec.rb index 6a9d27aad46..22793d92b11 100644 --- a/spec/migrations/20240102150000_add_annotation_label_uniqueness_spec.rb +++ b/spec/migrations/20240102150000_add_annotation_label_uniqueness_spec.rb @@ -1,195 +1,202 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add unique constraint to annotation and labels', isolation: :truncation, type: :migration do include_context 'migration' do let(:migration_filename) { '20240102150000_add_annotation_label_uniqueness.rb' } end - let(:isolation_segment) { VCAP::CloudController::IsolationSegmentModel } - let(:annotation) { VCAP::CloudController::IsolationSegmentAnnotationModel } - let(:label) { VCAP::CloudController::IsolationSegmentLabelModel } + + let(:iso_segs) { db[:isolation_segments] } + let(:annotations) { db[:isolation_segment_annotations] } + let(:labels) { db[:isolation_segment_labels] } + + def insert_segment(name) + guid = SecureRandom.uuid + now = Time.now.utc + iso_segs.insert(guid: guid, name: name, created_at: now, updated_at: now) + guid + end + + def insert_metadata(dataset, resource_guid, key_name:, value:, key_prefix: '') + now = Time.now.utc + dataset.insert(guid: SecureRandom.uuid, resource_guid: resource_guid, + key_prefix: key_prefix, key_name: key_name, value: value, + created_at: now, updated_at: now) + end + + def insert_annotation(resource_guid, key_name:, value:, key_prefix: '') + insert_metadata(annotations, resource_guid, key_name: key_name, value: value, key_prefix: key_prefix) + end + + def insert_label(resource_guid, key_name:, value:, key_prefix: '') + insert_metadata(labels, resource_guid, key_name: key_name, value: value, key_prefix: key_prefix) + end + + def exists?(table, id) + db[table].where(id: id).any? + end describe 'annotation tables' do it 'handles key truncation, duplicate removal, and uniqueness constraints' do - # Setup data for truncation test - i1 = isolation_segment.create(name: 'bommel') + seg1_guid = insert_segment('bommel') key_name_long = 'a' * 64 truncated_key_name = 'a' * 63 key_name_short = 'b' * 63 - trunc_a1 = annotation.create(resource_guid: i1.guid, key_name: key_name_long, value: 'some_value') - trunc_a2 = annotation.create(resource_guid: i1.guid, key_name: key_name_short, value: 'some_value2') + trunc_a1_id = insert_annotation(seg1_guid, key_name: key_name_long, value: 'some_value') + trunc_a2_id = insert_annotation(seg1_guid, key_name: key_name_short, value: 'some_value2') - # Setup data for duplicate removal test - i2 = isolation_segment.create(name: 'duplicate_test') + seg2_guid = insert_segment('duplicate_test') key_c = 'c' * 63 - # In case key_prefix is not set - dup_a1 = annotation.create(resource_guid: i2.guid, key_name: key_c, value: 'v1') - dup_a2 = annotation.create(resource_guid: i2.guid, key_name: key_c, value: 'v2') - dup_a3 = annotation.create(resource_guid: i2.guid, key_name: key_c, value: 'v3') + dup_a1_id = insert_annotation(seg2_guid, key_name: key_c, value: 'v1') + dup_a2_id = insert_annotation(seg2_guid, key_name: key_c, value: 'v2') + dup_a3_id = insert_annotation(seg2_guid, key_name: key_c, value: 'v3') - # In case key_prefix is set - dup_b1 = annotation.create(resource_guid: i2.guid, key_prefix: 'bommel', key_name: key_c, value: 'v1') - dup_b2 = annotation.create(resource_guid: i2.guid, key_prefix: 'bommel', key_name: key_c, value: 'v2') - dup_b3 = annotation.create(resource_guid: i2.guid, key_prefix: 'bommel', key_name: key_c, value: 'v3') + dup_b1_id = insert_annotation(seg2_guid, key_name: key_c, value: 'v1', key_prefix: 'bommel') + dup_b2_id = insert_annotation(seg2_guid, key_name: key_c, value: 'v2', key_prefix: 'bommel') + dup_b3_id = insert_annotation(seg2_guid, key_name: key_c, value: 'v3', key_prefix: 'bommel') - expect(dup_a1.id).to be < dup_a2.id - expect(dup_a1.id).to be < dup_a3.id - expect(dup_b1.id).to be < dup_b2.id - expect(dup_b1.id).to be < dup_b3.id + expect(dup_a1_id).to be < dup_a2_id + expect(dup_a1_id).to be < dup_a3_id + expect(dup_b1_id).to be < dup_b2_id + expect(dup_b1_id).to be < dup_b3_id - # Setup data for preservation test (different columns) - i3 = isolation_segment.create(name: 'sword') + seg3_guid = insert_segment('sword') key_d = 'd' * 63 key_e = 'e' * 63 - # In case key_prefix is not set - pres_a1 = annotation.create(resource_guid: i1.guid, key_name: key_d, value: 'v1') - pres_a2 = annotation.create(resource_guid: i3.guid, key_name: key_d, value: 'v2') - pres_a3 = annotation.create(resource_guid: i1.guid, key_name: key_e, value: 'v3') + pres_a1_id = insert_annotation(seg1_guid, key_name: key_d, value: 'v1') + pres_a2_id = insert_annotation(seg3_guid, key_name: key_d, value: 'v2') + pres_a3_id = insert_annotation(seg1_guid, key_name: key_e, value: 'v3') - # In case key_prefix is set - pres_b1 = annotation.create(resource_guid: i1.guid, key_prefix: 'prefix1', key_name: key_d, value: 'v1') - pres_b2 = annotation.create(resource_guid: i3.guid, key_prefix: 'prefix1', key_name: key_d, value: 'v2') - pres_b3 = annotation.create(resource_guid: i1.guid, key_prefix: 'prefix1', key_name: key_e, value: 'v3') - pres_b4 = annotation.create(resource_guid: i1.guid, key_prefix: 'prefix2', key_name: key_d, value: 'v4') + pres_b1_id = insert_annotation(seg1_guid, key_name: key_d, value: 'v1', key_prefix: 'prefix1') + pres_b2_id = insert_annotation(seg3_guid, key_name: key_d, value: 'v2', key_prefix: 'prefix1') + pres_b3_id = insert_annotation(seg1_guid, key_name: key_e, value: 'v3', key_prefix: 'prefix1') + pres_b4_id = insert_annotation(seg1_guid, key_name: key_d, value: 'v4', key_prefix: 'prefix2') - # Setup data for uniqueness constraint test - i4 = isolation_segment.create(name: 'unique_test') - i5 = isolation_segment.create(name: 'unique_test2') + seg4_guid = insert_segment('unique_test') + seg5_guid = insert_segment('unique_test2') key_f = 'f' * 63 key_g = 'g' * 63 - # In case key_prefix is not set - annotation.create(resource_guid: i4.guid, key_name: key_f, value: 'v1') - - # In case key_prefix is set - annotation.create(resource_guid: i5.guid, key_prefix: 'unique_prefix', key_name: key_f, value: 'v1') + insert_annotation(seg4_guid, key_name: key_f, value: 'v1') + insert_annotation(seg5_guid, key_name: key_f, value: 'v1', key_prefix: 'unique_prefix') # Run migration once expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error # Verify truncation behavior - expect(trunc_a1.reload.key_name).to eq(truncated_key_name) - expect(trunc_a2.reload.key_name).to eq(key_name_short) + expect(annotations.where(id: trunc_a1_id).get(:key_name)).to eq(truncated_key_name) + expect(annotations.where(id: trunc_a2_id).get(:key_name)).to eq(key_name_short) # Verify duplicate removal (keeps smallest id) - expect(annotation.where(resource_guid: i2.guid, key_name: key_c).count).to eq(2) - expect(dup_a1.reload).to be_a(annotation) - expect { dup_a2.reload }.to raise_error(Sequel::NoExistingObject) - expect { dup_a3.reload }.to raise_error(Sequel::NoExistingObject) - expect(dup_b1.reload).to be_a(annotation) - expect { dup_b2.reload }.to raise_error(Sequel::NoExistingObject) - expect { dup_b3.reload }.to raise_error(Sequel::NoExistingObject) + expect(annotations.where(resource_guid: seg2_guid, key_name: key_c).count).to eq(2) + expect(exists?(:isolation_segment_annotations, dup_a1_id)).to be true + expect(exists?(:isolation_segment_annotations, dup_a2_id)).to be false + expect(exists?(:isolation_segment_annotations, dup_a3_id)).to be false + expect(exists?(:isolation_segment_annotations, dup_b1_id)).to be true + expect(exists?(:isolation_segment_annotations, dup_b2_id)).to be false + expect(exists?(:isolation_segment_annotations, dup_b3_id)).to be false # Verify preservation of records with different columns - expect(pres_a1.reload).to be_a(annotation) - expect(pres_a2.reload).to be_a(annotation) - expect(pres_a3.reload).to be_a(annotation) - expect(pres_b1.reload).to be_a(annotation) - expect(pres_b2.reload).to be_a(annotation) - expect(pres_b3.reload).to be_a(annotation) - expect(pres_b4.reload).to be_a(annotation) + expect(exists?(:isolation_segment_annotations, pres_a1_id)).to be true + expect(exists?(:isolation_segment_annotations, pres_a2_id)).to be true + expect(exists?(:isolation_segment_annotations, pres_a3_id)).to be true + expect(exists?(:isolation_segment_annotations, pres_b1_id)).to be true + expect(exists?(:isolation_segment_annotations, pres_b2_id)).to be true + expect(exists?(:isolation_segment_annotations, pres_b3_id)).to be true + expect(exists?(:isolation_segment_annotations, pres_b4_id)).to be true # Verify uniqueness constraints: does not allow adding a duplicate - expect { annotation.create(resource_guid: i4.guid, key_name: key_f, value: 'v2') }.to raise_error(Sequel::UniqueConstraintViolation) - expect { annotation.create(resource_guid: i5.guid, key_prefix: 'unique_prefix', key_name: key_f, value: 'v2') }.to raise_error(Sequel::UniqueConstraintViolation) + expect { insert_annotation(seg4_guid, key_name: key_f, value: 'v2') }.to raise_error(Sequel::UniqueConstraintViolation) + expect { insert_annotation(seg5_guid, key_name: key_f, value: 'v2', key_prefix: 'unique_prefix') }.to raise_error(Sequel::UniqueConstraintViolation) # Verify uniqueness constraints: does allow adding different annotations - uniq_a1 = annotation.create(resource_guid: i4.guid, key_name: key_g, value: 'v3') - uniq_a2 = annotation.create(resource_guid: i5.guid, key_name: key_g, value: 'v2') - uniq_b1 = annotation.create(resource_guid: i4.guid, key_prefix: 'other_prefix', key_name: key_f, value: 'v4') - uniq_b2 = annotation.create(resource_guid: i5.guid, key_prefix: 'other_prefix', key_name: key_f, value: 'v5') - expect(annotation.where(key_name: key_g).count).to eq(2) - expect(uniq_a1.reload).to be_a(annotation) - expect(uniq_a2.reload).to be_a(annotation) - expect(uniq_b1.reload).to be_a(annotation) - expect(uniq_b2.reload).to be_a(annotation) + uniq_a1_id = insert_annotation(seg4_guid, key_name: key_g, value: 'v3') + uniq_a2_id = insert_annotation(seg5_guid, key_name: key_g, value: 'v2') + uniq_b1_id = insert_annotation(seg4_guid, key_name: key_f, value: 'v4', key_prefix: 'other_prefix') + uniq_b2_id = insert_annotation(seg5_guid, key_name: key_f, value: 'v5', key_prefix: 'other_prefix') + expect(annotations.where(key_name: key_g).count).to eq(2) + expect(exists?(:isolation_segment_annotations, uniq_a1_id)).to be true + expect(exists?(:isolation_segment_annotations, uniq_a2_id)).to be true + expect(exists?(:isolation_segment_annotations, uniq_b1_id)).to be true + expect(exists?(:isolation_segment_annotations, uniq_b2_id)).to be true end end describe 'labels tables' do it 'handles duplicate removal and uniqueness constraints' do - # Setup data for duplicate removal test - i1 = isolation_segment.create(name: 'label_dup_test') + seg1_guid = insert_segment('label_dup_test') key_a = 'a' * 63 - # In case key_prefix is not set - dup_a1 = label.create(resource_guid: i1.guid, key_name: key_a, value: 'v1') - dup_a2 = label.create(resource_guid: i1.guid, key_name: key_a, value: 'v2') - dup_a3 = label.create(resource_guid: i1.guid, key_name: key_a, value: 'v3') - - # In case key_prefix is set - dup_b1 = label.create(resource_guid: i1.guid, key_prefix: 'bommel', key_name: key_a, value: 'v1') - dup_b2 = label.create(resource_guid: i1.guid, key_prefix: 'bommel', key_name: key_a, value: 'v2') - dup_b3 = label.create(resource_guid: i1.guid, key_prefix: 'bommel', key_name: key_a, value: 'v3') - expect(dup_a1.id).to be < dup_a2.id - expect(dup_a1.id).to be < dup_a3.id - expect(dup_b1.id).to be < dup_b2.id - expect(dup_b1.id).to be < dup_b3.id - - # Setup data for preservation test (different columns) - i2 = isolation_segment.create(name: 'label_preserve_test') + dup_a1_id = insert_label(seg1_guid, key_name: key_a, value: 'v1') + dup_a2_id = insert_label(seg1_guid, key_name: key_a, value: 'v2') + dup_a3_id = insert_label(seg1_guid, key_name: key_a, value: 'v3') + + dup_b1_id = insert_label(seg1_guid, key_name: key_a, value: 'v1', key_prefix: 'bommel') + dup_b2_id = insert_label(seg1_guid, key_name: key_a, value: 'v2', key_prefix: 'bommel') + dup_b3_id = insert_label(seg1_guid, key_name: key_a, value: 'v3', key_prefix: 'bommel') + + expect(dup_a1_id).to be < dup_a2_id + expect(dup_a1_id).to be < dup_a3_id + expect(dup_b1_id).to be < dup_b2_id + expect(dup_b1_id).to be < dup_b3_id + + seg2_guid = insert_segment('label_preserve_test') key_b = 'b' * 63 key_c = 'c' * 63 - # In case key_prefix is not set - pres_a1 = label.create(resource_guid: i1.guid, key_name: key_b, value: 'v1') - pres_a2 = label.create(resource_guid: i2.guid, key_name: key_b, value: 'v2') - pres_a3 = label.create(resource_guid: i1.guid, key_name: key_c, value: 'v3') + pres_a1_id = insert_label(seg1_guid, key_name: key_b, value: 'v1') + pres_a2_id = insert_label(seg2_guid, key_name: key_b, value: 'v2') + pres_a3_id = insert_label(seg1_guid, key_name: key_c, value: 'v3') - # In case key_prefix is set - pres_b1 = label.create(resource_guid: i1.guid, key_prefix: 'prefix1', key_name: key_b, value: 'v1') - pres_b2 = label.create(resource_guid: i2.guid, key_prefix: 'prefix1', key_name: key_b, value: 'v2') - pres_b3 = label.create(resource_guid: i1.guid, key_prefix: 'prefix1', key_name: key_c, value: 'v3') - pres_b4 = label.create(resource_guid: i1.guid, key_prefix: 'prefix2', key_name: key_b, value: 'v4') + pres_b1_id = insert_label(seg1_guid, key_name: key_b, value: 'v1', key_prefix: 'prefix1') + pres_b2_id = insert_label(seg2_guid, key_name: key_b, value: 'v2', key_prefix: 'prefix1') + pres_b3_id = insert_label(seg1_guid, key_name: key_c, value: 'v3', key_prefix: 'prefix1') + pres_b4_id = insert_label(seg1_guid, key_name: key_b, value: 'v4', key_prefix: 'prefix2') - # Setup data for uniqueness constraint test - i3 = isolation_segment.create(name: 'label_unique_test') - i4 = isolation_segment.create(name: 'label_unique_test2') + seg3_guid = insert_segment('label_unique_test') + seg4_guid = insert_segment('label_unique_test2') key_d = 'd' * 63 key_e = 'e' * 63 - # In case key_prefix is not set - label.create(resource_guid: i3.guid, key_name: key_d, value: 'v1') - - # In case key_prefix is set - label.create(resource_guid: i4.guid, key_prefix: 'unique_prefix', key_name: key_d, value: 'v1') + insert_label(seg3_guid, key_name: key_d, value: 'v1') + insert_label(seg4_guid, key_name: key_d, value: 'v1', key_prefix: 'unique_prefix') # Run migration once expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error # Verify duplicate removal (keeps smallest id) - expect(label.where(resource_guid: i1.guid, key_name: key_a).count).to eq(2) - expect(dup_a1.reload).to be_a(label) - expect { dup_a2.reload }.to raise_error(Sequel::NoExistingObject) - expect { dup_a3.reload }.to raise_error(Sequel::NoExistingObject) - expect(dup_b1.reload).to be_a(label) - expect { dup_b2.reload }.to raise_error(Sequel::NoExistingObject) - expect { dup_b3.reload }.to raise_error(Sequel::NoExistingObject) + expect(labels.where(resource_guid: seg1_guid, key_name: key_a).count).to eq(2) + expect(exists?(:isolation_segment_labels, dup_a1_id)).to be true + expect(exists?(:isolation_segment_labels, dup_a2_id)).to be false + expect(exists?(:isolation_segment_labels, dup_a3_id)).to be false + expect(exists?(:isolation_segment_labels, dup_b1_id)).to be true + expect(exists?(:isolation_segment_labels, dup_b2_id)).to be false + expect(exists?(:isolation_segment_labels, dup_b3_id)).to be false # Verify preservation of records with different columns - expect(pres_a1.reload).to be_a(label) - expect(pres_a2.reload).to be_a(label) - expect(pres_a3.reload).to be_a(label) - expect(pres_b1.reload).to be_a(label) - expect(pres_b2.reload).to be_a(label) - expect(pres_b3.reload).to be_a(label) - expect(pres_b4.reload).to be_a(label) + expect(exists?(:isolation_segment_labels, pres_a1_id)).to be true + expect(exists?(:isolation_segment_labels, pres_a2_id)).to be true + expect(exists?(:isolation_segment_labels, pres_a3_id)).to be true + expect(exists?(:isolation_segment_labels, pres_b1_id)).to be true + expect(exists?(:isolation_segment_labels, pres_b2_id)).to be true + expect(exists?(:isolation_segment_labels, pres_b3_id)).to be true + expect(exists?(:isolation_segment_labels, pres_b4_id)).to be true # Verify uniqueness constraints: does not allow adding a duplicate - expect { label.create(resource_guid: i3.guid, key_name: key_d, value: 'v2') }.to raise_error(Sequel::UniqueConstraintViolation) - expect { label.create(resource_guid: i4.guid, key_prefix: 'unique_prefix', key_name: key_d, value: 'v2') }.to raise_error(Sequel::UniqueConstraintViolation) + expect { insert_label(seg3_guid, key_name: key_d, value: 'v2') }.to raise_error(Sequel::UniqueConstraintViolation) + expect { insert_label(seg4_guid, key_name: key_d, value: 'v2', key_prefix: 'unique_prefix') }.to raise_error(Sequel::UniqueConstraintViolation) # Verify uniqueness constraints: does allow adding different labels - uniq_a1 = label.create(resource_guid: i3.guid, key_name: key_e, value: 'v3') - uniq_a2 = label.create(resource_guid: i4.guid, key_name: key_e, value: 'v2') - uniq_b1 = label.create(resource_guid: i3.guid, key_prefix: 'other_prefix', key_name: key_d, value: 'v4') - uniq_b2 = label.create(resource_guid: i4.guid, key_prefix: 'other_prefix', key_name: key_d, value: 'v5') - expect(label.where(key_name: key_e).count).to eq(2) - expect(uniq_a1.reload).to be_a(label) - expect(uniq_a2.reload).to be_a(label) - expect(uniq_b1.reload).to be_a(label) - expect(uniq_b2.reload).to be_a(label) + uniq_a1_id = insert_label(seg3_guid, key_name: key_e, value: 'v3') + uniq_a2_id = insert_label(seg4_guid, key_name: key_e, value: 'v2') + uniq_b1_id = insert_label(seg3_guid, key_name: key_d, value: 'v4', key_prefix: 'other_prefix') + uniq_b2_id = insert_label(seg4_guid, key_name: key_d, value: 'v5', key_prefix: 'other_prefix') + expect(labels.where(key_name: key_e).count).to eq(2) + expect(exists?(:isolation_segment_labels, uniq_a1_id)).to be true + expect(exists?(:isolation_segment_labels, uniq_a2_id)).to be true + expect(exists?(:isolation_segment_labels, uniq_b1_id)).to be true + expect(exists?(:isolation_segment_labels, uniq_b2_id)).to be true end end end diff --git a/spec/migrations/20240115163000_add_delete_cascade_to_foreign_keys_spec.rb b/spec/migrations/20240115163000_add_delete_cascade_to_foreign_keys_spec.rb index 9e0d290847a..3690994f94c 100644 --- a/spec/migrations/20240115163000_add_delete_cascade_to_foreign_keys_spec.rb +++ b/spec/migrations/20240115163000_add_delete_cascade_to_foreign_keys_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add delete cascade to foreign keys', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20240219113000_add_routes_space_id_index_spec.rb b/spec/migrations/20240219113000_add_routes_space_id_index_spec.rb index 3c04f8b98f0..8a68c8616a5 100644 --- a/spec/migrations/20240219113000_add_routes_space_id_index_spec.rb +++ b/spec/migrations/20240219113000_add_routes_space_id_index_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add the routes_space_id index', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20240222131500_change_delayed_jobs_reserve_index_spec.rb b/spec/migrations/20240222131500_change_delayed_jobs_reserve_index_spec.rb index fa0683d7029..694956b0ef0 100644 --- a/spec/migrations/20240222131500_change_delayed_jobs_reserve_index_spec.rb +++ b/spec/migrations/20240222131500_change_delayed_jobs_reserve_index_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to change the delayed_jobs_reserve index', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20240314131908_add_user_guid_to_jobs_table_spec.rb b/spec/migrations/20240314131908_add_user_guid_to_jobs_table_spec.rb index 7fc29acbacd..f0917747fb1 100644 --- a/spec/migrations/20240314131908_add_user_guid_to_jobs_table_spec.rb +++ b/spec/migrations/20240314131908_add_user_guid_to_jobs_table_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add user_guid column to jobs table and add an index for that column', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20240529195136_add_task_guid_index_to_app_usage_events_spec.rb b/spec/migrations/20240529195136_add_task_guid_index_to_app_usage_events_spec.rb index 48b1513cd1f..1ee4d3c2cba 100644 --- a/spec/migrations/20240529195136_add_task_guid_index_to_app_usage_events_spec.rb +++ b/spec/migrations/20240529195136_add_task_guid_index_to_app_usage_events_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add an index for task_guid on app_usage_events table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20240820070742_add_jobs_user_guid_state_index_spec.rb b/spec/migrations/20240820070742_add_jobs_user_guid_state_index_spec.rb index f8601a9ef64..d5a48dd250f 100644 --- a/spec/migrations/20240820070742_add_jobs_user_guid_state_index_spec.rb +++ b/spec/migrations/20240820070742_add_jobs_user_guid_state_index_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' def partial_index_present diff --git a/spec/migrations/20240904132000_add_foreign_key_apps_droplet_guid_spec.rb b/spec/migrations/20240904132000_add_foreign_key_apps_droplet_guid_spec.rb index 1d2940b8673..9df3b6648de 100644 --- a/spec/migrations/20240904132000_add_foreign_key_apps_droplet_guid_spec.rb +++ b/spec/migrations/20240904132000_add_foreign_key_apps_droplet_guid_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe "migration to add foreign key on column 'droplet_guid' in table 'apps'", isolation: :truncation, type: :migration do diff --git a/spec/migrations/20241016118000_drop_unique_constraint_quota_definitions_name_key_spec.rb b/spec/migrations/20241016118000_drop_unique_constraint_quota_definitions_name_key_spec.rb index c3af1f6c70b..12faa83270a 100644 --- a/spec/migrations/20241016118000_drop_unique_constraint_quota_definitions_name_key_spec.rb +++ b/spec/migrations/20241016118000_drop_unique_constraint_quota_definitions_name_key_spec.rb @@ -1,5 +1,5 @@ # rubocop:disable Migration/TooManyMigrationRuns -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add or remove unique constraint on name column in quota_definitions table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20241203085500_add_apps_file_based_service_bindings_enabled_column_spec.rb b/spec/migrations/20241203085500_add_apps_file_based_service_bindings_enabled_column_spec.rb index 8d48828fe0f..3caffd7de1a 100644 --- a/spec/migrations/20241203085500_add_apps_file_based_service_bindings_enabled_column_spec.rb +++ b/spec/migrations/20241203085500_add_apps_file_based_service_bindings_enabled_column_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add file_based_service_bindings_enabled column to apps table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250116144231_remove_unnecessary_fk_in_job_warnings_spec.rb b/spec/migrations/20250116144231_remove_unnecessary_fk_in_job_warnings_spec.rb index c954f0cb703..132754ad163 100644 --- a/spec/migrations/20250116144231_remove_unnecessary_fk_in_job_warnings_spec.rb +++ b/spec/migrations/20250116144231_remove_unnecessary_fk_in_job_warnings_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe "migration to remove foreign key constraint on table 'job_warnings' and column 'fk_jobs_id'", isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250225132929_add_apps_file_based_service_binding_feature_columns_spec.rb b/spec/migrations/20250225132929_add_apps_file_based_service_binding_feature_columns_spec.rb index 436a459aa72..0d2e5dc99a2 100644 --- a/spec/migrations/20250225132929_add_apps_file_based_service_binding_feature_columns_spec.rb +++ b/spec/migrations/20250225132929_add_apps_file_based_service_binding_feature_columns_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add file-based service binding feature columns to apps table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250318112800_add_user_id_index_to_roles_tables_spec.rb b/spec/migrations/20250318112800_add_user_id_index_to_roles_tables_spec.rb index 87e7c9c1da5..0f8f23ba965 100644 --- a/spec/migrations/20250318112800_add_user_id_index_to_roles_tables_spec.rb +++ b/spec/migrations/20250318112800_add_user_id_index_to_roles_tables_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add an index for user_id on all roles tables', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250327142351_bigint_migration_events_step1_spec.rb b/spec/migrations/20250327142351_bigint_migration_events_step1_spec.rb index 23ee2e68385..ec2fd6417df 100644 --- a/spec/migrations/20250327142351_bigint_migration_events_step1_spec.rb +++ b/spec/migrations/20250327142351_bigint_migration_events_step1_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step1_shared_context' RSpec.describe 'bigint migration - events table - step1', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250603103400_bigint_migration_events_step3a_spec.rb b/spec/migrations/20250603103400_bigint_migration_events_step3a_spec.rb index 9d2920a0ee2..f5d2f272781 100644 --- a/spec/migrations/20250603103400_bigint_migration_events_step3a_spec.rb +++ b/spec/migrations/20250603103400_bigint_migration_events_step3a_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - events table - step3a', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250603103500_bigint_migration_events_step3b_spec.rb b/spec/migrations/20250603103500_bigint_migration_events_step3b_spec.rb index 078d7f58f76..f16f7518886 100644 --- a/spec/migrations/20250603103500_bigint_migration_events_step3b_spec.rb +++ b/spec/migrations/20250603103500_bigint_migration_events_step3b_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - events table - step3b', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250610212414_add_user_to_processes_spec.rb b/spec/migrations/20250610212414_add_user_to_processes_spec.rb index 6b859a26139..5a1aa303d88 100644 --- a/spec/migrations/20250610212414_add_user_to_processes_spec.rb +++ b/spec/migrations/20250610212414_add_user_to_processes_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add user column to processes table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250630170610_add_user_to_tasks_spec.rb b/spec/migrations/20250630170610_add_user_to_tasks_spec.rb index 015487f6a7b..eb52cf84019 100644 --- a/spec/migrations/20250630170610_add_user_to_tasks_spec.rb +++ b/spec/migrations/20250630170610_add_user_to_tasks_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add user column to tasks table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250729142800_bigint_migration_delayed_jobs_step1_spec.rb b/spec/migrations/20250729142800_bigint_migration_delayed_jobs_step1_spec.rb index 8cbd5ac8deb..e091cc86067 100644 --- a/spec/migrations/20250729142800_bigint_migration_delayed_jobs_step1_spec.rb +++ b/spec/migrations/20250729142800_bigint_migration_delayed_jobs_step1_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step1_shared_context' RSpec.describe 'bigint migration - delayed_jobs table - step1', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250729142900_bigint_migration_jobs_step1_spec.rb b/spec/migrations/20250729142900_bigint_migration_jobs_step1_spec.rb index 5a340cd3322..727107c2661 100644 --- a/spec/migrations/20250729142900_bigint_migration_jobs_step1_spec.rb +++ b/spec/migrations/20250729142900_bigint_migration_jobs_step1_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step1_shared_context' RSpec.describe 'bigint migration - jobs table - step1', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250729143000_bigint_migration_app_usage_events_step1_spec.rb b/spec/migrations/20250729143000_bigint_migration_app_usage_events_step1_spec.rb index 2029a53df65..e7e51182008 100644 --- a/spec/migrations/20250729143000_bigint_migration_app_usage_events_step1_spec.rb +++ b/spec/migrations/20250729143000_bigint_migration_app_usage_events_step1_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step1_shared_context' RSpec.describe 'bigint migration - app_usage_events table - step1', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250729143100_bigint_migration_service_usage_events_step1_spec.rb b/spec/migrations/20250729143100_bigint_migration_service_usage_events_step1_spec.rb index c8a0a0dc46f..e7747d2ac83 100644 --- a/spec/migrations/20250729143100_bigint_migration_service_usage_events_step1_spec.rb +++ b/spec/migrations/20250729143100_bigint_migration_service_usage_events_step1_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step1_shared_context' RSpec.describe 'bigint migration - service_usage_events table - step1', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250729143100_bigint_migration_service_usage_events_step3a_spec.rb b/spec/migrations/20250729143100_bigint_migration_service_usage_events_step3a_spec.rb index a47236a26e8..936bd20b0de 100644 --- a/spec/migrations/20250729143100_bigint_migration_service_usage_events_step3a_spec.rb +++ b/spec/migrations/20250729143100_bigint_migration_service_usage_events_step3a_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - service_usage_events table - step3a', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250930135451_bigint_migration_delayed_jobs_step3a_spec.rb b/spec/migrations/20250930135451_bigint_migration_delayed_jobs_step3a_spec.rb index 89134c4393c..6c5bf62529d 100644 --- a/spec/migrations/20250930135451_bigint_migration_delayed_jobs_step3a_spec.rb +++ b/spec/migrations/20250930135451_bigint_migration_delayed_jobs_step3a_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - delayed jobs table - step3a', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250930135459_bigint_migration_delayed_jobs_step3b_spec.rb b/spec/migrations/20250930135459_bigint_migration_delayed_jobs_step3b_spec.rb index 0e78cd3c929..006a67e8c2d 100644 --- a/spec/migrations/20250930135459_bigint_migration_delayed_jobs_step3b_spec.rb +++ b/spec/migrations/20250930135459_bigint_migration_delayed_jobs_step3b_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - delayed jobs table - step3b', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250930135517_bigint_migration_jobs_step3a_spec.rb b/spec/migrations/20250930135517_bigint_migration_jobs_step3a_spec.rb index 7b5cc6f0ab1..c383bab7fed 100644 --- a/spec/migrations/20250930135517_bigint_migration_jobs_step3a_spec.rb +++ b/spec/migrations/20250930135517_bigint_migration_jobs_step3a_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - jobs table - step3a', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250930135527_bigint_migration_jobs_step3b_spec.rb b/spec/migrations/20250930135527_bigint_migration_jobs_step3b_spec.rb index 8d34d120865..4ec5ee8673b 100644 --- a/spec/migrations/20250930135527_bigint_migration_jobs_step3b_spec.rb +++ b/spec/migrations/20250930135527_bigint_migration_jobs_step3b_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - jobs table - step3b', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250930135548_bigint_migration_app_usage_events_step3a_spec.rb b/spec/migrations/20250930135548_bigint_migration_app_usage_events_step3a_spec.rb index 03bd22b4f0f..8bbfc9af9fe 100644 --- a/spec/migrations/20250930135548_bigint_migration_app_usage_events_step3a_spec.rb +++ b/spec/migrations/20250930135548_bigint_migration_app_usage_events_step3a_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - app_usage_events table - step3a', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250930135554_bigint_migration_app_usage_events_step3b_spec.rb b/spec/migrations/20250930135554_bigint_migration_app_usage_events_step3b_spec.rb index cea59d787de..9335cb2b646 100644 --- a/spec/migrations/20250930135554_bigint_migration_app_usage_events_step3b_spec.rb +++ b/spec/migrations/20250930135554_bigint_migration_app_usage_events_step3b_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - app_usage_events table - step3b', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20250930135619_bigint_migration_service_usage_events_step3b_spec.rb b/spec/migrations/20250930135619_bigint_migration_service_usage_events_step3b_spec.rb index 3cead228538..208f6ab8fcb 100644 --- a/spec/migrations/20250930135619_bigint_migration_service_usage_events_step3b_spec.rb +++ b/spec/migrations/20250930135619_bigint_migration_service_usage_events_step3b_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/bigint_migration_step3_shared_context' RSpec.describe 'bigint migration - service_usage_events table - step3b', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20251015071027_allow_multiple_service_bindings_spec.rb b/spec/migrations/20251015071027_allow_multiple_service_bindings_spec.rb index d1a75a4db87..6a30c8bb070 100644 --- a/spec/migrations/20251015071027_allow_multiple_service_bindings_spec.rb +++ b/spec/migrations/20251015071027_allow_multiple_service_bindings_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to allow multiple service bindings', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20251016120006_security_groups_spaces_unique_index_spec.rb b/spec/migrations/20251016120006_security_groups_spaces_unique_index_spec.rb index 0528a5dd2f2..703e1d18722 100644 --- a/spec/migrations/20251016120006_security_groups_spaces_unique_index_spec.rb +++ b/spec/migrations/20251016120006_security_groups_spaces_unique_index_spec.rb @@ -1,15 +1,32 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'security groups spaces unique index', isolation: :truncation, type: :migration do include_context 'migration' do - let(:migration_filename) { '20251016120006_security_groups_spaces_unique_index_spec.rb' } + let(:migration_filename) { '20251016120006_security_groups_spaces_unique_index.rb' } end - let(:space_1) { create(:space) } - let(:space_2) { create(:space) } - let(:sec_group_1) { create(:security_group) } - let(:sec_group_2) { create(:security_group) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", non_basic_services_allowed: false, total_services: -1, memory_limit: 0, + total_routes: -1, created_at: Time.now.utc, updated_at: Time.now.utc) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", quota_definition_id: quota_def_id, created_at: Time.now.utc, updated_at: Time.now.utc) + end + let(:space_1_id) do + db[:spaces].insert(guid: SecureRandom.uuid, name: "space-1-#{SecureRandom.uuid}", organization_id: org_id, created_at: Time.now.utc, updated_at: Time.now.utc) + end + let(:space_2_id) do + db[:spaces].insert(guid: SecureRandom.uuid, name: "space-2-#{SecureRandom.uuid}", organization_id: org_id, created_at: Time.now.utc, updated_at: Time.now.utc) + end + let(:sec_group_1_id) do + db[:security_groups].insert(guid: SecureRandom.uuid, name: "sg-1-#{SecureRandom.uuid}", rules: '[]', staging_default: false, running_default: false, created_at: Time.now.utc, + updated_at: Time.now.utc) + end + let(:sec_group_2_id) do + db[:security_groups].insert(guid: SecureRandom.uuid, name: "sg-2-#{SecureRandom.uuid}", rules: '[]', staging_default: false, running_default: false, created_at: Time.now.utc, + updated_at: Time.now.utc) + end describe 'security_groups_spaces table' do it 'removes duplicates, updates indexes, and handles idempotency' do @@ -18,28 +35,28 @@ expect(db.indexes(:security_groups_spaces)).not_to include(:security_groups_spaces_ids) # Insert test data with duplicates - db[:security_groups_spaces].insert(security_group_id: sec_group_1.id, space_id: space_1.id) - db[:security_groups_spaces].insert(security_group_id: sec_group_1.id, space_id: space_1.id) - db[:security_groups_spaces].insert(security_group_id: sec_group_1.id, space_id: space_2.id) - db[:security_groups_spaces].insert(security_group_id: sec_group_2.id, space_id: space_1.id) - db[:security_groups_spaces].insert(security_group_id: sec_group_2.id, space_id: space_2.id) - db[:security_groups_spaces].insert(security_group_id: sec_group_2.id, space_id: space_2.id) - db[:security_groups_spaces].insert(security_group_id: sec_group_2.id, space_id: space_2.id) + db[:security_groups_spaces].insert(security_group_id: sec_group_1_id, space_id: space_1_id) + db[:security_groups_spaces].insert(security_group_id: sec_group_1_id, space_id: space_1_id) + db[:security_groups_spaces].insert(security_group_id: sec_group_1_id, space_id: space_2_id) + db[:security_groups_spaces].insert(security_group_id: sec_group_2_id, space_id: space_1_id) + db[:security_groups_spaces].insert(security_group_id: sec_group_2_id, space_id: space_2_id) + db[:security_groups_spaces].insert(security_group_id: sec_group_2_id, space_id: space_2_id) + db[:security_groups_spaces].insert(security_group_id: sec_group_2_id, space_id: space_2_id) # Count duplicates before migration - expect(db[:security_groups_spaces].where(security_group_id: sec_group_1.id, space_id: space_1.id).count).to eq(2) - expect(db[:security_groups_spaces].where(security_group_id: sec_group_1.id, space_id: space_2.id).count).to eq(1) - expect(db[:security_groups_spaces].where(security_group_id: sec_group_2.id, space_id: space_1.id).count).to eq(1) - expect(db[:security_groups_spaces].where(security_group_id: sec_group_2.id, space_id: space_2.id).count).to eq(3) + expect(db[:security_groups_spaces].where(security_group_id: sec_group_1_id, space_id: space_1_id).count).to eq(2) + expect(db[:security_groups_spaces].where(security_group_id: sec_group_1_id, space_id: space_2_id).count).to eq(1) + expect(db[:security_groups_spaces].where(security_group_id: sec_group_2_id, space_id: space_1_id).count).to eq(1) + expect(db[:security_groups_spaces].where(security_group_id: sec_group_2_id, space_id: space_2_id).count).to eq(3) # === UP MIGRATION === expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error # Verify duplicates are removed after migration - expect(db[:security_groups_spaces].where(security_group_id: sec_group_1.id, space_id: space_1.id).count).to eq(1) - expect(db[:security_groups_spaces].where(security_group_id: sec_group_1.id, space_id: space_2.id).count).to eq(1) - expect(db[:security_groups_spaces].where(security_group_id: sec_group_2.id, space_id: space_1.id).count).to eq(1) - expect(db[:security_groups_spaces].where(security_group_id: sec_group_2.id, space_id: space_2.id).count).to eq(1) + expect(db[:security_groups_spaces].where(security_group_id: sec_group_1_id, space_id: space_1_id).count).to eq(1) + expect(db[:security_groups_spaces].where(security_group_id: sec_group_1_id, space_id: space_2_id).count).to eq(1) + expect(db[:security_groups_spaces].where(security_group_id: sec_group_2_id, space_id: space_1_id).count).to eq(1) + expect(db[:security_groups_spaces].where(security_group_id: sec_group_2_id, space_id: space_2_id).count).to eq(1) # Verify indexes are updated expect(db.indexes(:security_groups_spaces)).not_to include(:sgs_spaces_ids) diff --git a/spec/migrations/20251028135214_route_bindings_unique_index_spec.rb b/spec/migrations/20251028135214_route_bindings_unique_index_spec.rb index dc6dedef54b..9f484e3584b 100644 --- a/spec/migrations/20251028135214_route_bindings_unique_index_spec.rb +++ b/spec/migrations/20251028135214_route_bindings_unique_index_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'route bindings unique index', isolation: :truncation, type: :migration do @@ -6,11 +6,17 @@ let(:migration_filename) { '20251028135214_route_bindings_unique_index.rb' } end - let(:space) { create(:space) } - let(:service_instance_1) { create(:service_instance, space:) } - let(:service_instance_2) { create(:service_instance, space:) } - let(:route_1) { create(:route, space:) } - let(:route_2) { create(:route, space:) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", non_basic_services_allowed: false, total_services: -1, memory_limit: 0, + total_routes: -1) + end + let(:org_id) { db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", quota_definition_id: quota_def_id) } + let(:space_id) { db[:spaces].insert(guid: SecureRandom.uuid, name: "space-#{SecureRandom.uuid}", organization_id: org_id) } + let(:domain_id) { db[:domains].insert(guid: SecureRandom.uuid, name: "domain-#{SecureRandom.uuid}.example.com") } + let(:service_instance_1_id) { db[:service_instances].insert(guid: SecureRandom.uuid, name: "si1-#{SecureRandom.uuid}", space_id: space_id) } + let(:service_instance_2_id) { db[:service_instances].insert(guid: SecureRandom.uuid, name: "si2-#{SecureRandom.uuid}", space_id: space_id) } + let(:route_1_id) { db[:routes].insert(guid: SecureRandom.uuid, host: 'r1', domain_id: domain_id, space_id: space_id) } + let(:route_2_id) { db[:routes].insert(guid: SecureRandom.uuid, host: 'r2', domain_id: domain_id, space_id: space_id) } describe 'route_bindings table' do it 'removes duplicates, manages unique index, and handles idempotency' do @@ -18,28 +24,28 @@ expect(db.indexes(:route_bindings)).not_to include(:route_bindings_route_id_service_instance_id_index) # Insert test data with duplicates - db[:route_bindings].insert(route_id: route_1.id, service_instance_id: service_instance_1.id, guid: SecureRandom.uuid) - db[:route_bindings].insert(route_id: route_1.id, service_instance_id: service_instance_1.id, guid: SecureRandom.uuid) - db[:route_bindings].insert(route_id: route_2.id, service_instance_id: service_instance_1.id, guid: SecureRandom.uuid) - db[:route_bindings].insert(route_id: route_1.id, service_instance_id: service_instance_2.id, guid: SecureRandom.uuid) - db[:route_bindings].insert(route_id: route_2.id, service_instance_id: service_instance_2.id, guid: SecureRandom.uuid) - db[:route_bindings].insert(route_id: route_2.id, service_instance_id: service_instance_2.id, guid: SecureRandom.uuid) - db[:route_bindings].insert(route_id: route_2.id, service_instance_id: service_instance_2.id, guid: SecureRandom.uuid) + db[:route_bindings].insert(route_id: route_1_id, service_instance_id: service_instance_1_id, guid: SecureRandom.uuid) + db[:route_bindings].insert(route_id: route_1_id, service_instance_id: service_instance_1_id, guid: SecureRandom.uuid) + db[:route_bindings].insert(route_id: route_2_id, service_instance_id: service_instance_1_id, guid: SecureRandom.uuid) + db[:route_bindings].insert(route_id: route_1_id, service_instance_id: service_instance_2_id, guid: SecureRandom.uuid) + db[:route_bindings].insert(route_id: route_2_id, service_instance_id: service_instance_2_id, guid: SecureRandom.uuid) + db[:route_bindings].insert(route_id: route_2_id, service_instance_id: service_instance_2_id, guid: SecureRandom.uuid) + db[:route_bindings].insert(route_id: route_2_id, service_instance_id: service_instance_2_id, guid: SecureRandom.uuid) # Count duplicates before migration - expect(db[:route_bindings].where(service_instance_id: service_instance_1.id, route_id: route_1.id).count).to eq(2) - expect(db[:route_bindings].where(service_instance_id: service_instance_1.id, route_id: route_2.id).count).to eq(1) - expect(db[:route_bindings].where(service_instance_id: service_instance_2.id, route_id: route_1.id).count).to eq(1) - expect(db[:route_bindings].where(service_instance_id: service_instance_2.id, route_id: route_2.id).count).to eq(3) + expect(db[:route_bindings].where(service_instance_id: service_instance_1_id, route_id: route_1_id).count).to eq(2) + expect(db[:route_bindings].where(service_instance_id: service_instance_1_id, route_id: route_2_id).count).to eq(1) + expect(db[:route_bindings].where(service_instance_id: service_instance_2_id, route_id: route_1_id).count).to eq(1) + expect(db[:route_bindings].where(service_instance_id: service_instance_2_id, route_id: route_2_id).count).to eq(3) # === UP MIGRATION === expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error # Verify duplicates are removed after migration - expect(db[:route_bindings].where(service_instance_id: service_instance_1.id, route_id: route_1.id).count).to eq(1) - expect(db[:route_bindings].where(service_instance_id: service_instance_1.id, route_id: route_2.id).count).to eq(1) - expect(db[:route_bindings].where(service_instance_id: service_instance_2.id, route_id: route_1.id).count).to eq(1) - expect(db[:route_bindings].where(service_instance_id: service_instance_2.id, route_id: route_2.id).count).to eq(1) + expect(db[:route_bindings].where(service_instance_id: service_instance_1_id, route_id: route_1_id).count).to eq(1) + expect(db[:route_bindings].where(service_instance_id: service_instance_1_id, route_id: route_2_id).count).to eq(1) + expect(db[:route_bindings].where(service_instance_id: service_instance_2_id, route_id: route_1_id).count).to eq(1) + expect(db[:route_bindings].where(service_instance_id: service_instance_2_id, route_id: route_2_id).count).to eq(1) # Verify index is added expect(db.indexes(:route_bindings)).to include(:route_bindings_route_id_service_instance_id_index) diff --git a/spec/migrations/20251030100000_add_unique_constraint_to_sidecar_process_types_spec.rb b/spec/migrations/20251030100000_add_unique_constraint_to_sidecar_process_types_spec.rb index 1186fe84586..eb2eb3ff382 100644 --- a/spec/migrations/20251030100000_add_unique_constraint_to_sidecar_process_types_spec.rb +++ b/spec/migrations/20251030100000_add_unique_constraint_to_sidecar_process_types_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'add unique constraint to sidecar process types', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20251117123719_add_state_to_stacks_spec.rb b/spec/migrations/20251117123719_add_state_to_stacks_spec.rb index 504ad77ba9c..d3e32dc0ea5 100644 --- a/spec/migrations/20251117123719_add_state_to_stacks_spec.rb +++ b/spec/migrations/20251117123719_add_state_to_stacks_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add state column to stacks table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20251121174647_add_broker_provided_metadata_to_service_instances_spec.rb b/spec/migrations/20251121174647_add_broker_provided_metadata_to_service_instances_spec.rb index da70615cb4d..ab73b66be25 100644 --- a/spec/migrations/20251121174647_add_broker_provided_metadata_to_service_instances_spec.rb +++ b/spec/migrations/20251121174647_add_broker_provided_metadata_to_service_instances_spec.rb @@ -1,9 +1,4 @@ -# Migration test for adding broker_provided_metadata column to service_instances table -# -# This test verifies that the migration correctly adds the broker_provided_metadata -# column to the service_instances table with the correct properties (text type, nullable). - -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add broker_provided_metadata column to service_instances table', isolation: :truncation, type: :migration do @@ -12,47 +7,53 @@ end describe 'service_instances table' do - let(:space) { create(:space) } + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", non_basic_services_allowed: false, total_services: -1, memory_limit: 0, + total_routes: -1, created_at: Time.now.utc, updated_at: Time.now.utc) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", quota_definition_id: quota_def_id, created_at: Time.now.utc, updated_at: Time.now.utc) + end + let(:space_id) { db[:spaces].insert(guid: SecureRandom.uuid, name: "space-#{SecureRandom.uuid}", organization_id: org_id, created_at: Time.now.utc, updated_at: Time.now.utc) } it 'adds broker_provided_metadata column with correct properties' do - # Insert a service instance before migration to test preservation db[:service_instances].insert( guid: 'existing-service-instance-guid', name: 'existing-instance', - space_id: space.id + space_id: space_id, + created_at: Time.now.utc, + updated_at: Time.now.utc ) - # Verify column doesn't exist yet expect(db[:service_instances].columns).not_to include(:broker_provided_metadata) - # Run migration Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - # Verify column was added expect(db[:service_instances].columns).to include(:broker_provided_metadata) - # Verify existing instance was preserved with null metadata existing_instance = db[:service_instances].first(guid: 'existing-service-instance-guid') expect(existing_instance).not_to be_nil expect(existing_instance[:broker_provided_metadata]).to be_nil - # Verify null values are allowed db[:service_instances].insert( guid: 'test-service-instance-guid', name: 'test-instance', - space_id: space.id, - broker_provided_metadata: nil + space_id: space_id, + broker_provided_metadata: nil, + created_at: Time.now.utc, + updated_at: Time.now.utc ) instance_with_null = db[:service_instances].first(guid: 'test-service-instance-guid') expect(instance_with_null[:broker_provided_metadata]).to be_nil - # Verify text values are accepted metadata_json = '{"labels": {"version": "1.0"}, "attributes": {"engine": "postgresql"}}' db[:service_instances].insert( guid: 'test-service-instance-with-metadata', name: 'test-instance-with-metadata', - space_id: space.id, - broker_provided_metadata: metadata_json + space_id: space_id, + broker_provided_metadata: metadata_json, + created_at: Time.now.utc, + updated_at: Time.now.utc ) instance_with_metadata = db[:service_instances].first(guid: 'test-service-instance-with-metadata') expect(instance_with_metadata[:broker_provided_metadata]).to eq(metadata_json) diff --git a/spec/migrations/20260318083940_add_unique_constraint_to_buildpacks_spec.rb b/spec/migrations/20260318083940_add_unique_constraint_to_buildpacks_spec.rb index 2347f8b1b59..89fb40f0da4 100644 --- a/spec/migrations/20260318083940_add_unique_constraint_to_buildpacks_spec.rb +++ b/spec/migrations/20260318083940_add_unique_constraint_to_buildpacks_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'add unique constraint to buildpacks', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20260320141005_add_unique_constraint_to_revision_sidecars_spec.rb b/spec/migrations/20260320141005_add_unique_constraint_to_revision_sidecars_spec.rb index cf427949f09..ea51b91eb0d 100644 --- a/spec/migrations/20260320141005_add_unique_constraint_to_revision_sidecars_spec.rb +++ b/spec/migrations/20260320141005_add_unique_constraint_to_revision_sidecars_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'add unique constraint to revision sidecar types', isolation: :truncation, type: :migration do include_context 'migration' do diff --git a/spec/migrations/20260323092954_add_unique_constraint_to_sidecars_spec.rb b/spec/migrations/20260323092954_add_unique_constraint_to_sidecars_spec.rb index cbc84da70db..25e1c8f7fd7 100644 --- a/spec/migrations/20260323092954_add_unique_constraint_to_sidecars_spec.rb +++ b/spec/migrations/20260323092954_add_unique_constraint_to_sidecars_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'add unique constraint to sidecars', isolation: :truncation, type: :migration do include_context 'migration' do diff --git a/spec/migrations/20260323130619_add_unique_constraint_to_security_groups_spec.rb b/spec/migrations/20260323130619_add_unique_constraint_to_security_groups_spec.rb index 6385ef80ba0..60d331e0dfa 100644 --- a/spec/migrations/20260323130619_add_unique_constraint_to_security_groups_spec.rb +++ b/spec/migrations/20260323130619_add_unique_constraint_to_security_groups_spec.rb @@ -1,48 +1,55 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' + RSpec.describe 'add unique constraint to security_groups', isolation: :truncation, type: :migration do include_context 'migration' do let(:migration_filename) { '20260323130619_add_unique_constraint_to_security_groups.rb' } end - it 'removes duplicates, adds constraint and reverts migration' do - space = create(:space) + let(:quota_def_id) do + db[:quota_definitions].insert(guid: SecureRandom.uuid, name: "quota-#{SecureRandom.uuid}", non_basic_services_allowed: false, total_services: -1, memory_limit: 0, + total_routes: -1, created_at: Time.now.utc, updated_at: Time.now.utc) + end + let(:org_id) do + db[:organizations].insert(guid: SecureRandom.uuid, name: "org-#{SecureRandom.uuid}", quota_definition_id: quota_def_id, created_at: Time.now.utc, updated_at: Time.now.utc) + end + let(:space_id) { db[:spaces].insert(guid: SecureRandom.uuid, name: "space-#{SecureRandom.uuid}", organization_id: org_id, created_at: Time.now.utc, updated_at: Time.now.utc) } - # create duplicate entries with join table references - surviving_id = db[:security_groups].insert(guid: SecureRandom.uuid, name: 'sec1') - duplicate_id = db[:security_groups].insert(guid: SecureRandom.uuid, name: 'sec1') + it 'removes duplicates, adds constraint and reverts migration' do + now = Time.now.utc + surviving_id = db[:security_groups].insert(guid: SecureRandom.uuid, name: 'sec1', rules: '[]', staging_default: false, running_default: false, created_at: now, updated_at: now) + duplicate_id = db[:security_groups].insert(guid: SecureRandom.uuid, name: 'sec1', rules: '[]', staging_default: false, running_default: false, created_at: now, updated_at: now) expect(db[:security_groups].where(name: 'sec1').count).to eq(2) - # add security_groups_spaces and staging_security_groups_spaces referencing the duplicate security_groups - db[:security_groups_spaces].insert(security_group_id: surviving_id, space_id: space.id) - db[:security_groups_spaces].insert(security_group_id: duplicate_id, space_id: space.id) - db[:staging_security_groups_spaces].insert(staging_security_group_id: surviving_id, staging_space_id: space.id) - db[:staging_security_groups_spaces].insert(staging_security_group_id: duplicate_id, staging_space_id: space.id) + db[:security_groups_spaces].insert(security_group_id: surviving_id, space_id: space_id) + db[:security_groups_spaces].insert(security_group_id: duplicate_id, space_id: space_id) + db[:staging_security_groups_spaces].insert(staging_security_group_id: surviving_id, staging_space_id: space_id) + db[:staging_security_groups_spaces].insert(staging_security_group_id: duplicate_id, staging_space_id: space_id) - # run the migration Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - # verify duplicates and their join table references are removed, surviving group intact expect(db[:security_groups].where(name: 'sec1').count).to eq(1) expect(db[:security_groups].where(name: 'sec1').first[:id]).to eq(surviving_id) expect(db[:security_groups_spaces].where(security_group_id: duplicate_id).count).to eq(0) expect(db[:staging_security_groups_spaces].where(staging_security_group_id: duplicate_id).count).to eq(0) - expect(db[:security_groups_spaces].where(security_group_id: surviving_id, space_id: space.id).count).to eq(1) - expect(db[:staging_security_groups_spaces].where(staging_security_group_id: surviving_id, staging_space_id: space.id).count).to eq(1) + expect(db[:security_groups_spaces].where(security_group_id: surviving_id, space_id: space_id).count).to eq(1) + expect(db[:staging_security_groups_spaces].where(staging_security_group_id: surviving_id, staging_space_id: space_id).count).to eq(1) - # verify constraint is enforced expect(db.indexes(:security_groups)).to include(:security_groups_name_index) - expect { db[:security_groups].insert(guid: SecureRandom.uuid, name: 'sec1') }.to raise_error(Sequel::UniqueConstraintViolation) + expect do + db[:security_groups].insert(guid: SecureRandom.uuid, name: 'sec1', rules: '[]', staging_default: false, running_default: false, created_at: now, + updated_at: now) + end.to raise_error(Sequel::UniqueConstraintViolation) - # running the migration again should not cause any errors expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error - # roll back the migration Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) - # verify constraint is removed and duplicates can be re-inserted expect(db.indexes(:security_groups)).not_to include(:security_groups_name_index) expect(db.indexes(:security_groups)).to include(:sg_name_index) - expect { db[:security_groups].insert(guid: SecureRandom.uuid, name: 'sec1') }.not_to raise_error + expect do + db[:security_groups].insert(guid: SecureRandom.uuid, name: 'sec1', rules: '[]', staging_default: false, running_default: false, created_at: now, + updated_at: now) + end.not_to raise_error end end diff --git a/spec/migrations/20260323144429_add_unique_constraint_to_revision_process_commands_spec.rb b/spec/migrations/20260323144429_add_unique_constraint_to_revision_process_commands_spec.rb index ae49122ee66..3aee4d5524a 100644 --- a/spec/migrations/20260323144429_add_unique_constraint_to_revision_process_commands_spec.rb +++ b/spec/migrations/20260323144429_add_unique_constraint_to_revision_process_commands_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'add unique constraint to revision_process_commands', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20260324120000_add_composite_indexes_to_events_spec.rb b/spec/migrations/20260324120000_add_composite_indexes_to_events_spec.rb index a2c39d58799..f4075912f78 100644 --- a/spec/migrations/20260324120000_add_composite_indexes_to_events_spec.rb +++ b/spec/migrations/20260324120000_add_composite_indexes_to_events_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add composite indexes to events table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20260428120000_add_lifecycle_type_to_apps_spec.rb b/spec/migrations/20260428120000_add_lifecycle_type_to_apps_spec.rb index c806949c85e..00c64dc1b0c 100644 --- a/spec/migrations/20260428120000_add_lifecycle_type_to_apps_spec.rb +++ b/spec/migrations/20260428120000_add_lifecycle_type_to_apps_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add lifecycle_type to apps', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20260428120100_add_lifecycle_type_to_droplets_spec.rb b/spec/migrations/20260428120100_add_lifecycle_type_to_droplets_spec.rb index 1b944ac2548..8e591515214 100644 --- a/spec/migrations/20260428120100_add_lifecycle_type_to_droplets_spec.rb +++ b/spec/migrations/20260428120100_add_lifecycle_type_to_droplets_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add lifecycle_type to droplets', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20260428120200_add_lifecycle_type_to_builds_spec.rb b/spec/migrations/20260428120200_add_lifecycle_type_to_builds_spec.rb index a0834c24784..e681c19eb68 100644 --- a/spec/migrations/20260428120200_add_lifecycle_type_to_builds_spec.rb +++ b/spec/migrations/20260428120200_add_lifecycle_type_to_builds_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add lifecycle_type to builds', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20260505071445_add_jobs_operation_state_index_spec.rb b/spec/migrations/20260505071445_add_jobs_operation_state_index_spec.rb index 162c1e51b00..62460f099e9 100644 --- a/spec/migrations/20260505071445_add_jobs_operation_state_index_spec.rb +++ b/spec/migrations/20260505071445_add_jobs_operation_state_index_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add operation_state_index on jobs table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20260630120000_add_status_to_spaces_spec.rb b/spec/migrations/20260630120000_add_status_to_spaces_spec.rb index c339fd087fd..ed5c7730f4a 100644 --- a/spec/migrations/20260630120000_add_status_to_spaces_spec.rb +++ b/spec/migrations/20260630120000_add_status_to_spaces_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add status column to spaces table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/20260811120000_add_root_job_guid_to_jobs_spec.rb b/spec/migrations/20260811120000_add_root_job_guid_to_jobs_spec.rb index d82aa96ad6d..cb526313f6e 100644 --- a/spec/migrations/20260811120000_add_root_job_guid_to_jobs_spec.rb +++ b/spec/migrations/20260811120000_add_root_job_guid_to_jobs_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' RSpec.describe 'migration to add root_job_guid column to jobs table', isolation: :truncation, type: :migration do diff --git a/spec/migrations/helpers/bigint_migration_step1_shared_context.rb b/spec/migrations/helpers/bigint_migration_step1_shared_context.rb index 81b48f484a7..5eeb52fb3ba 100644 --- a/spec/migrations/helpers/bigint_migration_step1_shared_context.rb +++ b/spec/migrations/helpers/bigint_migration_step1_shared_context.rb @@ -1,4 +1,5 @@ # rubocop:disable Migration/TooManyMigrationRuns +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' require 'database/bigint_migration' @@ -8,126 +9,92 @@ include_context 'migration' let(:skip_bigint_id_migration) { nil } + let(:logger) { double(:logger, info: nil) } + let(:migration_config) do + instance_double(VCAP::CloudController::Config, + get: nil).tap do |cfg| + allow(cfg).to receive(:get).with(:skip_bigint_id_migration).and_return(skip_bigint_id_migration) + allow(cfg).to receive(:get).with(:migration_psql_concurrent_statement_timeout_in_seconds).and_return(300) + end + end before do - allow_any_instance_of(VCAP::CloudController::Config).to receive(:get).with(:skip_bigint_id_migration).and_return(skip_bigint_id_migration) - allow_any_instance_of(VCAP::CloudController::Config).to receive(:get).with(:migration_psql_concurrent_statement_timeout_in_seconds).and_return(300) + allow(VCAP::CloudController::Config).to receive(:config).and_return(migration_config) + allow(Steno).to receive(:logger).and_return(logger) end describe 'up' do context 'when skip_bigint_id_migration is false' do let(:skip_bigint_id_migration) { false } - let(:logger) { double(:logger, info: nil) } - before do - allow(Steno).to receive(:logger).and_return(logger) + after do + db[table].delete end - context 'when the table is empty' do - before do - db[table].delete - end - - it "changes the id column's type to bigint and does not add id_bigint column" do - expect(db).to have_table_with_column_and_type(table, :id, 'integer') - expect(db).not_to have_table_with_column(table, :id_bigint) + it 'when table is empty: changes id to bigint, no id_bigint column; backfill raises error' do + db[table].delete - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error + expect(db).to have_table_with_column_and_type(table, :id, 'integer') + expect(db).not_to have_table_with_column(table, :id_bigint) - expect(db).to have_table_with_column_and_type(table, :id, 'bigint') - expect(db).not_to have_table_with_column(table, :id_bigint) - end + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error - describe 'backfill' do - before do - Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - end + expect(db).to have_table_with_column_and_type(table, :id, 'bigint') + expect(db).not_to have_table_with_column(table, :id_bigint) - it 'fails with a proper error message' do - expect do - VCAP::BigintMigration.backfill(logger, db, table) - end.to raise_error(RuntimeError, /table '#{table}' does not contain column 'id_bigint'/) - end - end + expect do + VCAP::BigintMigration.backfill(logger, db, table) + end.to raise_error(RuntimeError, /table '#{table}' does not contain column 'id_bigint'/) end - context 'when the table is not empty' do - let!(:old_id) { insert.call(db) } - - after do - db[table].delete # Necessary to successfully run subsequent migrations in the after block of the migration shared context... - end - - it 'keeps id as integer, adds id_bigint column and creates trigger function' do - expect(db).to have_table_with_column_and_type(table, :id, 'integer') - expect(db).not_to have_table_with_column(table, :id_bigint) - expect(db).not_to have_trigger_function_for_table(table) - - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error - - expect(db).to have_table_with_column_and_type(table, :id, 'integer') - expect(db).to have_table_with_column_and_type(table, :id_bigint, 'bigint') - - expect(db).to have_trigger_function_for_table(table) - end - - it 'does not populate id_bigint for existing entries but does for new entries' do - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error - - # Existing entry should not have id_bigint populated - expect(db[table].where(id: old_id).get(:id_bigint)).to be_nil - - # New entry should have id_bigint automatically populated - new_id = insert.call(db) - expect(db[table].where(id: new_id).get(:id_bigint)).to eq(new_id) - end - - describe 'backfill' do - before do - 100.times { insert.call(db) } - Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - end - - context 'default batch size' do - it 'backfills all entries in a single run' do - expect(db).to have_table_with_unpopulated_column(table, :id_bigint) - - expect do - VCAP::BigintMigration.backfill(logger, db, table) - end.to have_queried_db_times(/update/i, 1) - - expect(db).not_to have_table_with_unpopulated_column(table, :id_bigint) - end - end - - context 'custom batch size' do - let(:batch_size) { 30 } - - it 'backfills entries in multiple runs' do - expect(db).to have_table_with_unpopulated_column(table, :id_bigint) + it 'when table is not empty: keeps id as integer, adds id_bigint column with trigger; backfill works correctly' do + old_id = insert.call(db) + 100.times { insert.call(db) } - expect do - VCAP::BigintMigration.backfill(logger, db, table, batch_size:) - end.to have_queried_db_times(/update/i, 4) + expect(db).to have_table_with_column_and_type(table, :id, 'integer') + expect(db).not_to have_table_with_column(table, :id_bigint) + expect(db).not_to have_trigger_function_for_table(table) - expect(db).not_to have_table_with_unpopulated_column(table, :id_bigint) - end + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }.not_to raise_error - context 'limited number of iterations' do - let(:iterations) { 2 } + expect(db).to have_table_with_column_and_type(table, :id, 'integer') + expect(db).to have_table_with_column_and_type(table, :id_bigint, 'bigint') + expect(db).to have_trigger_function_for_table(table) - it 'stops backfilling' do - expect(db).to have_table_with_unpopulated_column(table, :id_bigint) + # Existing entry should not have id_bigint populated; new entries should + expect(db[table].where(id: old_id).get(:id_bigint)).to be_nil + new_id = insert.call(db) + expect(db[table].where(id: new_id).get(:id_bigint)).to eq(new_id) - expect do - VCAP::BigintMigration.backfill(logger, db, table, batch_size:, iterations:) - end.to have_queried_db_times(/update/i, 2) + # Default batch size: backfills all entries in a single update + expect(db).to have_table_with_unpopulated_column(table, :id_bigint) + expect do + VCAP::BigintMigration.backfill(logger, db, table) + end.to have_queried_db_times(/update/i, 1) + expect(db).not_to have_table_with_unpopulated_column(table, :id_bigint) - expect(db).to have_table_with_unpopulated_column(table, :id_bigint) - end - end - end - end + # Re-insert rows for subsequent backfill batch-size tests + db[table].delete + insert.call(db) + 100.times { insert.call(db) } + # Trigger already set; id_bigint populated for new rows; re-create unpopulated state via direct update + db[table].update(id_bigint: nil) + + # Custom batch size (30): needs 4 updates for 101 rows + expect(db).to have_table_with_unpopulated_column(table, :id_bigint) + expect do + VCAP::BigintMigration.backfill(logger, db, table, batch_size: 30) + end.to have_queried_db_times(/update/i, 4) + expect(db).not_to have_table_with_unpopulated_column(table, :id_bigint) + + db[table].update(id_bigint: nil) + + # Limited iterations (2): stops early, leaving some rows unpopulated + expect(db).to have_table_with_unpopulated_column(table, :id_bigint) + expect do + VCAP::BigintMigration.backfill(logger, db, table, batch_size: 30, iterations: 2) + end.to have_queried_db_times(/update/i, 2) + expect(db).to have_table_with_unpopulated_column(table, :id_bigint) end end @@ -147,40 +114,32 @@ end describe 'down' do - context 'when the table is empty' do - before do - db[table].delete - Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - end + after do + db[table].delete + end - it "reverts the id column's type to integer" do - expect(db).to have_table_with_column_and_type(table, :id, 'bigint') + it 'when table is empty: reverts id column type to integer' do + db[table].delete + Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error + expect(db).to have_table_with_column_and_type(table, :id, 'bigint') - expect(db).to have_table_with_column_and_type(table, :id, 'integer') - end - end + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error - context 'when the table is not empty' do - before do - insert.call(db) - Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - end + expect(db).to have_table_with_column_and_type(table, :id, 'integer') + end - after do - db[table].delete # Necessary to successfully run subsequent migrations in the after block of the migration shared context... - end + it 'when table is not empty: drops id_bigint column and trigger function' do + insert.call(db) + Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - it 'drops the id_bigint column and trigger function' do - expect(db).to have_table_with_column(table, :id_bigint) - expect(db).to have_trigger_function_for_table(table) + expect(db).to have_table_with_column(table, :id_bigint) + expect(db).to have_trigger_function_for_table(table) - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index - 1, allow_missing_migration_files: true) }.not_to raise_error - expect(db).not_to have_table_with_column(table, :id_bigint) - expect(db).not_to have_trigger_function_for_table(table) - end + expect(db).not_to have_table_with_column(table, :id_bigint) + expect(db).not_to have_trigger_function_for_table(table) end end end diff --git a/spec/migrations/helpers/bigint_migration_step3_shared_context.rb b/spec/migrations/helpers/bigint_migration_step3_shared_context.rb index 2378c849909..2a5ecec954d 100644 --- a/spec/migrations/helpers/bigint_migration_step3_shared_context.rb +++ b/spec/migrations/helpers/bigint_migration_step3_shared_context.rb @@ -1,4 +1,5 @@ # rubocop:disable Migration/TooManyMigrationRuns +require 'migration_spec_helper' require 'migrations/helpers/migration_shared_context' require 'database/bigint_migration' @@ -12,67 +13,49 @@ let(:skip_bigint_id_migration) { false } let(:logger) { double(:logger, info: nil) } + let(:migration_config) do + instance_double(VCAP::CloudController::Config, + get: nil).tap do |cfg| + allow(cfg).to receive(:get).with(:skip_bigint_id_migration).and_return(skip_bigint_id_migration) + allow(cfg).to receive(:get).with(:migration_psql_concurrent_statement_timeout_in_seconds).and_return(300) + end + end before do - allow_any_instance_of(VCAP::CloudController::Config).to receive(:get).with(:skip_bigint_id_migration).and_return(skip_bigint_id_migration) - allow_any_instance_of(VCAP::CloudController::Config).to receive(:get).with(:migration_psql_concurrent_statement_timeout_in_seconds).and_return(300) + allow(VCAP::CloudController::Config).to receive(:config).and_return(migration_config) end describe 'up' do - context 'when migration step 1 was executed' do - context 'when the id_bigint column was added' do - before do - insert.call(db) - Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - end - - context 'when backfilling was completed' do - before do - VCAP::BigintMigration.backfill(logger, db, table) - end - - it 'adds a check constraint' do - expect(db).not_to have_table_with_check_constraint(table) - - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) }.not_to raise_error - - expect(db).to have_table_with_check_constraint(table) - end - end - - context 'when backfilling was not completed' do - after do - db[table].delete # Necessary as the migration will be executed again in the after block of the migration shared context - and should not fail... - end - - it 'fails ...' do - expect do - Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) - end.to raise_error(/Failed to add check constraint on '#{table}' table!/) - end - end + context 'when migration step 1 was executed and id_bigint column was added' do + before do + insert.call(db) + Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) end - context "when the migration was concluded (id column's type switched)" do - before do - db[table].delete - Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - end + after do + db[table].delete + end - it 'does not add a check constraint' do - expect(db).not_to have_table_with_check_constraint(table) + it 'fails when backfilling is incomplete' do + expect do + Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) + end.to raise_error(/Failed to add check constraint on '#{table}' table!/) + end - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) }.not_to raise_error + it 'adds check constraint after backfill is complete' do + VCAP::BigintMigration.backfill(logger, db, table) - expect(db).not_to have_table_with_check_constraint(table) - end + expect(db).not_to have_table_with_check_constraint(table) + + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) }.not_to raise_error + + expect(db).to have_table_with_check_constraint(table) end end - context 'when migration step 1 was skipped' do - let(:skip_bigint_id_migration) { true } - + context "when migration step 1 concluded (empty table: id column's type switched)" do before do + db[table].delete Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) end @@ -84,26 +67,40 @@ expect(db).not_to have_table_with_check_constraint(table) end end - end - describe 'down' do - context 'when migration step 3a was executed' do + context 'when migration step 1 was skipped' do + let(:skip_bigint_id_migration) { true } + before do - insert.call(db) Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - VCAP::BigintMigration.backfill(logger, db, table) - Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) end - it 'drops the check constraint' do - expect(db).to have_table_with_check_constraint(table) + it 'does not add a check constraint' do + expect(db).not_to have_table_with_check_constraint(table) - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a - 1, allow_missing_migration_files: true) }.not_to raise_error + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) }.not_to raise_error expect(db).not_to have_table_with_check_constraint(table) end end end + + describe 'down' do + before do + insert.call(db) + Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) + VCAP::BigintMigration.backfill(logger, db, table) + Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) + end + + it 'drops the check constraint' do + expect(db).to have_table_with_check_constraint(table) + + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a - 1, allow_missing_migration_files: true) }.not_to raise_error + + expect(db).not_to have_table_with_check_constraint(table) + end + end end RSpec.shared_context 'bigint migration step3b' do @@ -117,104 +114,94 @@ let(:skip_bigint_id_migration) { false } let(:logger) { double(:logger, info: nil) } + let(:migration_config) do + instance_double(VCAP::CloudController::Config, + get: nil).tap do |cfg| + allow(cfg).to receive(:get).with(:skip_bigint_id_migration).and_return(skip_bigint_id_migration) + allow(cfg).to receive(:get).with(:migration_psql_concurrent_statement_timeout_in_seconds).and_return(300) + end + end before do - allow_any_instance_of(VCAP::CloudController::Config).to receive(:get).with(:skip_bigint_id_migration).and_return(skip_bigint_id_migration) - allow_any_instance_of(VCAP::CloudController::Config).to receive(:get).with(:migration_psql_concurrent_statement_timeout_in_seconds).and_return(300) + allow(VCAP::CloudController::Config).to receive(:config).and_return(migration_config) end describe 'up' do - context 'when migration step 3a was executed' do - before do - insert.call(db) - Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - VCAP::BigintMigration.backfill(logger, db, table) - Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) - end - - it 'completes the bigint migration: drops constraints, renames columns, and maintains primary key' do - # Verify pre-migration state - expect(db).to have_table_with_check_constraint(table) - expect(db).to have_trigger_function_for_table(table) - expect(db).to have_table_with_column_and_type(table, :id, 'integer') - expect(db).to have_table_with_column_and_type(table, :id_bigint, 'bigint') - expect(db).to have_table_with_primary_key(table, :id) - - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3b, allow_missing_migration_files: true) }.not_to raise_error - - # Verify post-migration state - expect(db).not_to have_table_with_check_constraint(table) - expect(db).not_to have_trigger_function_for_table(table) - expect(db).to have_table_with_column_and_type(table, :id, 'bigint') - expect(db).not_to have_table_with_column(table, :id_bigint) - - expect(db).to have_table_with_primary_key(table, :id) - end + before do + insert.call(db) + Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) + VCAP::BigintMigration.backfill(logger, db, table) + Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) + end - it 'has an index on timestamp + (bigint) id column' do - if db.schema(table).any? { |col| col[0] == :timestamp } + it 'completes bigint migration: drops constraints/trigger, renames columns, maintains primary key; identity sequence is correct' do + # Verify pre-migration state + expect(db).to have_table_with_check_constraint(table) + expect(db).to have_trigger_function_for_table(table) + expect(db).to have_table_with_column_and_type(table, :id, 'integer') + expect(db).to have_table_with_column_and_type(table, :id_bigint, 'bigint') + expect(db).to have_table_with_primary_key(table, :id) - expect(db).to have_table_with_index_on_columns(table, %i[timestamp id]) + last_id_before_migration = insert.call(db) - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3b, allow_missing_migration_files: true) }.not_to raise_error + # Timestamp index exists before migration if applicable + expect(db).to have_table_with_index_on_columns(table, %i[timestamp id]) if db.schema(table).any? { |col| col[0] == :timestamp } - expect(db).to have_table_with_index_on_columns(table, %i[timestamp id]) - end - end + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3b, allow_missing_migration_files: true) }.not_to raise_error - it 'uses an identity with correct start value for the (bigint) id column' do - last_id_before_migration = insert.call(db) + # Verify post-migration state + expect(db).not_to have_table_with_check_constraint(table) + expect(db).not_to have_trigger_function_for_table(table) + expect(db).to have_table_with_column_and_type(table, :id, 'bigint') + expect(db).not_to have_table_with_column(table, :id_bigint) + expect(db).to have_table_with_primary_key(table, :id) - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3b, allow_missing_migration_files: true) }.not_to raise_error + # Identity sequence continues from the last id + first_id_after_migration = insert.call(db) + expect(first_id_after_migration).to eq(last_id_before_migration + 1) - first_id_after_migration = insert.call(db) - expect(first_id_after_migration).to eq(last_id_before_migration + 1) - end + # Timestamp index preserved if applicable + expect(db).to have_table_with_index_on_columns(table, %i[timestamp id]) if db.schema(table).any? { |col| col[0] == :timestamp } end end describe 'down' do - context 'when migration step 3b was executed' do - before do - insert.call(db) - Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) - VCAP::BigintMigration.backfill(logger, db, table) - Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) - Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3b, allow_missing_migration_files: true) - end - - it 'uses an identity with correct start value for the (integer) id column' do - last_id_before_migration = insert.call(db) - - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3b - 1, allow_missing_migration_files: true) }.not_to raise_error - - first_id_after_migration = insert.call(db) - expect(first_id_after_migration).to eq(last_id_before_migration + 1) - end - - it 'reverts the bigint migration: restores columns, constraints, and indexes' do - # Verify pre-rollback state - expect(db).to have_table_with_column_and_type(table, :id, 'bigint') - expect(db).not_to have_table_with_column(table, :id_bigint) - expect(db).to have_table_with_primary_key(table, :id) - expect(db).not_to have_trigger_function_for_table(table) - expect(db).not_to have_table_with_check_constraint(table) - - expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3b - 1, allow_missing_migration_files: true) }.not_to raise_error - - # Verify post-rollback state - expect(db).to have_table_with_column_and_type(table, :id, 'integer') - expect(db).to have_table_with_column_and_type(table, :id_bigint, 'bigint') - expect(db).to have_table_with_column_and_attribute(table, :id_bigint, :allow_null, true) - expect(db).to have_table_with_primary_key(table, :id) - expect(db).to have_trigger_function_for_table(table) - expect(db).to have_table_with_check_constraint(table) + before do + insert.call(db) + Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) + VCAP::BigintMigration.backfill(logger, db, table) + Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3a, allow_missing_migration_files: true) + Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3b, allow_missing_migration_files: true) + end - # Verify timestamp index if applicable - if db.schema(table).any? { |col| col[0] == :timestamp } - expect(db).to have_table_with_index_on_columns(table, %i[timestamp id]) - expect(db).not_to have_table_with_index_on_columns(table, %i[timestamp id_bigint]) - end + it 'reverts bigint migration: restores columns, constraints, trigger, indexes; identity sequence is correct' do + # Verify pre-rollback state + expect(db).to have_table_with_column_and_type(table, :id, 'bigint') + expect(db).not_to have_table_with_column(table, :id_bigint) + expect(db).to have_table_with_primary_key(table, :id) + expect(db).not_to have_trigger_function_for_table(table) + expect(db).not_to have_table_with_check_constraint(table) + + last_id_before_rollback = insert.call(db) + + expect { Sequel::Migrator.run(db, migrations_path, target: current_migration_index_step3b - 1, allow_missing_migration_files: true) }.not_to raise_error + + # Verify post-rollback state + expect(db).to have_table_with_column_and_type(table, :id, 'integer') + expect(db).to have_table_with_column_and_type(table, :id_bigint, 'bigint') + expect(db).to have_table_with_column_and_attribute(table, :id_bigint, :allow_null, true) + expect(db).to have_table_with_primary_key(table, :id) + expect(db).to have_trigger_function_for_table(table) + expect(db).to have_table_with_check_constraint(table) + + # Identity sequence continues from the last id + first_id_after_rollback = insert.call(db) + expect(first_id_after_rollback).to eq(last_id_before_rollback + 1) + + # Timestamp index reverted if applicable + if db.schema(table).any? { |col| col[0] == :timestamp } + expect(db).to have_table_with_index_on_columns(table, %i[timestamp id]) + expect(db).not_to have_table_with_index_on_columns(table, %i[timestamp id_bigint]) end end end diff --git a/spec/migrations/migration_concurrent_statement_timeout_spec.rb b/spec/migrations/migration_concurrent_statement_timeout_spec.rb index 1a9ed112ed1..21f2cc558e3 100644 --- a/spec/migrations/migration_concurrent_statement_timeout_spec.rb +++ b/spec/migrations/migration_concurrent_statement_timeout_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'migration concurrent statement timeout', isolation: :truncation, type: :migration do let(:db) { Sequel::Model.db } @@ -20,7 +20,11 @@ migration_file = "#{tmp_migrations_dir}/001_test_for_concurrent_statement_timeout_migration.rb" File.write(migration_file, long_time_migration) - allow(VCAP::CloudController::Config.config).to receive(:get).with(:migration_psql_concurrent_statement_timeout_in_seconds).and_return(1899) + allow(VCAP::CloudController::Config).to receive(:config).and_return( + instance_double(VCAP::CloudController::Config, get: nil).tap do |cfg| + allow(cfg).to receive(:get).with(:migration_psql_concurrent_statement_timeout_in_seconds).and_return(1899) + end + ) allow(db).to receive(:run).and_call_original end diff --git a/spec/migrations/migration_logs_spec.rb b/spec/migrations/migration_logs_spec.rb index 677cdb0ca8b..ab3937632b4 100644 --- a/spec/migrations/migration_logs_spec.rb +++ b/spec/migrations/migration_logs_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'migration_spec_helper' RSpec.describe 'migration logs', isolation: :truncation, type: :migration do let(:db) { Sequel::Model.db } diff --git a/spec/unit/messages/validators/label_selector_requirement_validator_spec.rb b/spec/unit/messages/validators/label_selector_requirement_validator_spec.rb index 396973ba2fd..bf5b3301a0f 100644 --- a/spec/unit/messages/validators/label_selector_requirement_validator_spec.rb +++ b/spec/unit/messages/validators/label_selector_requirement_validator_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'lightweight_spec_helper' require 'messages/validators/label_selector_requirement_validator' module VCAP::CloudController::Validators