mirror of
https://github.com/Respect/Validation.git
synced 2026-03-16 07:15:45 +01:00
Now empty values are again allowed in FilteredArray-style validators. To solve the issue with negation, a Result attribute was added to signal indeciseveness (when a result cannot be reliably inverted). On such cases, we consider that result to be valid. For example, `v::not(v::min(v::equals(10)))` says "The lowest value of the iterable input should not be equal 10". If the input is empty, we cannot decide whether its minimum is equal to 10 or not, so the validator essentially becomes a null-op. Users that want to ensure these validators have a valid decidable target must use it in combination with `Length` or other similar validators to achieve the same result.
303 lines
13 KiB
PHP
303 lines
13 KiB
PHP
<?php
|
|
|
|
/*
|
|
* SPDX-License-Identifier: MIT
|
|
* SPDX-FileCopyrightText: (c) Respect Project Contributors
|
|
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
|
|
* SPDX-FileContributor: Dominick Johnson
|
|
* SPDX-FileContributor: Henrique Moody <henriquemoody@gmail.com>
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
test('Non-iterable', catchAll(
|
|
fn() => v::each(v::intType())->assert(null),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`null` must be iterable')
|
|
->and($fullMessage)->toBe('- `null` must be iterable')
|
|
->and($messages)->toBe(['each' => '`null` must be iterable']),
|
|
));
|
|
|
|
test('Default', catchAll(
|
|
fn() => v::each(v::intType())->assert(['a', 'b', 'c']),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`.0` must be an integer')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Each item in `["a", "b", "c"]` must be valid
|
|
- `.0` must be an integer
|
|
- `.1` must be an integer
|
|
- `.2` must be an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Each item in `["a", "b", "c"]` must be valid',
|
|
0 => '`.0` must be an integer',
|
|
1 => '`.1` must be an integer',
|
|
2 => '`.2` must be an integer',
|
|
]),
|
|
));
|
|
|
|
test('Inverted', catchAll(
|
|
fn() => v::not(v::each(v::intType()))->assert([1, 2, 3]),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`.0` must not be an integer')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Each item in `[1, 2, 3]` must be invalid
|
|
- `.0` must not be an integer
|
|
- `.1` must not be an integer
|
|
- `.2` must not be an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Each item in `[1, 2, 3]` must be invalid',
|
|
0 => '`.0` must not be an integer',
|
|
1 => '`.1` must not be an integer',
|
|
2 => '`.2` must not be an integer',
|
|
]),
|
|
));
|
|
|
|
test('With name, non-iterable', catchAll(
|
|
fn() => v::each(v::named('Wrapped', v::intType()))->assert(null),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`null` must be iterable')
|
|
->and($fullMessage)->toBe('- `null` must be iterable')
|
|
->and($messages)->toBe(['each' => '`null` must be iterable']),
|
|
));
|
|
|
|
test('With name, default', catchAll(
|
|
fn() => v::named('Outer', v::each(v::named('Inner', v::intType())))->assert(['a', 'b', 'c']),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`.0` (<- Inner) must be an integer')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Each item in Outer must be valid
|
|
- `.0` (<- Inner) must be an integer
|
|
- `.1` (<- Inner) must be an integer
|
|
- `.2` (<- Inner) must be an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Each item in Outer must be valid',
|
|
0 => '`.0` (<- Inner) must be an integer',
|
|
1 => '`.1` (<- Inner) must be an integer',
|
|
2 => '`.2` (<- Inner) must be an integer',
|
|
]),
|
|
));
|
|
|
|
test('With name, inverted', catchAll(
|
|
fn() => v::named('Not', v::not(v::named('Outer', v::each(v::named('Inner', v::intType())))))->assert([1, 2, 3]),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`.0` (<- Inner) must not be an integer')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Each item in Outer must be invalid
|
|
- `.0` (<- Inner) must not be an integer
|
|
- `.1` (<- Inner) must not be an integer
|
|
- `.2` (<- Inner) must not be an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Each item in Outer must be invalid',
|
|
0 => '`.0` (<- Inner) must not be an integer',
|
|
1 => '`.1` (<- Inner) must not be an integer',
|
|
2 => '`.2` (<- Inner) must not be an integer',
|
|
]),
|
|
));
|
|
|
|
test('With wrapper name, default', catchAll(
|
|
fn() => v::named('Wrapper', v::each(v::intType()))->assert(['a', 'b', 'c']),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`.0` (<- Wrapper) must be an integer')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Each item in Wrapper must be valid
|
|
- `.0` must be an integer
|
|
- `.1` must be an integer
|
|
- `.2` must be an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Each item in Wrapper must be valid',
|
|
0 => '`.0` must be an integer',
|
|
1 => '`.1` must be an integer',
|
|
2 => '`.2` must be an integer',
|
|
]),
|
|
));
|
|
|
|
test('With wrapper name, inverted', catchAll(
|
|
fn() => v::named('Not', v::not(v::named('Wrapper', v::each(v::intType()))))->assert([1, 2, 3]),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`.0` (<- Wrapper) must not be an integer')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Each item in Wrapper must be invalid
|
|
- `.0` must not be an integer
|
|
- `.1` must not be an integer
|
|
- `.2` must not be an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Each item in Wrapper must be invalid',
|
|
0 => '`.0` must not be an integer',
|
|
1 => '`.1` must not be an integer',
|
|
2 => '`.2` must not be an integer',
|
|
]),
|
|
));
|
|
|
|
test('With Not name, inverted', catchAll(
|
|
fn() => v::named('Not', v::not(v::each(v::intType())))->assert([1, 2, 3]),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`.0` (<- Not) must not be an integer')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Each item in Not must be invalid
|
|
- `.0` must not be an integer
|
|
- `.1` must not be an integer
|
|
- `.2` must not be an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Each item in Not must be invalid',
|
|
0 => '`.0` must not be an integer',
|
|
1 => '`.1` must not be an integer',
|
|
2 => '`.2` must not be an integer',
|
|
]),
|
|
));
|
|
|
|
test('With template, non-iterable', catchAll(
|
|
fn() => v::templated('You should have passed an iterable', v::each(v::intType()))->assert(null),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('You should have passed an iterable')
|
|
->and($fullMessage)->toBe('- You should have passed an iterable')
|
|
->and($messages)->toBe(['each' => 'You should have passed an iterable']),
|
|
));
|
|
|
|
test('With template, default', catchAll(
|
|
fn() => v::templated('All items should have been integers', v::each(v::intType()))
|
|
->assert(['a', 'b', 'c']),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('All items should have been integers')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- All items should have been integers
|
|
- `.0` must be an integer
|
|
- `.1` must be an integer
|
|
- `.2` must be an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'All items should have been integers',
|
|
0 => '`.0` must be an integer',
|
|
1 => '`.1` must be an integer',
|
|
2 => '`.2` must be an integer',
|
|
]),
|
|
));
|
|
|
|
test('with template, inverted', catchAll(
|
|
fn() => v::templated('All items should not have been integers', v::not(v::each(v::intType())))
|
|
->assert([1, 2, 3]),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('All items should not have been integers')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- All items should not have been integers
|
|
- `.0` must not be an integer
|
|
- `.1` must not be an integer
|
|
- `.2` must not be an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'All items should not have been integers',
|
|
0 => '`.0` must not be an integer',
|
|
1 => '`.1` must not be an integer',
|
|
2 => '`.2` must not be an integer',
|
|
]),
|
|
));
|
|
|
|
test('With array template, default', catchAll(
|
|
fn() => v::each(v::intType())
|
|
->assert(['a', 'b', 'c'], [
|
|
'__root__' => 'Here a sequence of items that did not pass the validation',
|
|
0 => 'First item should have been an integer',
|
|
1 => 'Second item should have been an integer',
|
|
2 => 'Third item should have been an integer',
|
|
]),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('First item should have been an integer')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Here a sequence of items that did not pass the validation
|
|
- First item should have been an integer
|
|
- Second item should have been an integer
|
|
- Third item should have been an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Here a sequence of items that did not pass the validation',
|
|
0 => 'First item should have been an integer',
|
|
1 => 'Second item should have been an integer',
|
|
2 => 'Third item should have been an integer',
|
|
]),
|
|
));
|
|
|
|
test('With array template and name, default', catchAll(
|
|
fn() => v::named('Wrapper', v::each(v::named('Wrapped', v::intType())))
|
|
->assert(['a', 'b', 'c'], [
|
|
'__root__' => 'Here a sequence of items that did not pass the validation',
|
|
0 => 'First item should have been an integer',
|
|
1 => 'Second item should have been an integer',
|
|
2 => 'Third item should have been an integer',
|
|
]),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('First item should have been an integer')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Here a sequence of items that did not pass the validation
|
|
- First item should have been an integer
|
|
- Second item should have been an integer
|
|
- Third item should have been an integer
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Here a sequence of items that did not pass the validation',
|
|
0 => 'First item should have been an integer',
|
|
1 => 'Second item should have been an integer',
|
|
2 => 'Third item should have been an integer',
|
|
]),
|
|
));
|
|
|
|
test('Chained wrapped rule', catchAll(
|
|
fn() => v::each(v::between(5, 7)->odd())->assert([2, 4]),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`.0` must be between 5 and 7')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Each item in `[2, 4]` must be valid
|
|
- `.0` must pass all the rules
|
|
- `.0` must be between 5 and 7
|
|
- `.0` must be an odd number
|
|
- `.1` must pass all the rules
|
|
- `.1` must be between 5 and 7
|
|
- `.1` must be an odd number
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Each item in `[2, 4]` must be valid',
|
|
0 => [
|
|
'__root__' => '`.0` must pass all the rules',
|
|
0 => '`.0` must be between 5 and 7',
|
|
1 => '`.0` must be an odd number',
|
|
],
|
|
1 => [
|
|
'__root__' => '`.1` must pass all the rules',
|
|
0 => '`.1` must be between 5 and 7',
|
|
1 => '`.1` must be an odd number',
|
|
],
|
|
]),
|
|
));
|
|
|
|
test('Multiple nested rules', catchAll(
|
|
fn() => v::each(v::arrayType()->key('my_int', v::intType()->odd())->length(v::equals(1)))->assert([['not_int' => 'wrong'], ['my_int' => 2], 'not an array']),
|
|
fn(string $message, string $fullMessage, array $messages) => expect()
|
|
->and($message)->toBe('`.0.my_int` must be present')
|
|
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
|
|
- Each item in `[["not_int": "wrong"], ["my_int": 2], "not an array"]` must be valid
|
|
- `.0` must pass the rules
|
|
- `.0.my_int` must be present
|
|
- `.1` must pass the rules
|
|
- `.1.my_int` must be an odd number
|
|
- `.2` must pass all the rules
|
|
- `.2` must be an array
|
|
- `.2.my_int` must be present
|
|
- The length of `.2` must be equal to 1
|
|
FULL_MESSAGE)
|
|
->and($messages)->toBe([
|
|
'__root__' => 'Each item in `[["not_int": "wrong"], ["my_int": 2], "not an array"]` must be valid',
|
|
0 => '`.0.my_int` must be present',
|
|
1 => '`.1.my_int` must be an odd number',
|
|
2 => [
|
|
'__root__' => '`.2` must pass all the rules',
|
|
'arrayType' => '`.2` must be an array',
|
|
'my_int' => '`.2.my_int` must be present',
|
|
'lengthEquals' => 'The length of `.2` must be equal to 1',
|
|
],
|
|
]),
|
|
));
|