spec/unit/lib/services/service_brokers/v2/schema_spec.rb
require 'lightweight_spec_helper'
require 'services/service_brokers/v2/schema'
module VCAP::Services::ServiceBrokers::V2
RSpec.describe Schema do
let(:schema) { Schema.new(raw_schema) }
describe '#to_json' do
let(:raw_schema) do
{
'$schema' => 'http://json-schema.org/draft-04/schema#',
:properties => { foo: { type: 'string' } },
:required => ['foo']
}
end
it 'converts a hash into json' do
expect(schema.to_json).to eq '{"$schema":"http://json-schema.org/draft-04/schema#",' \
'"properties":{"foo":{"type":"string"}},' \
'"required":["foo"]}'
end
end
describe 'schema validations' do
let(:draft_schema) { "http://json-schema.org/#{version}/schema#" }
let(:raw_schema) { { '$schema' => draft_schema } }
context 'when the schema is compliant to JSON Schema draft04' do
let(:version) { 'draft-04' }
let(:raw_schema) { { '$schema' => draft_schema } }
it 'is valid' do
expect(schema.validate).to be true
expect(schema.errors.full_messages.length).to be 0
end
end
context 'when the schema is compliant to JSON Schema draft06' do
let(:version) { 'draft-06' }
let(:raw_schema) { { '$schema' => draft_schema } }
it 'is valid' do
expect(schema.validate).to be true
expect(schema.errors.full_messages.length).to eq 0
end
end
context 'when the schema has an internal reference' do
let(:raw_schema) do
{
'$schema' => 'http://json-schema.org/draft-04/schema#',
properties: {
foo: { type: 'integer' },
bar: { '$ref': '#/properties/foo' }
}
}
end
it 'is valid' do
expect(schema.validate).to be true
expect(schema.errors.full_messages.length).to be 0
end
end
describe 'metaschema errors' do
context 'when JSON::Validator returns an error' do
let(:raw_schema) { { '$schema' => 'http://json-schema.org/draft-04/schema#', properties: true } }
before do
allow(JSON::Validator).to receive(:fully_validate).and_return([{ message: 'whoops' }])
end
it 'add a schema error message with a wrapped error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to eq 'Must conform to JSON Schema Draft 04 (experimental support for later versions): whoops' \
end
end
context 'when JSON::Validator raises an error' do
let(:raw_schema) { { '$schema' => 'http://json-schema.org/draft-04/schema#', properties: true } }
before do
allow(JSON::Validator).to receive(:fully_validate).and_raise('uh oh')
end
it 'add a schema error message with the error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to eq 'uh oh'
end
end
context 'when there are multiple errors' do
let(:raw_schema) { { '$schema' => 'http://json-schema.org/draft-04/schema#', properties: true, anyOf: true } }
it 'has more than one error message' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 2
expect(schema.errors.full_messages.first).to eq 'Must conform to JSON Schema Draft 04 (experimental support for later versions): ' \
'The property \'#/properties\' of type boolean did not match the following type: object in schema http://json-schema.org/draft-04/schema#'
expect(schema.errors.full_messages.last).to eq 'Must conform to JSON Schema Draft 04 (experimental support for later versions): ' \
'The property \'#/anyOf\' of type boolean did not match the following type: array in schema http://json-schema.org/draft-04/schema#'
end
end
end
describe 'open service broker restrictions' do
context 'when the schema raises a SchemaError' do
let(:raw_schema) { { '$schema' => 'blahblahblah' } }
it 'as schema error message with the error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to eq 'Custom meta schemas are not supported.'
end
end
context 'when the schema includes an external reference' do
context 'when $ref is an external reference' do
let(:raw_schema) { { '$schema' => 'http://json-schema.org/draft-04/schema#', '$ref' => 'http://example.com/ref' } }
it 'adds a schema error message with the error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to eq 'No external references are allowed: Read of URI at http://example.com/ref refused'
end
end
context 'when custom schema is not recognized by the library' do
let(:raw_schema) { { '$schema' => 'http://example.com/schema' } }
it 'is not valid' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'Custom meta schemas are not supported.'
end
end
end
context 'when the schema has an external file reference' do
let(:raw_schema) { { '$schema' => 'http://json-schema.org/draft-04/schema#', '$ref': 'path/to/schema.json' } }
it 'is not valid' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'No external references are allowed.+path/to/schema.json'
end
end
context 'when the schema has an unknown parse error' do
let(:raw_schema) { { '$schema' => 'http://json-schema.org/draft-04/schema#' } }
before do
allow(JSON::Validator).to receive(:validate!).and_raise('some unknown error')
end
it 'as schema error message with the error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to eq 'some unknown error'
end
end
end
describe 'validation ordering' do
context 'when an invalid schema has size/schema/draft04/reference validations errors' do
let(:raw_schema) do
schema = create_schema_of_size(65 * 1024)
schema.delete('$schema')
schema['properties'] = true
schema['$raw'] = 'https://example.com/ref'
schema
end
it 'only returns schema size error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'Must not be larger than 64KB'
end
end
context 'when an invalid schema has schema/draft04/reference validations errors' do
let(:raw_schema) do
schema = create_schema_of_size(1024)
schema.delete('$schema')
schema['properties'] = true
schema['$raw'] = 'https://example.com/ref'
schema
end
it 'only returns schema not present error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'Schema must have $schema key but was not present'
end
end
context 'when an invalid schema has draft04/reference validations errors' do
let(:raw_schema) do
schema = create_schema_of_size(1024)
schema['properties'] = true
schema['$ref'] = 'https://example.com/ref'
schema
end
it 'only returns draft04 error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'Must conform to JSON Schema Draft 04'
end
end
context 'when an invalid schema has reference validations errors' do
let(:raw_schema) do
schema = create_schema_of_size(1024)
schema['$ref'] = 'https://example.com/ref'
schema
end
it 'only returns external reference error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'No external references are allowed'
end
end
end
describe 'schema sizes' do
context 'that are valid' do
{
'well below the limit': 1,
'just below the limit': 63,
'on the limit': 64
}.each do |desc, size_in_kb|
context "when the schema json is #{desc}" do
let(:raw_schema) { create_schema_of_size(size_in_kb * 1024) }
it 'is valid' do
expect(schema.validate).to be true
expect(schema.errors.full_messages.length).to eq 0
end
end
end
end
context 'that are invalid' do
{
'just above the limit': 65,
'well above the limit': 10 * 1024
}.each do |desc, size_in_kb|
context "when the schema json is #{desc}" do
let(:raw_schema) { create_schema_of_size(size_in_kb * 1024) }
it 'returns one error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'Must not be larger than 64KB'
end
end
end
end
end
context 'when neither draft06 nor draft04 schema has been specified' do
context 'when the schema is a different draft version' do
let(:version) { 'some-random-schema' }
it 'returns a helpful error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'Custom meta schemas are not supported'
end
end
context 'when no schema has been specified' do
let(:raw_schema) { {} }
it 'returns a helpful error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'Schema must have $schema key but was not present'
end
end
context 'when an unknown schema has been specified' do
let(:draft_schema) { 'http://example.com/someschema' }
it 'returns a helpful error' do
expect(schema.validate).to be false
expect(schema.errors.full_messages.length).to eq 1
expect(schema.errors.full_messages.first).to match 'Custom meta schemas are not supported'
end
end
end
end
def create_schema_of_size(bytes)
surrounding_bytes = 62
{
'$schema' => 'http://json-schema.org/draft-04/schema#',
'foo' => 'x' * (bytes - surrounding_bytes)
}
end
end
end