respect-validation/tests/feature/Issues/Issue1469Test.php
Henrique Moody 1915b6fff7
Use paths to identify when a rule fails
When nested-structural validation fails, it's challenging to identify
which rule failed from the main exception message. A great example is
the `Issue796Test.php` file. The exception message says:

host must be a string

But you're left unsure whether it's the `host` key from the `mysql` key
or the `postgresql` key.

This commit changes that behaviour by introducing the concept of "Path."
The `path` represents the path that a rule has taken, and we can use it
in structural rules to identify the path of an array or object.

Here's what it looks like before and after:

```diff
-host must be a string
+`.mysql.host` must be a string
```

Because paths are a specific concept, I added a dot (`.`) at the
beginning of all paths when displaying them. I was inspired by the `jq`
syntax. I also added backticks around paths to distinguish them from any
other value.

I didn't manage to fix a test, and I skipped it instead of fixing it
because I want to make changes in how we display error messages as
arrays, and it will be easier to fix it then.
2024-12-27 23:28:35 +01:00

72 lines
2.5 KiB
PHP

<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
test('https://github.com/Respect/Validation/issues/1469', expectAll(
function (): void {
v::create()
->arrayVal()
->keySet(
v::key(
'order_items',
v::create()
->arrayVal()
->each(
v::keySet(
v::key('product_title', v::stringVal()->notEmpty()),
v::key('quantity', v::intVal()->notEmpty()),
)
)
->notEmpty()
),
)
->assert([
'order_items' => [
[
'product_title' => 'test',
'quantity' => 'test',
],
[
'product_title2' => 'test',
],
],
]);
},
'`.order_items.0.quantity` must be an integer value',
<<<'FULL_MESSAGE'
- Each item in `.order_items` must be valid
- `.0` validation failed
- `.quantity` must be an integer value
- `.1` contains both missing and extra keys
- `.product_title` must be present
- `.quantity` must be present
- `.product_title2` must not be present
FULL_MESSAGE,
[
'keySet' => [
'__root__' => 'Each item in `.order_items` must be valid',
0 => 'quantity must be an integer value',
1 => [
'__root__' => '`.order_items` contains both missing and extra keys',
'product_title' => '`.product_title` must be present',
'quantity' => '`.quantity` must be present',
'product_title2' => '`.product_title2` must not be present',
],
],
],
))->skip(<<<TEST_SKIP
This test is skipped because the issue is not fixed yet.
When changing the path of a `Result` we don't change the path of its children. I took this approach because we don't
want to duplicated `.path1.path1` in the message (`.parent.child`).
However, that means that when one has a rule/result in-between paths (`KeySet` in this case), the children will be
totally unaware of the path of the parent. Although that's partially the intended, it causes problems like these.
I'm not sure how to fix this yet.
TEST_SKIP);