Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add enumExists and enumCase assertions #330

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ Assertion::directory(string $value);
Assertion::e164(string $value);
Assertion::email(mixed $value);
Assertion::endsWith(mixed $string, string $needle);
Assertion::enumCase(mixed $value, string $enumClass);
Assertion::enumExists(mixed $value);
Assertion::eq(mixed $value, mixed $value2);
Assertion::eqArraySubset(mixed $value, mixed $value2);
Assertion::extensionLoaded(mixed $value);
Expand All @@ -219,7 +221,7 @@ Assertion::ipv6(string $value, int $flag = null);
Assertion::isArray(mixed $value);
Assertion::isArrayAccessible(mixed $value);
Assertion::isCallable(mixed $value);
Assertion::isCountable(array|Countable|ResourceBundle|SimpleXMLElement $value);
Assertion::isCountable(mixed $value);
Assertion::isInstanceOf(mixed $value, string $className);
Assertion::isJsonString(mixed $value);
Assertion::isObject(mixed $value);
Expand Down
78 changes: 78 additions & 0 deletions lib/Assert/Assertion.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use SimpleXMLElement;
use Throwable;
use Traversable;
use ValueError;

/**
* Assert library.
Expand All @@ -48,6 +49,8 @@
* @method static bool allE164(string[] $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number for all values.
* @method static bool allEmail(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL) for all values.
* @method static bool allEndsWith(mixed[] $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars for all values.
* @method static bool allEnumCase(mixed[] $value, string $enumClass, string|callable $message = null, ?string $propertyPath = null) Assert that the value is a valid backed enum case for all values.
* @method static bool allEnumExists(mixed[] $value, string|callable $message = null, ?string $propertyPath = null) Assert that the enum exists for all values.
* @method static bool allEq(mixed[] $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==) for all values.
* @method static bool allEqArraySubset(mixed[] $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset for all values.
* @method static bool allExtensionLoaded(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded for all values.
Expand Down Expand Up @@ -137,6 +140,8 @@
* @method static bool nullOrE164(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number or that the value is null.
* @method static bool nullOrEmail(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL) or that the value is null.
* @method static bool nullOrEndsWith(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars or that the value is null.
* @method static bool nullOrEnumCase(mixed|null $value, string $enumClass, string|callable $message = null, ?string $propertyPath = null) Assert that the value is a valid backed enum case or that the value is null.
* @method static bool nullOrEnumExists(mixed|null $value, string|callable $message = null, ?string $propertyPath = null) Assert that the enum exists or that the value is null.
* @method static bool nullOrEq(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==) or that the value is null.
* @method static bool nullOrEqArraySubset(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset or that the value is null.
* @method static bool nullOrExtensionLoaded(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded or that the value is null.
Expand Down Expand Up @@ -290,6 +295,8 @@ class Assertion
const INVALID_MAX_COUNT = 228;
const INVALID_STRING_NOT_CONTAINS = 229;
const INVALID_UNIQUE_VALUES = 230;
const INVALID_ENUM = 231;
const INVALID_ENUM_CASE = 232;

/**
* Exception to throw when an assertion failed.
Expand Down Expand Up @@ -2710,6 +2717,77 @@ public static function base64($value, $message = null, string $propertyPath = nu
return true;
}

/**
* Assert that the enum exists.
*
* @param mixed $value
* @param string|callable|null $message
* @param string|null $propertyPath
*
* @psalm-assert class-string $value
*
* @return bool
*
* @throws AssertionFailedException
*/
public static function enumExists($value, $message = null, string $propertyPath = null): bool
{
if (!function_exists('enum_exists')) {
throw new \BadMethodCallException('The "enum_exists" function is not available. Please install the "symfony/polyfill-php81" package.');
}

if (!\enum_exists($value)) {
$message = \sprintf(
static::generateMessage($message ?: 'Enum "%s" does not exist.'),
static::stringify($value)
);

throw static::createException($value, $message, static::INVALID_ENUM, $propertyPath);
}

return true;
}

/**
* Assert that the value is a valid backed enum case.
*
* @param mixed $value
* @param string|callable|null $message
* @param string|null $propertyPath
*
* @psalm-assert class-string $value
*
* @throws AssertionFailedException
*/
public static function enumCase($value, string $enumClass, $message = null, string $propertyPath = null): bool
{
static::enumExists($enumClass);
static::implementsInterface($enumClass, \BackedEnum::class);

try {
$enumClass::from($value);
} catch (\ValueError) {
$message = \sprintf(
static::generateMessage($message ?: 'Value "%s" is not a valid backing value for enum "%s". Allowed : %s'),
static::stringify($value),
$enumClass,
\implode(
', ',
\array_map(
function ($case) {
return self::stringify($case->value);
},
$enumClass::cases()
)
),
);

throw static::createException($value, $message, static::INVALID_ENUM_CASE, $propertyPath);
}

return true;
}

