Replace "LazyConsecutive" with "Consecutive"

With this and the Lazy rule, the LazyConsecutive lost its purpose.

While working on it, I did refactor the Domain rule a bit, but mainly to
check how this rule could behave.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
Henrique Moody 2024-03-06 21:15:14 +01:00
parent 78715fb844
commit 2610a380dc
No known key found for this signature in database
GPG key ID: 221E9281655813A6
21 changed files with 309 additions and 287 deletions

View file

@ -40,7 +40,6 @@
- [CallableType](rules/CallableType.md)
- [Callback](rules/Callback.md)
- [Lazy](rules/Lazy.md)
- [LazyConsecutive](rules/LazyConsecutive.md)
## Comparisons
@ -60,12 +59,13 @@
- [AllOf](rules/AllOf.md)
- [AnyOf](rules/AnyOf.md)
- [LazyConsecutive](rules/LazyConsecutive.md)
- [Consecutive](rules/Consecutive.md)
- [NoneOf](rules/NoneOf.md)
- [OneOf](rules/OneOf.md)
## Conditions
- [Consecutive](rules/Consecutive.md)
- [Not](rules/Not.md)
- [When](rules/When.md)
@ -161,11 +161,11 @@
- [AllOf](rules/AllOf.md)
- [AnyOf](rules/AnyOf.md)
- [Call](rules/Call.md)
- [Consecutive](rules/Consecutive.md)
- [Each](rules/Each.md)
- [Key](rules/Key.md)
- [KeySet](rules/KeySet.md)
- [Lazy](rules/Lazy.md)
- [LazyConsecutive](rules/LazyConsecutive.md)
- [NoneOf](rules/NoneOf.md)
- [Not](rules/Not.md)
- [Nullable](rules/Nullable.md)
@ -306,6 +306,7 @@
- [Charset](rules/Charset.md)
- [Cnh](rules/Cnh.md)
- [Cnpj](rules/Cnpj.md)
- [Consecutive](rules/Consecutive.md)
- [Consonant](rules/Consonant.md)
- [Contains](rules/Contains.md)
- [ContainsAny](rules/ContainsAny.md)
@ -362,7 +363,6 @@
- [KeySet](rules/KeySet.md)
- [LanguageCode](rules/LanguageCode.md)
- [Lazy](rules/Lazy.md)
- [LazyConsecutive](rules/LazyConsecutive.md)
- [LeapDate](rules/LeapDate.md)
- [LeapYear](rules/LeapYear.md)
- [Length](rules/Length.md)

View file

@ -24,7 +24,7 @@ Version | Description
See also:
- [AnyOf](AnyOf.md)
- [LazyConsecutive](LazyConsecutive.md)
- [Consecutive](Consecutive.md)
- [NoneOf](NoneOf.md)
- [OneOf](OneOf.md)
- [When](When.md)

View file

@ -29,8 +29,8 @@ Version | Description
See also:
- [AllOf](AllOf.md)
- [Consecutive](Consecutive.md)
- [ContainsAny](ContainsAny.md)
- [LazyConsecutive](LazyConsecutive.md)
- [NoneOf](NoneOf.md)
- [OneOf](OneOf.md)
- [When](When.md)

View file

@ -59,5 +59,4 @@ See also:
- [Callback](Callback.md)
- [Each](Each.md)
- [Lazy](Lazy.md)
- [LazyConsecutive](LazyConsecutive.md)
- [Sorted](Sorted.md)

44
docs/rules/Consecutive.md Normal file
View file

@ -0,0 +1,44 @@
# Consecutive
- `Consecutive(Validatable $rule1, Validatable $rule2, Validatable ...$rule)`
Validates the input against a series of rules until one fails.
This rule can be handy for getting the least error messages possible from a chain.
This rule can be helpful in combinations with [Lazy](Lazy.md). An excellent example is when you want to validate a
country code and a subdivision code.
```php
v::consecutive(
v::key('countryCode', v::countryCode()),
v::lazy(static fn($input) => v::key('subdivisionCode', v::subdivisionCode($input['countryCode']))),
)->validate($_POST);
```
You need a valid country code to create a [SubdivisionCode](SubdivisionCode.md), so it makes sense only to validate the
subdivision code only if the country code is valid. In this case, you could also have used [When](When.md), but you
would then have to write `v::key('countryCode', v::countryCode())` twice in your chain.
## Categorization
- Composite
- Conditions
- Nesting
## Changelog
| Version | Description |
|--------:|-------------|
| 3.0.0 | Created |
***
See also:
- [AllOf](AllOf.md)
- [AnyOf](AnyOf.md)
- [Lazy](Lazy.md)
- [NoneOf](NoneOf.md)
- [OneOf](OneOf.md)
- [SubdivisionCode](SubdivisionCode.md)
- [When](When.md)

