spec/unit/models/services/service_broker_spec.rb
require 'spec_helper'
module VCAP::CloudController
RSpec.describe ServiceBroker, :services, type: :model do
let(:name) { Sham.name }
let(:broker_url) { 'http://cf-service-broker.example.com' }
let(:auth_username) { 'me' }
let(:auth_password) { 'abc123' }
let(:broker) { ServiceBroker.new(name:, broker_url:, auth_username:, auth_password:) }
it_behaves_like 'a model with an encrypted attribute' do
let(:encrypted_attr) { :auth_password }
let(:attr_salt) { :salt }
end
it { is_expected.to have_timestamp_columns }
describe 'Associations' do
it { is_expected.to have_associated :services }
it { is_expected.to have_associated :space }
it 'has associated service_plans' do
service = Service.make(:v2)
service_plan = ServicePlan.make(service:)
service_broker = service.service_broker
expect(service_broker.service_plans).to include(service_plan)
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
broker.broker_url = '127.0.0.1/api'
expect(broker).not_to be_valid
broker.broker_url = 'ftp://127.0.0.1/api'
expect(broker).not_to be_valid
broker.broker_url = 'http://127.0.0.1/api'
expect(broker).to be_valid
broker.broker_url = 'https://127.0.0.1/api'
expect(broker).to be_valid
broker.broker_url = 'https://admin:password@127.0.0.1/api'
expect(broker).not_to be_valid
end
end
describe 'Serialization' do
it { is_expected.to export_attributes :name, :broker_url, :auth_username, :space_guid }
it { is_expected.to import_attributes :name, :broker_url, :auth_username, :auth_password }
end
describe '#client' do
it 'returns a client created with the correct arguments' do
v2_client = double('client')
expect(VCAP::Services::ServiceBrokers::V2::Client).to receive(:new).with(url: broker_url, auth_username: auth_username, auth_password: auth_password).and_return(v2_client)
expect(broker.client).to be(v2_client)
end
end
describe '#in_transitional_state?' do
let(:broker) { ServiceBroker.make }
it 'returns true when state is SYNCHRONIZING' do
broker.state = ServiceBrokerStateEnum::SYNCHRONIZING
expect(broker.in_transitional_state?).to be true
end
it 'returns false when state is SYNCHRONIZATION_FAILED' do
broker.state = ServiceBrokerStateEnum::SYNCHRONIZATION_FAILED
expect(broker.in_transitional_state?).to be false
end
it 'returns false when state is AVAILABLE' do
broker.state = ServiceBrokerStateEnum::AVAILABLE
expect(broker.in_transitional_state?).to be false
end
it 'returns true when state is DELETE_IN_PROGRESS' do
broker.state = ServiceBrokerStateEnum::DELETE_IN_PROGRESS
expect(broker.in_transitional_state?).to be true
end
it 'returns false when state is DELETE_FAILED' do
broker.state = ServiceBrokerStateEnum::DELETE_FAILED
expect(broker.in_transitional_state?).to be false
end
end
describe '#available?' do
let(:broker) { ServiceBroker.make }
it 'returns false when state is SYNCHRONIZING' do
broker.state = ServiceBrokerStateEnum::SYNCHRONIZING
expect(broker.available?).to be false
end
it 'returns false when state is SYNCHRONIZATION_FAILED' do
broker.state = ServiceBrokerStateEnum::SYNCHRONIZATION_FAILED
expect(broker.available?).to be false
end
it 'returns true when state is AVAILABLE' do
broker.state = ServiceBrokerStateEnum::AVAILABLE
expect(broker.available?).to be true
end
it 'returns true when state is empty to support old brokers' do
broker.state = ''
expect(broker.available?).to be true
end
it 'returns false when state is DELETE_IN_PROGRESS' do
broker.state = ServiceBrokerStateEnum::DELETE_IN_PROGRESS
expect(broker.available?).to be false
end
it 'returns false when state is DELETE_FAILED' do
broker.state = ServiceBrokerStateEnum::DELETE_FAILED
expect(broker.available?).to be false
end
end
describe '#destroy' do
let(:service_broker) { ServiceBroker.make }
it 'destroys all resources associated with the broker' do
service = Service.make(service_broker:)
service_plan = ServicePlan.make(service:)
label = ServiceBrokerLabelModel.make(resource_guid: service_broker.guid, key_name: 'foo', value: 'bar')
annotation = ServiceBrokerAnnotationModel.make(resource_guid: service_broker.guid, key_name: 'alpha', value: 'beta')
service_broker.destroy
expect(ServiceBroker.where(id: service_broker.id)).to be_empty
expect(Service.where(id: service.id)).to be_empty
expect(ServicePlan.where(id: service_plan.id)).to be_empty
expect(ServiceBrokerLabelModel.where(id: label.id)).to be_empty
expect(ServiceBrokerAnnotationModel.where(id: annotation.id)).to be_empty
end
context 'when a service instance exists' do
it 'does not allow the broker to be destroyed' do
service = Service.make(service_broker:)
service_plan = ServicePlan.make(service:)
ManagedServiceInstance.make(service_plan:)
expect do
service_broker.destroy
rescue Sequel::ForeignKeyConstraintViolation
end.not_to(change do
Service.where(id: service.id).count
end)
end
end
context 'when associated with a dashboard client' do
before do
ServiceDashboardClient.claim_client('some-uaa-id', service_broker)
end
it 'successfully destroys the broker' do
expect { service_broker.destroy }.
to change(ServiceBroker, :count).by(-1)
end
it 'sets the broker_id of the dashboard client to nil' do
client = ServiceDashboardClient.find_claimed_client(service_broker).first
expect(client.service_broker_id).to eq(service_broker.id)
service_broker.destroy
expect(client.reload.service_broker_id).to be_nil
end
end
end
describe 'space_scoped?' do
context 'when the broker has an associated space' do
let(:space) { Space.make }
let(:broker) do
ServiceBroker.new(
name: name,
broker_url: broker_url,
auth_username: auth_username,
auth_password: auth_password,
space_id: space.id
)
end
it 'is true' do
expect(broker).to be_space_scoped
end
end
it 'is false if the broker does not have a space id' do
expect(broker).not_to be_space_scoped
end
end
describe 'has_service_instances?' do
let(:service_broker) { ServiceBroker.make }
let(:service) { Service.make(service_broker:) }
let!(:service_plan) { ServicePlan.make(service:) }
context 'when there are service instances' do
before do
ManagedServiceInstance.make(service_plan:)
end
it 'returns true' do
expect(service_broker.has_service_instances?).to be(true)
end
it 'does a single db query' do
expect { service_broker.has_service_instances? }.to have_queried_db_times(/select 1.*limit 1/i, 1)
end
end
context 'when there are no service instances' do
it 'returns false' do
expect(service_broker.has_service_instances?).to be(false)
end
it 'does a single db query' do
expect { service_broker.has_service_instances? }.to have_queried_db_times(/select 1.*limit 1/i, 1)
end
end
end
end
end