From c41a3dfe49faed6d741747c8ec1d9f8ddc3877ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Mon, 16 Mar 2026 15:01:35 +0100 Subject: [PATCH 01/16] feat: validate_unique removed - Model's that already has uniqueness constraint on db level are refactored. sequel validate_unique method removed from list. --- app/models/runtime/app_model.rb | 3 +-- app/models/runtime/domain.rb | 1 - .../runtime/helpers/organization_role_mixin.rb | 1 - app/models/runtime/helpers/space_role_mixin.rb | 1 - app/models/runtime/isolation_segment_model.rb | 4 +--- app/models/runtime/organization.rb | 1 - app/models/runtime/space.rb | 1 - app/models/runtime/space_quota_definition.rb | 1 - app/models/runtime/stack.rb | 1 - app/models/runtime/user.rb | 1 - app/models/services/service_broker.rb | 1 - app/models/services/service_key.rb | 1 - .../shared_examples/models/domain_validation.rb | 7 +++---- spec/unit/actions/app_create_spec.rb | 16 ---------------- spec/unit/actions/organization_create_spec.rb | 2 -- spec/unit/actions/service_broker_create_spec.rb | 2 -- spec/unit/actions/space_create_spec.rb | 2 -- spec/unit/actions/space_quotas_create_spec.rb | 2 -- spec/unit/actions/stack_create_spec.rb | 2 -- spec/unit/actions/user_create_spec.rb | 2 -- spec/unit/isolation_segment_create_spec.rb | 4 +--- spec/unit/models/runtime/domain_spec.rb | 1 - .../runtime/isolation_segment_model_spec.rb | 16 ---------------- .../models/runtime/organization_auditor_spec.rb | 10 +++++++++- .../runtime/organization_billing_manager_spec.rb | 10 +++++++++- .../models/runtime/organization_manager_spec.rb | 10 +++++++++- spec/unit/models/runtime/organization_spec.rb | 1 - .../models/runtime/organization_user_spec.rb | 10 +++++++++- spec/unit/models/runtime/shared_domain_spec.rb | 2 +- spec/unit/models/runtime/space_auditor_spec.rb | 10 +++++++++- spec/unit/models/runtime/space_developer_spec.rb | 9 ++++++++- spec/unit/models/runtime/space_manager_spec.rb | 9 ++++++++- .../runtime/space_quota_definition_spec.rb | 1 - spec/unit/models/runtime/space_spec.rb | 1 - spec/unit/models/runtime/space_supporter_spec.rb | 9 ++++++++- spec/unit/models/runtime/stack_spec.rb | 1 - spec/unit/models/runtime/user_spec.rb | 1 - spec/unit/models/services/service_broker_spec.rb | 1 - spec/unit/models/services/service_key_spec.rb | 10 +++++++++- 39 files changed, 85 insertions(+), 83 deletions(-) diff --git a/app/models/runtime/app_model.rb b/app/models/runtime/app_model.rb index b47733f7fb3..d4e14ecc54a 100644 --- a/app/models/runtime/app_model.rb +++ b/app/models/runtime/app_model.rb @@ -84,7 +84,7 @@ def around_save rescue Sequel::UniqueConstraintViolation => e raise e unless e.message.include?('apps_v3_space_guid_name_index') - errors.add(%i[space_guid name], :unique) + errors.add(%i[space_guid name], Sequel.lit("App with the name '#{name}' already exists.")) raise validation_failed_error rescue Sequel::ForeignKeyConstraintViolation => e raise e unless e.message.include?('fk_apps_droplet_guid') @@ -100,7 +100,6 @@ def validate validate_environment_variables validate_droplet_is_staged - validates_unique %i[space_guid name], message: Sequel.lit("App with the name '#{name}' already exists.") end def lifecycle_type diff --git a/app/models/runtime/domain.rb b/app/models/runtime/domain.rb index 56839444059..12b9288d397 100644 --- a/app/models/runtime/domain.rb +++ b/app/models/runtime/domain.rb @@ -97,7 +97,6 @@ def around_save def validate validates_presence :name - validates_unique :name, dataset: Domain.dataset validates_format CloudController::DomainDecorator::DOMAIN_REGEX, :name, message: 'can contain multiple subdomains, each having only alphanumeric characters and hyphens of up to 63 characters, see RFC 1035.' diff --git a/app/models/runtime/helpers/organization_role_mixin.rb b/app/models/runtime/helpers/organization_role_mixin.rb index 95a824c53e6..ef6942a4297 100644 --- a/app/models/runtime/helpers/organization_role_mixin.rb +++ b/app/models/runtime/helpers/organization_role_mixin.rb @@ -24,7 +24,6 @@ def around_save end def validate - validates_unique %i[organization_id user_id] validates_presence :organization_id validates_presence :user_id end diff --git a/app/models/runtime/helpers/space_role_mixin.rb b/app/models/runtime/helpers/space_role_mixin.rb index a220803ab3b..9c4d24139c0 100644 --- a/app/models/runtime/helpers/space_role_mixin.rb +++ b/app/models/runtime/helpers/space_role_mixin.rb @@ -26,7 +26,6 @@ def around_save def validate validates_presence :space_id validates_presence :user_id - validates_unique %i[space_id user_id] end end end diff --git a/app/models/runtime/isolation_segment_model.rb b/app/models/runtime/isolation_segment_model.rb index b66447b2c4e..e91b66e135a 100644 --- a/app/models/runtime/isolation_segment_model.rb +++ b/app/models/runtime/isolation_segment_model.rb @@ -25,14 +25,12 @@ def around_save rescue Sequel::UniqueConstraintViolation => e raise e unless e.message.include?('isolation_segment_name_unique_constraint') - errors.add(:name, :unique) + errors.add(:name, Sequel.lit('Isolation Segment names are case insensitive and must be unique')) raise validation_failed_error end def validate validates_format ISOLATION_SEGMENT_MODEL_REGEX, :name, message: Sequel.lit('Isolation Segment names can only contain non-blank unicode characters') - - validates_unique [:name], message: Sequel.lit('Isolation Segment names are case insensitive and must be unique') end def is_shared_segment? diff --git a/app/models/runtime/organization.rb b/app/models/runtime/organization.rb index 226d7dda885..c5701b8c633 100644 --- a/app/models/runtime/organization.rb +++ b/app/models/runtime/organization.rb @@ -218,7 +218,6 @@ def around_save def validate validates_presence :name - validates_unique :name validates_format ORG_NAME_REGEX, :name validates_includes ORG_STATUS_VALUES, :status, allow_missing: true diff --git a/app/models/runtime/space.rb b/app/models/runtime/space.rb index 18324a8be81..507260fb856 100644 --- a/app/models/runtime/space.rb +++ b/app/models/runtime/space.rb @@ -229,7 +229,6 @@ def around_save def validate validates_presence :name validates_presence :organization - validates_unique %i[organization_id name] validates_format SPACE_NAME_REGEX, :name errors.add(:space_quota_definition, :invalid_organization) if space_quota_definition && space_quota_definition.organization_id != organization.id diff --git a/app/models/runtime/space_quota_definition.rb b/app/models/runtime/space_quota_definition.rb index dd8681e3fed..cceebadc46b 100644 --- a/app/models/runtime/space_quota_definition.rb +++ b/app/models/runtime/space_quota_definition.rb @@ -41,7 +41,6 @@ def validate validates_presence :total_routes validates_presence :memory_limit validates_presence :organization - validates_unique %i[organization_id name] validates_limit(:memory_limit, memory_limit) validates_limit(:instance_memory_limit, instance_memory_limit) diff --git a/app/models/runtime/stack.rb b/app/models/runtime/stack.rb index c2fb2d81222..730695ee9db 100644 --- a/app/models/runtime/stack.rb +++ b/app/models/runtime/stack.rb @@ -43,7 +43,6 @@ def around_save def validate validates_presence :name - validates_unique :name validates_includes StackStates::VALID_STATES, :state, allow_missing: true end diff --git a/app/models/runtime/user.rb b/app/models/runtime/user.rb index 212ce236089..2ef4adcfdac 100644 --- a/app/models/runtime/user.rb +++ b/app/models/runtime/user.rb @@ -87,7 +87,6 @@ def around_save def validate validates_presence :guid - validates_unique :guid end def validate_organization(org) diff --git a/app/models/services/service_broker.rb b/app/models/services/service_broker.rb index 90b56eb41c9..7cba05219c8 100644 --- a/app/models/services/service_broker.rb +++ b/app/models/services/service_broker.rb @@ -34,7 +34,6 @@ def validate validates_presence :broker_url validates_presence :auth_username validates_presence :auth_password - validates_unique :name, message: Sequel.lit('Name must be unique') validates_url :broker_url validates_url_no_basic_auth end diff --git a/app/models/services/service_key.rb b/app/models/services/service_key.rb index 0cc8545b3f1..cd33e12034f 100644 --- a/app/models/services/service_key.rb +++ b/app/models/services/service_key.rb @@ -49,7 +49,6 @@ def around_save def validate validates_presence :name validates_presence :service_instance - validates_unique %i[name service_instance_id] return unless service_instance diff --git a/spec/support/shared_examples/models/domain_validation.rb b/spec/support/shared_examples/models/domain_validation.rb index 55d08f1d757..a38ad243de0 100644 --- a/spec/support/shared_examples/models/domain_validation.rb +++ b/spec/support/shared_examples/models/domain_validation.rb @@ -13,10 +13,9 @@ module VCAP::CloudController context "when there's another domain with the same name" do it 'fails to validate' do - other_domain = described_class.make - other_domain.name = subject.name - expect(other_domain).not_to be_valid - expect(other_domain.errors[:name]).to include(:unique) + expect { + described_class.make(name: subject.name) + }.to raise_error(Sequel::ValidationFailed, /already reserved by another domain|unique/) end end diff --git a/spec/unit/actions/app_create_spec.rb b/spec/unit/actions/app_create_spec.rb index da951d624ce..2b31125426c 100644 --- a/spec/unit/actions/app_create_spec.rb +++ b/spec/unit/actions/app_create_spec.rb @@ -200,22 +200,6 @@ module VCAP::CloudController end end - context 'when creating apps concurrently' do - it 'ensures one creation is successful and the other fails due to name conflict' do - # First request, should succeed - expect do - app_create.create(message, lifecycle) - end.not_to raise_error - - # Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation - allow_any_instance_of(AppModel).to receive(:validate).and_return(true) - - # Second request, should fail with correct error - expect do - app_create.create(message, lifecycle) - end.to raise_error(CloudController::Errors::V3::ApiError) - end - end describe 'stack state validation' do let(:test_stack) { Stack.make(name: 'test-stack-for-validation') } diff --git a/spec/unit/actions/organization_create_spec.rb b/spec/unit/actions/organization_create_spec.rb index 7efbe430aaa..569eb1e371b 100644 --- a/spec/unit/actions/organization_create_spec.rb +++ b/spec/unit/actions/organization_create_spec.rb @@ -121,8 +121,6 @@ module VCAP::CloudController org_create.create(message) end.not_to raise_error - # Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation - allow_any_instance_of(Organization).to receive(:validate).and_return(true) # Second request, should fail with correct error expect do diff --git a/spec/unit/actions/service_broker_create_spec.rb b/spec/unit/actions/service_broker_create_spec.rb index 973405df6d6..49ff4efb50d 100644 --- a/spec/unit/actions/service_broker_create_spec.rb +++ b/spec/unit/actions/service_broker_create_spec.rb @@ -140,8 +140,6 @@ module CloudController action.create(message) end.not_to raise_error - # Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation - allow_any_instance_of(ServiceBroker).to receive(:validate).and_return(true) # Second request, should fail with correct error expect do diff --git a/spec/unit/actions/space_create_spec.rb b/spec/unit/actions/space_create_spec.rb index 6d356167753..2d43674bdf8 100644 --- a/spec/unit/actions/space_create_spec.rb +++ b/spec/unit/actions/space_create_spec.rb @@ -90,8 +90,6 @@ module VCAP::CloudController SpaceCreate.new(user_audit_info:).create(org, message) end.not_to raise_error - # Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation - allow_any_instance_of(Space).to receive(:validate).and_return(true) # Second request, should fail with correct error expect do diff --git a/spec/unit/actions/space_quotas_create_spec.rb b/spec/unit/actions/space_quotas_create_spec.rb index e3573fb9a0f..08c6f7d5dfd 100644 --- a/spec/unit/actions/space_quotas_create_spec.rb +++ b/spec/unit/actions/space_quotas_create_spec.rb @@ -210,8 +210,6 @@ module VCAP::CloudController space_quotas_create.create(message, organization: org) end.not_to raise_error - # Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation - allow_any_instance_of(SpaceQuotaDefinition).to receive(:validate).and_return(true) # Second request, should fail with correct error expect do diff --git a/spec/unit/actions/stack_create_spec.rb b/spec/unit/actions/stack_create_spec.rb index 156019fd567..c661bd9db45 100644 --- a/spec/unit/actions/stack_create_spec.rb +++ b/spec/unit/actions/stack_create_spec.rb @@ -130,8 +130,6 @@ module VCAP::CloudController stack_create.create(message) end.not_to raise_error - # Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation - allow_any_instance_of(Stack).to receive(:validate).and_return(true) # Second request, should fail with correct error expect do diff --git a/spec/unit/actions/user_create_spec.rb b/spec/unit/actions/user_create_spec.rb index 6d182d97549..37a785f9f28 100644 --- a/spec/unit/actions/user_create_spec.rb +++ b/spec/unit/actions/user_create_spec.rb @@ -44,8 +44,6 @@ module VCAP::CloudController subject.create(message:) end.not_to raise_error - # Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation - allow_any_instance_of(User).to receive(:validate).and_return(true) # Second request, should fail with correct error expect do diff --git a/spec/unit/isolation_segment_create_spec.rb b/spec/unit/isolation_segment_create_spec.rb index a79655e3628..2aac7938bee 100644 --- a/spec/unit/isolation_segment_create_spec.rb +++ b/spec/unit/isolation_segment_create_spec.rb @@ -53,13 +53,11 @@ module VCAP::CloudController IsolationSegmentCreate.create(message) end.not_to raise_error - # Mock the validation for the second request to simulate the race condition and trigger a unique constraint violation - allow_any_instance_of(IsolationSegmentModel).to receive(:validate).and_return(true) # Second request, should fail with correct error expect do IsolationSegmentCreate.create(message) - end.to raise_error(IsolationSegmentCreate::Error, 'name unique') + end.to raise_error(IsolationSegmentCreate::Error, 'Isolation Segment names are case insensitive and must be unique') end end end diff --git a/spec/unit/models/runtime/domain_spec.rb b/spec/unit/models/runtime/domain_spec.rb index e3e94564e8b..5c7c3c7ccf0 100644 --- a/spec/unit/models/runtime/domain_spec.rb +++ b/spec/unit/models/runtime/domain_spec.rb @@ -105,7 +105,6 @@ module VCAP::CloudController describe 'Validations' do it { is_expected.to validate_presence :name } - it { is_expected.to validate_uniqueness :name } describe 'route collisions' do let!(:existing_domain) { SharedDomain.make(name: 'base.domain') } diff --git a/spec/unit/models/runtime/isolation_segment_model_spec.rb b/spec/unit/models/runtime/isolation_segment_model_spec.rb index 6b76de2d616..2b0268a279a 100644 --- a/spec/unit/models/runtime/isolation_segment_model_spec.rb +++ b/spec/unit/models/runtime/isolation_segment_model_spec.rb @@ -124,22 +124,6 @@ module VCAP::CloudController end.to raise_error(Sequel::ValidationFailed, 'Isolation Segment names can only contain non-blank unicode characters') end - it 'requires a unique name' do - IsolationSegmentModel.make(name: 'segment1') - - expect do - IsolationSegmentModel.make(name: 'segment1') - end.to raise_error(Sequel::ValidationFailed, 'Isolation Segment names are case insensitive and must be unique') - end - - it 'uniqueness is case insensitive' do - IsolationSegmentModel.make(name: 'lowercase') - - expect do - IsolationSegmentModel.make(name: 'lowerCase') - end.to raise_error(Sequel::ValidationFailed, 'Isolation Segment names are case insensitive and must be unique') - end - it 'allows standard ascii characters' do expect do IsolationSegmentModel.make(name: "A -_- word 2!?()'\"&+.") diff --git a/spec/unit/models/runtime/organization_auditor_spec.rb b/spec/unit/models/runtime/organization_auditor_spec.rb index 8737b8d4bfc..7675657ffd5 100644 --- a/spec/unit/models/runtime/organization_auditor_spec.rb +++ b/spec/unit/models/runtime/organization_auditor_spec.rb @@ -5,8 +5,16 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } + describe "uniquiness" do + it "prevend dublicate organization_id and user_id combination" do + OrganizationAuditor.create(organization_id: organization.id, user_id: user.id) + expect { + OrganizationAuditor.create(organization_id: organization.id, user_id: user.id) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do - it { is_expected.to validate_uniqueness %i[organization_id user_id] } it { is_expected.to validate_presence :organization_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/organization_billing_manager_spec.rb b/spec/unit/models/runtime/organization_billing_manager_spec.rb index b981cd596c2..4ed8c2dd8de 100644 --- a/spec/unit/models/runtime/organization_billing_manager_spec.rb +++ b/spec/unit/models/runtime/organization_billing_manager_spec.rb @@ -5,8 +5,16 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } + describe "uniquiness" do + it "prevend dublicate organization_id and user_id combination" do + OrganizationBillingManager.create(organization_id: organization.id, user_id: user.id) + expect { + OrganizationBillingManager.create(organization_id: organization.id, user_id: user.id) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do - it { is_expected.to validate_uniqueness %i[organization_id user_id] } it { is_expected.to validate_presence :organization_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/organization_manager_spec.rb b/spec/unit/models/runtime/organization_manager_spec.rb index 8f7bd4f41a3..e5c3993576b 100644 --- a/spec/unit/models/runtime/organization_manager_spec.rb +++ b/spec/unit/models/runtime/organization_manager_spec.rb @@ -5,8 +5,16 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } + describe "uniquiness" do + it "prevend dublicate organization_id and user_id combination" do + OrganizationManager.create(organization_id: organization.id, user_id: user.id) + expect { + OrganizationManager.create(organization_id: organization.id, user_id: user.id) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do - it { is_expected.to validate_uniqueness %i[organization_id user_id] } it { is_expected.to validate_presence :organization_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/organization_spec.rb b/spec/unit/models/runtime/organization_spec.rb index de1f8c930f8..8ad2510d9dd 100644 --- a/spec/unit/models/runtime/organization_spec.rb +++ b/spec/unit/models/runtime/organization_spec.rb @@ -125,7 +125,6 @@ module VCAP::CloudController let(:org) { Organization.make } it { is_expected.to validate_presence :name } - it { is_expected.to validate_uniqueness :name } it { is_expected.to strip_whitespace :name } describe 'name' do diff --git a/spec/unit/models/runtime/organization_user_spec.rb b/spec/unit/models/runtime/organization_user_spec.rb index c0c896fa43c..bb72b1a4cfa 100644 --- a/spec/unit/models/runtime/organization_user_spec.rb +++ b/spec/unit/models/runtime/organization_user_spec.rb @@ -5,8 +5,16 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } + describe "uniquiness" do + it "prevend dublicate organization_id and user_id combination" do + OrganizationUser.create(organization_id: organization.id, user_id: user.id) + expect { + OrganizationUser.create(organization_id: organization.id, user_id: user.id) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do - it { is_expected.to validate_uniqueness %i[organization_id user_id] } it { is_expected.to validate_presence :organization_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/shared_domain_spec.rb b/spec/unit/models/runtime/shared_domain_spec.rb index 6b587cfb922..342447a6178 100644 --- a/spec/unit/models/runtime/shared_domain_spec.rb +++ b/spec/unit/models/runtime/shared_domain_spec.rb @@ -67,7 +67,7 @@ module VCAP::CloudController it 'denies shared foo.com when private foo.com exists' do PrivateDomain.make name: 'foo.com' - expect { SharedDomain.make name: 'foo.com' }.to raise_error(Sequel::ValidationFailed, /name unique/) + expect { SharedDomain.make name: 'foo.com' }.to raise_error(Sequel::ValidationFailed, /already reserved by another domain|unique/) end context 'when the domain is internal' do diff --git a/spec/unit/models/runtime/space_auditor_spec.rb b/spec/unit/models/runtime/space_auditor_spec.rb index 6203084ae93..3101fa61384 100644 --- a/spec/unit/models/runtime/space_auditor_spec.rb +++ b/spec/unit/models/runtime/space_auditor_spec.rb @@ -5,8 +5,16 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } + describe "uniquiness" do + it "prevend dublicate space_id and user_id combination" do + SpaceAuditor.create(space_id: space.id, user_id: user.id) + expect{ + SpaceAuditor.create(space_id: space.id, user_id: user.id) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do - it { is_expected.to validate_uniqueness %i[space_id user_id] } it { is_expected.to validate_presence :space_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/space_developer_spec.rb b/spec/unit/models/runtime/space_developer_spec.rb index 60e2a0cbb43..8b7a61ae22f 100644 --- a/spec/unit/models/runtime/space_developer_spec.rb +++ b/spec/unit/models/runtime/space_developer_spec.rb @@ -5,8 +5,15 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } + describe "uniquiness" do + it "prevend dublicate space_id and user_id combination" do + SpaceDeveloper.create(space_id: space.id, user_id: user.id) + expect{ + SpaceDeveloper.create(space_id: space.id, user_id: user.id) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end describe 'Validations' do - it { is_expected.to validate_uniqueness %i[space_id user_id] } it { is_expected.to validate_presence :space_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/space_manager_spec.rb b/spec/unit/models/runtime/space_manager_spec.rb index 45a5e2ded30..de46d51ece0 100644 --- a/spec/unit/models/runtime/space_manager_spec.rb +++ b/spec/unit/models/runtime/space_manager_spec.rb @@ -5,8 +5,15 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } + describe "uniquiness" do + it "prevend dublicate space_id and user_id combination" do + SpaceManager.create(space_id: space.id, user_id: user.id) + expect{ + SpaceManager.create(space_id: space.id, user_id: user.id) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end describe 'Validations' do - it { is_expected.to validate_uniqueness %i[space_id user_id] } it { is_expected.to validate_presence :space_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/space_quota_definition_spec.rb b/spec/unit/models/runtime/space_quota_definition_spec.rb index 07b64ebdbac..c7599170634 100644 --- a/spec/unit/models/runtime/space_quota_definition_spec.rb +++ b/spec/unit/models/runtime/space_quota_definition_spec.rb @@ -24,7 +24,6 @@ module VCAP::CloudController it { is_expected.to validate_presence :total_routes } it { is_expected.to validate_presence :memory_limit } it { is_expected.to validate_presence :organization } - it { is_expected.to validate_uniqueness %i[organization_id name] } describe 'memory_limits' do it 'total memory_limit cannot be less than zero' do diff --git a/spec/unit/models/runtime/space_spec.rb b/spec/unit/models/runtime/space_spec.rb index b4c4711c648..67dadd913fc 100644 --- a/spec/unit/models/runtime/space_spec.rb +++ b/spec/unit/models/runtime/space_spec.rb @@ -7,7 +7,6 @@ module VCAP::CloudController describe 'Validations' do it { is_expected.to validate_presence :name } it { is_expected.to validate_presence :organization } - it { is_expected.to validate_uniqueness %i[organization_id name] } it { is_expected.to strip_whitespace :name } context 'name' do diff --git a/spec/unit/models/runtime/space_supporter_spec.rb b/spec/unit/models/runtime/space_supporter_spec.rb index dfb037ba594..6df94437f86 100644 --- a/spec/unit/models/runtime/space_supporter_spec.rb +++ b/spec/unit/models/runtime/space_supporter_spec.rb @@ -6,8 +6,15 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } + describe "uniquiness" do + it "prevend dublicate space_id and user_id combination" do + SpaceSupporter.create(space_id: space.id, user_id: user.id) + expect{ + SpaceSupporter.create(space_id: space.id, user_id: user.id) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end describe 'Validations' do - it { is_expected.to validate_uniqueness %i[space_id user_id] } it { is_expected.to validate_presence :space_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/stack_spec.rb b/spec/unit/models/runtime/stack_spec.rb index ec3fe336ee6..dafc0bdb851 100644 --- a/spec/unit/models/runtime/stack_spec.rb +++ b/spec/unit/models/runtime/stack_spec.rb @@ -24,7 +24,6 @@ module VCAP::CloudController describe 'Validations' do it { is_expected.to validate_presence :name } - it { is_expected.to validate_uniqueness :name } it { is_expected.to strip_whitespace :name } describe 'state validation' do diff --git a/spec/unit/models/runtime/user_spec.rb b/spec/unit/models/runtime/user_spec.rb index ad10cdcf0d2..2b7d15655e2 100644 --- a/spec/unit/models/runtime/user_spec.rb +++ b/spec/unit/models/runtime/user_spec.rb @@ -63,7 +63,6 @@ module VCAP::CloudController describe 'Validations' do it { is_expected.to validate_presence :guid } - it { is_expected.to validate_uniqueness :guid } end describe 'Serialization' do diff --git a/spec/unit/models/services/service_broker_spec.rb b/spec/unit/models/services/service_broker_spec.rb index fd2c8641541..a5bbea8e1b1 100644 --- a/spec/unit/models/services/service_broker_spec.rb +++ b/spec/unit/models/services/service_broker_spec.rb @@ -33,7 +33,6 @@ module VCAP::CloudController it { is_expected.to validate_presence :broker_url } it { is_expected.to validate_presence :auth_username } it { is_expected.to validate_presence :auth_password } - it { is_expected.to validate_uniqueness :name, message: Sequel.lit('Name must be unique') } it 'validates the url is a valid http/https url' do expect(broker).to be_valid diff --git a/spec/unit/models/services/service_key_spec.rb b/spec/unit/models/services/service_key_spec.rb index f4a224bbf60..a267ab21abe 100644 --- a/spec/unit/models/services/service_key_spec.rb +++ b/spec/unit/models/services/service_key_spec.rb @@ -19,12 +19,20 @@ module VCAP::CloudController it { is_expected.to have_associated :service_instance, associated_instance: ->(service_key) { ServiceInstance.make(space: service_key.space) } } end + describe 'uniqueness' do + it 'enforces uniqueness of name and service_instance_id' do + existing = ServiceKey.make + expect { + ServiceKey.make(name: existing.name, service_instance: existing.service_instance) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :service_instance } it { is_expected.to validate_presence :name } it { is_expected.to validate_db_presence :service_instance_id } it { is_expected.to validate_db_presence :credentials } - it { is_expected.to validate_uniqueness %i[name service_instance_id] } context 'MaxServiceKeysPolicy' do let(:service_key) { ServiceKey.make } From 66dc1f505a4b6fad91d9113bf18e9c458ddddf87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Mon, 16 Mar 2026 15:53:36 +0100 Subject: [PATCH 02/16] feat: around_save added for existing unique index for service_plan --- app/models/services/service_plan.rb | 11 ++++++++++- spec/unit/models/services/service_plan_spec.rb | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/models/services/service_plan.rb b/app/models/services/service_plan.rb index f713352828b..8a655d95e86 100644 --- a/app/models/services/service_plan.rb +++ b/app/models/services/service_plan.rb @@ -75,13 +75,22 @@ def active? alias_method :broker_provided_id, :unique_id + def around_save + yield + rescue Sequel::UniqueConstraintViolation => e + raise e unless e.message.include?('svc_plan_svc_id_name_index') + + errors.add(%i[name service_id], Sequel.lit("Plan names must be unique within a service. Service #{service.try(:label)} already has a plan named #{name}")) + raise validation_failed_error + end + + def validate validates_presence :name, message: 'is required' validates_presence :description, message: 'is required' validates_presence :free, message: 'is required' validates_presence :service, message: 'is required' validates_presence :unique_id, message: 'is required' - validates_unique %i[service_id name], message: Sequel.lit("Plan names must be unique within a service. Service #{service.try(:label)} already has a plan named #{name}") validate_private_broker_plan_not_public end diff --git a/spec/unit/models/services/service_plan_spec.rb b/spec/unit/models/services/service_plan_spec.rb index 6a0310cffef..583bf577dde 100644 --- a/spec/unit/models/services/service_plan_spec.rb +++ b/spec/unit/models/services/service_plan_spec.rb @@ -12,6 +12,15 @@ module VCAP::CloudController it { is_expected.to have_associated :annotations, class: ServicePlanAnnotationModel } end + describe 'uniqueness' do + it 'enforces uniqueness of name within a service' do + existing = ServicePlan.make + expect { + ServicePlan.make(name: existing.name, service: existing.service) + }.to raise_error(Sequel::ValidationFailed, /already has a plan named/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :name, message: 'is required' } it { is_expected.to validate_presence :free, message: 'is required' } From 156e48a9a98f9803212f0a8e2bf5482160f8caf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Mon, 16 Mar 2026 16:06:52 +0100 Subject: [PATCH 03/16] feat: around_save added for existing unique index for service_plan_visibility --- app/models/services/service_plan_visibility.rb | 10 +++++++++- .../models/services/service_plan_visibility_spec.rb | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/models/services/service_plan_visibility.rb b/app/models/services/service_plan_visibility.rb index ec7d399f510..f728b1c9b46 100644 --- a/app/models/services/service_plan_visibility.rb +++ b/app/models/services/service_plan_visibility.rb @@ -6,10 +6,18 @@ class ServicePlanVisibility < Sequel::Model import_attributes :service_plan_guid, :organization_guid export_attributes :service_plan_guid, :organization_guid + def around_save + yield + rescue Sequel::UniqueConstraintViolation => e + raise e unless e.message.include?('spv_org_id_sp_id_index') + + errors.add(%i[organization_id service_plan_id], :unique) + raise validation_failed_error + end + def validate validates_presence :service_plan validates_presence :organization - validates_unique %i[organization_id service_plan_id] validate_plan_is_not_private validate_plan_is_not_public end diff --git a/spec/unit/models/services/service_plan_visibility_spec.rb b/spec/unit/models/services/service_plan_visibility_spec.rb index f8708027578..7e14c181623 100644 --- a/spec/unit/models/services/service_plan_visibility_spec.rb +++ b/spec/unit/models/services/service_plan_visibility_spec.rb @@ -9,10 +9,18 @@ module VCAP::CloudController it { is_expected.to have_associated :organization } end + describe 'uniqueness' do + it 'enforces uniqueness of organization and service plan combination' do + existing = ServicePlanVisibility.make + expect { + ServicePlanVisibility.create(service_plan: existing.service_plan, organization: existing.organization) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :service_plan } it { is_expected.to validate_presence :organization } - it { is_expected.to validate_uniqueness %i[organization_id service_plan_id] } context 'when the service plan visibility is for a private broker' do it 'returns a validation error' do From 41ca8806956e14afbf86543764b29a2a4eff67a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Mon, 16 Mar 2026 16:38:11 +0100 Subject: [PATCH 04/16] feat: around_save added for existing unique index for service_dashboard_client model --- app/models/services/service_dashboard_client.rb | 10 +++++++++- .../models/services/service_dashboard_client_spec.rb | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/models/services/service_dashboard_client.rb b/app/models/services/service_dashboard_client.rb index 175df575e41..c608a2a6d42 100644 --- a/app/models/services/service_dashboard_client.rb +++ b/app/models/services/service_dashboard_client.rb @@ -2,9 +2,17 @@ module VCAP::CloudController class ServiceDashboardClient < Sequel::Model many_to_one :service_broker + def around_save + yield + rescue Sequel::UniqueConstraintViolation => e + raise e unless e.message.include?('s_d_clients_uaa_id_unique') + + errors.add(:uaa_id, :unique) + raise validation_failed_error + end + def validate validates_presence :uaa_id - validates_unique :uaa_id end class << self diff --git a/spec/unit/models/services/service_dashboard_client_spec.rb b/spec/unit/models/services/service_dashboard_client_spec.rb index 342e85c84dc..8b1ffee0042 100644 --- a/spec/unit/models/services/service_dashboard_client_spec.rb +++ b/spec/unit/models/services/service_dashboard_client_spec.rb @@ -12,9 +12,17 @@ module VCAP::CloudController it { is_expected.to have_associated :service_broker } end + describe 'uniqueness' do + it 'enforces uniqueness of uaa_id' do + existing = ServiceDashboardClient.make(service_broker:) + expect { + ServiceDashboardClient.create(uaa_id: existing.uaa_id, service_broker: other_broker) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :uaa_id } - it { is_expected.to validate_uniqueness :uaa_id } context 'when all fields are valid' do let(:client) { ServiceDashboardClient.make_unsaved(service_broker:) } From bca2128b0d37e0951d93d16b60aed29d001ea113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Mon, 16 Mar 2026 16:51:47 +0100 Subject: [PATCH 05/16] feat: around_save added for existing unique index for feature_flag model --- app/models/runtime/feature_flag.rb | 10 +++++++++- spec/unit/models/runtime/feature_flag_spec.rb | 16 +++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/models/runtime/feature_flag.rb b/app/models/runtime/feature_flag.rb index df991d89f7e..e64b7d60e7b 100644 --- a/app/models/runtime/feature_flag.rb +++ b/app/models/runtime/feature_flag.rb @@ -43,9 +43,17 @@ class UndefinedFeatureFlagError < StandardError export_attributes :name, :enabled, :error_message import_attributes :name, :enabled, :error_message + def around_save + yield + rescue Sequel::UniqueConstraintViolation => e + raise e unless e.message.include?('feature_flags_name_index') + + errors.add(:name, :unique) + raise validation_failed_error + end + def validate validates_presence :name - validates_unique :name validates_presence :enabled validates_includes DEFAULT_FLAGS.keys.map(&:to_s), :name diff --git a/spec/unit/models/runtime/feature_flag_spec.rb b/spec/unit/models/runtime/feature_flag_spec.rb index 458f93c4cdd..99febd580d6 100644 --- a/spec/unit/models/runtime/feature_flag_spec.rb +++ b/spec/unit/models/runtime/feature_flag_spec.rb @@ -6,17 +6,19 @@ module VCAP::CloudController it { is_expected.to have_timestamp_columns } + describe 'uniqueness' do + it 'enforces uniqueness of name' do + existing_flag = FeatureFlag.make + expect { + FeatureFlag.create(name: existing_flag.name, enabled: true) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :name } it { is_expected.to validate_presence :enabled } - it 'validates name is unique' do - existing_flag = FeatureFlag.make - duplicate_flag = FeatureFlag.new - duplicate_flag.name = existing_flag.name - expect { duplicate_flag.save }.to raise_error(Sequel::ValidationFailed, /name unique/) - end - context 'name validation' do context 'with a valid name' do it 'allows creation of a feature flag that has a corresponding default' do From 84cd8d4e65e86a4cd280f6836d901d218eb4ef0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Tue, 17 Mar 2026 10:23:21 +0100 Subject: [PATCH 06/16] feat: around_save added for existing unique index for qutoa_definition model --- app/models/runtime/quota_definition.rb | 1 - spec/unit/models/runtime/quota_definition_spec.rb | 10 +++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/models/runtime/quota_definition.rb b/app/models/runtime/quota_definition.rb index 738570d1bc3..ba9a61357ba 100644 --- a/app/models/runtime/quota_definition.rb +++ b/app/models/runtime/quota_definition.rb @@ -30,7 +30,6 @@ def around_save def validate validates_presence :name - validates_unique :name validates_presence :non_basic_services_allowed validates_presence :total_services validates_presence :total_routes diff --git a/spec/unit/models/runtime/quota_definition_spec.rb b/spec/unit/models/runtime/quota_definition_spec.rb index 21d67e7f60c..5868d3c6452 100644 --- a/spec/unit/models/runtime/quota_definition_spec.rb +++ b/spec/unit/models/runtime/quota_definition_spec.rb @@ -14,13 +14,21 @@ module VCAP::CloudController it { is_expected.to have_associated :organizations } end + describe 'uniqueness' do + it 'enforces uniqueness of name' do + existing = QuotaDefinition.make + expect { + QuotaDefinition.create(name: existing.name, non_basic_services_allowed: true, total_services: 0, total_routes: 0, memory_limit: 0) + }.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :name } it { is_expected.to validate_presence :non_basic_services_allowed } it { is_expected.to validate_presence :total_services } it { is_expected.to validate_presence :total_routes } it { is_expected.to validate_presence :memory_limit } - it { is_expected.to validate_uniqueness :name } describe 'memory_limits' do it 'total memory_limit cannot be less than -1 ("unlimited")' do From bb30c9894c45db6f0bce313518491ef2858bc84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Tue, 17 Mar 2026 10:25:03 +0100 Subject: [PATCH 07/16] fix: rubocop errors are fixed --- app/models/runtime/app_model.rb | 1 - app/models/services/service_plan.rb | 1 - spec/support/shared_examples/models/domain_validation.rb | 4 ++-- spec/unit/actions/app_create_spec.rb | 1 - spec/unit/actions/organization_create_spec.rb | 1 - spec/unit/actions/service_broker_create_spec.rb | 1 - spec/unit/actions/space_create_spec.rb | 1 - spec/unit/actions/space_quotas_create_spec.rb | 1 - spec/unit/actions/stack_create_spec.rb | 1 - spec/unit/actions/user_create_spec.rb | 1 - spec/unit/isolation_segment_create_spec.rb | 1 - spec/unit/models/runtime/feature_flag_spec.rb | 4 ++-- spec/unit/models/runtime/organization_auditor_spec.rb | 8 ++++---- .../models/runtime/organization_billing_manager_spec.rb | 8 ++++---- spec/unit/models/runtime/organization_manager_spec.rb | 8 ++++---- spec/unit/models/runtime/organization_user_spec.rb | 8 ++++---- spec/unit/models/runtime/quota_definition_spec.rb | 4 ++-- spec/unit/models/runtime/space_auditor_spec.rb | 8 ++++---- spec/unit/models/runtime/space_developer_spec.rb | 9 +++++---- spec/unit/models/runtime/space_manager_spec.rb | 9 +++++---- spec/unit/models/runtime/space_supporter_spec.rb | 9 +++++---- .../models/services/service_dashboard_client_spec.rb | 4 ++-- spec/unit/models/services/service_key_spec.rb | 4 ++-- spec/unit/models/services/service_plan_spec.rb | 4 ++-- .../unit/models/services/service_plan_visibility_spec.rb | 4 ++-- 25 files changed, 49 insertions(+), 56 deletions(-) diff --git a/app/models/runtime/app_model.rb b/app/models/runtime/app_model.rb index d4e14ecc54a..b02e1550c56 100644 --- a/app/models/runtime/app_model.rb +++ b/app/models/runtime/app_model.rb @@ -99,7 +99,6 @@ def validate validates_format APP_NAME_REGEX, :name validate_environment_variables validate_droplet_is_staged - end def lifecycle_type diff --git a/app/models/services/service_plan.rb b/app/models/services/service_plan.rb index 8a655d95e86..50fbe006bdc 100644 --- a/app/models/services/service_plan.rb +++ b/app/models/services/service_plan.rb @@ -84,7 +84,6 @@ def around_save raise validation_failed_error end - def validate validates_presence :name, message: 'is required' validates_presence :description, message: 'is required' diff --git a/spec/support/shared_examples/models/domain_validation.rb b/spec/support/shared_examples/models/domain_validation.rb index a38ad243de0..cedbdbca8b8 100644 --- a/spec/support/shared_examples/models/domain_validation.rb +++ b/spec/support/shared_examples/models/domain_validation.rb @@ -13,9 +13,9 @@ module VCAP::CloudController context "when there's another domain with the same name" do it 'fails to validate' do - expect { + expect do described_class.make(name: subject.name) - }.to raise_error(Sequel::ValidationFailed, /already reserved by another domain|unique/) + end.to raise_error(Sequel::ValidationFailed, /already reserved by another domain|unique/) end end diff --git a/spec/unit/actions/app_create_spec.rb b/spec/unit/actions/app_create_spec.rb index 2b31125426c..8730f187d4a 100644 --- a/spec/unit/actions/app_create_spec.rb +++ b/spec/unit/actions/app_create_spec.rb @@ -200,7 +200,6 @@ module VCAP::CloudController end end - describe 'stack state validation' do let(:test_stack) { Stack.make(name: 'test-stack-for-validation') } let(:lifecycle_request) { { type: 'buildpack', data: { buildpacks: [buildpack_identifier], stack: test_stack.name } } } diff --git a/spec/unit/actions/organization_create_spec.rb b/spec/unit/actions/organization_create_spec.rb index 569eb1e371b..47d63f4b6f2 100644 --- a/spec/unit/actions/organization_create_spec.rb +++ b/spec/unit/actions/organization_create_spec.rb @@ -121,7 +121,6 @@ module VCAP::CloudController org_create.create(message) end.not_to raise_error - # Second request, should fail with correct error expect do org_create.create(message) diff --git a/spec/unit/actions/service_broker_create_spec.rb b/spec/unit/actions/service_broker_create_spec.rb index 49ff4efb50d..9cfa2aecf65 100644 --- a/spec/unit/actions/service_broker_create_spec.rb +++ b/spec/unit/actions/service_broker_create_spec.rb @@ -140,7 +140,6 @@ module CloudController action.create(message) end.not_to raise_error - # Second request, should fail with correct error expect do action.create(message) diff --git a/spec/unit/actions/space_create_spec.rb b/spec/unit/actions/space_create_spec.rb index 2d43674bdf8..dd06f2e60c5 100644 --- a/spec/unit/actions/space_create_spec.rb +++ b/spec/unit/actions/space_create_spec.rb @@ -90,7 +90,6 @@ module VCAP::CloudController SpaceCreate.new(user_audit_info:).create(org, message) end.not_to raise_error - # Second request, should fail with correct error expect do SpaceCreate.new(user_audit_info:).create(org, message) diff --git a/spec/unit/actions/space_quotas_create_spec.rb b/spec/unit/actions/space_quotas_create_spec.rb index 08c6f7d5dfd..f5f8723ea95 100644 --- a/spec/unit/actions/space_quotas_create_spec.rb +++ b/spec/unit/actions/space_quotas_create_spec.rb @@ -210,7 +210,6 @@ module VCAP::CloudController space_quotas_create.create(message, organization: org) end.not_to raise_error - # Second request, should fail with correct error expect do space_quotas_create.create(message, organization: org) diff --git a/spec/unit/actions/stack_create_spec.rb b/spec/unit/actions/stack_create_spec.rb index c661bd9db45..bf088a7395d 100644 --- a/spec/unit/actions/stack_create_spec.rb +++ b/spec/unit/actions/stack_create_spec.rb @@ -130,7 +130,6 @@ module VCAP::CloudController stack_create.create(message) end.not_to raise_error - # Second request, should fail with correct error expect do stack_create.create(message) diff --git a/spec/unit/actions/user_create_spec.rb b/spec/unit/actions/user_create_spec.rb index 37a785f9f28..838b88a4bc3 100644 --- a/spec/unit/actions/user_create_spec.rb +++ b/spec/unit/actions/user_create_spec.rb @@ -44,7 +44,6 @@ module VCAP::CloudController subject.create(message:) end.not_to raise_error - # Second request, should fail with correct error expect do subject.create(message:) diff --git a/spec/unit/isolation_segment_create_spec.rb b/spec/unit/isolation_segment_create_spec.rb index 2aac7938bee..ef2659cf274 100644 --- a/spec/unit/isolation_segment_create_spec.rb +++ b/spec/unit/isolation_segment_create_spec.rb @@ -53,7 +53,6 @@ module VCAP::CloudController IsolationSegmentCreate.create(message) end.not_to raise_error - # Second request, should fail with correct error expect do IsolationSegmentCreate.create(message) diff --git a/spec/unit/models/runtime/feature_flag_spec.rb b/spec/unit/models/runtime/feature_flag_spec.rb index 99febd580d6..88b41fd0e93 100644 --- a/spec/unit/models/runtime/feature_flag_spec.rb +++ b/spec/unit/models/runtime/feature_flag_spec.rb @@ -9,9 +9,9 @@ module VCAP::CloudController describe 'uniqueness' do it 'enforces uniqueness of name' do existing_flag = FeatureFlag.make - expect { + expect do FeatureFlag.create(name: existing_flag.name, enabled: true) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end diff --git a/spec/unit/models/runtime/organization_auditor_spec.rb b/spec/unit/models/runtime/organization_auditor_spec.rb index 7675657ffd5..66aea240981 100644 --- a/spec/unit/models/runtime/organization_auditor_spec.rb +++ b/spec/unit/models/runtime/organization_auditor_spec.rb @@ -5,12 +5,12 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } - describe "uniquiness" do - it "prevend dublicate organization_id and user_id combination" do + describe 'uniquiness' do + it 'prevend dublicate organization_id and user_id combination' do OrganizationAuditor.create(organization_id: organization.id, user_id: user.id) - expect { + expect do OrganizationAuditor.create(organization_id: organization.id, user_id: user.id) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end diff --git a/spec/unit/models/runtime/organization_billing_manager_spec.rb b/spec/unit/models/runtime/organization_billing_manager_spec.rb index 4ed8c2dd8de..b6b07a8286c 100644 --- a/spec/unit/models/runtime/organization_billing_manager_spec.rb +++ b/spec/unit/models/runtime/organization_billing_manager_spec.rb @@ -5,12 +5,12 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } - describe "uniquiness" do - it "prevend dublicate organization_id and user_id combination" do + describe 'uniquiness' do + it 'prevend dublicate organization_id and user_id combination' do OrganizationBillingManager.create(organization_id: organization.id, user_id: user.id) - expect { + expect do OrganizationBillingManager.create(organization_id: organization.id, user_id: user.id) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end diff --git a/spec/unit/models/runtime/organization_manager_spec.rb b/spec/unit/models/runtime/organization_manager_spec.rb index e5c3993576b..ff563ae2a9d 100644 --- a/spec/unit/models/runtime/organization_manager_spec.rb +++ b/spec/unit/models/runtime/organization_manager_spec.rb @@ -5,12 +5,12 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } - describe "uniquiness" do - it "prevend dublicate organization_id and user_id combination" do + describe 'uniquiness' do + it 'prevend dublicate organization_id and user_id combination' do OrganizationManager.create(organization_id: organization.id, user_id: user.id) - expect { + expect do OrganizationManager.create(organization_id: organization.id, user_id: user.id) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end diff --git a/spec/unit/models/runtime/organization_user_spec.rb b/spec/unit/models/runtime/organization_user_spec.rb index bb72b1a4cfa..97f84111df3 100644 --- a/spec/unit/models/runtime/organization_user_spec.rb +++ b/spec/unit/models/runtime/organization_user_spec.rb @@ -5,12 +5,12 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } - describe "uniquiness" do - it "prevend dublicate organization_id and user_id combination" do + describe 'uniquiness' do + it 'prevend dublicate organization_id and user_id combination' do OrganizationUser.create(organization_id: organization.id, user_id: user.id) - expect { + expect do OrganizationUser.create(organization_id: organization.id, user_id: user.id) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end diff --git a/spec/unit/models/runtime/quota_definition_spec.rb b/spec/unit/models/runtime/quota_definition_spec.rb index 5868d3c6452..47bce2377ad 100644 --- a/spec/unit/models/runtime/quota_definition_spec.rb +++ b/spec/unit/models/runtime/quota_definition_spec.rb @@ -17,9 +17,9 @@ module VCAP::CloudController describe 'uniqueness' do it 'enforces uniqueness of name' do existing = QuotaDefinition.make - expect { + expect do QuotaDefinition.create(name: existing.name, non_basic_services_allowed: true, total_services: 0, total_routes: 0, memory_limit: 0) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end diff --git a/spec/unit/models/runtime/space_auditor_spec.rb b/spec/unit/models/runtime/space_auditor_spec.rb index 3101fa61384..440320255ff 100644 --- a/spec/unit/models/runtime/space_auditor_spec.rb +++ b/spec/unit/models/runtime/space_auditor_spec.rb @@ -5,12 +5,12 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } - describe "uniquiness" do - it "prevend dublicate space_id and user_id combination" do + describe 'uniquiness' do + it 'prevend dublicate space_id and user_id combination' do SpaceAuditor.create(space_id: space.id, user_id: user.id) - expect{ + expect do SpaceAuditor.create(space_id: space.id, user_id: user.id) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end diff --git a/spec/unit/models/runtime/space_developer_spec.rb b/spec/unit/models/runtime/space_developer_spec.rb index 8b7a61ae22f..8e0922c8d58 100644 --- a/spec/unit/models/runtime/space_developer_spec.rb +++ b/spec/unit/models/runtime/space_developer_spec.rb @@ -5,14 +5,15 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } - describe "uniquiness" do - it "prevend dublicate space_id and user_id combination" do + describe 'uniquiness' do + it 'prevend dublicate space_id and user_id combination' do SpaceDeveloper.create(space_id: space.id, user_id: user.id) - expect{ + expect do SpaceDeveloper.create(space_id: space.id, user_id: user.id) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end + describe 'Validations' do it { is_expected.to validate_presence :space_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/space_manager_spec.rb b/spec/unit/models/runtime/space_manager_spec.rb index de46d51ece0..36a8c94038d 100644 --- a/spec/unit/models/runtime/space_manager_spec.rb +++ b/spec/unit/models/runtime/space_manager_spec.rb @@ -5,14 +5,15 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } - describe "uniquiness" do - it "prevend dublicate space_id and user_id combination" do + describe 'uniquiness' do + it 'prevend dublicate space_id and user_id combination' do SpaceManager.create(space_id: space.id, user_id: user.id) - expect{ + expect do SpaceManager.create(space_id: space.id, user_id: user.id) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end + describe 'Validations' do it { is_expected.to validate_presence :space_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/runtime/space_supporter_spec.rb b/spec/unit/models/runtime/space_supporter_spec.rb index 6df94437f86..cba0eee62a6 100644 --- a/spec/unit/models/runtime/space_supporter_spec.rb +++ b/spec/unit/models/runtime/space_supporter_spec.rb @@ -6,14 +6,15 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } - describe "uniquiness" do - it "prevend dublicate space_id and user_id combination" do + describe 'uniquiness' do + it 'prevend dublicate space_id and user_id combination' do SpaceSupporter.create(space_id: space.id, user_id: user.id) - expect{ + expect do SpaceSupporter.create(space_id: space.id, user_id: user.id) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end + describe 'Validations' do it { is_expected.to validate_presence :space_id } it { is_expected.to validate_presence :user_id } diff --git a/spec/unit/models/services/service_dashboard_client_spec.rb b/spec/unit/models/services/service_dashboard_client_spec.rb index 8b1ffee0042..35ed4ee8867 100644 --- a/spec/unit/models/services/service_dashboard_client_spec.rb +++ b/spec/unit/models/services/service_dashboard_client_spec.rb @@ -15,9 +15,9 @@ module VCAP::CloudController describe 'uniqueness' do it 'enforces uniqueness of uaa_id' do existing = ServiceDashboardClient.make(service_broker:) - expect { + expect do ServiceDashboardClient.create(uaa_id: existing.uaa_id, service_broker: other_broker) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end diff --git a/spec/unit/models/services/service_key_spec.rb b/spec/unit/models/services/service_key_spec.rb index a267ab21abe..4ddf0c50d0c 100644 --- a/spec/unit/models/services/service_key_spec.rb +++ b/spec/unit/models/services/service_key_spec.rb @@ -22,9 +22,9 @@ module VCAP::CloudController describe 'uniqueness' do it 'enforces uniqueness of name and service_instance_id' do existing = ServiceKey.make - expect { + expect do ServiceKey.make(name: existing.name, service_instance: existing.service_instance) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end diff --git a/spec/unit/models/services/service_plan_spec.rb b/spec/unit/models/services/service_plan_spec.rb index 583bf577dde..f9e49c3dfa8 100644 --- a/spec/unit/models/services/service_plan_spec.rb +++ b/spec/unit/models/services/service_plan_spec.rb @@ -15,9 +15,9 @@ module VCAP::CloudController describe 'uniqueness' do it 'enforces uniqueness of name within a service' do existing = ServicePlan.make - expect { + expect do ServicePlan.make(name: existing.name, service: existing.service) - }.to raise_error(Sequel::ValidationFailed, /already has a plan named/) + end.to raise_error(Sequel::ValidationFailed, /already has a plan named/) end end diff --git a/spec/unit/models/services/service_plan_visibility_spec.rb b/spec/unit/models/services/service_plan_visibility_spec.rb index 7e14c181623..1b0d81b5df9 100644 --- a/spec/unit/models/services/service_plan_visibility_spec.rb +++ b/spec/unit/models/services/service_plan_visibility_spec.rb @@ -12,9 +12,9 @@ module VCAP::CloudController describe 'uniqueness' do it 'enforces uniqueness of organization and service plan combination' do existing = ServicePlanVisibility.make - expect { + expect do ServicePlanVisibility.create(service_plan: existing.service_plan, organization: existing.organization) - }.to raise_error(Sequel::ValidationFailed, /unique/) + end.to raise_error(Sequel::ValidationFailed, /unique/) end end From c496a362385da85f934d3127017610095561f586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Tue, 17 Mar 2026 11:46:25 +0100 Subject: [PATCH 08/16] fix: error message fixed for service_broker model --- app/models/services/service_broker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/services/service_broker.rb b/app/models/services/service_broker.rb index 7cba05219c8..0769f5e7320 100644 --- a/app/models/services/service_broker.rb +++ b/app/models/services/service_broker.rb @@ -25,7 +25,7 @@ def around_save rescue Sequel::UniqueConstraintViolation => e raise e unless e.message.include?('service_brokers_name_index') - errors.add(:name, :unique) + errors.add(:name, Sequel.lit('Name must be unique')) raise validation_failed_error end From be178e28c1c6a62a83b07ddc6e7511817f1d7a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Tue, 17 Mar 2026 12:23:01 +0100 Subject: [PATCH 09/16] fix: service_broker validate_unique reintroduced because, it uses this validation in service_broker_registraion service to refraing http call invalid brokers --- app/models/services/service_broker.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/services/service_broker.rb b/app/models/services/service_broker.rb index 0769f5e7320..0d85ba6005e 100644 --- a/app/models/services/service_broker.rb +++ b/app/models/services/service_broker.rb @@ -34,6 +34,7 @@ def validate validates_presence :broker_url validates_presence :auth_username validates_presence :auth_password + validates_unique :name, message: Sequel.lit('Name must be unique') validates_url :broker_url validates_url_no_basic_auth end From 0b73ef979f77c1ea296a1edf0f076410f1620a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Tue, 17 Mar 2026 13:01:56 +0100 Subject: [PATCH 10/16] fix: service_broker validate_unique reintroduced because, it uses this validation in service_key_manager to refraing http call in service_key_create --- app/models/services/service_key.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/services/service_key.rb b/app/models/services/service_key.rb index cd33e12034f..0cc8545b3f1 100644 --- a/app/models/services/service_key.rb +++ b/app/models/services/service_key.rb @@ -49,6 +49,7 @@ def around_save def validate validates_presence :name validates_presence :service_instance + validates_unique %i[name service_instance_id] return unless service_instance From 240dd21ad77b7bab7c9672d3c7a560cfa208605e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Wed, 18 Mar 2026 08:30:07 +0100 Subject: [PATCH 11/16] fix: service_broker validation_unique is removed --- app/models/services/service_broker.rb | 1 - lib/services/service_brokers/service_broker_registration.rb | 4 ++++ .../controllers/services/service_brokers_controller_spec.rb | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/services/service_broker.rb b/app/models/services/service_broker.rb index 0d85ba6005e..0769f5e7320 100644 --- a/app/models/services/service_broker.rb +++ b/app/models/services/service_broker.rb @@ -34,7 +34,6 @@ def validate validates_presence :broker_url validates_presence :auth_username validates_presence :auth_password - validates_unique :name, message: Sequel.lit('Name must be unique') validates_url :broker_url validates_url_no_basic_auth end diff --git a/lib/services/service_brokers/service_broker_registration.rb b/lib/services/service_brokers/service_broker_registration.rb index 3cc4e8ab42d..4c4e6f4322d 100644 --- a/lib/services/service_brokers/service_broker_registration.rb +++ b/lib/services/service_brokers/service_broker_registration.rb @@ -30,6 +30,8 @@ def create raise e end self + rescue Sequel::ValidationFailed + nil end def update @@ -50,6 +52,8 @@ def update synchronize_services_and_plans! end self + rescue Sequel::ValidationFailed + nil end delegate :errors, to: :broker diff --git a/spec/unit/controllers/services/service_brokers_controller_spec.rb b/spec/unit/controllers/services/service_brokers_controller_spec.rb index eff7192f8de..9c4ead91998 100644 --- a/spec/unit/controllers/services/service_brokers_controller_spec.rb +++ b/spec/unit/controllers/services/service_brokers_controller_spec.rb @@ -291,6 +291,7 @@ def stub_catalog(broker_url: nil, username: nil, password: nil) post '/v2/service_brokers', public_body expect(last_response).to have_status_code(201) + stub_catalog post '/v2/service_brokers', body expect(last_response).to have_status_code(400) end From 8c7d5ed8d51bddf968799f9a5f928d0055928c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Wed, 18 Mar 2026 16:25:27 +0100 Subject: [PATCH 12/16] fix: typos are fixed --- spec/unit/models/runtime/organization_auditor_spec.rb | 4 ++-- spec/unit/models/runtime/organization_billing_manager_spec.rb | 4 ++-- spec/unit/models/runtime/organization_manager_spec.rb | 4 ++-- spec/unit/models/runtime/organization_user_spec.rb | 4 ++-- spec/unit/models/runtime/space_auditor_spec.rb | 4 ++-- spec/unit/models/runtime/space_developer_spec.rb | 4 ++-- spec/unit/models/runtime/space_manager_spec.rb | 4 ++-- spec/unit/models/runtime/space_supporter_spec.rb | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/spec/unit/models/runtime/organization_auditor_spec.rb b/spec/unit/models/runtime/organization_auditor_spec.rb index 66aea240981..e7c12080c85 100644 --- a/spec/unit/models/runtime/organization_auditor_spec.rb +++ b/spec/unit/models/runtime/organization_auditor_spec.rb @@ -5,8 +5,8 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } - describe 'uniquiness' do - it 'prevend dublicate organization_id and user_id combination' do + describe 'uniqueness' do + it 'prevents duplicate organization_id and user_id combination' do OrganizationAuditor.create(organization_id: organization.id, user_id: user.id) expect do OrganizationAuditor.create(organization_id: organization.id, user_id: user.id) diff --git a/spec/unit/models/runtime/organization_billing_manager_spec.rb b/spec/unit/models/runtime/organization_billing_manager_spec.rb index b6b07a8286c..8c101550d79 100644 --- a/spec/unit/models/runtime/organization_billing_manager_spec.rb +++ b/spec/unit/models/runtime/organization_billing_manager_spec.rb @@ -5,8 +5,8 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } - describe 'uniquiness' do - it 'prevend dublicate organization_id and user_id combination' do + describe 'uniqueness' do + it 'prevents duplicate organization_id and user_id combination' do OrganizationBillingManager.create(organization_id: organization.id, user_id: user.id) expect do OrganizationBillingManager.create(organization_id: organization.id, user_id: user.id) diff --git a/spec/unit/models/runtime/organization_manager_spec.rb b/spec/unit/models/runtime/organization_manager_spec.rb index ff563ae2a9d..c26d026ead5 100644 --- a/spec/unit/models/runtime/organization_manager_spec.rb +++ b/spec/unit/models/runtime/organization_manager_spec.rb @@ -5,8 +5,8 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } - describe 'uniquiness' do - it 'prevend dublicate organization_id and user_id combination' do + describe 'uniqueness' do + it 'prevents duplicate organization_id and user_id combination' do OrganizationManager.create(organization_id: organization.id, user_id: user.id) expect do OrganizationManager.create(organization_id: organization.id, user_id: user.id) diff --git a/spec/unit/models/runtime/organization_user_spec.rb b/spec/unit/models/runtime/organization_user_spec.rb index 97f84111df3..1217f66b431 100644 --- a/spec/unit/models/runtime/organization_user_spec.rb +++ b/spec/unit/models/runtime/organization_user_spec.rb @@ -5,8 +5,8 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } - describe 'uniquiness' do - it 'prevend dublicate organization_id and user_id combination' do + describe 'uniqueness' do + it 'prevents duplicate organization_id and user_id combination' do OrganizationUser.create(organization_id: organization.id, user_id: user.id) expect do OrganizationUser.create(organization_id: organization.id, user_id: user.id) diff --git a/spec/unit/models/runtime/space_auditor_spec.rb b/spec/unit/models/runtime/space_auditor_spec.rb index 440320255ff..1383c8ae302 100644 --- a/spec/unit/models/runtime/space_auditor_spec.rb +++ b/spec/unit/models/runtime/space_auditor_spec.rb @@ -5,8 +5,8 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } - describe 'uniquiness' do - it 'prevend dublicate space_id and user_id combination' do + describe 'uniqueness' do + it 'prevents duplicate space_id and user_id combination' do SpaceAuditor.create(space_id: space.id, user_id: user.id) expect do SpaceAuditor.create(space_id: space.id, user_id: user.id) diff --git a/spec/unit/models/runtime/space_developer_spec.rb b/spec/unit/models/runtime/space_developer_spec.rb index 8e0922c8d58..97c04e82215 100644 --- a/spec/unit/models/runtime/space_developer_spec.rb +++ b/spec/unit/models/runtime/space_developer_spec.rb @@ -5,8 +5,8 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } - describe 'uniquiness' do - it 'prevend dublicate space_id and user_id combination' do + describe 'uniqueness' do + it 'prevents duplicate space_id and user_id combination' do SpaceDeveloper.create(space_id: space.id, user_id: user.id) expect do SpaceDeveloper.create(space_id: space.id, user_id: user.id) diff --git a/spec/unit/models/runtime/space_manager_spec.rb b/spec/unit/models/runtime/space_manager_spec.rb index 36a8c94038d..5d832f2c576 100644 --- a/spec/unit/models/runtime/space_manager_spec.rb +++ b/spec/unit/models/runtime/space_manager_spec.rb @@ -5,8 +5,8 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } - describe 'uniquiness' do - it 'prevend dublicate space_id and user_id combination' do + describe 'uniqueness' do + it 'prevents duplicate space_id and user_id combination' do SpaceManager.create(space_id: space.id, user_id: user.id) expect do SpaceManager.create(space_id: space.id, user_id: user.id) diff --git a/spec/unit/models/runtime/space_supporter_spec.rb b/spec/unit/models/runtime/space_supporter_spec.rb index cba0eee62a6..1581c17fc76 100644 --- a/spec/unit/models/runtime/space_supporter_spec.rb +++ b/spec/unit/models/runtime/space_supporter_spec.rb @@ -6,8 +6,8 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } - describe 'uniquiness' do - it 'prevend dublicate space_id and user_id combination' do + describe 'uniqueness' do + it 'prevents duplicate space_id and user_id combination' do SpaceSupporter.create(space_id: space.id, user_id: user.id) expect do SpaceSupporter.create(space_id: space.id, user_id: user.id) From 3e6166d8aa8e336163115e6849cfde60d101d608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Wed, 18 Mar 2026 17:46:03 +0100 Subject: [PATCH 13/16] fix: concurrent context tests are removed in unit/actions. Uniqueness tests are reintroduced in unit/models. --- spec/unit/actions/organization_create_spec.rb | 17 ----------------- spec/unit/actions/service_broker_create_spec.rb | 14 -------------- spec/unit/actions/space_create_spec.rb | 17 ----------------- spec/unit/actions/space_quotas_create_spec.rb | 14 -------------- spec/unit/actions/stack_create_spec.rb | 17 ----------------- spec/unit/actions/user_create_spec.rb | 16 ---------------- spec/unit/isolation_segment_create_spec.rb | 15 --------------- .../runtime/isolation_segment_model_spec.rb | 16 ++++++++++++++++ spec/unit/models/runtime/organization_spec.rb | 9 +++++++++ .../runtime/space_quota_definition_spec.rb | 9 +++++++++ spec/unit/models/runtime/space_spec.rb | 9 +++++++++ spec/unit/models/runtime/stack_spec.rb | 9 +++++++++ spec/unit/models/runtime/user_spec.rb | 9 +++++++++ .../unit/models/services/service_broker_spec.rb | 9 +++++++++ 14 files changed, 70 insertions(+), 110 deletions(-) diff --git a/spec/unit/actions/organization_create_spec.rb b/spec/unit/actions/organization_create_spec.rb index 47d63f4b6f2..77e810e1d0e 100644 --- a/spec/unit/actions/organization_create_spec.rb +++ b/spec/unit/actions/organization_create_spec.rb @@ -110,23 +110,6 @@ module VCAP::CloudController end.to raise_error(OrganizationCreate::Error, "Organization '#{name}' already exists.") end end - - context 'when creating organizations concurrently' do - let(:name) { 'Alfredo' } - - it 'ensures one creation is successful and the other fails due to name conflict' do - # First request, should succeed - message = VCAP::CloudController::OrganizationUpdateMessage.new(name:) - expect do - org_create.create(message) - end.not_to raise_error - - # Second request, should fail with correct error - expect do - org_create.create(message) - end.to raise_error(OrganizationCreate::Error, "Organization 'Alfredo' already exists.") - end - end end end end diff --git a/spec/unit/actions/service_broker_create_spec.rb b/spec/unit/actions/service_broker_create_spec.rb index 9cfa2aecf65..22caa6b50e8 100644 --- a/spec/unit/actions/service_broker_create_spec.rb +++ b/spec/unit/actions/service_broker_create_spec.rb @@ -132,20 +132,6 @@ module CloudController end end end - - describe 'when creating a service broker with the same name concurrently' do - it 'ensures one creation is successful and the other fails due to name conflict' do - # First request, should succeed - expect do - action.create(message) - end.not_to raise_error - - # Second request, should fail with correct error - expect do - action.create(message) - end.to raise_error(V3::ServiceBrokerCreate::InvalidServiceBroker) - end - end end end end diff --git a/spec/unit/actions/space_create_spec.rb b/spec/unit/actions/space_create_spec.rb index dd06f2e60c5..c1547d30001 100644 --- a/spec/unit/actions/space_create_spec.rb +++ b/spec/unit/actions/space_create_spec.rb @@ -79,23 +79,6 @@ module VCAP::CloudController end.to raise_error(SpaceCreate::Error, 'Name must be unique per organization') end end - - context 'when creating spaces concurrently' do - let(:name) { 'Rose' } - - it 'ensures one creation is successful and the other fails due to name conflict' do - # First request, should succeed - message = VCAP::CloudController::SpaceCreateMessage.new(name:) - expect do - SpaceCreate.new(user_audit_info:).create(org, message) - end.not_to raise_error - - # Second request, should fail with correct error - expect do - SpaceCreate.new(user_audit_info:).create(org, message) - end.to raise_error(SpaceCreate::Error, 'Name must be unique per organization') - end - end end end end diff --git a/spec/unit/actions/space_quotas_create_spec.rb b/spec/unit/actions/space_quotas_create_spec.rb index f5f8723ea95..79ee409f9d5 100644 --- a/spec/unit/actions/space_quotas_create_spec.rb +++ b/spec/unit/actions/space_quotas_create_spec.rb @@ -202,20 +202,6 @@ module VCAP::CloudController end end end - - context 'when creating space quota with the same name concurrently' do - it 'ensures one creation is successful and the other fails due to name conflict' do - # First request, should succeed - expect do - space_quotas_create.create(message, organization: org) - end.not_to raise_error - - # Second request, should fail with correct error - expect do - space_quotas_create.create(message, organization: org) - end.to raise_error(SpaceQuotasCreate::Error, "Space Quota 'my-name' already exists.") - end - end end end end diff --git a/spec/unit/actions/stack_create_spec.rb b/spec/unit/actions/stack_create_spec.rb index bf088a7395d..1bc21fd2280 100644 --- a/spec/unit/actions/stack_create_spec.rb +++ b/spec/unit/actions/stack_create_spec.rb @@ -119,23 +119,6 @@ module VCAP::CloudController end.to raise_error(StackCreate::Error, 'Name must be unique') end end - - context 'when creating stack with the same name concurrently' do - let(:name) { 'Gaby' } - - it 'ensures one creation is successful and the other fails due to name conflict' do - message = VCAP::CloudController::StackCreateMessage.new(name:) - # First request, should succeed - expect do - stack_create.create(message) - end.not_to raise_error - - # Second request, should fail with correct error - expect do - stack_create.create(message) - end.to raise_error(StackCreate::Error, 'Name must be unique') - end - end end end end diff --git a/spec/unit/actions/user_create_spec.rb b/spec/unit/actions/user_create_spec.rb index 838b88a4bc3..9d215ad10ee 100644 --- a/spec/unit/actions/user_create_spec.rb +++ b/spec/unit/actions/user_create_spec.rb @@ -35,22 +35,6 @@ module VCAP::CloudController end end - context 'when creating users concurrently' do - let(:message) { UserCreateMessage.new({ guid: 'some-nice-user-gu-id' }) } - - it 'ensures one creation is successful and the other fails due to name conflict' do - # First request, should succeed - expect do - subject.create(message:) - end.not_to raise_error - - # Second request, should fail with correct error - expect do - subject.create(message:) - end.to raise_error(UserCreate::Error, "User with guid 'some-nice-user-gu-id' already exists.") - end - end - describe 'creating users' do before do allow(User).to receive_messages(create_uaa_shadow_user: { 'id' => guid }, get_user_id_by_username_and_origin: nil) diff --git a/spec/unit/isolation_segment_create_spec.rb b/spec/unit/isolation_segment_create_spec.rb index ef2659cf274..ccc67da435a 100644 --- a/spec/unit/isolation_segment_create_spec.rb +++ b/spec/unit/isolation_segment_create_spec.rb @@ -44,21 +44,6 @@ module VCAP::CloudController end.to raise_error(IsolationSegmentCreate::Error, 'blork is busted') end end - - context 'when creating isolation segments concurrently' do - it 'ensures one creation is successful and the other fails due to name conflict' do - # First request, should succeed - message = VCAP::CloudController::IsolationSegmentCreateMessage.new(name: 'foobar') - expect do - IsolationSegmentCreate.create(message) - end.not_to raise_error - - # Second request, should fail with correct error - expect do - IsolationSegmentCreate.create(message) - end.to raise_error(IsolationSegmentCreate::Error, 'Isolation Segment names are case insensitive and must be unique') - end - end end end end diff --git a/spec/unit/models/runtime/isolation_segment_model_spec.rb b/spec/unit/models/runtime/isolation_segment_model_spec.rb index 2b0268a279a..6b76de2d616 100644 --- a/spec/unit/models/runtime/isolation_segment_model_spec.rb +++ b/spec/unit/models/runtime/isolation_segment_model_spec.rb @@ -124,6 +124,22 @@ module VCAP::CloudController end.to raise_error(Sequel::ValidationFailed, 'Isolation Segment names can only contain non-blank unicode characters') end + it 'requires a unique name' do + IsolationSegmentModel.make(name: 'segment1') + + expect do + IsolationSegmentModel.make(name: 'segment1') + end.to raise_error(Sequel::ValidationFailed, 'Isolation Segment names are case insensitive and must be unique') + end + + it 'uniqueness is case insensitive' do + IsolationSegmentModel.make(name: 'lowercase') + + expect do + IsolationSegmentModel.make(name: 'lowerCase') + end.to raise_error(Sequel::ValidationFailed, 'Isolation Segment names are case insensitive and must be unique') + end + it 'allows standard ascii characters' do expect do IsolationSegmentModel.make(name: "A -_- word 2!?()'\"&+.") diff --git a/spec/unit/models/runtime/organization_spec.rb b/spec/unit/models/runtime/organization_spec.rb index 8ad2510d9dd..52945ecadb1 100644 --- a/spec/unit/models/runtime/organization_spec.rb +++ b/spec/unit/models/runtime/organization_spec.rb @@ -121,6 +121,15 @@ module VCAP::CloudController end end + describe 'uniqueness' do + it 'enforces uniqueness of name' do + existing_org = Organization.make + expect do + Organization.create(name: existing_org.name) + end.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do let(:org) { Organization.make } diff --git a/spec/unit/models/runtime/space_quota_definition_spec.rb b/spec/unit/models/runtime/space_quota_definition_spec.rb index c7599170634..e248390e660 100644 --- a/spec/unit/models/runtime/space_quota_definition_spec.rb +++ b/spec/unit/models/runtime/space_quota_definition_spec.rb @@ -17,6 +17,15 @@ module VCAP::CloudController end end + describe 'uniqueness' do + it 'enforces uniqueness of name within an organization' do + existing_quota = SpaceQuotaDefinition.make + expect do + SpaceQuotaDefinition.make(name: existing_quota.name, organization: existing_quota.organization) + end.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :name } it { is_expected.to validate_presence :non_basic_services_allowed } diff --git a/spec/unit/models/runtime/space_spec.rb b/spec/unit/models/runtime/space_spec.rb index 67dadd913fc..c22df7313aa 100644 --- a/spec/unit/models/runtime/space_spec.rb +++ b/spec/unit/models/runtime/space_spec.rb @@ -4,6 +4,15 @@ module VCAP::CloudController RSpec.describe VCAP::CloudController::Space, type: :model do it { is_expected.to have_timestamp_columns } + describe 'uniqueness' do + it 'enforces uniqueness of name within an organization' do + existing_space = Space.make + expect do + Space.create(name: existing_space.name, organization: existing_space.organization) + end.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :name } it { is_expected.to validate_presence :organization } diff --git a/spec/unit/models/runtime/stack_spec.rb b/spec/unit/models/runtime/stack_spec.rb index dafc0bdb851..cb17e7c8064 100644 --- a/spec/unit/models/runtime/stack_spec.rb +++ b/spec/unit/models/runtime/stack_spec.rb @@ -22,6 +22,15 @@ module VCAP::CloudController end end + describe 'uniqueness' do + it 'enforces uniqueness of name' do + existing_stack = Stack.make + expect do + Stack.create(name: existing_stack.name) + end.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :name } it { is_expected.to strip_whitespace :name } diff --git a/spec/unit/models/runtime/user_spec.rb b/spec/unit/models/runtime/user_spec.rb index 2b7d15655e2..17451d91581 100644 --- a/spec/unit/models/runtime/user_spec.rb +++ b/spec/unit/models/runtime/user_spec.rb @@ -61,6 +61,15 @@ module VCAP::CloudController end end + describe 'uniqueness' do + it 'enforces uniqueness of guid' do + existing_user = User.make + expect do + User.create(guid: existing_user.guid) + end.to raise_error(Sequel::ValidationFailed, /unique/) + end + end + describe 'Validations' do it { is_expected.to validate_presence :guid } end diff --git a/spec/unit/models/services/service_broker_spec.rb b/spec/unit/models/services/service_broker_spec.rb index a5bbea8e1b1..a02e0f7ee22 100644 --- a/spec/unit/models/services/service_broker_spec.rb +++ b/spec/unit/models/services/service_broker_spec.rb @@ -28,6 +28,15 @@ module VCAP::CloudController end end + describe 'uniqueness' do + it 'enforces uniqueness of name' do + existing_broker = ServiceBroker.make + expect do + ServiceBroker.create(name: existing_broker.name, broker_url: 'http://example.com', auth_username: 'user', auth_password: 'pass') + end.to raise_error(Sequel::ValidationFailed, 'Name must be unique') + end + end + describe 'Validations' do it { is_expected.to validate_presence :name } it { is_expected.to validate_presence :broker_url } From e2f5027429ae65dd10dbd06f0ef04badeabab93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Thu, 19 Mar 2026 09:40:10 +0100 Subject: [PATCH 14/16] fix: changes reverted because if we remove validate_uniqueness, it fires name_overlap and that leads misleading error --- app/models/runtime/domain.rb | 1 + spec/support/shared_examples/models/domain_validation.rb | 7 ++++--- spec/unit/models/runtime/domain_spec.rb | 1 + spec/unit/models/runtime/shared_domain_spec.rb | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/models/runtime/domain.rb b/app/models/runtime/domain.rb index 12b9288d397..56839444059 100644 --- a/app/models/runtime/domain.rb +++ b/app/models/runtime/domain.rb @@ -97,6 +97,7 @@ def around_save def validate validates_presence :name + validates_unique :name, dataset: Domain.dataset validates_format CloudController::DomainDecorator::DOMAIN_REGEX, :name, message: 'can contain multiple subdomains, each having only alphanumeric characters and hyphens of up to 63 characters, see RFC 1035.' diff --git a/spec/support/shared_examples/models/domain_validation.rb b/spec/support/shared_examples/models/domain_validation.rb index cedbdbca8b8..55d08f1d757 100644 --- a/spec/support/shared_examples/models/domain_validation.rb +++ b/spec/support/shared_examples/models/domain_validation.rb @@ -13,9 +13,10 @@ module VCAP::CloudController context "when there's another domain with the same name" do it 'fails to validate' do - expect do - described_class.make(name: subject.name) - end.to raise_error(Sequel::ValidationFailed, /already reserved by another domain|unique/) + other_domain = described_class.make + other_domain.name = subject.name + expect(other_domain).not_to be_valid + expect(other_domain.errors[:name]).to include(:unique) end end diff --git a/spec/unit/models/runtime/domain_spec.rb b/spec/unit/models/runtime/domain_spec.rb index 5c7c3c7ccf0..e3e94564e8b 100644 --- a/spec/unit/models/runtime/domain_spec.rb +++ b/spec/unit/models/runtime/domain_spec.rb @@ -105,6 +105,7 @@ module VCAP::CloudController describe 'Validations' do it { is_expected.to validate_presence :name } + it { is_expected.to validate_uniqueness :name } describe 'route collisions' do let!(:existing_domain) { SharedDomain.make(name: 'base.domain') } diff --git a/spec/unit/models/runtime/shared_domain_spec.rb b/spec/unit/models/runtime/shared_domain_spec.rb index 342447a6178..6b587cfb922 100644 --- a/spec/unit/models/runtime/shared_domain_spec.rb +++ b/spec/unit/models/runtime/shared_domain_spec.rb @@ -67,7 +67,7 @@ module VCAP::CloudController it 'denies shared foo.com when private foo.com exists' do PrivateDomain.make name: 'foo.com' - expect { SharedDomain.make name: 'foo.com' }.to raise_error(Sequel::ValidationFailed, /already reserved by another domain|unique/) + expect { SharedDomain.make name: 'foo.com' }.to raise_error(Sequel::ValidationFailed, /name unique/) end context 'when the domain is internal' do From c7786d81a1107fc1567050f2fc1724a36e7b7fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Thu, 19 Mar 2026 10:17:05 +0100 Subject: [PATCH 15/16] fix: add comment explaining rescue Sequel::ValidationFailed in service broker registration The around_save hook on the ServiceBroker model catches unique constraint violations and adds errors to the model before raising ValidationFailed. The rescue block returns nil, and callers access errors via the delegated errors method. --- lib/services/service_brokers/service_broker_registration.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/services/service_brokers/service_broker_registration.rb b/lib/services/service_brokers/service_broker_registration.rb index 4c4e6f4322d..442a65ad19b 100644 --- a/lib/services/service_brokers/service_broker_registration.rb +++ b/lib/services/service_brokers/service_broker_registration.rb @@ -31,6 +31,7 @@ def create end self rescue Sequel::ValidationFailed + # Errors have been added to the broker model by around_save (e.g. unique constraint violations) nil end @@ -53,6 +54,7 @@ def update end self rescue Sequel::ValidationFailed + # Errors have been added to the broker model by around_save (e.g. unique constraint violations) nil end From 84dad61f76c31447ab634003d780614ef1c93da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serdar=20=C3=96zer?= Date: Thu, 19 Mar 2026 13:45:41 +0100 Subject: [PATCH 16/16] fix: service_broker validation_unique removel is reverted. Since it is used as guard before calling some http endpoint in check in service_broker_registration. Comments are added --- app/models/runtime/domain.rb | 2 ++ app/models/services/service_broker.rb | 5 ++++- lib/services/service_brokers/service_broker_registration.rb | 6 ------ .../controllers/services/service_brokers_controller_spec.rb | 1 - spec/unit/models/services/service_broker_spec.rb | 1 + 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/models/runtime/domain.rb b/app/models/runtime/domain.rb index 56839444059..4ca18ef9b6f 100644 --- a/app/models/runtime/domain.rb +++ b/app/models/runtime/domain.rb @@ -97,6 +97,8 @@ def around_save def validate validates_presence :name + # Keep validates_unique to check across all Domain subclasses (STI: SharedDomain, PrivateDomain). + # Without it, exact name duplicates would be caught by name_overlaps? below with a misleading error message. validates_unique :name, dataset: Domain.dataset validates_format CloudController::DomainDecorator::DOMAIN_REGEX, :name, diff --git a/app/models/services/service_broker.rb b/app/models/services/service_broker.rb index 0769f5e7320..efc05c3d350 100644 --- a/app/models/services/service_broker.rb +++ b/app/models/services/service_broker.rb @@ -25,7 +25,7 @@ def around_save rescue Sequel::UniqueConstraintViolation => e raise e unless e.message.include?('service_brokers_name_index') - errors.add(:name, Sequel.lit('Name must be unique')) + errors.add(:name, :unique) raise validation_failed_error end @@ -34,6 +34,9 @@ def validate validates_presence :broker_url validates_presence :auth_username validates_presence :auth_password + # Keep validates_unique as a pre-guard for ServiceBrokerRegistration + # to avoid unnecessary HTTP calls to the broker if the name is not unique. + validates_unique :name, message: Sequel.lit('Name must be unique') validates_url :broker_url validates_url_no_basic_auth end diff --git a/lib/services/service_brokers/service_broker_registration.rb b/lib/services/service_brokers/service_broker_registration.rb index 442a65ad19b..3cc4e8ab42d 100644 --- a/lib/services/service_brokers/service_broker_registration.rb +++ b/lib/services/service_brokers/service_broker_registration.rb @@ -30,9 +30,6 @@ def create raise e end self - rescue Sequel::ValidationFailed - # Errors have been added to the broker model by around_save (e.g. unique constraint violations) - nil end def update @@ -53,9 +50,6 @@ def update synchronize_services_and_plans! end self - rescue Sequel::ValidationFailed - # Errors have been added to the broker model by around_save (e.g. unique constraint violations) - nil end delegate :errors, to: :broker diff --git a/spec/unit/controllers/services/service_brokers_controller_spec.rb b/spec/unit/controllers/services/service_brokers_controller_spec.rb index 9c4ead91998..eff7192f8de 100644 --- a/spec/unit/controllers/services/service_brokers_controller_spec.rb +++ b/spec/unit/controllers/services/service_brokers_controller_spec.rb @@ -291,7 +291,6 @@ def stub_catalog(broker_url: nil, username: nil, password: nil) post '/v2/service_brokers', public_body expect(last_response).to have_status_code(201) - stub_catalog post '/v2/service_brokers', body expect(last_response).to have_status_code(400) end diff --git a/spec/unit/models/services/service_broker_spec.rb b/spec/unit/models/services/service_broker_spec.rb index a02e0f7ee22..ceb7f9c89ed 100644 --- a/spec/unit/models/services/service_broker_spec.rb +++ b/spec/unit/models/services/service_broker_spec.rb @@ -42,6 +42,7 @@ module VCAP::CloudController it { is_expected.to validate_presence :broker_url } it { is_expected.to validate_presence :auth_username } it { is_expected.to validate_presence :auth_password } + it { is_expected.to validate_uniqueness :name, message: Sequel.lit('Name must be unique') } it 'validates the url is a valid http/https url' do expect(broker).to be_valid