Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

array(:hash) not validating a filter #448

Open
ritxi opened this issue Dec 7, 2022 · 2 comments
Open

array(:hash) not validating a filter #448

ritxi opened this issue Dec 7, 2022 · 2 comments

Comments

@ritxi
Copy link

ritxi commented Dec 7, 2022

Describe the bug

I have the following schema that works in isolation, but not inside an array(:hash) with a required field with a filter that won't work

To Reproduce

# Working in isolation
schema = Dry::Schema.Params do
    required(:date).filter { 
      format?(%r[\d{2}/\d{2}/\d{4}])
    }.value(:date)
end

schema.call(id: 'foo', date: '2022/05/02')

#<Dry::Schema::Result{:date=>"2022/05/02"} errors={:date=>["is in invalid format"]} path=[]>

# Not working when inside `array(:hash)`

schema = Dry::Schema.Params do
  required(:group).array(:hash) do
    required(:id).value(:integer)
    required(:date).filter { 
      format?(%r[\d{2}/\d{2}/\d{4}])
    }.value(:date)
  end
end
schema.call(group: [{id: 1, date: '2022/05/02'}])

#<Dry::Schema::Result{:group=>[{:id=>1, :date=>Mon, 02 May 2022}]} errors={} path=[]>

Expected behavior

I expect filter validation to work

#<Dry::Schema::Result{:group=>[{:id=>1, :date=>"2022/05/02"}]} errors={:group=>{0=>{:date=>["is in invalid format"]}}} path=[]>

My environment

  • Affects my production application: NO
  • Ruby version: 2.7.6
  • OS: Debian Bullseye (Docker image ruby:2.7.6)
@ritxi
Copy link
Author

ritxi commented Dec 7, 2022

I've tested with the latest version

irb(main):008:1* schema = Dry::Schema.Params do
irb(main):009:2*   required(:group).array(:hash) do
irb(main):010:2*     required(:id).value(:integer)
irb(main):011:3*     required(:date).filter {
irb(main):012:3*       format?(%r[\d{2}/\d{2}/\d{4}])
irb(main):013:2*       }.value(:date)
irb(main):014:1*   end
irb(main):015:0> end
=> #<Dry::Schema::Params keys=[["group", ["id", "date"]]] rules={:group=>"key?(:group) AND key[group](array? AND each(hash? AND hash? AND set(key?(:id) AND key[id](int?), key?(:date) AND key[dat...
irb(main):016:0> schema.call(group: [{id: 1, date: '2022/05/02'}])
=> #<Dry::Schema::Result{:group=>[{:id=>1, :date=>#<Date: 2022-05-02 ((2459702j,0s,0n),+0s,2299161j)>}]} errors={} path=[]>
irb(main):017:0> require "dry/schema/version"
=> true
irb(main):018:0> Dry::Schema::VERSION
=> "1.13.0"

@x2es
Copy link

x2es commented Dec 13, 2023

Describe the bug

Confirming exact the same issue:

I have the following schema that works in isolation, but not inside an array(:hash) with a field with a filter that won't work

Test suite provided below

To Reproduce

https://gist.github.com/x2es/ed6d401bd02698f48e9bf927120e0eb5

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem "dry-schema"
  gem "awesome_print"
  gem "minitest"
end

require 'minitest/autorun'

DocumentSchema = Dry::Schema.JSON do
  optional(:date).filter(format?: /^\d{4}-\d{2}-\d{2}$/).filled(:date)
end

DocumentsSchema = Dry::Schema.JSON do
  required(:documents).array(DocumentSchema)
end

module Sample
  VALID_DOCUMENT = { date: "2023-01-01" }
  INVALID_FORMAT = { date: "20239-01-01" }
  INVALID_DATE =   { date: "2023-38-01" }
  FULL_LIST = {
    documents: [
      VALID_DOCUMENT, # 0
      INVALID_FORMAT, # 1
      INVALID_DATE    # 2
    ]
  }
  FORMAT_LIST = {
    documents: [
      VALID_DOCUMENT, # 0
      INVALID_FORMAT, # 1
    ]
  }
  DATE_LIST = {
    documents: [
      VALID_DOCUMENT, # 0
      INVALID_DATE,   # 1
    ]
  }
end

class DateTest < Minitest::Test
  def test_format
    expected = {:date=>["is in invalid format"]}
    assert_equal expected, DocumentSchema.call(Sample::INVALID_FORMAT).errors.to_h
  end

  def test_date
    expected = {:date=>["must be a date"]}
    assert_equal expected, DocumentSchema.call(Sample::INVALID_DATE).errors.to_h
  end

  # UNEXPECTED BEHAVIOUR
  # and inconsistent with `DocumentSchema.call(Sample::INVALID_FORMAT)`
  #   2) Failure:
  # DateTest#test_format_list [dry_schema_array_hash_filter_issue.rb:68]:
  # --- expected
  # +++ actual
  # @@ -1 +1 @@
  # -{:documents=>{1=>{:date=>["is in invalid format"]}}}
  # +{}
  def test_format_list
    expected = {:documents=>{1=>{:date=>["is in invalid format"]}}}
    assert_equal expected, DocumentsSchema.call(Sample::FORMAT_LIST).errors.to_h
  end

  def test_date_list
    expected = {:documents=>{1=>{:date=>["must be a date"]}}}
    assert_equal expected, DocumentsSchema.call(Sample::DATE_LIST).errors.to_h
  end

  #   1) Failure:
  # DateTest#test_full_list [dry_schema_array_hash_filter_issue.rb:83]:
  # --- expected
  # +++ actual
  # @@ -1 +1 @@
  # -{:documents=>{1=>{:date=>["is in invalid format"]}, 2=>{:date=>["must be a date"]}}}
  # +{:documents=>{1=>{:date=>["must be a date"]}}}
  def test_full_list
    expected = {
      :documents=>{
        1=>{:date=>["is in invalid format"]},
        2=>{:date=>["must be a date"]}
      }
    }
    assert_equal(expected, DocumentsSchema.call(Sample::DATE_LIST).errors.to_h)
  end