View file

@ -27,5 +27,4 @@ See also:
- [Contains](Contains.md)
- [Equivalent](Equivalent.md)
- [Identical](Identical.md)
- [LazyConsecutive](LazyConsecutive.md)
- [Version](Version.md)

View file

@ -39,3 +39,4 @@ See also:
- [Call](Call.md)
- [CallableType](CallableType.md)
- [Consecutive](Consecutive.md)

View file

@ -1,65 +0,0 @@
# LazyConsecutive
- `LazyConsecutive(callable(mixed $input): Validatable ...$ruleCreators)`
This executes a series of callbacks that create rules. Those callbacks accept the input as an argument and must return
an instance of `Validatable`.
This is particularly useful when the creation of rules would rely on the input itself. A good example is validating
whether a `password_confirmation` field matches the `password` field when processing data from a form.
```php
v::key('password', v::notEmpty())->validate($_POST);
v::key('password_confirmation', v::equals($_POST['password'] ?? null))->validate($_POST);
```
The problem with the above code is that you do not know if the `password` is a valid key, so you must check it manually
before performing the validation on `password_confirmation`. Besides, it could make it harder to reuse the validator.
The `lazyConsecutive()` rule makes this job much simpler and more elegantly:
```php
v::lazyConsecutive(
static fn() => v::key('password', v::stringType()->notEmpty()),
static fn($input) => v::key('password_confirmation', v::equals($input['password'])),
)->validate($_POST);
```
The return of the above code will be `true` if `$_POST['password_confirmation']` [equals](Equals.md)
`$_POST['password']`. The `lazyConsecutive()` rule will only execute the second callable if the rule from the first
callable passes.
Another typical example is validating country and subdivision codes:
```php
v::lazyConsecutive(
static fn() => v::key('countryCode', v::countryCode()),
static fn($input) => v::key('subdivisionCode', v::subdivisionCode($input['countryCode'])),
)->validate($_POST);
```
The return of the above code will be `true` if `$_POST['subdivisionCode']` is a [subdivision code](SubdivisionCode.md)
of `$_POST['countryCode']`.
## Categorization
- Callables
- Composite
- Nesting
## Changelog
| Version | Description |
| ------: | ----------- |
| 3.0.0 | Created |
***
See also:
- [AllOf](AllOf.md)
- [AnyOf](AnyOf.md)
- [Call](Call.md)
- [Equals](Equals.md)
- [NoneOf](NoneOf.md)
- [OneOf](OneOf.md)
- [SubdivisionCode](SubdivisionCode.md)

View file

@ -30,7 +30,7 @@ See also:
- [AllOf](AllOf.md)
- [AnyOf](AnyOf.md)
- [LazyConsecutive](LazyConsecutive.md)
- [Consecutive](Consecutive.md)
- [Not](Not.md)
- [OneOf](OneOf.md)
- [When](When.md)

View file

@ -31,6 +31,6 @@ See also:
- [AllOf](AllOf.md)
- [AnyOf](AnyOf.md)
- [LazyConsecutive](LazyConsecutive.md)
- [Consecutive](Consecutive.md)
- [NoneOf](NoneOf.md)
- [When](When.md)

View file

@ -28,9 +28,9 @@ Version | Description
***
See also:
- [Consecutive](Consecutive.md)
- [CountryCode](CountryCode.md)
- [CurrencyCode](CurrencyCode.md)
- [LazyConsecutive](LazyConsecutive.md)
- [Nip](Nip.md)
- [Pesel](Pesel.md)
- [PolishIdCard](PolishIdCard.md)

View file

@ -38,5 +38,6 @@ See also:
- [AllOf](AllOf.md)
- [AlwaysInvalid](AlwaysInvalid.md)
- [AnyOf](AnyOf.md)
- [Consecutive](Consecutive.md)
- [NoneOf](NoneOf.md)
- [OneOf](OneOf.md)

View file

@ -60,6 +60,8 @@ interface ChainedValidator extends Validatable
public function cnpj(): ChainedValidator;
public function consecutive(Validatable $rule1, Validatable $rule2, Validatable ...$rule): ChainedValidator;
public function control(string ...$additionalChars): ChainedValidator;
public function consonant(string ...$additionalChars): ChainedValidator;
@ -175,8 +177,6 @@ interface ChainedValidator extends Validatable
/** @param callable(mixed): Validatable $ruleCreator */
public function lazy(callable $ruleCreator): ChainedValidator;
public function lazyConsecutive(callable $ruleCreator, callable ...$ruleCreators): ChainedValidator;
/** @param "alpha-2"|"alpha-3" $set */
public function languageCode(string $set = 'alpha-2'): ChainedValidator;

