spec/integration/schema_spec.rb in dry-validation-0.6.0 vs spec/integration/schema_spec.rb in dry-validation-0.7.0
- old
+ new
@@ -1,310 +1,140 @@
-RSpec.describe Dry::Validation::Schema do
- subject(:validation) { schema.new }
-
- describe 'defining key-based schema (hash-like)' do
- let(:schema) do
- Class.new(Dry::Validation::Schema) do
- key(:email) { |email| email.filled? }
-
- key(:age) do |age|
- age.none? | (age.int? & age.gt?(18))
- end
-
- key(:address) do |address|
- address.hash? do
- address.key(:city) do |city|
- city.min_size?(3)
- end
-
- address.key(:street) do |street|
- street.filled?
- end
-
- address.key(:country) do |country|
- country.key(:name, &:filled?)
- country.key(:code, &:filled?)
- end
- end
- end
-
- key(:phone_numbers) do |phone_numbers|
- phone_numbers.array? { phone_numbers.each(&:str?) }
- end
+RSpec.describe Dry::Validation::Schema, 'defining key-based schema' do
+ describe 'with a flat structure' do
+ subject(:schema) do
+ Dry::Validation.Schema do
+ key(:email).required
+ key(:age) { none? | (int? & gt?(18)) }
end
end
- let(:input) do
- {
- email: 'jane@doe.org',
- age: 19,
- address: { city: 'NYC', street: 'Street 1/2', country: { code: 'US', name: 'USA' } },
- phone_numbers: [
- '123456', '234567'
- ]
- }.freeze
+ it 'passes when input is valid' do
+ expect(schema.(email: 'jane@doe', age: 19)).to be_success
+ expect(schema.(email: 'jane@doe', age: nil)).to be_success
end
- describe '#messages' do
- it 'returns compiled error messages' do
- expect(validation.(input.merge(email: '')).messages).to match_array([
- [:email, [['email must be filled'], '']]
- ])
- end
+ it 'fails when input is not valid' do
+ expect(schema.(email: 'jane@doe', age: 17)).to_not be_success
end
- describe '#call' do
- it 'passes when attributes are valid' do
- expect(validation.(input)).to be_empty
- end
+ it 'returns result which quacks like hash' do
+ input = { email: 'jane@doe', age: 19 }
+ result = schema.(input)
- it 'validates presence of an email and min age value' do
- expect(validation.(input.merge(email: '', age: 18))).to match_array([
- [:error, [:input, [:age, 18, [[:val, [:age, [:predicate, [:gt?, [18]]]]]]]]],
- [:error, [:input, [:email, "", [[:val, [:email, [:predicate, [:filled?, []]]]]]]]]
- ])
- end
+ expect(result[:email]).to eql('jane@doe')
+ expect(Hash[result]).to eql(input)
- it 'validates presence of the email key and type of age value' do
- expect(validation.(name: 'Jane', age: '18', address: input[:address], phone_numbers: input[:phone_numbers])).to match_array([
- [:error, [:input, [:age, "18", [[:val, [:age, [:predicate, [:int?, []]]]]]]]],
- [:error, [:input, [:email, nil, [[:key, [:email, [:predicate, [:key?, [:email]]]]]]]]]
- ])
- end
-
- it 'validates presence of the address and phone_number keys' do
- expect(validation.(email: 'jane@doe.org', age: 19)).to match_array([
- [:error, [:input, [:address, nil, [[:key, [:address, [:predicate, [:key?, [:address]]]]]]]]],
- [:error, [:input, [:phone_numbers, nil, [[:key, [:phone_numbers, [:predicate, [:key?, [:phone_numbers]]]]]]]]]
- ])
- end
-
- it 'validates presence of keys under address and min size of the city value' do
- expect(validation.(input.merge(address: { city: 'NY' }))).to match_array([
- [:error, [
- :input, [
- :address, {city: "NY"},
- [
- [:input, [:city, "NY", [[:val, [:city, [:predicate, [:min_size?, [3]]]]]]]],
- [:input, [:street, nil, [[:key, [:street, [:predicate, [:key?, [:street]]]]]]]],
- [:input, [:country, nil, [[:key, [:country, [:predicate, [:key?, [:country]]]]]]]]
- ]
- ]
- ]]
- ])
- end
-
- it 'validates address type' do
- expect(validation.(input.merge(address: 'totally not a hash'))).to match_array([
- [:error, [:input, [:address, "totally not a hash", [[:val, [:address, [:predicate, [:hash?, []]]]]]]]]
- ])
- end
-
- it 'validates address code and name values' do
- expect(validation.(input.merge(address: input[:address].merge(country: { code: 'US', name: '' })))).to match_array([
- [:error, [
- :input, [
- :address, {city: "NYC", street: "Street 1/2", country: {code: "US", name: ""}},
- [
- [
- :input, [
- :country, {code: "US", name: ""}, [
- [
- :input, [
- :name, "", [[:val, [:name, [:predicate, [:filled?, []]]]]]
- ]
- ]
- ]
- ]
- ]
- ]
- ]
- ]]
- ])
- end
-
- it 'validates each phone number' do
- expect(validation.(input.merge(phone_numbers: ['123', 312]))).to match_array([
- [:error, [
- :input, [
- :phone_numbers, ["123", 312],
- [
- [
- :input, [
- :phone_numbers, 312, [
- [:val, [:phone_numbers, [:predicate, [:str?, []]]]]
- ]
- ]
- ]
- ]
- ]
- ]]
- ])
- end
+ expect(result.to_a).to eql([[:email, 'jane@doe'], [:age, 19]])
end
end
- describe 'defining attr-based schema (model-like)' do
- let(:schema) do
- Class.new(Dry::Validation::Schema) do
- attr(:email) { |email| email.filled? }
+ describe 'with nested structures' do
+ subject(:schema) do
+ Dry::Validation.Schema do
+ key(:email).required
- attr(:age) do |age|
- age.none? | (age.int? & age.gt?(18))
- end
+ key(:age).maybe(:int?, gt?: 18)
- attr(:address) do |address|
- address.attr(:city) do |city|
- city.min_size?(3)
- end
+ key(:address).schema do
+ key(:city).required(min_size?: 3)
- address.attr(:street) do |street|
- street.filled?
- end
+ key(:street).required
- address.attr(:country) do |country|
- country.attr(:name, &:filled?)
- country.attr(:code, &:filled?)
+ key(:country).schema do
+ key(:name).required
+ key(:code).required
end
end
- attr(:phone_numbers) do |phone_numbers|
- phone_numbers.array? { phone_numbers.each(&:str?) }
- end
+ key(:phone_numbers).each(:str?)
end
end
- let(:input_data) do
+ let(:input) do
{
email: 'jane@doe.org',
age: 19,
address: { city: 'NYC', street: 'Street 1/2', country: { code: 'US', name: 'USA' } },
phone_numbers: [
'123456', '234567'
]
}.freeze
end
- def input(data = input_data)
- struct_from_hash(data)
- end
-
describe '#messages' do
it 'returns compiled error messages' do
- expect(validation.(input(input_data.merge(email: ''))).messages).to match_array([
- [:email, [["email must be filled"], '']]
- ])
+ expect(schema.(input.merge(email: '')).messages).to eql(
+ email: ['must be filled']
+ )
end
end
describe '#call' do
it 'passes when attributes are valid' do
- expect(validation.(input)).to be_empty
+ expect(schema.(input)).to be_success
end
it 'validates presence of an email and min age value' do
- expect(validation.(input(input_data.merge(email: '', age: 18)))).to match_array([
- [:error, [:input, [:age, 18, [[:val, [:age, [:predicate, [:gt?, [18]]]]]]]]],
- [:error, [:input, [:email, "", [[:val, [:email, [:predicate, [:filled?, []]]]]]]]]
- ])
+ expect(schema.(input.merge(email: '', age: 18)).messages).to eql(
+ email: ['must be filled'], age: ['must be greater than 18']
+ )
end
- it 'validates presence of the email attr and type of age value' do
- input_object = input(input_data.reject { |k, v| k == :email }.merge(age: '18'))
+ it 'validates presence of the email key and type of age value' do
+ attrs = {
+ name: 'Jane',
+ age: '18',
+ address: input[:address], phone_numbers: input[:phone_numbers]
+ }
- expect(validation.(input_object)).to match_array([
- [:error, [:input, [:age, "18", [[:val, [:age, [:predicate, [:int?, []]]]]]]]],
- [:error, [:input, [:email, input_object, [[:attr, [:email, [:predicate, [:attr?, [:email]]]]]]]]]
- ])
+ expect(schema.(attrs).messages).to eql(
+ email: ['is missing'],
+ age: ['must be an integer', 'must be greater than 18']
+ )
end
it 'validates presence of the address and phone_number keys' do
- input_object = input(email: 'jane@doe.org', age: 19)
+ attrs = { email: 'jane@doe.org', age: 19 }
- expect(validation.(input_object)).to match_array([
- [:error, [
- :input, [
- :address, input_object,
- [
- [:attr, [:address, [:predicate, [:attr?, [:address]]]]]
- ]
- ]
- ]],
- [:error, [
- :input, [
- :phone_numbers, input_object,
- [
- [:attr, [:phone_numbers, [:predicate, [:attr?, [:phone_numbers]]]]]
- ]
- ]
- ]]
- ])
+ expect(schema.(attrs).messages).to eql(
+ address: ['is missing'], phone_numbers: ['is missing']
+ )
end
it 'validates presence of keys under address and min size of the city value' do
- address = { city: 'NY' }
- input_object = input(input_data.merge(address: address))
- address_object = input_object.address.class.from_hash(address)
+ attrs = input.merge(address: { city: 'NY' })
- expect(validation.(input_object)).to match_array([
- [:error, [
- :input, [
- :address, address_object,
- [
- [:input, [:city, "NY", [[:val, [:city, [:predicate, [:min_size?, [3]]]]]]]],
- [:input, [:street, address_object, [[:attr, [:street, [:predicate, [:attr?, [:street]]]]]]]],
- [:input, [:country, address_object, [[:attr, [:country, [:predicate, [:attr?, [:country]]]]]]]]
- ]
- ]
- ]]
- ])
+ expect(schema.(attrs).messages).to eql(
+ address: {
+ street: ['is missing'],
+ country: ['is missing'],
+ city: ['size cannot be less than 3']
+ }
+ )
end
+ it 'validates address type' do
+ expect(schema.(input.merge(address: 'totally not a hash')).messages).to eql(
+ address: ['must be a hash']
+ )
+ end
+
it 'validates address code and name values' do
- input_object = input(input_data.merge(address: input_data[:address].merge(country: { code: 'US', name: '' })))
+ attrs = input.merge(
+ address: input[:address].merge(country: { code: 'US', name: '' })
+ )
- country_object = input_object.address.country.class.from_hash(code: "US", name: "")
-
- expect(validation.(input_object)).to match_array([
- [:error, [
- :input, [
- :address, input_object.address.class.from_hash(city: "NYC", street: "Street 1/2", country: country_object),
- [
- [
- :input, [
- :country, country_object, [
- [
- :input, [
- :name, "", [[:val, [:name, [:predicate, [:filled?, []]]]]]
- ]
- ]
- ]
- ]
- ]
- ]
- ]
- ]]
- ])
+ expect(schema.(attrs).messages).to eql(
+ address: { country: { name: ['must be filled'] } }
+ )
end
it 'validates each phone number' do
- input_object = input(input_data.merge(phone_numbers: ['123', 312]))
+ attrs = input.merge(phone_numbers: ['123', 312])
- expect(validation.(input_object)).to match_array([
- [:error, [
- :input, [
- :phone_numbers, ["123", 312],[
- [
- :input, [
- :phone_numbers, 312, [
- [:val, [:phone_numbers, [:predicate, [:str?, []]]]]
- ]
- ]
- ]
- ]
- ]
- ]]
- ])
+ expect(schema.(attrs).messages).to eql(
+ phone_numbers: { 1 => ['must be a string'] }
+ )
end
end
end
end