feat: add CurrencyValidator

fix: add CurrencyValidator

CurrencyValidator

zarad
This commit is contained in:
Ondrej Vlach 2024-01-17 13:50:50 +01:00
parent 64a07b232a
commit 1437ce4558
Signed by: ovlach
GPG Key ID: 4FF1A23B4914DE70
13 changed files with 2331 additions and 2 deletions

6
.env.test Normal file
View File

@ -0,0 +1,6 @@
# define your env variables for the test env here
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots

10
.gitignore vendored
View File

@ -20,3 +20,13 @@ phpstan.neon
/public/assets/ /public/assets/
/assets/vendor /assets/vendor
###< symfony/asset-mapper ### ###< symfony/asset-mapper ###
###> symfony/phpunit-bridge ###
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###
###> phpunit/phpunit ###
/phpunit.xml
.phpunit.result.cache
###< phpunit/phpunit ###

23
bin/phpunit Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env php
<?php
if (!ini_get('date.timezone')) {
ini_set('date.timezone', 'UTC');
}
if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) {
if (PHP_VERSION_ID >= 80000) {
require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit';
} else {
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
PHPUnit\TextUI\Command::main();
}
} else {
if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
exit(1);
}
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php';
}

View File

@ -87,6 +87,10 @@
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "^1.10", "phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.5",
"symfony/browser-kit": "7.0.*",
"symfony/css-selector": "7.0.*",
"symfony/phpunit-bridge": "^7.0",
"symfony/stopwatch": "7.0.*", "symfony/stopwatch": "7.0.*",
"symfony/web-profiler-bundle": "7.0.*" "symfony/web-profiler-bundle": "7.0.*"
} }

2084
composer.lock generated

File diff suppressed because it is too large Load Diff

38
phpunit.xml.dist Normal file
View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
convertDeprecationsToExceptions="false"
>
<php>
<ini name="display_errors" value="1" />
<ini name="error_reporting" value="-1" />
<server name="APP_ENV" value="test" force="true" />
<server name="SHELL_VERBOSITY" value="-1" />
<server name="SYMFONY_PHPUNIT_REMOVE" value="" />
<server name="SYMFONY_PHPUNIT_VERSION" value="9.6" />
</php>
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
</listeners>
<extensions>
</extensions>
</phpunit>

View File

@ -3,7 +3,7 @@ declare(strict_types=1);
namespace App\Entity\QRCode; namespace App\Entity\QRCode;
use App\Service\CurrencyListerInterface; use App\Validator\Currency;
use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Constraints as Assert;
/** /**
@ -19,6 +19,7 @@ class QRCodeMoney
// TODO: getCurrencies validation // TODO: getCurrencies validation
#[Assert\NotBlank(message: 'messages.fill_value')] #[Assert\NotBlank(message: 'messages.fill_value')]
#[Currency(message: 'messages.not_currency')]
private ?string $currency; private ?string $currency;
/** /**

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace App\Validator;
use Symfony\Component\Validator\Constraint;
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Currency extends Constraint
{
public const NOT_CURRENCY_ERROR = '548618e7-95ba-473d-9101-cabd45e49115';
protected const ERROR_NAMES = [
self::NOT_CURRENCY_ERROR => 'NOT_CURRENCY_ERROR',
];
public string $message = 'This value should be currency.';
/**
* @param mixed|null $options
* @param string|null $message
* @param array<string>|null $groups
* @param mixed|null $payload
*/
public function __construct(mixed $options = null, string $message = null, array $groups = null, mixed $payload = null)
{
parent::__construct($options ?? [], $groups, $payload);
$this->message = $message ?? $this->message;
}
}

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace App\Validator;
use App\Service\CurrencyListerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
class CurrencyValidator extends ConstraintValidator {
public function __construct(private readonly CurrencyListerInterface $currencyLister) {}
public function validate(mixed $value, Constraint $constraint): void
{
if (!$constraint instanceof Currency) {
throw new UnexpectedTypeException($constraint, Currency::class);
}
if (null === $value || '' === $value) {
return ;
}
if (is_string($value) === false) {
throw new UnexpectedValueException($value, "string");
}
$availCurrencies = iterator_to_array($this->currencyLister->getCurrencies());
if (in_array($value, $availCurrencies, true)) {
return ;
}
$this->context->buildViolation($constraint->message)
->setParameter('{{ string }}', $value)
->addViolation();
}
}

View File

@ -35,6 +35,20 @@
"phpstan.dist.neon" "phpstan.dist.neon"
] ]
}, },
"phpunit/phpunit": {
"version": "9.6",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "9.6",
"ref": "7364a21d87e658eb363c5020c072ecfdc12e2326"
},
"files": [
".env.test",
"phpunit.xml.dist",
"tests/bootstrap.php"
]
},
"symfony/asset-mapper": { "symfony/asset-mapper": {
"version": "7.0", "version": "7.0",
"recipe": { "recipe": {
@ -93,6 +107,21 @@
"src/Kernel.php" "src/Kernel.php"
] ]
}, },
"symfony/phpunit-bridge": {
"version": "7.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.3",
"ref": "1f5830c331065b6e4c9d5fa2105e322d29fcd573"
},
"files": [
".env.test",
"bin/phpunit",
"phpunit.xml.dist",
"tests/bootstrap.php"
]
},
"symfony/routing": { "symfony/routing": {
"version": "7.0", "version": "7.0",
"recipe": { "recipe": {

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace App\Tests\Validator;
use App\Service\CurrencyListerInterface;
use App\Validator\Currency;
use App\Validator\CurrencyValidator;
class CurrencyValidatorTest extends ValidatorTestCase
{
public function testSuccess(): void {
$currencyValidator = $this->createCurrencyValidator();
$currencyValidator->initialize($this->createExecutionContext(false));
$currencyValidator->validate('USD', new Currency(['message' => 'foo']));
}
public function testFailure(): void {
$currencyValidator = $this->createCurrencyValidator();
$currencyValidator->initialize($this->createExecutionContext(true));
$currencyValidator->validate('EUR', new Currency(['message' => 'foo']));
}
private function createCurrencyValidator(): CurrencyValidator {
$mock = $this->createMock(CurrencyListerInterface::class);
$mock->expects($this->once())
->method('getCurrencies')
->will($this->returnValue(['USD', 'CZK']));
return new CurrencyValidator($mock);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Tests\Validator;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
abstract class ValidatorTestCase extends TestCase
{
protected function createExecutionContext(bool $violation): ExecutionContextInterface {
$expected = $violation ? $this->once() : $this->never();
$context = $this->createMock(ExecutionContextInterface::class);
$context->expects($expected)
->method('buildViolation');
return $context;
}
}

15
tests/bootstrap.php Normal file
View File

@ -0,0 +1,15 @@
<?php
use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__).'/vendor/autoload.php';
if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) {
require dirname(__DIR__).'/config/bootstrap.php';
} elseif (method_exists(Dotenv::class, 'bootEnv')) {
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
}
if ($_SERVER['APP_DEBUG']) {
umask(0000);
}