/**
* Helper method that handles building the assertion failure exceptions.
* They are returned from this method so that the stack trace still shows
Expand Down
2 changes: 2 additions & 0 deletions lib/Assert/AssertionChain.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
* @method AssertionChain e164(string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number.
* @method AssertionChain email(string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL).
* @method AssertionChain endsWith(string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars.
* @method AssertionChain enumCase(string $enumClass, string|callable $message = null, ?string $propertyPath = null) Assert that the value is a valid backed enum case.
* @method AssertionChain enumExists(string|callable $message = null, ?string $propertyPath = null) Assert that the enum exists.
* @method AssertionChain eq(mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==).
* @method AssertionChain eqArraySubset(mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset.
* @method AssertionChain extensionLoaded(string|callable $message = null, string $propertyPath = null) Assert that extension is loaded.
Expand Down
2 changes: 2 additions & 0 deletions lib/Assert/LazyAssertion.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
* @method LazyAssertion e164(string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number.
* @method LazyAssertion email(string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL).
* @method LazyAssertion endsWith(string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars.
* @method LazyAssertion enumCase(string $enumClass, string|callable $message = null, ?string $propertyPath = null) Assert that the value is a valid backed enum case.
* @method LazyAssertion enumExists(string|callable $message = null, ?string $propertyPath = null) Assert that the enum exists.
* @method LazyAssertion eq(mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==).
* @method LazyAssertion eqArraySubset(mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset.
* @method LazyAssertion extensionLoaded(string|callable $message = null, string $propertyPath = null) Assert that extension is loaded.
Expand Down
2 changes: 1 addition & 1 deletion phpstan-tests.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ parameters:
- '#Call to an undefined method Assert\\AssertionChain::unknownAssertion\(\)#'
- '#Call to an undefined static method Assert\\Assertion::nullOrAssertionDoesNotExist\(\)#'
- '#Class Foo not found#'
- '#Parameter \#1 $value of static method Assert\\Assertion::isCountable\(\) expects array|Countable|ResourceBundle|SimpleXMLElement, string given#'
- '#Parameter \#1 \$value of static method Assert\\Assertion::isCountable\(\) expects array|Countable|ResourceBundle|SimpleXMLElement, string given#'
- '#Parameter \#2 \$operator of static method Assert\\Assertion::version\(\) expects string, null given#'
- '#Static method Assert\\Assertion::allTrue\(\) invoked with 0 parameters, 1-3 required#'
- '#Static method Assert\\Assertion::nullOrMax\(\) invoked with 0 parameters, 2-4 required#'
68 changes: 68 additions & 0 deletions tests/Assert/Tests/AssertEnumTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

/**
* Assert
*
* LICENSE
*
* This source file is subject to the MIT license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to kontakt@beberlei.de so I can send you a copy immediately.
*/

namespace Assert\Tests;

use Assert\Assertion;
use Assert\Tests\Fixtures\TestBackedEnum;
use Assert\Tests\Fixtures\TestUnitEnum;
use Yoast\PHPUnitPolyfills\TestCases\TestCase;

class AssertEnumTest extends TestCase
{
public static function setUpBeforeClass(): void
{
if (\PHP_VERSION_ID < 80100) {
static::markTestSkipped('Enum is not supported in this PHP version.');
}
}

public function testValidEnumExists()
{
$this->assertTrue(Assertion::enumExists(TestUnitEnum::class));
}

public function testInvalidEnumExists()
{
$this->expectException('Assert\AssertionFailedException');
$this->expectExceptionCode(\Assert\Assertion::INVALID_ENUM);
Assertion::enumExists('InvalidEnum');
}

public function testValidEnumCase()
{
$this->assertTrue(Assertion::enumCase('one', TestBackedEnum::class));
}

public function testEnumCaseInvalidValue()
{
$this->expectException('Assert\AssertionFailedException');
$this->expectExceptionCode(\Assert\Assertion::INVALID_ENUM_CASE);
Assertion::enumCase('three', TestBackedEnum::class);
}

public function testEnumCaseInvalidEnum()
{
$this->expectException('Assert\AssertionFailedException');
$this->expectExceptionCode(\Assert\Assertion::INVALID_ENUM);
Assertion::enumCase('three', 'InvalidEnum');
}

public function testEnumCaseNonBackedEnum()
{
$this->expectException('Assert\AssertionFailedException');
$this->expectExceptionCode(\Assert\Assertion::INTERFACE_NOT_IMPLEMENTED);
Assertion::enumCase('first', TestUnitEnum::class);
}
}
20 changes: 20 additions & 0 deletions tests/Assert/Tests/Fixtures/TestBackedEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* Assert
*
* LICENSE
*
* This source file is subject to the MIT license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to kontakt@beberlei.de so I can send you a copy immediately.
*/

namespace Assert\Tests\Fixtures;

enum TestBackedEnum: string {
case ONE = 'one';
case TWO = 'two';
}
20 changes: 20 additions & 0 deletions tests/Assert/Tests/Fixtures/TestUnitEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* Assert
*
* LICENSE
*
* This source file is subject to the MIT license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to kontakt@beberlei.de so I can send you a copy immediately.
*/

namespace Assert\Tests\Fixtures;

enum TestUnitEnum {
case FIRST;
case SECOND;
}