dry-rb / dry-rb/dry-validation

Dry::Validation::Contract reports an error for coercible integer strings, but only when a hash field fails validation

Open
#682 8 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Ruby
Stars
1.4k
Forks
195
PR merge metrics
No merged PRs in 30d

Description

## Describe the bug

Hello. I'm running into an issue where a Dry::Validation::Contract reports an error for coercible integer strings, but only when a hash field fails validation. I've included some rspec tests below that demonstrate a few successful validation cases and one unexpected failure case .

## To Reproduce

```rb
describe 'contract test' do
class TestContract < Dry::Validation::Contract
params do
required(:outer_key).schema do
required(:str_field).value(:string)
required(:integer_field).value(:integer)
required(:integer_field2).value(:integer)
required(:hash_field).value(:hash)
end
end
end

let(:contract) { TestContract.new }

# This test passes
it 'does not return any errors when provided with valid params (and allows coercable string integers in integer fields)' do
expect(
contract.call({
outer_key: {
str_field: 'abc',
integer_field: '1',
integer_field2: '2',
hash_field: {'key' => 'val'},
}
}).errors.to_h
).to be_empty
end

# This test passes
it 'returns the expected error when a non-integer value is provided to an integer field' do
expect(
contract.call({
outer_key: {
str_field: 'abc',
integer_field: 'NOT AN INTEGER',
integer_field2: '2',
hash_field: {'key' => 'val'},
}
}).errors.to_h
).to eq({ outer_key: { integer_field: ["must be an integer"] } })
end

# This test fails and I get THREE errors rather than ONE expected error
it 'returns the expected error when a non-hash value is provided to a hash field' do
expect(
contract.call({
outer_key: {
str_field: 'abc',
integer_field: '1',
integer_field2: '2',
hash_field: 'NOT A HASH',
}
}).errors.to_h
).to eq({ outer_key: { hash_field: ["must be a hash"] } })

# Instead of one error, we get these three:
# {
# outer_key:
# {
# hash_field: ["must be a hash"]
# integer_field: ["must be an integer"],
# integer_field2: ["must be an integer"],
# }
# }

# This is unexpected because the failing integer fields didn't fail with valid coercible
# string values in the earlier test.
end

# Unlike the above test, the test below passes when the integer fields are provided as integers
# rather than coercible strings
it 'returns the expected error when a non-hash value is provided to a hash field' do
expect(
contract.call({
outer_key: {
str_field: 'abc',
integer_field: 1,
integer_field2: 2,
hash_field: 'NOT A HASH',
}
}).errors.to_h
).to eq({ outer_key: { hash_field: ["must be a hash"] } })
end
end
```

## Expected behavior

Since string values of `'1'` and `'2'` don't fail validation in my earlier tests, I would have expected those values to continue to be coerced properly in the failing test. The validation failure on the hash field seems to interfere with the integer validation.

Is it possible that I'm doing something wrong? Or is this a bug? Thank you for taking a look.

## My environment

- Affects my production application: **NO** (not deployed to production, just new code under development)
- Ruby version: Ruby 2.6.4
- OS: macOS 10.15.7

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.