blob: fc105e5ec92b813def5e6426d624cfc1699c762c [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'test_help'
class TestSchema < Test::Unit::TestCase
def validate!(schema, value, options=nil)
Avro::SchemaValidator.validate!(schema, value, options)
end
def validate_simple!(schema, value)
Avro::SchemaValidator.validate!(schema, value, recursive: false)
end
def hash_to_schema(hash)
Avro::Schema.parse(hash.to_json)
end
def assert_failed_validation(messages)
error = assert_raise(Avro::SchemaValidator::ValidationError) { yield }
assert_messages = [messages].flatten
result_errors = error.result.errors
assert_messages.each do |message|
assert(result_errors.include?(message), "expected '#{message}' to be in '#{result_errors}'")
end
assert_equal(assert_messages.size, result_errors.size)
end
def assert_valid_schema(schema, valid, invalid, simple = false)
valid.each do |value|
assert_nothing_raised { Avro::SchemaValidator.validate!(schema, value) }
assert_nothing_raised { Avro::SchemaValidator.validate!(schema, value, recursive: false) } if simple
end
invalid.each do |value|
assert_raise { Avro::SchemaValidator.validate!(schema, value) }
assert_raise { Avro::SchemaValidator.validate!(schema, value, recursive: false) } if simple
assert_nothing_raised { Avro::SchemaValidator.validate!(schema, value, recursive: false) } unless simple
end
end
def test_validate_nil
schema = hash_to_schema(type: 'null', name: 'name')
assert_nothing_raised { validate!(schema, nil) }
assert_nothing_raised { validate_simple!(schema, nil) }
assert_failed_validation('at . expected type null, got int with value 1') do
validate!(schema, 1)
end
assert_failed_validation('at . expected type null, got int with value 1') do
validate_simple!(schema, 1)
end
end
def test_validate_boolean
schema = hash_to_schema(type: 'boolean', name: 'name')
assert_nothing_raised { validate!(schema, true) }
assert_nothing_raised { validate!(schema, false) }
assert_nothing_raised { validate_simple!(schema, true) }
assert_nothing_raised { validate_simple!(schema, false) }
assert_failed_validation('at . expected type boolean, got int with value 1') do
validate!(schema, 1)
end
assert_failed_validation('at . expected type boolean, got int with value 1') do
validate_simple!(schema, 1)
end
assert_failed_validation('at . expected type boolean, got null') do
validate!(schema, nil)
end
assert_failed_validation('at . expected type boolean, got null') do
validate_simple!(schema, nil)
end
end
def test_fixed_size_string
schema = hash_to_schema(type: 'fixed', name: 'some', size: 3)
assert_nothing_raised { validate!(schema, 'baf') }
assert_nothing_raised { validate_simple!(schema, 'baf') }
assert_failed_validation('at . expected fixed with size 3, got "some" with size 4') do
validate!(schema, 'some')
end
assert_failed_validation('at . expected fixed with size 3, got "some" with size 4') do
validate_simple!(schema, 'some')
end
assert_failed_validation('at . expected fixed with size 3, got null') do
validate!(schema, nil)
end
assert_failed_validation('at . expected fixed with size 3, got null') do
validate_simple!(schema, nil)
end
assert_failed_validation("at . expected fixed with size 3, got \"a\u2014b\" with size 5") do
validate!(schema, "a\u2014b")
end
assert_failed_validation("at . expected fixed with size 3, got \"a\u2014b\" with size 5") do
validate_simple!(schema, "a\u2014b")
end
end
def test_original_validate_nil
schema = hash_to_schema(type: 'null', name: 'name')
assert_valid_schema(schema, [nil], ['something'], true)
end
def test_original_validate_boolean
schema = hash_to_schema(type: 'boolean', name: 'name')
assert_valid_schema(schema, [true, false], [nil, 1], true)
end
def test_validate_string
schema = hash_to_schema(type: 'string', name: 'name')
assert_valid_schema(schema, ['string'], [nil, 1], true)
end
def test_validate_bytes
schema = hash_to_schema(type: 'bytes', name: 'name')
assert_valid_schema(schema, ['string'], [nil, 1], true)
end
def test_validate_int
schema = hash_to_schema(type: 'int', name: 'name')
assert_valid_schema(
schema,
[Avro::Schema::INT_MIN_VALUE, Avro::Schema::INT_MAX_VALUE, 1],
[Avro::Schema::LONG_MIN_VALUE, Avro::Schema::LONG_MAX_VALUE, 'string'],
true
)
assert_failed_validation('at . out of bound value 9223372036854775807') do
validate!(schema, Avro::Schema::LONG_MAX_VALUE)
end
assert_failed_validation('at . out of bound value 9223372036854775807') do
validate_simple!(schema, Avro::Schema::LONG_MAX_VALUE)
end
end
def test_validate_long
schema = hash_to_schema(type: 'long', name: 'name')
assert_valid_schema(schema, [Avro::Schema::LONG_MIN_VALUE, Avro::Schema::LONG_MAX_VALUE, 1], [1.1, 'string'], true)
end
def test_validate_float
schema = hash_to_schema(type: 'float', name: 'name')
assert_valid_schema(schema, [1.1, 1, Avro::Schema::LONG_MAX_VALUE], ['string'], true)
end
def test_validate_double
schema = hash_to_schema(type: 'double', name: 'name')
assert_valid_schema(schema, [1.1, 1, Avro::Schema::LONG_MAX_VALUE], ['string'], true)
end
def test_validate_fixed
schema = hash_to_schema(type: 'fixed', name: 'name', size: 3)
assert_valid_schema(schema, ['abc'], ['ab', 1, 1.1, true], true)
end
def test_validate_original_num
schema = hash_to_schema(type: 'enum', name: 'name', symbols: %w(a b))
assert_valid_schema(schema, ['a', 'b'], ['c'], true)
end
def test_validate_record
schema = hash_to_schema(type: 'record', name: 'name', fields: [{ type: 'null', name: 'sub' }])
assert_valid_schema(schema, [{ 'sub' => nil }], [{ 'sub' => 1 }])
end
def test_validate_shallow_record
schema = hash_to_schema(
type: 'record', name: 'name', fields: [{ type: 'int', name: 'sub' }]
)
assert_nothing_raised { validate!(schema, 'sub' => 1) }
assert_nothing_raised { validate_simple!(schema, 'sub' => 1) }
assert_failed_validation('at .sub expected type int, got null') do
validate!(schema, {})
end
assert_nothing_raised { validate_simple!(schema, {}) }
assert_failed_validation('at . expected type record, got float with value 1.2') do
validate!(schema, 1.2)
end
assert_nothing_raised { validate_simple!(schema, 1.2) }
assert_failed_validation('at .sub expected type int, got float with value 1.2') do
validate!(schema, 'sub' => 1.2)
end
assert_nothing_raised { validate_simple!(schema, 'sub' => 1.2) }
end
def test_validate_array
schema = hash_to_schema(type: 'array',
name: 'person',
items: [{ type: 'int', name: 'height' }])
assert_nothing_raised { validate!(schema, []) }
assert_nothing_raised { validate_simple!(schema, []) }
assert_failed_validation 'at . expected type array, got null' do
validate!(schema, nil)
end
assert_nothing_raised { validate_simple!(schema, nil) }
assert_failed_validation('at .[0] expected type int, got null') do
validate!(schema, [nil])
end
assert_nothing_raised { validate_simple!(schema, [nil]) }
assert_failed_validation('at .[3] expected type int, got string with value "so wrong"') do
validate!(schema, [1, 3, 9, 'so wrong'])
end
assert_nothing_raised { validate_simple!(schema, [1, 3, 9, 'so wrong']) }
end
def test_validate_enum
schema = hash_to_schema(type: 'enum',
name: 'person',
symbols: %w(one two three))
assert_nothing_raised { validate!(schema, 'one') }
assert_nothing_raised { validate_simple!(schema, 'one') }
assert_failed_validation('at . expected enum with values ["one", "two", "three"], got string with value "five"') do
validate!(schema, 'five')
end
assert_failed_validation('at . expected enum with values ["one", "two", "three"], got string with value "five"') do
validate_simple!(schema, 'five')
end
end
def test_validate_union_on_primitive_types
schema = hash_to_schema(
name: 'should_not_matter',
type: 'record',
fields: [
{ name: 'what_ever', type: %w(long string) }
]
)
assert_failed_validation('at .what_ever expected union of [\'long\', \'string\'], got null') {
validate!(schema, 'what_ever' => nil)
}
assert_nothing_raised { validate_simple!(schema, 'what_ever' => nil) }
end
def test_validate_union_of_nil_and_record_inside_array
schema = hash_to_schema(
name: 'this does not matter',
type: 'record',
fields: [
{
name: 'person',
type: {
name: 'person_entry',
type: 'record',
fields: [
{
name: 'houses',
type: [
'null',
{
name: 'houses_entry',
type: 'array',
items: [
{
name: 'house_entry',
type: 'record',
fields: [
{ name: 'number_of_rooms', type: 'long' }
]
}
]
}
],
}
]
}
}
]
)
assert_failed_validation('at .person expected type record, got null') {
validate!(schema, 'not at all' => nil)
}
assert_nothing_raised { validate_simple!(schema, 'person' => {}) }
assert_nothing_raised { validate!(schema, 'person' => {}) }
assert_nothing_raised { validate!(schema, 'person' => { houses: [] }) }
assert_nothing_raised { validate!(schema, 'person' => { 'houses' => [{ 'number_of_rooms' => 1 }] }) }
assert_nothing_raised { validate_simple!(schema, 'person' => {}) }
assert_nothing_raised { validate_simple!(schema, 'person' => { houses: [] }) }
assert_nothing_raised { validate_simple!(schema, 'person' => { 'houses' => [{ 'number_of_rooms' => 1 }] }) }
message = 'at .person.houses[1].number_of_rooms expected type long, got string with value "not valid at all"'
datum = {
'person' => {
'houses' => [
{ 'number_of_rooms' => 2 },
{ 'number_of_rooms' => 'not valid at all' }
]
}
}
assert_failed_validation(message) { validate!(schema, datum) }
assert_nothing_raised { validate_simple!(schema, datum) }
end
def test_validate_map
schema = hash_to_schema(type: 'map',
name: 'numbers',
values: [
{ name: 'some', type: 'int' }
])
assert_nothing_raised { validate!(schema, 'some' => 1) }
assert_nothing_raised { validate_simple!(schema, 'some' => 1) }
assert_failed_validation('at .some expected type int, got string with value "nope"') do
validate!(schema, 'some' => 'nope')
end
assert_nothing_raised { validate_simple!(schema, 'some' => 'nope')}
assert_failed_validation("at . unexpected key type 'Symbol' in map") do
validate!(schema, some: 1)
end
assert_nothing_raised { validate_simple!(schema, some: 1) }
assert_failed_validation('at . expected type map, got null') do
validate!(schema, nil)
end
assert_nothing_raised { validate_simple!(schema, nil) }
end
def test_validate_deep_record
schema = hash_to_schema(type: 'record',
name: 'person',
fields: [
{
name: 'head',
type: {
name: 'head',
type: 'record',
fields: [
{
name: 'hair',
type: {
name: 'hair',
type: 'record',
fields: [
{
name: 'color',
type: 'string'
}
]
}
}
]
}
}
])
assert_nothing_raised { validate!(schema, 'head' => { 'hair' => { 'color' => 'black' } }) }
assert_nothing_raised { validate_simple!(schema, 'head' => { 'hair' => { 'color' => 'black' } }) }
assert_failed_validation('at .head.hair.color expected type string, got null') do
validate!(schema, 'head' => { 'hair' => { 'color' => nil } })
end
assert_nothing_raised { validate_simple!(schema, 'head' => { 'hair' => { 'color' => nil } }) }
assert_failed_validation('at .head.hair.color expected type string, got null') do
validate!(schema, 'head' => { 'hair' => {} })
end
assert_nothing_raised { validate_simple!(schema, 'head' => { 'hair' => {} }) }
assert_failed_validation('at .head.hair expected type record, got null') do
validate!(schema, 'head' => {})
end
assert_nothing_raised { validate_simple!(schema, 'head' => {}) }
assert_failed_validation('at . expected type record, got null') do
validate!(schema, nil)
end
assert_nothing_raised { validate_simple!(schema, nil) }
end
def test_validate_deep_record_with_array
schema = hash_to_schema(type: 'record',
name: 'fruits',
fields: [
{
name: 'fruits',
type: {
name: 'fruits',
type: 'array',
items: [
{
name: 'fruit',
type: 'record',
fields: [
{ name: 'name', type: 'string' },
{ name: 'weight', type: 'float' }
]
}
]
}
}
])
assert_nothing_raised { validate!(schema, 'fruits' => [{ 'name' => 'apple', 'weight' => 30.2 }]) }
assert_nothing_raised { validate_simple!(schema, 'fruits' => [{ 'name' => 'apple', 'weight' => 30.2 }]) }
assert_failed_validation('at .fruits[0].name expected type string, got null') do
validate!(schema, 'fruits' => [{ 'name' => nil, 'weight' => 30.2 }])
end
assert_nothing_raised { validate_simple!(schema, 'fruits' => [{ 'name' => nil, 'weight' => 30.2 }]) }
assert_failed_validation('at .fruits expected type array, got int with value 1') do
validate!(schema, 'fruits' => 1)
end
assert_nothing_raised { validate_simple!(schema, 'fruits' => 1) }
end
def test_validate_multiple_errors
schema = hash_to_schema(type: 'array',
name: 'ages',
items: [
{ type: 'int', name: 'age' }
])
exception = assert_raise(Avro::SchemaValidator::ValidationError) do
validate!(schema, [nil, 'e'])
end
assert_nothing_raised { validate_simple!(schema, [nil, 'e']) }
assert_equal 2, exception.result.errors.size
assert_equal(
"at .[0] expected type int, got null\nat .[1] expected type int, got string with value \"e\"",
exception.to_s
)
end
def test_validate_extra_fields
schema = hash_to_schema(
type: 'record',
name: 'fruits',
fields: [
{
name: 'veggies',
type: 'string'
}
]
)
exception = assert_raise(Avro::SchemaValidator::ValidationError) do
validate!(schema, {'veggies' => 'tomato', 'bread' => 'rye'}, fail_on_extra_fields: true)
end
assert_equal(1, exception.result.errors.size)
assert_equal("at . extra field 'bread' - not in schema",
exception.to_s)
end
def test_validate_subrecord_extra_fields
schema = hash_to_schema(type: 'record',
name: 'top',
fields: [
{
name: 'fruit',
type: {
name: 'fruit',
type: 'record',
fields: [{ name: 'name', type: 'string' }]
}
}
])
exception = assert_raise(Avro::SchemaValidator::ValidationError) do
validate!(schema, { 'fruit' => { 'name' => 'orange', 'color' => 'orange' } }, fail_on_extra_fields: true)
end
assert_equal(1, exception.result.errors.size)
assert_equal("at .fruit extra field 'color' - not in schema", exception.to_s)
end
def test_validate_array_extra_fields
schema = hash_to_schema(type: 'array',
items: {
name: 'fruit',
type: 'record',
fields: [{ name: 'name', type: 'string' }]
})
exception = assert_raise(Avro::SchemaValidator::ValidationError) do
validate!(schema, [{ 'name' => 'orange', 'color' => 'orange' }], fail_on_extra_fields: true)
end
assert_equal(1, exception.result.errors.size)
assert_equal("at .[0] extra field 'color' - not in schema", exception.to_s)
end
def test_validate_map_extra_fields
schema = hash_to_schema(type: 'map',
values: {
name: 'fruit',
type: 'record',
fields: [{ name: 'color', type: 'string' }]
})
exception = assert_raise(Avro::SchemaValidator::ValidationError) do
validate!(schema, { 'apple' => { 'color' => 'green', 'extra' => 1 } }, fail_on_extra_fields: true)
end
assert_equal(1, exception.result.errors.size)
assert_equal("at .apple extra field 'extra' - not in schema", exception.to_s)
end
def test_validate_union_extra_fields
schema = hash_to_schema([
'null',
{
type: 'record',
name: 'fruit',
fields: [{ name: 'name', type: 'string' }]
}
])
exception = assert_raise(Avro::SchemaValidator::ValidationError) do
validate!(schema, { 'name' => 'apple', 'color' => 'green' }, fail_on_extra_fields: true)
end
assert_equal(1, exception.result.errors.size)
assert_equal("at . extra field 'color' - not in schema", exception.to_s)
end
end