From 2ebb4fef4c8bb4851a644b4f59e362bef2542668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 2 Jul 2023 23:59:06 +0200 Subject: [PATCH] Added some tests to constraint validators --- .../NoneOfItsChildrenValidator.php | 2 +- .../Constraints/SelectableValidator.php | 2 +- .../UniqueObjectCollectionValidator.php | 7 +- .../Constraints/ValidGoogleAuthCode.php | 12 ++ .../ValidGoogleAuthCodeValidator.php | 23 +-- .../NoneOfItsChildrenValidatorTest.php | 139 ++++++++++++++++ .../Constraints/SelectableValidatorTest.php | 71 ++++++++ .../UniqueObjectCollectionValidatorTest.php | 157 ++++++++++++++++++ .../Constraints/UrlOrBuiltinValidatorTest.php | 73 ++++++++ .../ValidGoogleAuthCodeValidatorTest.php | 143 ++++++++++++++++ .../Constraints/ValidThemeValidatorTest.php | 63 +++++++ .../DummyUniqueValidatableObject.php | 41 +++++ 12 files changed, 719 insertions(+), 14 deletions(-) create mode 100644 tests/Validator/Constraints/NoneOfItsChildrenValidatorTest.php create mode 100644 tests/Validator/Constraints/SelectableValidatorTest.php create mode 100644 tests/Validator/Constraints/UniqueObjectCollectionValidatorTest.php create mode 100644 tests/Validator/Constraints/UrlOrBuiltinValidatorTest.php create mode 100644 tests/Validator/Constraints/ValidGoogleAuthCodeValidatorTest.php create mode 100644 tests/Validator/Constraints/ValidThemeValidatorTest.php create mode 100644 tests/Validator/DummyUniqueValidatableObject.php diff --git a/src/Validator/Constraints/NoneOfItsChildrenValidator.php b/src/Validator/Constraints/NoneOfItsChildrenValidator.php index 7bc3fd5a..3846c2cc 100644 --- a/src/Validator/Constraints/NoneOfItsChildrenValidator.php +++ b/src/Validator/Constraints/NoneOfItsChildrenValidator.php @@ -63,7 +63,7 @@ class NoneOfItsChildrenValidator extends ConstraintValidator // Check if the targeted parent is the object itself: $entity_id = $entity->getID(); - if (null !== $entity_id && $entity_id === $value->getID()) { + if ($entity === $value || (null !== $entity_id && $entity_id === $value->getID())) { //Set the entity to a valid state $entity->setParent(null); $this->context->buildViolation($constraint->self_message)->addViolation(); diff --git a/src/Validator/Constraints/SelectableValidator.php b/src/Validator/Constraints/SelectableValidator.php index 8b93865d..8519d230 100644 --- a/src/Validator/Constraints/SelectableValidator.php +++ b/src/Validator/Constraints/SelectableValidator.php @@ -53,7 +53,7 @@ class SelectableValidator extends ConstraintValidator //Check type of value. Validating only works for StructuralDBElements if (!$value instanceof AbstractStructuralDBElement) { - throw new UnexpectedValueException($value, 'StructuralDBElement'); + throw new UnexpectedValueException($value, AbstractStructuralDBElement::class); } //Check if the value is not selectable -> show error message then. diff --git a/src/Validator/Constraints/UniqueObjectCollectionValidator.php b/src/Validator/Constraints/UniqueObjectCollectionValidator.php index 60486c2b..5522ca19 100644 --- a/src/Validator/Constraints/UniqueObjectCollectionValidator.php +++ b/src/Validator/Constraints/UniqueObjectCollectionValidator.php @@ -69,9 +69,12 @@ class UniqueObjectCollectionValidator extends ConstraintValidator $violation = $this->context->buildViolation($constraint->message); - $violation->atPath('[' . $key . ']' . '.' . $constraint->fields[0]); + //Use the first supplied field as the target field, or the first defined field name of the element if none is supplied + $target_field = $constraint->fields[0] ?? array_keys($element)[0]; - $violation->setParameter('{{ value }}', $this->formatValue($value)) + $violation->atPath('[' . $key . ']' . '.' . $target_field); + + $violation->setParameter('{{ object }}', $this->formatValue($object, ConstraintValidator::OBJECT_TO_STRING)) ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) ->addViolation(); diff --git a/src/Validator/Constraints/ValidGoogleAuthCode.php b/src/Validator/Constraints/ValidGoogleAuthCode.php index 0956b961..d555dcae 100644 --- a/src/Validator/Constraints/ValidGoogleAuthCode.php +++ b/src/Validator/Constraints/ValidGoogleAuthCode.php @@ -22,8 +22,20 @@ declare(strict_types=1); namespace App\Validator\Constraints; +use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface; use Symfony\Component\Validator\Constraint; class ValidGoogleAuthCode extends Constraint { + /** + * @param TwoFactorInterface|null $user The user to use for the validation process, if null, the current user is used + */ + public function __construct( + array $options = null, + string $message = null, + array $groups = null, + public ?TwoFactorInterface $user = null) + { + parent::__construct($options, $message, $groups); + } } diff --git a/src/Validator/Constraints/ValidGoogleAuthCodeValidator.php b/src/Validator/Constraints/ValidGoogleAuthCodeValidator.php index 276201f3..be935a71 100644 --- a/src/Validator/Constraints/ValidGoogleAuthCodeValidator.php +++ b/src/Validator/Constraints/ValidGoogleAuthCodeValidator.php @@ -23,8 +23,10 @@ declare(strict_types=1); namespace App\Validator\Constraints; use App\Entity\UserSystem\User; +use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface; use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticator; use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Form\FormInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -36,7 +38,7 @@ use function strlen; class ValidGoogleAuthCodeValidator extends ConstraintValidator { - public function __construct(protected GoogleAuthenticatorInterface $googleAuthenticator) + public function __construct(private GoogleAuthenticatorInterface $googleAuthenticator, private Security $security) { } @@ -56,23 +58,24 @@ class ValidGoogleAuthCodeValidator extends ConstraintValidator if (!ctype_digit($value)) { $this->context->addViolation('validator.google_code.only_digits_allowed'); + return; } //Number must have 6 digits if (6 !== strlen($value)) { $this->context->addViolation('validator.google_code.wrong_digit_count'); + return; } - //Try to retrieve the user we want to check - if ($this->context->getObject() instanceof FormInterface && - $this->context->getObject()->getParent() instanceof FormInterface - && $this->context->getObject()->getParent()->getData() instanceof User) { - $user = $this->context->getObject()->getParent()->getData(); + //Use the current user to check the code + $user = $constraint->user ?? $this->security->getUser(); + if (!$user instanceof TwoFactorInterface) { + throw new UnexpectedValueException($user, TwoFactorInterface::class); + } - //Check if the given code is valid - if (!$this->googleAuthenticator->checkCode($user, $value)) { - $this->context->addViolation('validator.google_code.wrong_code'); - } + //Check if the given code is valid + if (!$this->googleAuthenticator->checkCode($user, $value)) { + $this->context->addViolation('validator.google_code.wrong_code'); } } } diff --git a/tests/Validator/Constraints/NoneOfItsChildrenValidatorTest.php b/tests/Validator/Constraints/NoneOfItsChildrenValidatorTest.php new file mode 100644 index 00000000..39eb9336 --- /dev/null +++ b/tests/Validator/Constraints/NoneOfItsChildrenValidatorTest.php @@ -0,0 +1,139 @@ +. + */ + +namespace App\Tests\Validator\Constraints; + +use App\Entity\Attachments\AttachmentType; +use App\Entity\Base\AbstractStructuralDBElement; +use App\Validator\Constraints\NoneOfItsChildren; +use App\Validator\Constraints\NoneOfItsChildrenValidator; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class NoneOfItsChildrenValidatorTest extends ConstraintValidatorTestCase +{ + + protected AttachmentType $root_node; + protected AttachmentType $child1; + protected AttachmentType $child2; + protected AttachmentType $child3; + protected AttachmentType $child1_1; + protected AttachmentType $child1_2; + + protected function setUp(): void + { + // TODO: Change the autogenerated stub + + parent::setUp(); + + //Build a simple hierachy + $this->root_node = new AttachmentType(); + $this->root_node->setName('root')->setParent(null); + $this->child1 = new AttachmentType(); + $this->child1->setParent($this->root_node)->setName('child1'); + $this->child2 = new AttachmentType(); + $this->child2->setName('child2')->setParent($this->root_node); + $this->child3 = new AttachmentType(); + $this->child3->setName('child3')->setParent($this->root_node); + $this->child1_1 = new AttachmentType(); + $this->child1_1->setName('child1_1')->setParent($this->child1); + $this->child1_2 = new AttachmentType(); + $this->child1_2->setName('child1_2')->setParent($this->child1); + } + + private function getDummyObjects(): array + { + $obj1 = new AttachmentType(); + } + + protected function createValidator(): NoneOfItsChildrenValidator + { + return new NoneOfItsChildrenValidator(); + } + + public function testNullIsValid(): void + { + $this->setObject($this->child1); + $this->validator->validate(null, new NoneOfItsChildren()); + $this->assertNoViolation(); + } + + public function testWithUnrelatedObject(): void + { + $this->setObject($this->child1); + $this->validator->validate(new AttachmentType(), new NoneOfItsChildren()); + $this->assertNoViolation(); + } + + public function testWithParentObject(): void + { + $this->setObject($this->child1); + $this->validator->validate($this->root_node, new NoneOfItsChildren()); + $this->assertNoViolation(); + } + + public function testWithIntermediateChild(): void + { + $this->setObject($this->child1); + $this->validator->validate($this->child1_1, new NoneOfItsChildren()); + $this->buildViolation('validator.noneofitschild.children') + ->assertRaised(); + } + + public function testWithIndirectChild(): void + { + $this->setObject($this->root_node); + $this->validator->validate($this->child1_1, new NoneOfItsChildren()); + $this->buildViolation('validator.noneofitschild.children') + ->assertRaised(); + } + + public function testWithSelfInstance(): void + { + $this->setObject($this->root_node); + $this->validator->validate($this->root_node, new NoneOfItsChildren()); + $this->buildViolation('validator.noneofitschild.self') + ->assertRaised(); + } + + public function testWithSelfByID(): void + { + $obj1 = new class extends AbstractStructuralDBElement { + public function __construct() + { + $this->id = 1; + parent::__construct(); + } + }; + + $obj2 = new class extends AbstractStructuralDBElement { + public function __construct() + { + $this->id = 1; + parent::__construct(); + } + }; + + $this->setObject($obj1); + $this->validator->validate($obj2, new NoneOfItsChildren()); + $this->buildViolation('validator.noneofitschild.self') + ->assertRaised(); + + } +} diff --git a/tests/Validator/Constraints/SelectableValidatorTest.php b/tests/Validator/Constraints/SelectableValidatorTest.php new file mode 100644 index 00000000..bb6f7bcc --- /dev/null +++ b/tests/Validator/Constraints/SelectableValidatorTest.php @@ -0,0 +1,71 @@ +. + */ + +namespace App\Tests\Validator\Constraints; + +use App\Entity\Attachments\AttachmentType; +use App\Validator\Constraints\Selectable; +use App\Validator\Constraints\SelectableValidator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class SelectableValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): SelectableValidator + { + return new SelectableValidator(); + } + + public function testNullIsValid(): void + { + $this->validator->validate(null, new Selectable()); + $this->assertNoViolation(); + } + + public function testExpectAbstractStructuralElement(): void + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate('test', new Selectable()); + } + + public function testWithSelectableObj(): void + { + $selectable_obj = new AttachmentType(); + $selectable_obj->setNotSelectable(false); + + $this->validator->validate($selectable_obj, new Selectable()); + $this->assertNoViolation(); + } + + public function testWithNotSelectableObj(): void + { + $selectable_obj = new AttachmentType(); + $selectable_obj->setNotSelectable(true); + $selectable_obj->setName('Test'); + + $this->validator->validate($selectable_obj, new Selectable()); + $this->buildViolation('validator.isSelectable') + ->setParameter('{{ name }}', 'Test') + ->setParameter('{{ full_path }}', 'Test') + ->assertRaised(); + } + +} diff --git a/tests/Validator/Constraints/UniqueObjectCollectionValidatorTest.php b/tests/Validator/Constraints/UniqueObjectCollectionValidatorTest.php new file mode 100644 index 00000000..886e27e1 --- /dev/null +++ b/tests/Validator/Constraints/UniqueObjectCollectionValidatorTest.php @@ -0,0 +1,157 @@ +. + */ + +namespace App\Tests\Validator\Constraints; + +use App\Tests\Validator\DummyUniqueValidatableObject; +use App\Validator\Constraints\UniqueObjectCollection; +use App\Validator\Constraints\UniqueObjectCollectionValidator; +use Doctrine\Common\Collections\ArrayCollection; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Exception\UnexpectedValueException; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class UniqueObjectCollectionValidatorTest extends ConstraintValidatorTestCase +{ + protected function createValidator(): UniqueObjectCollectionValidator + { + return new UniqueObjectCollectionValidator(); + } + + public function testEmptyCollection(): void + { + $this->validator->validate(new ArrayCollection([]), new UniqueObjectCollection()); + $this->assertNoViolation(); + } + + public function testUnqiueCollectionDefaultSettings(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 2, 'b' => 1]) + ]), + new UniqueObjectCollection()); + + $this->assertNoViolation(); + } + + public function testUnqiueCollectionSpecifiedFields(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 2, 'b' => 1]) + ]), + new UniqueObjectCollection(fields: ['a'])); + + $this->assertNoViolation(); + } + + public function testExpectsIterableElement(): void + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate('string', new UniqueObjectCollection()); + } + + public function testExpectsUniqueValidatableObject(): void + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate(new ArrayCollection([new \stdClass()]), new UniqueObjectCollection()); + } + + public function testNonUniqueCollectionDefaultSettings(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]) + ]), + new UniqueObjectCollection()); + + $this + ->buildViolation('This collection should contain only unique elements.') + ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) + ->setParameter('{{ object }}', 'objectString') + ->atPath('property.path[1].a') + ->assertRaised(); + } + + public function testNonUniqueCollectionSpecifyFields(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]) + ]), + new UniqueObjectCollection(fields: ['b'])); + + $this + ->buildViolation('This collection should contain only unique elements.') + ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) + ->setParameter('{{ object }}', 'objectString') + ->atPath('property.path[1].b') + ->assertRaised(); + } + + public function testNonUniqueCollectionFirstFieldIsTarget(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]), + new DummyUniqueValidatableObject(['a' => 1, 'b' => 1]) + ]), + new UniqueObjectCollection(fields: ['b', 'a'])); + + $this + ->buildViolation('This collection should contain only unique elements.') + ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) + ->setParameter('{{ object }}', 'objectString') + ->atPath('property.path[1].b') + ->assertRaised(); + } + + public function testNonUniqueCollectionAllowNull(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => null]), + new DummyUniqueValidatableObject(['a' => 2, 'b' => 2]), + new DummyUniqueValidatableObject(['a' => 3, 'b' => null]) + ]), + new UniqueObjectCollection(fields: ['b'], allowNull: true)); + + $this->assertNoViolation(); + } + + public function testNonUniqueCollectionDoNotAllowNull(): void + { + $this->validator->validate(new ArrayCollection([ + new DummyUniqueValidatableObject(['a' => 1, 'b' => null]), + new DummyUniqueValidatableObject(['a' => 2, 'b' => 2]), + new DummyUniqueValidatableObject(['a' => 3, 'b' => null]) + ]), + new UniqueObjectCollection(fields: ['b'], allowNull: false)); + + $this + ->buildViolation('This collection should contain only unique elements.') + ->setCode(UniqueObjectCollection::IS_NOT_UNIQUE) + ->setParameter('{{ object }}', 'objectString') + ->atPath('property.path[2].b') + ->assertRaised(); + } + + + +} diff --git a/tests/Validator/Constraints/UrlOrBuiltinValidatorTest.php b/tests/Validator/Constraints/UrlOrBuiltinValidatorTest.php new file mode 100644 index 00000000..de7e47d5 --- /dev/null +++ b/tests/Validator/Constraints/UrlOrBuiltinValidatorTest.php @@ -0,0 +1,73 @@ +. + */ + +namespace App\Tests\Validator\Constraints; + +use App\Validator\Constraints\UrlOrBuiltin; +use App\Validator\Constraints\UrlOrBuiltinValidator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class UrlOrBuiltinValidatorTest extends ConstraintValidatorTestCase +{ + + protected function createValidator(): UrlOrBuiltinValidator + { + return new UrlOrBuiltinValidator(); + } + + public function testNullIsValid(): void + { + $this->validator->validate(null, new UrlOrBuiltin()); + $this->assertNoViolation(); + } + + public function testEmptyStringIsValid(): void + { + $this->validator->validate('', new UrlOrBuiltin()); + $this->assertNoViolation(); + } + + public function testValidUrlIsValid(): void + { + $this->validator->validate('https://example.com', new UrlOrBuiltin()); + $this->assertNoViolation(); + } + + public function testValidBuiltinIsValid(): void + { + $this->validator->validate('%FOOTPRINTS%/test/footprint.png', new UrlOrBuiltin()); + $this->assertNoViolation(); + } + + public function testInvalidUrlIsInvalid(): void + { + $constraint = new UrlOrBuiltin([ + 'message' => 'myMessage', + ]); + + $this->validator->validate('invalid-url', $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"invalid-url"') + ->setCode(UrlOrBuiltin::INVALID_URL_ERROR) + ->assertRaised(); + } +} diff --git a/tests/Validator/Constraints/ValidGoogleAuthCodeValidatorTest.php b/tests/Validator/Constraints/ValidGoogleAuthCodeValidatorTest.php new file mode 100644 index 00000000..6b3887e4 --- /dev/null +++ b/tests/Validator/Constraints/ValidGoogleAuthCodeValidatorTest.php @@ -0,0 +1,143 @@ +. + */ + +namespace App\Tests\Validator\Constraints; + +use App\Entity\UserSystem\User; +use App\Validator\Constraints\ValidGoogleAuthCode; +use App\Validator\Constraints\ValidGoogleAuthCodeValidator; +use PHPUnit\Framework\TestCase; +use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface; +use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class ValidGoogleAuthCodeValidatorTest extends ConstraintValidatorTestCase +{ + + protected function createValidator() + { + $googleAuth = new class implements GoogleAuthenticatorInterface + { + + public function checkCode(TwoFactorInterface $user, string $code): bool + { + if ($code === '123456') { + return true; + } + return false; + } + + public function getQRContent(TwoFactorInterface $user): string + { + return 'not_needed'; + } + + public function generateSecret(): string + { + return 'not_needed'; + } + }; + + $security = new class extends Security { + public function __construct() + { + //Leave empty + } + public function getUser(): ?\Symfony\Component\Security\Core\User\UserInterface + { + return new class implements TwoFactorInterface, UserInterface { + + public function isGoogleAuthenticatorEnabled(): bool + { + return true; + } + + public function getGoogleAuthenticatorUsername(): string + { + return "test"; + } + + public function getGoogleAuthenticatorSecret(): ?string + { + return "not_needed"; + } + + public function getRoles(): array + { + return []; + } + + public function eraseCredentials() + { + } + + public function getUserIdentifier(): string + { + return 'test'; + } + }; + } + }; + + + return new ValidGoogleAuthCodeValidator($googleAuth, $security); + } + + public function testAllowNull(): void + { + $this->validator->validate(null, new ValidGoogleAuthCode()); + $this->assertNoViolation(); + } + + public function testAllowEmpty(): void + { + $this->validator->validate('', new ValidGoogleAuthCode()); + $this->assertNoViolation(); + } + + public function testValidCode(): void + { + $this->validator->validate('123456', new ValidGoogleAuthCode()); + $this->assertNoViolation(); + } + + public function testInvalidCode(): void + { + $this->validator->validate('111111', new ValidGoogleAuthCode()); + $this->buildViolation('validator.google_code.wrong_code') + ->assertRaised(); + } + + public function testCheckNumerical(): void + { + $this->validator->validate('123456a', new ValidGoogleAuthCode()); + $this->buildViolation('validator.google_code.only_digits_allowed') + ->assertRaised(); + } + + public function testCheckLength(): void + { + $this->validator->validate('12345', new ValidGoogleAuthCode()); + $this->buildViolation('validator.google_code.wrong_digit_count') + ->assertRaised(); + } +} diff --git a/tests/Validator/Constraints/ValidThemeValidatorTest.php b/tests/Validator/Constraints/ValidThemeValidatorTest.php new file mode 100644 index 00000000..8ad95e7a --- /dev/null +++ b/tests/Validator/Constraints/ValidThemeValidatorTest.php @@ -0,0 +1,63 @@ +. + */ + +namespace App\Tests\Validator\Constraints; + +use App\Validator\Constraints\ValidTheme; +use App\Validator\Constraints\ValidThemeValidator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + +class ValidThemeValidatorTest extends ConstraintValidatorTestCase +{ + + protected function createValidator(): ValidThemeValidator + { + return new ValidThemeValidator(['bootstrap', 'theme1', 'theme2']); + } + + public function testAllowNull(): void + { + $this->validator->validate(null, new ValidTheme()); + $this->assertNoViolation(); + } + + public function testAllowEmpty(): void + { + $this->validator->validate('', new ValidTheme()); + $this->assertNoViolation(); + } + + public function testValidTheme(): void + { + $this->validator->validate('bootstrap', new ValidTheme()); + $this->assertNoViolation(); + } + + public function testInvalidTheme(): void + { + $this->validator->validate('invalid', new ValidTheme()); + $this->buildViolation('validator.selected_theme_is_invalid') + ->setParameter('{{ value }}', 'invalid') + ->assertRaised(); + } + + +} diff --git a/tests/Validator/DummyUniqueValidatableObject.php b/tests/Validator/DummyUniqueValidatableObject.php new file mode 100644 index 00000000..7ca2565d --- /dev/null +++ b/tests/Validator/DummyUniqueValidatableObject.php @@ -0,0 +1,41 @@ +. + */ + +namespace App\Tests\Validator; + +use App\Validator\UniqueValidatableInterface; + +class DummyUniqueValidatableObject implements UniqueValidatableInterface, \Stringable +{ + + public function __construct(public readonly array $array = []) + { + } + + public function getComparableFields(): array + { + return $this->array; + } + + public function __toString(): string + { + return 'objectString'; + } +} \ No newline at end of file