spec/unit/presenters/v3/service_plan_presenter_spec.rb
require 'spec_helper'
require 'presenters/v3/service_plan_presenter'
RSpec.describe VCAP::CloudController::Presenters::V3::ServicePlanPresenter do
let(:guid) { service_plan.guid }
let(:maintenance_info) do
{
version: '1.0.0',
description: 'best plan ever'
}
end
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(maintenance_info:)
end
let!(:potato_label) do
VCAP::CloudController::ServicePlanLabelModel.make(
key_prefix: 'canberra.au',
key_name: 'potato',
value: 'mashed',
resource_guid: service_plan.guid
)
end
let!(:mountain_annotation) do
VCAP::CloudController::ServicePlanAnnotationModel.make(
key_name: 'altitude',
value: '14,412',
resource_guid: service_plan.guid
)
end
describe '#to_hash' do
let(:result) { described_class.new(service_plan).to_hash.deep_symbolize_keys }
it 'presents the service plan' do
expect(result).to eq({
guid: guid,
created_at: service_plan.created_at,
updated_at: service_plan.updated_at,
visibility_type: 'public',
available: true,
name: service_plan.name,
free: false,
costs: [],
description: service_plan.description,
maintenance_info: {
version: '1.0.0',
description: 'best plan ever'
},
broker_catalog: {
metadata: {},
id: service_plan.unique_id,
maximum_polling_duration: nil,
features: {
bindable: true,
plan_updateable: false
}
},
schemas: {
service_instance: {
create: {
parameters: {}
},
update: {
parameters: {}
}
},
service_binding: {
create: {
parameters: {}
}
}
},
metadata: {
labels: {
'canberra.au/potato': 'mashed'
},
annotations: {
altitude: '14,412'
}
},
relationships: {
service_offering: {
data: {
guid: service_plan.service.guid
}
}
},
links: {
self: {
href: "#{link_prefix}/v3/service_plans/#{guid}"
},
service_offering: {
href: "#{link_prefix}/v3/service_offerings/#{service_plan.service.guid}"
},
visibility: {
href: "#{link_prefix}/v3/service_plans/#{service_plan.guid}/visibility"
}
}
})
end
context 'when `active` is false' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(active: false)
end
it 'presents the service plan with available false' do
expect(result[:available]).to be(false)
end
end
context 'when `free` is false' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(free: false)
end
it 'presents the service plan with free false' do
expect(result[:free]).to be(false)
end
end
context 'when plan has `maximum_polling_duration`' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(maximum_polling_duration: 60)
end
it 'presents the service plan with maximum_polling_duration' do
expect(result[:broker_catalog][:maximum_polling_duration]).to eq(60)
end
end
context 'when `bindable` is false' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(bindable: false)
end
it 'presents the service plan with bindable false' do
expect(result[:broker_catalog][:features][:bindable]).to be(false)
end
end
context 'when `plan_updateable` is false' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(plan_updateable: false)
end
it 'presents the service plan with plan_updateable false' do
expect(result[:broker_catalog][:features][:plan_updateable]).to be(false)
end
end
context 'when plan has `extra`' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(extra: '{"some_key": "some-value"}')
end
it 'presents the service plan with metadata' do
expect(result[:broker_catalog][:metadata][:some_key]).to eq('some-value')
end
end
context 'when plan has costs' do
it 'flattens different currencies in the same unit' do
service_plan =
VCAP::CloudController::ServicePlan.make(extra: '{"costs": [
{
"amount": {
"usd": 649.0,
"gbp": 500
},
"unit": "MONTHLY"
},
{
"amount": {
"usd": 6.00,
"gbp": 5.05
},
"unit": "daily"
}
]}')
result = described_class.new(service_plan).to_hash.deep_symbolize_keys
expect(result[:costs][0][:amount]).to eq(649.0)
expect(result[:costs][0][:currency]).to eq('USD')
expect(result[:costs][0][:unit]).to eq('MONTHLY')
expect(result[:costs][1][:amount]).to eq(500.0)
expect(result[:costs][1][:currency]).to eq('GBP')
expect(result[:costs][1][:unit]).to eq('MONTHLY')
expect(result[:costs][2][:amount]).to eq(6.00)
expect(result[:costs][2][:currency]).to eq('USD')
expect(result[:costs][2][:unit]).to eq('daily')
expect(result[:costs][3][:amount]).to eq(5.05)
expect(result[:costs][3][:currency]).to eq('GBP')
expect(result[:costs][3][:unit]).to eq('daily')
end
it 'handles currency symbols' do
service_plan =
VCAP::CloudController::ServicePlan.make(extra: '{"costs": [
{
"amount": {
"$": 0.06
},
"unit": "Daily"
}
]}')
result = described_class.new(service_plan).to_hash.deep_symbolize_keys
expect(result[:costs][0][:amount]).to eq(0.06)
expect(result[:costs][0][:currency]).to eq('$')
expect(result[:costs][0][:unit]).to eq('Daily')
end
end
context 'when plan has no cost' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make
end
it 'presents the service plan with cost' do
expect(result[:costs]).to eq([])
end
end
context 'when plan cost is not valid array' do
[
['cost is not an array',
'{
"costs":
{
"amount": {
"usd": 649.0,
"gbp": 600.015454
},
"unit": "MONTHLY"
}
}'],
['amount missing',
'{
"costs": [
{
"unit": "Weekly"
},
{
"amount": {
"usd": 649.0,
"gbp": 600.015454
},
"unit": "Weekly"
}
]
}'],
['unit is missing',
'{
"costs": [
{
"amount": {
"usd": 649.0
},
"unit": "Daily"
},
{
"amount": {
"usd": 649.0,
"gbp": 600.015454
}
}
]
}'],
['amount is empty object',
'{
"costs": [
{
"amount": {},
"unit": "Daily"
},
{
"amount": {
"usd": 649.0
},
"unit": "Daily"
}
]
}'],
['amount is not a valid string:float key value pair',
'{
"costs": [
{
"amount": {
"usd": "649.0"
},
"unit": "Weekly"
}
]
}'],
['currency is empty string',
'{
"costs": [
{
"amount": {
"gpb": 0.06
},
"unit": "Daily"
},
{
"amount": {
"": 0.06,
"usd": 0.10
},
"unit": "Daily"
}
]
}'],
['unit is empty string',
'{
"costs": [
{
"amount": {
"gpb": 0.06
},
"unit": "Daily"
},
{
"amount": {
"usd": 0.10
},
"unit": ""
}
]
}']
].each do |scenario, extra|
it "returns empty cost array when #{scenario}" do
service_plan = VCAP::CloudController::ServicePlan.make(extra:)
result = described_class.new(service_plan).to_hash.deep_symbolize_keys
expect(result[:costs]).to eq([])
end
end
end
context 'when plan has no `maintenance_info`' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make
end
it 'presents the service plan with empty maintenance_info' do
expect(result[:maintenance_info]).to be_empty
end
end
context 'schemas' do
let(:schema) do
'{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"billing-account": {
"description": "Billing account number used to charge use of shared fake server.",
"type": "string"
}
}
}'
end
let(:parsed_schema) { Oj.load(schema).deep_symbolize_keys }
context 'when plan has create service_instance schema' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(create_instance_schema: schema)
end
it 'presents the service plan create service_instance with the schema' do
expect(result[:schemas][:service_instance][:create][:parameters]).to eq(parsed_schema)
expect(result[:schemas][:service_instance][:update][:parameters]).to be_empty
expect(result[:schemas][:service_binding][:create][:parameters]).to be_empty
end
end
context 'when plan has update service_instance schema' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(update_instance_schema: schema)
end
it 'presents the service plan update service_instance with the schema' do
expect(result[:schemas][:service_instance][:update][:parameters]).to eq(parsed_schema)
expect(result[:schemas][:service_instance][:create][:parameters]).to be_empty
expect(result[:schemas][:service_binding][:create][:parameters]).to be_empty
end
end
context 'when plan has create service_binding schema' do
let(:service_plan) do
VCAP::CloudController::ServicePlan.make(create_binding_schema: schema)
end
it 'presents the service plan update service_instance with the schema' do
expect(result[:schemas][:service_instance][:update][:parameters]).to be_empty
expect(result[:schemas][:service_instance][:create][:parameters]).to be_empty
expect(result[:schemas][:service_binding][:create][:parameters]).to eq(parsed_schema)
end
end
end
context 'when service plan is from a space-scoped broker' do
let(:space) { VCAP::CloudController::Space.make }
let(:service_broker) { VCAP::CloudController::ServiceBroker.make(space:) }
let(:service_offering) { VCAP::CloudController::Service.make(service_broker:) }
let(:service_plan) { VCAP::CloudController::ServicePlan.make(service: service_offering) }
it 'includes a space relationship and link' do
expect(result).to include({
relationships: include({
space: { data: { guid: space.guid } }
}),
links: include({
space: { href: "#{link_prefix}/v3/spaces/#{space.guid}" }
})
})
end
end
context 'when a decorator is provided' do
class FakeDecorator
def self.decorate(hash, resources)
hash[:included] = { resource: { guid: resources[0].guid } }
hash
end
end
let(:result) { described_class.new(service_plan, decorators: [FakeDecorator]).to_hash.deep_symbolize_keys }
let(:service_plan) { VCAP::CloudController::ServicePlan.make }
it 'uses the decorator' do
expect(result[:included]).to match({ resource: { guid: service_plan.guid } })
end
end
end
end