dry-rb / dry-rb/dry-validation

Arrays of multiple custom types not validating correctly

Open
#750 1 comment 0 reactions 0 assignees View on GitHub
bug help wanted
Dominant language
Ruby
Stars
1.4k
Forks
195
PR merge metrics
No merged PRs in 30d

Description

This bug report is being submitted following on from [this forum discussion](https://discourse.hanakai.org/t/dry-schema-validation-array-of-multiple-custom-types-is-it-possible/1504). There's more context there if necessary, but I'll try to keep to the specifics of the actual issue in this report.

Note: I wasn't sure whether to open the report on this repo or on [`dry-schema`]() repo (I guess it's mainly a schema issue but since it occurred in the context of using `dry-validation` I thought I would open it here so that the examples/steps to reproduce make sense).

## Describe the bug

When defining a schema in a `Dry::Validation::Contract` class definition that contains an array of multiple custom types, then invoking `call` on that contract while passing in a data structure which _should_ be invalid against the defined schema, the returned object returns `true` when `valid?` is called on it, and the `errors` hash is empty.

## To Reproduce

Given the following `Dry::Validation::Contract` class definition:

```ruby
require 'dry-validation'
require 'dry-schema'
require 'dry-types'

Types = Dry.Types()

Color = Types::Hash.schema(type: Types::String, name: Types::String, code: Types::Integer)
Book = Types::Hash.schema(type: Types::String, title: Types::String, genre: Types::String)

class ThingsContract < Dry::Validation::Contract
schema do
required(:things).value(array[Color | Book])
end
end
```

Instantiate a new contract object:

```ruby
things_contract = ThingsContract.new
```

Define some data which includes a Hash which doesn't match the structure of either the `Color` or `Book` types, and should therefore be invalid.

```ruby
invalid_things = {
things: [
{ type: 'Color', name: 'red', code: 1 },
{ type: 'Book', title: 'The Great Gatsby', genre: 'Fiction' },
{ type: 'Foo', foo: 'bar' } # This should fail validation since it does not match either `Color` or `Book` types
]
}
```

Pass that data into the `call` invocation:

```ruby
invalid_things_result = person_contract.call(invalid_person)
# invalid_things_result.success? => true
# invalid_things_result.errors.to_h => {}
```

## Expected behavior

The expected behaviour would be that `invalid_things_result.success?` returns `false`, and that `invalid_things_result.errors.to_h` would not return an empty hash.

## My environment

- Ruby version: 3.4
- OS: Mac OS (ventura 13.5)

Contributor guide

Open the contributing guide

Research direction

Start by running the ThingsContract and invalid_things reproduction described in the issue, using Contract#call and checking success? and errors.to_h. Trace how schema handles an array containing the Color | Book union; done means the Foo hash makes validation fail and produces non-empty errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.