View file

@ -62,6 +62,8 @@ interface StaticValidator
public static function cnpj(): ChainedValidator;
public static function consecutive(Validatable $rule1, Validatable $rule2, Validatable ...$rule): ChainedValidator;
public static function control(string ...$additionalChars): ChainedValidator;
public static function consonant(string ...$additionalChars): ChainedValidator;
@ -177,8 +179,6 @@ interface StaticValidator
/** @param callable(mixed): Validatable $ruleCreator */
public static function lazy(callable $ruleCreator): ChainedValidator;
public static function lazyConsecutive(callable $ruleCreator, callable ...$ruleCreators): ChainedValidator;
/** @param "alpha-2"|"alpha-3" $set */
public static function languageCode(string $set = 'alpha-2'): ChainedValidator;

View file

@ -0,0 +1,31 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Helpers\CanBindEvaluateRule;
use Respect\Validation\Result;
use Respect\Validation\Rules\Core\Composite;
final class Consecutive extends Composite
{
use CanBindEvaluateRule;
public function evaluate(mixed $input): Result
{
foreach ($this->rules as $rule) {
$result = $this->bindEvaluate($rule, $this, $input);
if (!$result->isValid) {
return $result;
}
}
return $result;
}
}

View file

@ -17,7 +17,6 @@ use Respect\Validation\Result;
use Respect\Validation\Validatable;
use function array_filter;
use function array_map;
use function array_merge;
use function array_pop;
use function count;
@ -32,7 +31,7 @@ use function mb_substr_count;
)]
final class Domain extends AbstractRule
{
private readonly AllOf $genericRule;
private readonly Consecutive $genericRule;
private readonly Validatable $tldRule;
@ -66,13 +65,9 @@ final class Domain extends AbstractRule
public function evaluate(mixed $input): Result
{
$failedGenericResults = array_filter(array_map(
static fn (Validatable $rule) => $rule->evaluate($input),
$this->genericRule->getRules()
), static fn (Result $result) => !$result->isValid);
if (count($failedGenericResults)) {
return (new Result(false, $input, $this))->withChildren(...$failedGenericResults);
$genericResult = $this->genericRule->evaluate($input);
if (!$genericResult->isValid) {
return (new Result(false, $input, $this))->withChildren($genericResult);
}
$children = [];
@ -80,19 +75,15 @@ final class Domain extends AbstractRule
$parts = explode('.', (string) $input);
if (count($parts) >= 2) {
$tld = array_pop($parts);
foreach ($this->tldRule instanceof AllOf ? $this->tldRule->getRules() : [$this->tldRule] as $rule) {
$childResult = $rule->evaluate($tld);
$valid = $valid && $childResult->isValid;
$children[] = $childResult;
}
$childResult = $this->tldRule->evaluate($tld);
$valid = $childResult->isValid;
$children[] = $childResult;
}
foreach ($parts as $part) {
foreach ($this->partsRule->getRules() as $rule) {
$childResult = $rule->evaluate($part);
$valid = $valid && $childResult->isValid;
$children[] = $childResult;
}
$partsResult = $this->partsRule->evaluate($part);
$valid = $valid && $partsResult->isValid;
$children = array_merge($children, $partsResult->children);
}
return (new Result($valid, $input, $this))
@ -141,9 +132,9 @@ final class Domain extends AbstractRule
}
}
private function createGenericRule(): AllOf
private function createGenericRule(): Consecutive
{
return new AllOf(
return new Consecutive(
new StringType(),
new NoWhitespace(),
new Contains('.'),
@ -157,7 +148,7 @@ final class Domain extends AbstractRule
return new Tld();
}
return new AllOf(
return new Consecutive(
new Not(new StartsWith('-')),
new NoWhitespace(),
new Length(2)

View file

@ -1,54 +0,0 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Helpers\CanBindEvaluateRule;
use Respect\Validation\Result;
use Respect\Validation\Rules\Core\Standard;
use Respect\Validation\Validatable;
use function array_merge;
final class LazyConsecutive extends Standard
{
use CanBindEvaluateRule;
/** @var non-empty-array<callable> */
private array $ruleCreators;
/**
* @param callable(mixed): Validatable $ruleCreator
* @param callable(mixed): Validatable ...$ruleCreators
*/
public function __construct(callable $ruleCreator, callable ...$ruleCreators)
{
$this->ruleCreators = array_merge([$ruleCreator], $ruleCreators);
}
public function evaluate(mixed $input): Result
{
foreach ($this->ruleCreators as $key => $ruleCreator) {
$rule = $ruleCreator($input);
if (!$rule instanceof Validatable) {
throw new ComponentException(
'LazyConsecutive failed because it could not create rule #' . ($key + 1)
);
}
$result = $this->bindEvaluate($rule, $this, $input);
if (!$result->isValid) {
return $result;
}
}
return $result;
}
}

View file

@ -0,0 +1,143 @@
--FILE--
<?php
declare(strict_types=1);
require 'vendor/autoload.php';
use Respect\Validation\Validator as v;
run([
'Default' => [v::consecutive(v::alwaysValid(), v::trueVal()), false],
'Negative' => [v::not(v::consecutive(v::alwaysValid(), v::trueVal())), true],
'Default with inverted failing rule' => [v::consecutive(v::alwaysValid(), v::not(v::trueVal())), true],
'With wrapped name, default' => [
v::consecutive(v::alwaysValid(), v::trueVal()->setName('Wrapped'))->setName('Wrapper'),
false,
],
'With wrapper name, default' => [
v::consecutive(v::alwaysValid(), v::trueVal())->setName('Wrapper'),
false,
],
'With the name set in the wrapped rule of an inverted failing rule' => [
v::consecutive(v::alwaysValid(), v::not(v::trueVal()->setName('Wrapped'))->setName('Not'))->setName('Wrapper'),
true,
],
'With the name set in an inverted failing rule' => [
v::consecutive(v::alwaysValid(), v::not(v::trueVal())->setName('Not'))->setName('Wrapper'),
true,
],
'With the name set in the "consecutive" that has an inverted failing rule' => [
v::consecutive(v::alwaysValid(), v::not(v::trueVal()))->setName('Wrapper'),
true,
],
'With template' => [
v::consecutive(v::alwaysValid(), v::trueVal()),
false,
'Consecutive cool cats cunningly continuous cookies',
],
'With multiple templates' => [
v::consecutive(v::alwaysValid(), v::trueVal()),
false,
['trueVal' => 'Clever clowns craft consecutive clever clocks'],
],
'Real example' => [
v::consecutive(
v::key('countyCode', v::countryCode()),
v::lazy(static fn($input) => v::key('subdivisionCode', v::subdivisionCode($input['countyCode']))),
),
[
'countyCode' => 'BR',
'subdivisionCode' => 'CA',
],
],
]);
?>
--EXPECT--
Default
⎺⎺⎺⎺⎺⎺⎺
`false` must evaluate to `true`
- `false` must evaluate to `true`
[
'trueVal' => '`false` must evaluate to `true`',
]
Negative
⎺⎺⎺⎺⎺⎺⎺⎺
`true` must not evaluate to `true`
- `true` must not evaluate to `true`
[
'trueVal' => '`true` must not evaluate to `true`',
]
Default with inverted failing rule
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
`true` must not evaluate to `true`
- `true` must not evaluate to `true`
[
'trueVal' => '`true` must not evaluate to `true`',
]
With wrapped name, default
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
Wrapped must evaluate to `true`
- Wrapped must evaluate to `true`
[
'Wrapped' => 'Wrapped must evaluate to `true`',
]
With wrapper name, default
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
Wrapper must evaluate to `true`
- Wrapper must evaluate to `true`
[
'Wrapper' => 'Wrapper must evaluate to `true`',
]
With the name set in the wrapped rule of an inverted failing rule
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
Wrapped must not evaluate to `true`
- Wrapped must not evaluate to `true`
[
'Wrapped' => 'Wrapped must not evaluate to `true`',
]
With the name set in an inverted failing rule
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
Not must not evaluate to `true`
- Not must not evaluate to `true`
[
'Not' => 'Not must not evaluate to `true`',
]
With the name set in the "consecutive" that has an inverted failing rule
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
Wrapper must not evaluate to `true`
- Wrapper must not evaluate to `true`
[
'Wrapper' => 'Wrapper must not evaluate to `true`',
]
With template
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
Consecutive cool cats cunningly continuous cookies
- Consecutive cool cats cunningly continuous cookies
[
'trueVal' => 'Consecutive cool cats cunningly continuous cookies',
]
With multiple templates
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
Clever clowns craft consecutive clever clocks
- Clever clowns craft consecutive clever clocks
[
'trueVal' => 'Clever clowns craft consecutive clever clocks',
]
Real example
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
subdivisionCode must be a subdivision code of Brazil
- subdivisionCode must be a subdivision code of Brazil
[
'subdivisionCode' => 'subdivisionCode must be a subdivision code of Brazil',
]

View file

@ -1,48 +0,0 @@
--FILE--
<?php
declare(strict_types=1);
require 'vendor/autoload.php';
use Respect\Validation\Validator as v;
run([
'Password confirmation' => [
v::lazyConsecutive(
static fn() => v::key('password', v::stringType()),
static fn($input) => v::key('password_confirmation', v::equals($input['password']))
),
[
'password' => '123456',
'password_confirmation' => '1234s56',
],
],
'Localization confirmation' => [
v::lazyConsecutive(
static fn() => v::key('countyCode', v::countryCode()),
static fn($input) => v::key('subdivisionCode', v::subdivisionCode($input['countyCode'])),
),
[
'countyCode' => 'BR',
'subdivisionCode' => 'CA',
],
],
]);
?>
--EXPECT--
Password confirmation
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
password_confirmation must equal "123456"
- password_confirmation must equal "123456"
[
'password_confirmation' => 'password_confirmation must equal "123456"',
]
Localization confirmation
⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺
subdivisionCode must be a subdivision code of Brazil
- subdivisionCode must be a subdivision code of Brazil
[
'subdivisionCode' => 'subdivisionCode must be a subdivision code of Brazil',
]

View file

@ -0,0 +1,63 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use Respect\Validation\Test\Rules\Stub;
use Respect\Validation\Test\TestCase;
use function rand;
#[Group('rule')]
#[CoversClass(Consecutive::class)]
final class ConsecutiveTest extends TestCase
{
#[Test]
#[DataProvider('providerForAnyValues')]
public function itShouldValidateInputWhenAllRulesValidatesTheInput(mixed $input): void
{
self::assertValidInput(new Consecutive(Stub::pass(1), Stub::pass(1)), $input);
}
#[Test]
#[DataProvider('providerForFailingRules')]
public function itShouldExecuteRulesInSequenceUntilOneFails(Stub ...$stub): void
{
$rule = new Consecutive(...$stub);
self::assertInvalidInput($rule, rand());
}
#[Test]
public function itShouldReturnTheResultOfTheFailingRule(): void
{
$input = rand();
$rule = new Consecutive(Stub::fail(1), Stub::daze());
$actual = $rule->evaluate($input);
$expected = Stub::fail(1)->evaluate($input);
self::assertEquals($expected, $actual);
}
/** @return array<array<Stub>> */
public static function providerForFailingRules(): array
{
return [
'first fails' => [Stub::fail(1), Stub::daze()],
'second fails' => [Stub::pass(1), Stub::fail(1), Stub::daze()],
'third fails' => [Stub::pass(1), Stub::fail(1), Stub::fail(1), Stub::daze(), Stub::daze()],
];
}
}

View file

@ -1,83 +0,0 @@
<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Test\Rules\Stub;
use Respect\Validation\Test\RuleTestCase;
use function rand;
#[Group('rule')]
#[CoversClass(LazyConsecutive::class)]
final class LazyConsecutiveTest extends RuleTestCase
{
#[Test]
public function itShouldThrowAnExceptionWhenRuleCreatorDoesNotReturnValidatable(): void
{
// @phpstan-ignore-next-line
$rule = new LazyConsecutive(static fn () => null);
$this->expectException(ComponentException::class);
$this->expectExceptionMessage('LazyConsecutive failed because it could not create rule #1');
$rule->evaluate('something');
}
#[Test]
#[DataProvider('providerForAnyValues')]
public function itShouldPassInputToTheRuleCreators(mixed $input): void
{
$rule = new LazyConsecutive(static fn ($creatorInput) => Stub::fail(1));
self::assertEquals(
Stub::fail(1)->evaluate($input),
$rule->evaluate($input)
);
}
/** @return iterable<string, array{LazyConsecutive, mixed}> */
public static function providerForValidInput(): iterable
{
return [
'1st pass' => [new LazyConsecutive(static fn () => Stub::pass(1)), true],
'1st pass + 2nd pass' => [
new LazyConsecutive(static fn () => Stub::pass(1), static fn () => Stub::pass(1)),
true,
],
];
}
/** @return iterable<string, array{LazyConsecutive, mixed}> */
public static function providerForInvalidInput(): iterable
{
return [
'1st fail' => [
new LazyConsecutive(
static fn () => Stub::fail(1),
static fn () => Stub::daze(),
),
rand(),
],
'1st pass + 2nd fail' => [
new LazyConsecutive(
static fn () => Stub::pass(1),
static fn () => Stub::fail(1),
static fn () => Stub::daze(),
),
rand(),
],
];
}
}