end

# {
#       :full_list => #<Dry::Schema::Result{:documents=>[{:date=>#<Date: 2023-01-01 ((2459946j,0s,0n),+0s,2299161j)>}, # {:date=>#<Date: 20239-01-01 ((9113203j,0s,0n),+0s,2299161j)>}, {:date=>"2023-38-01"}]} errors={:documents=>{2=># {:date=>["must be a date"]}}} path=[]>,
#     :format_list => #<Dry::Schema::Result{:documents=>[{:date=>#<Date: 2023-01-01 ((2459946j,0s,0n),+0s,2299161j)>}, {:date=>#<Date: 20239-01-01 ((9113203j,0s,0n),+0s,2299161j)>}]} errors={} path=# []>,
#       :date_list => #<Dry::Schema::Result{:documents=>[{:date=>#<Date: 2023-01-01 ((2459946j,0s,0n),+0s,2299161j)>}, {:date=>"2023-38-01"}]} errors={:documents=>{1=>{:date=>["must be a date"]}}} path=[]>,
#          :format => #<Dry::Schema::Result{:date=>"20239-01-01"} errors={:date=>["is in invalid format"]} path=[]>,
#            :date => #<Dry::Schema::Result{:date=>"2023-38-01"} errors={:date=>["must be a date"]} path=[]>
# }
ap ({
  full_list:   DocumentsSchema.call(Sample::FULL_LIST),
  format_list: DocumentsSchema.call(Sample::FORMAT_LIST),
  date_list:   DocumentsSchema.call(Sample::DATE_LIST),

  format:      DocumentSchema.call(Sample::INVALID_FORMAT),
  date:        DocumentSchema.call(Sample::INVALID_DATE)
})

Expected behavior

DocumentSchema.call(Sample::INVALID_FORMAT) expected to be consistent with DocumentsSchema.call(Sample::FORMAT_LIST):

  • should have the same errors
  • all spec samples should be green

Actual output

Pay attention to :format, :format_list and :full_list outputs

{
      :full_list => #<Dry::Schema::Result{:documents=>[{:date=>#<Date: 2023-01-01 ((2459946j,0s,0n),+0s,2299161j)>}, {:date=>#<Date: 20239-01-01 ((9113203j,0s,0n),+0s,2299161j)>}, {:date=>"2023-38-01"}]} errors={:documents=>{2=>{:date=>["must be a date"]}}} path=[]>,
    :format_list => #<Dry::Schema::Result{:documents=>[{:date=>#<Date: 2023-01-01 ((2459946j,0s,0n),+0s,2299161j)>}, {:date=>#<Date: 20239-01-01 ((9113203j,0s,0n),+0s,2299161j)>}]} errors={} path=[]>,
      :date_list => #<Dry::Schema::Result{:documents=>[{:date=>#<Date: 2023-01-01 ((2459946j,0s,0n),+0s,2299161j)>}, {:date=>"2023-38-01"}]} errors={:documents=>{1=>{:date=>["must be a date"]}}} path=[]>,
         :format => #<Dry::Schema::Result{:date=>"20239-01-01"} errors={:date=>["is in invalid format"]} path=[]>,
           :date => #<Dry::Schema::Result{:date=>"2023-38-01"} errors={:date=>["must be a date"]} path=[]>
}
Run options: --seed 58415

# Running:

F..F.

Finished in 0.007960s, 628.1263 runs/s, 628.1263 assertions/s.

  1) Failure:
DateTest#test_full_list [dry_schema_array_hash_filter_issue.rb:83]:
--- expected
+++ actual
@@ -1 +1 @@
-{:documents=>{1=>{:date=>["is in invalid format"]}, 2=>{:date=>["must be a date"]}}}
+{:documents=>{1=>{:date=>["must be a date"]}}}


  2) Failure:
DateTest#test_format_list [dry_schema_array_hash_filter_issue.rb:68]:
--- expected
+++ actual
@@ -1 +1 @@
-{:documents=>{1=>{:date=>["is in invalid format"]}}}
+{}


5 runs, 5 assertions, 2 failures, 0 errors, 0 skips

My environment

  • Affects my production application: YES
  • Ruby version: 3.2.2
  • OS: Linux Mint (rbenv ruby:3.2.2)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants