diff --git a/app/models/runtime/app_model.rb b/app/models/runtime/app_model.rb index b47733f7fb3..b02e1550c56 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') @@ -99,8 +99,6 @@ def validate validates_format APP_NAME_REGEX, :name 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/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/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/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/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..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 @@ -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_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/app/models/services/service_plan.rb b/app/models/services/service_plan.rb index f713352828b..50fbe006bdc 100644 --- a/app/models/services/service_plan.rb +++ b/app/models/services/service_plan.rb @@ -75,13 +75,21 @@ 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/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/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/support/shared_examples/models/domain_validation.rb b/spec/support/shared_examples/models/domain_validation.rb index 55d08f1d757..cedbdbca8b8 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 do + described_class.make(name: subject.name) + 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 da951d624ce..8730f187d4a 100644 --- a/spec/unit/actions/app_create_spec.rb +++ b/spec/unit/actions/app_create_spec.rb @@ -200,23 +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') } 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 7efbe430aaa..77e810e1d0e 100644 --- a/spec/unit/actions/organization_create_spec.rb +++ b/spec/unit/actions/organization_create_spec.rb @@ -110,26 +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 - - # 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 - 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 973405df6d6..22caa6b50e8 100644 --- a/spec/unit/actions/service_broker_create_spec.rb +++ b/spec/unit/actions/service_broker_create_spec.rb @@ -132,23 +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 - - # 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 - 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 6d356167753..c1547d30001 100644 --- a/spec/unit/actions/space_create_spec.rb +++ b/spec/unit/actions/space_create_spec.rb @@ -79,26 +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 - - # 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 - 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 e3573fb9a0f..79ee409f9d5 100644 --- a/spec/unit/actions/space_quotas_create_spec.rb +++ b/spec/unit/actions/space_quotas_create_spec.rb @@ -202,23 +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 - - # 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 - 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 156019fd567..1bc21fd2280 100644 --- a/spec/unit/actions/stack_create_spec.rb +++ b/spec/unit/actions/stack_create_spec.rb @@ -119,26 +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 - - # 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 - 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 6d182d97549..9d215ad10ee 100644 --- a/spec/unit/actions/user_create_spec.rb +++ b/spec/unit/actions/user_create_spec.rb @@ -35,25 +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 - - # 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 - 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/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 diff --git a/spec/unit/isolation_segment_create_spec.rb b/spec/unit/isolation_segment_create_spec.rb index a79655e3628..ccc67da435a 100644 --- a/spec/unit/isolation_segment_create_spec.rb +++ b/spec/unit/isolation_segment_create_spec.rb @@ -44,24 +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 - - # 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 - end 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/feature_flag_spec.rb b/spec/unit/models/runtime/feature_flag_spec.rb index 458f93c4cdd..88b41fd0e93 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 do + FeatureFlag.create(name: existing_flag.name, enabled: true) + 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 :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 diff --git a/spec/unit/models/runtime/organization_auditor_spec.rb b/spec/unit/models/runtime/organization_auditor_spec.rb index 8737b8d4bfc..e7c12080c85 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 '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) + end.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..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,16 @@ module VCAP::CloudController let(:organization) { Organization.make } let(:user) { User.make } + 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) + end.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..c26d026ead5 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 '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) + end.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..52945ecadb1 100644 --- a/spec/unit/models/runtime/organization_spec.rb +++ b/spec/unit/models/runtime/organization_spec.rb @@ -121,11 +121,19 @@ 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 } 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..1217f66b431 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 '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) + end.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/quota_definition_spec.rb b/spec/unit/models/runtime/quota_definition_spec.rb index 21d67e7f60c..47bce2377ad 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 do + QuotaDefinition.create(name: existing.name, non_basic_services_allowed: true, total_services: 0, total_routes: 0, memory_limit: 0) + 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 } 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 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..1383c8ae302 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 '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) + end.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..97c04e82215 100644 --- a/spec/unit/models/runtime/space_developer_spec.rb +++ b/spec/unit/models/runtime/space_developer_spec.rb @@ -5,8 +5,16 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } + 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) + end.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..5d832f2c576 100644 --- a/spec/unit/models/runtime/space_manager_spec.rb +++ b/spec/unit/models/runtime/space_manager_spec.rb @@ -5,8 +5,16 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } + 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) + end.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..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 } @@ -24,7 +33,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..c22df7313aa 100644 --- a/spec/unit/models/runtime/space_spec.rb +++ b/spec/unit/models/runtime/space_spec.rb @@ -4,10 +4,18 @@ 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 } - 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..1581c17fc76 100644 --- a/spec/unit/models/runtime/space_supporter_spec.rb +++ b/spec/unit/models/runtime/space_supporter_spec.rb @@ -6,8 +6,16 @@ module VCAP::CloudController let(:space) { Space.make } let(:user) { User.make } + 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) + end.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..cb17e7c8064 100644 --- a/spec/unit/models/runtime/stack_spec.rb +++ b/spec/unit/models/runtime/stack_spec.rb @@ -22,9 +22,17 @@ 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 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..17451d91581 100644 --- a/spec/unit/models/runtime/user_spec.rb +++ b/spec/unit/models/runtime/user_spec.rb @@ -61,9 +61,17 @@ 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 } - 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..a02e0f7ee22 100644 --- a/spec/unit/models/services/service_broker_spec.rb +++ b/spec/unit/models/services/service_broker_spec.rb @@ -28,12 +28,20 @@ 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 } 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_dashboard_client_spec.rb b/spec/unit/models/services/service_dashboard_client_spec.rb index 342e85c84dc..35ed4ee8867 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 do + ServiceDashboardClient.create(uaa_id: existing.uaa_id, service_broker: other_broker) + end.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:) } diff --git a/spec/unit/models/services/service_key_spec.rb b/spec/unit/models/services/service_key_spec.rb index f4a224bbf60..4ddf0c50d0c 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 do + ServiceKey.make(name: existing.name, service_instance: existing.service_instance) + end.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 } diff --git a/spec/unit/models/services/service_plan_spec.rb b/spec/unit/models/services/service_plan_spec.rb index 6a0310cffef..f9e49c3dfa8 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 do + ServicePlan.make(name: existing.name, service: existing.service) + end.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' } diff --git a/spec/unit/models/services/service_plan_visibility_spec.rb b/spec/unit/models/services/service_plan_visibility_spec.rb index f8708027578..1b0d81b5df9 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 do + ServicePlanVisibility.create(service_plan: existing.service_plan, organization: existing.organization) + end.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