mirror of
https://github.com/Respect/Validation.git
synced 2026-03-17 23:59:51 +01:00
Currently, the Property rule has a third parameter that allows the validation of the wrapped rule to be optional, meaning that the validation will only happen if the property exists. That parameter makes the rule harder to understand at times. I'm splitting the Property rule into Property, PropertyExists, and PropertyOptional. That way, it becomes apparent when someone wants only to validate whether a property exists or if they will validate the value of the property only when it exists. I deliberately didn't create an abstract class because those rules are different enough not to have an abstraction. In fact, I can see myself deleting the AbstractRelated after I refactor the KeyNested rule. Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
288 lines
8.9 KiB
PHP
288 lines
8.9 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Respect\Validation\Test;
|
|
|
|
use ArrayObject;
|
|
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
|
|
use Respect\Validation\Test\Stubs\WithProperties;
|
|
use Respect\Validation\Test\Stubs\WithStaticProperties;
|
|
use Respect\Validation\Test\Stubs\WithUninitialized;
|
|
use Respect\Validation\Validatable;
|
|
use stdClass;
|
|
|
|
use function array_merge;
|
|
use function implode;
|
|
use function ltrim;
|
|
use function realpath;
|
|
use function Respect\Stringifier\stringify;
|
|
use function sprintf;
|
|
use function strrchr;
|
|
use function substr;
|
|
use function tmpfile;
|
|
|
|
use const PHP_INT_MAX;
|
|
use const PHP_INT_MIN;
|
|
|
|
abstract class TestCase extends PHPUnitTestCase
|
|
{
|
|
public static function fixture(?string $filename = null): string
|
|
{
|
|
$parts = [(string) realpath(__DIR__ . '/../fixtures')];
|
|
if ($filename !== null) {
|
|
$parts[] = ltrim($filename, '/');
|
|
}
|
|
|
|
return implode('/', $parts);
|
|
}
|
|
|
|
public static function assertValidInput(Validatable $rule, mixed $input): void
|
|
{
|
|
$result = $rule->evaluate($input);
|
|
|
|
self::assertTrue(
|
|
$result->isValid,
|
|
sprintf(
|
|
'%s should pass with input %s and parameters %s',
|
|
substr((string) strrchr($rule::class, '\\'), 1),
|
|
stringify($input),
|
|
stringify($result->parameters)
|
|
)
|
|
);
|
|
}
|
|
|
|
public static function assertInvalidInput(Validatable $rule, mixed $input): void
|
|
{
|
|
$result = $rule->evaluate($input);
|
|
|
|
self::assertFalse(
|
|
$result->isValid,
|
|
sprintf(
|
|
'%s should fail with input %s and parameters %s',
|
|
substr((string) strrchr($rule::class, '\\'), 1),
|
|
stringify($input),
|
|
stringify($result->parameters)
|
|
)
|
|
);
|
|
}
|
|
|
|
/** @return array<array{mixed}> */
|
|
public static function providerForAnyValues(): array
|
|
{
|
|
return array_merge(
|
|
self::providerForStringValues(),
|
|
self::providerForNonScalarValues(),
|
|
self::providerForEmptyIterableValues(),
|
|
self::providerForNonEmptyIterableValues(),
|
|
self::providerForNonIterableValues(),
|
|
self::providerForIntegerValues(),
|
|
self::providerForBooleanValues(),
|
|
self::providerForFloatValues(),
|
|
);
|
|
}
|
|
|
|
/** @return array<array{scalar}> */
|
|
public static function providerForScalarValues(): array
|
|
{
|
|
return array_merge(
|
|
self::providerForStringValues(),
|
|
self::providerForIntegerValues(),
|
|
self::providerForBooleanValues(),
|
|
self::providerForFloatValues(),
|
|
);
|
|
}
|
|
|
|
/** @return array<array{scalar}> */
|
|
public static function providerForEmptyScalarValues(): array
|
|
{
|
|
return [
|
|
'empty string' => [''],
|
|
'false' => [false],
|
|
];
|
|
}
|
|
|
|
/** @return array<array{mixed}> */
|
|
public static function providerForNonScalarValues(): array
|
|
{
|
|
return self::providerForNonEmptyIterableValues() + self::providerForNonEmptyIterableValues() + [
|
|
'closure' => [static fn() => 'foo'],
|
|
'stdClass' => [new stdClass()],
|
|
'null' => [null],
|
|
'resource' => [tmpfile()],
|
|
];
|
|
}
|
|
|
|
/** @return array<array{mixed}> */
|
|
public static function providerForNonIterableValues(): array
|
|
{
|
|
return array_merge(
|
|
self::providerForScalarValues(),
|
|
[
|
|
'closure' => [static fn() => 'foo'],
|
|
'stdClass' => [new stdClass()],
|
|
'null' => [null],
|
|
'resource' => [tmpfile()],
|
|
]
|
|
);
|
|
}
|
|
|
|
/** @return array<array{iterable<mixed>}> */
|
|
public static function providerForIterableValues(): array
|
|
{
|
|
return array_merge(
|
|
self::providerForNonEmptyIterableValues(),
|
|
self::providerForEmptyIterableValues(),
|
|
);
|
|
}
|
|
|
|
/** @return array<array{iterable<mixed>}> */
|
|
public static function providerForNonEmptyIterableValues(): array
|
|
{
|
|
return [
|
|
'ArrayObject' => [new ArrayObject([1, 2, 3])],
|
|
'array' => [[4, 5, 6]],
|
|
'generator' => [(static fn() => yield 7)()], // phpcs:ignore
|
|
];
|
|
}
|
|
|
|
/** @return array<array{iterable<mixed>}> */
|
|
public static function providerForEmptyIterableValues(): array
|
|
{
|
|
return [
|
|
'empty ArrayObject' => [new ArrayObject([])],
|
|
'empty array' => [[]],
|
|
'empty generator' => [(static fn() => yield from [])()],
|
|
];
|
|
}
|
|
|
|
/** @return array<array{string}> */
|
|
public static function providerForStringValues(): array
|
|
{
|
|
return [
|
|
'string' => ['string'],
|
|
'empty string' => [''],
|
|
'integer string' => ['500'],
|
|
'float string' => ['56.8'],
|
|
'zero string' => ['0'],
|
|
];
|
|
}
|
|
|
|
/** @return array<array{string}> */
|
|
public static function providerForNonEmptyStringValues(): array
|
|
{
|
|
$dataProvider = self::providerForStringValues();
|
|
unset($dataProvider['empty string']);
|
|
|
|
return $dataProvider;
|
|
}
|
|
|
|
/** @return array<array{mixed}> */
|
|
public static function providerForNonStringValues(): array
|
|
{
|
|
return array_merge(
|
|
self::providerForNonScalarValues(),
|
|
self::providerForIntegerValues(),
|
|
self::providerForBooleanValues(),
|
|
self::providerForFloatValues(),
|
|
);
|
|
}
|
|
|
|
/** @return array<array{int}> */
|
|
public static function providerForIntegerValues(): array
|
|
{
|
|
return [
|
|
'zero integer' => [0],
|
|
'positive integer' => [PHP_INT_MAX],
|
|
'negative integer' => [PHP_INT_MIN],
|
|
];
|
|
}
|
|
|
|
/** @return array<array{bool}> */
|
|
public static function providerForBooleanValues(): array
|
|
{
|
|
return [
|
|
'true' => [true],
|
|
'false' => [false],
|
|
];
|
|
}
|
|
|
|
/** @return array<array{float}> */
|
|
public static function providerForFloatValues(): array
|
|
{
|
|
return [
|
|
'zero float' => [0.0],
|
|
'negative float' => [-893.1],
|
|
'positive float' => [32.890],
|
|
];
|
|
}
|
|
|
|
/** @return array<array{mixed}> */
|
|
public static function providerForNonArrayValues(): array
|
|
{
|
|
$scalarValues = self::providerForNonScalarValues();
|
|
unset($scalarValues['array']);
|
|
|
|
return array_merge(
|
|
self::providerForIntegerValues(),
|
|
self::providerForBooleanValues(),
|
|
self::providerForFloatValues(),
|
|
self::providerForStringValues(),
|
|
$scalarValues,
|
|
);
|
|
}
|
|
|
|
/** @return array<string, array{string|int, array<mixed>}> */
|
|
public static function providerForArrayWithMissingKeys(): array
|
|
{
|
|
return [
|
|
'integer key, non-empty input' => [0, [1 => true, 2 => true]],
|
|
'string key, non-empty input' => ['foo', ['bar' => true, 'baz' => true]],
|
|
'integer key, empty input' => [0, []],
|
|
'string key, empty input' => ['foo', []],
|
|
];
|
|
}
|
|
|
|
/** @return array<string, array{string|int, array<mixed>}> */
|
|
public static function providerForArrayWithExistingKeys(): array
|
|
{
|
|
return [
|
|
'integer key with a single value array' => [1, [1 => true]],
|
|
'integer key with a multiple value array' => [2, [1 => true, 2 => true]],
|
|
'string key with a single value array' => ['foo', ['foo' => true, 'bar' => true]],
|
|
'string key with a multiple value array' => ['bar', ['foo' => true, 'bar' => true]],
|
|
'integer key with null for a value' => [0, [null]],
|
|
'string key with null for a value' => ['foo', ['foo' => null]],
|
|
'integer key with false for a value' => [0, [false]],
|
|
'string key with false for a value' => ['foo', ['foo' => false]],
|
|
];
|
|
}
|
|
|
|
/** @return array<array{string, object}> */
|
|
public static function providerForObjectsWithMissingProperties(): array
|
|
{
|
|
return [
|
|
'object with no properties' => ['something', new stdClass()],
|
|
'object with missing property' => ['nonExisting', new WithProperties()],
|
|
];
|
|
}
|
|
|
|
/** @return array<array{string, object}> */
|
|
public static function providerForObjectsWithExistingProperties(): array
|
|
{
|
|
return [
|
|
'public' => ['public', new WithProperties()],
|
|
'protected' => ['protected', new WithProperties()],
|
|
'private' => ['private', new WithProperties()],
|
|
'uninitialized' => ['uninitialized', new WithUninitialized()],
|
|
'static public' => ['public', new WithStaticProperties()],
|
|
'static protected' => ['protected', new WithStaticProperties()],
|
|
'static private' => ['private', new WithStaticProperties()],
|
|
];
|
|
}
|
|
}
|