spec/integration/schema_spec.rb in dry-validation-0.5.0 vs spec/integration/schema_spec.rb in dry-validation-0.6.0

- old
+ new

@@ -1,9 +1,9 @@ RSpec.describe Dry::Validation::Schema do subject(:validation) { schema.new } - describe 'defining schema' do + 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| @@ -31,11 +31,11 @@ phone_numbers.array? { phone_numbers.each(&:str?) } end end end - let(:attrs) do + let(:input) do { email: 'jane@doe.org', age: 19, address: { city: 'NYC', street: 'Street 1/2', country: { code: 'US', name: 'USA' } }, phone_numbers: [ @@ -44,30 +44,30 @@ }.freeze end describe '#messages' do it 'returns compiled error messages' do - expect(validation.(attrs.merge(email: '')).messages).to match_array([ + expect(validation.(input.merge(email: '')).messages).to match_array([ [:email, [['email must be filled'], '']] ]) end end describe '#call' do it 'passes when attributes are valid' do - expect(validation.(attrs)).to be_empty + expect(validation.(input)).to be_empty end it 'validates presence of an email and min age value' do - expect(validation.(attrs.merge(email: '', age: 18))).to match_array([ + 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 it 'validates presence of the email key and type of age value' do - expect(validation.(name: 'Jane', age: '18', address: attrs[:address], phone_numbers: attrs[:phone_numbers])).to match_array([ + 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 @@ -77,11 +77,11 @@ [: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.(attrs.merge(address: { city: 'NY' }))).to match_array([ + expect(validation.(input.merge(address: { city: 'NY' }))).to match_array([ [:error, [ :input, [ :address, {city: "NY"}, [ [:input, [:city, "NY", [[:val, [:city, [:predicate, [:min_size?, [3]]]]]]]], @@ -92,17 +92,17 @@ ]] ]) end it 'validates address type' do - expect(validation.(attrs.merge(address: 'totally not a hash'))).to match_array([ + 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.(attrs.merge(address: attrs[:address].merge(country: { code: 'US', name: '' })))).to match_array([ + 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: ""}}, [ [ @@ -121,14 +121,179 @@ ]] ]) end it 'validates each phone number' do - expect(validation.(attrs.merge(phone_numbers: ['123', 312]))).to match_array([ + 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 + end + end + + describe 'defining attr-based schema (model-like)' do + let(:schema) do + Class.new(Dry::Validation::Schema) do + attr(:email) { |email| email.filled? } + + attr(:age) do |age| + age.none? | (age.int? & age.gt?(18)) + end + + attr(:address) do |address| + address.attr(:city) do |city| + city.min_size?(3) + end + + address.attr(:street) do |street| + street.filled? + end + + address.attr(:country) do |country| + country.attr(:name, &:filled?) + country.attr(:code, &:filled?) + end + end + + attr(:phone_numbers) do |phone_numbers| + phone_numbers.array? { phone_numbers.each(&:str?) } + end + end + end + + let(:input_data) 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"], '']] + ]) + end + end + + describe '#call' do + it 'passes when attributes are valid' do + expect(validation.(input)).to be_empty + 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?, []]]]]]]]] + ]) + 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')) + + 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]]]]]]]]] + ]) + end + + it 'validates presence of the address and phone_number keys' do + input_object = input(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]]]]] + ] + ] + ]] + ]) + 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) + + 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]]]]]]]] + ] + ] + ]] + ]) + end + + it 'validates address code and name values' do + input_object = input(input_data.merge(address: input_data[: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?, []]]]]] + ] + ] + ] + ] + ] + ] + ] + ]] + ]) + end + + it 'validates each phone number' do + input_object = input(input_data.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?, []]]]] ]