spec/unit/messages/base_message_spec.rb
require 'spec_helper'
require 'messages/base_message'
module VCAP::CloudController
RSpec.describe BaseMessage do
describe '.initialize' do
let(:fake_class) do
Class.new(BaseMessage) do
register_allowed_keys []
def self.model_name
ActiveModel::Name.new(self, nil, 'fake class')
end
end
end
it 'symbolizes keys' do
message = fake_class.new({ 'foo' => 'bar' })
expect(message).to be_requested(:foo)
end
it 'does not mutate the params for initialization' do
params = { 'foo' => 'bar' }
fake_class.new(params)
expect(params['foo']).to eq('bar')
expect(params).not_to have_key(:foo)
end
end
describe '#requested?' do
it 'returns true if the key was requested, false otherwise' do
FakeClass = Class.new(BaseMessage) do
register_allowed_keys []
def self.model_name
ActiveModel::Name.new(self, nil, 'fake class')
end
end
message = FakeClass.new({ requested: 'thing' })
expect(message).to be_requested(:requested)
expect(message).not_to be_requested(:notrequested)
end
end
describe '#audit_hash' do
class AuditMessage < BaseMessage
register_allowed_keys %i[field1 field2]
end
it 'returns only requested keys in a json object' do
message = AuditMessage.new({ field1: 'value1' })
response = message.audit_hash
expect(response).to eq({ 'field1' => 'value1' })
end
it 'recursively includes keys' do
message = AuditMessage.new({ field1: 'value1', field2: { 'subfield' => 'subfield' } })
response = message.audit_hash
expect(response).to eq({ 'field1' => 'value1', 'field2' => { 'subfield' => 'subfield' } })
end
it 'excludes keys' do
message = AuditMessage.new({ field1: 'value1', field2: { 'subfield' => 'subfield' } })
response = message.audit_hash(exclude: [:field2])
expect(response).to eq({ 'field1' => 'value1' })
end
end
describe '#to_param_hash' do
class ParamsClass < BaseMessage
register_allowed_keys %i[array_field num_field string_field nil_field fields_field]
end
let(:opts) do
{
array_field: ['st ate1', 'sta,te2'],
num_field: 1.2,
string_field: 'stringval&',
nil_field: nil
}
end
it 'returns query param hash with escaped commas in array members' do
expected_params = {
array_field: 'st ate1,sta%2Cte2',
num_field: 1.2,
string_field: 'stringval&',
nil_field: nil
}
expect(ParamsClass.new(opts).to_param_hash).to eq(expected_params)
end
it 'does not return params that are not requested during initialization' do
opts.delete(:nil_field)
expected_params = {
array_field: 'st ate1,sta%2Cte2',
num_field: 1.2,
string_field: 'stringval&'
}
expect(ParamsClass.new(opts).to_param_hash).to eq(expected_params)
end
it 'can exclude params' do
expected_params = {
array_field: 'st ate1,sta%2Cte2',
string_field: 'stringval&',
nil_field: nil
}
expect(ParamsClass.new(opts).to_param_hash(exclude: [:num_field])).to eq(expected_params)
end
context 'when using fields' do
let(:opts) do
{
fields_field: { foo: %w[bar baz], quz: %w[lala gaga] }
}
end
it 'correctly formats the specified field' do
expected_params = {
'fields_field[foo]': 'bar,baz',
'fields_field[quz]': 'lala,gaga'
}
expect(ParamsClass.new(opts).to_param_hash(fields: [:fields_field])).to eq(expected_params)
end
end
end
describe '.to_array!' do
let(:escaped_comma) { '%2C' }
let(:params) do
{
array_field: 'state1,state2',
array_with_comma_in_value_field: "st ate1,sta#{escaped_comma}te2",
array_with_plus: 'state+state2',
array_with_empty_field: 'state1,state2,',
num_field: 1.2,
string_field: 'stringval&',
nil_field: nil,
empty_field: '',
hash_not_array: { pi: 3.141592653589792 }
}
end
it 'separates on commas' do
expect(BaseMessage.to_array!(params, :array_field)).to eq(%w[state1 state2])
end
it 'url query decodes individual array values' do
expect(BaseMessage.to_array!(params, :array_with_comma_in_value_field)).to eq(['st ate1', 'sta,te2'])
end
it 'handles plus signs' do
expect(BaseMessage.to_array!(params, :array_with_plus)).to eq(['state+state2'])
end
it 'handles nil array values' do
expect(BaseMessage.to_array!(params, :array_with_empty_field)).to eq(['state1', 'state2', ''])
end
it 'handles single numeric values' do
expect(BaseMessage.to_array!(params, :num_field)).to eq(['1.2'])
end
it 'handles single string values' do
expect(BaseMessage.to_array!(params, :string_field)).to eq(['stringval&'])
end
it 'handles single nil values' do
expect(BaseMessage.to_array!(params, :nil_field)).to be_nil
end
it 'handles empty values' do
expect(BaseMessage.to_array!(params, :empty_field)).to eq([''])
end
it "doesn't try to convert a hash to an array" do
expect(BaseMessage.to_array!(params, :hash_not_array)).to be_nil
end
end
describe 'additional keys validation' do
let(:fake_class) do
Class.new(BaseMessage) do
register_allowed_keys [:allowed]
validates_with VCAP::CloudController::BaseMessage::NoAdditionalKeysValidator
def self.model_name
ActiveModel::Name.new(self, nil, 'fake class')
end
end
end
it 'is valid with an allowed message' do
message = fake_class.new({ allowed: 'something' })
expect(message).to be_valid
end
it 'is NOT valid with not allowed keys in the message' do
message = fake_class.new({ notallowed: 'something', extra: 'stuff' })
expect(message).not_to be_valid
expect(message.errors.full_messages[0]).to include("Unknown field(s): 'notallowed', 'extra'")
end
end
describe 'additional params validation' do
let(:fake_class) do
Class.new(BaseMessage) do
register_allowed_keys %i[allowed other_allowed]
validates_with VCAP::CloudController::BaseMessage::NoAdditionalParamsValidator
def self.model_name
ActiveModel::Name.new(self, nil, 'fake class')
end
end
end
it 'is valid with an allowed BaseMessage' do
message = fake_class.new({ allowed: 'something' })
expect(message).to be_valid
end
it 'is NOT valid with not allowed keys in the BaseMessage' do
message = fake_class.new({ notallowed: 'something', extra: 'stuff' })
expect(message).not_to be_valid
expect(message.errors.full_messages[0]).to include("Unknown query parameter(s): 'notallowed', 'extra'. Valid parameters are: 'allowed', 'other_allowed'")
end
end
describe 'include param validation' do
let(:fake_class) do
Class.new(BaseMessage) do
register_allowed_keys [:include]
validates_with VCAP::CloudController::BaseMessage::IncludeParamValidator, valid_values: %w[foo bar]
def self.model_name
ActiveModel::Name.new(self, nil, 'fake class')
end
end
end
it 'is valid with an allowed include value' do
message = fake_class.new({ include: ['bar'] })
expect(message).to be_valid
end
it 'is NOT valid with not allowed include value' do
message = fake_class.new({ include: ['stuff'] })
expect(message).not_to be_valid
expect(message.errors.full_messages[0]).to include("Invalid included resource: 'stuff'. Valid included resources are: 'foo', 'bar'")
end
end
describe 'lifecycle type param validation' do
let(:fake_class) do
Class.new(BaseMessage) do
register_allowed_keys [:lifecycle_type]
validates_with VCAP::CloudController::BaseMessage::LifecycleTypeParamValidator
def self.model_name
ActiveModel::Name.new(self, nil, 'fake class')
end
end
end
it 'is valid with docker lifecycle_type value' do
message = fake_class.new({ lifecycle_type: 'docker' })
expect(message).to be_valid
end
it 'is valid with cnb lifecycle_type value' do
message = fake_class.new({ lifecycle_type: 'cnb' })
expect(message).to be_valid
end
it 'is NOT valid with not allowed lifecycle_type value' do
message = fake_class.new({ lifecycle_type: 'stuff' })
expect(message).not_to be_valid
expect(message.errors.full_messages[0]).to include("Invalid lifecycle_type: 'stuff'")
end
end
describe '.from_params' do
FakeFieldsClass = Class.new(BaseMessage) do
register_allowed_keys %i[name names]
end
it 'creates an object with the hash keys as instance variables' do
instance = FakeFieldsClass.from_params({ 'name' => 'aname' }, [])
expect(instance.name).to eq('aname')
instance = FakeFieldsClass.from_params({ name: 'aname' }, [])
expect(instance.name).to eq('aname')
end
it 'converts comma-separated values to arrays when specified' do
instance = FakeFieldsClass.from_params({ 'names' => 'a-name,another-name' }, %w[names])
expect(instance.names).to contain_exactly('a-name', 'another-name')
end
it 'converts comma-separated hash values to arrays when specified' do
instance = FakeFieldsClass.from_params({ 'names' => { 'space' => 'a-name,another-name' } }, [], fields: %w[names])
expect(instance.names).to match({ space: %w[a-name another-name] })
end
context 'when fields parameters are invalid' do
it 'skips the conversion' do
instance = FakeFieldsClass.from_params({ 'names' => 'foo' }, [], fields: %w[name names])
expect(instance.name).to be_nil
expect(instance.names).to eq('foo')
end
end
end
end
end