From eb40f1df49e926c8febb8c98996249e193c4c083 Mon Sep 17 00:00:00 2001 From: lubiana Date: Sun, 2 Jun 2024 11:20:20 +0200 Subject: [PATCH] add tests --- composer.json | 3 +- composer.lock | 136 ++++++++++++++++++++++++++- src/IdGenerator/UlidGenerator.php | 15 ++- src/Types/UlidType.php | 63 +++++++++---- tests/Feature/ExampleTest.php | 8 -- tests/Pest.php | 10 +- tests/TestCase.php | 8 +- tests/Unit/ExampleTest.php | 8 -- tests/Unit/UlidGeneratorTest.php | 33 +++++++ tests/Unit/UlidTypeTest.php | 148 ++++++++++++++++++++++++++++++ 10 files changed, 388 insertions(+), 44 deletions(-) delete mode 100644 tests/Feature/ExampleTest.php delete mode 100644 tests/Unit/ExampleTest.php create mode 100644 tests/Unit/UlidGeneratorTest.php create mode 100644 tests/Unit/UlidTypeTest.php diff --git a/composer.json b/composer.json index 7643747..c0b2a70 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,8 @@ "phpstan/phpstan": "^1.11", "phpstan/phpstan-strict-rules": "^1.6", "phpstan/extension-installer": "^1.3", - "pestphp/pest": "^2.34" + "pestphp/pest": "^2.34", + "mockery/mockery": "^1.6" }, "config": { "allow-plugins": { diff --git a/composer.lock b/composer.lock index bf1c465..86dbfc1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e57b9ac60bbc4cb4066c8cefa8d6d1c4", + "content-hash": "04633c6e5d6a281e4f5cf33a8246d0d3", "packages": [ { "name": "doctrine/collections", @@ -2097,6 +2097,57 @@ ], "time": "2023-11-03T12:00:00+00:00" }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, { "name": "jean85/pretty-package-versions", "version": "2.0.6", @@ -2185,6 +2236,89 @@ ], "time": "2024-05-04T18:26:43+00:00" }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.11.1", diff --git a/src/IdGenerator/UlidGenerator.php b/src/IdGenerator/UlidGenerator.php index 64a7d9f..1f12eca 100644 --- a/src/IdGenerator/UlidGenerator.php +++ b/src/IdGenerator/UlidGenerator.php @@ -4,18 +4,23 @@ namespace Lubiana\DoctrineUlid\IdGenerator; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Id\AbstractIdGenerator; +use Override; use Symfony\Component\Uid\Factory\UlidFactory; use Symfony\Component\Uid\Ulid; final class UlidGenerator extends AbstractIdGenerator { - public function __construct( private readonly ?UlidFactory $factory = null ){} - public function generateId(EntityManagerInterface $em, ?object $entity): mixed + public function __construct( + private readonly UlidFactory|null $factory = null + ) {} + + #[Override] + public function generateId(EntityManagerInterface $em, object|null $entity): mixed { - if ($this->factory !== null) { + if ($this->factory instanceof UlidFactory) { return $this->factory->create(); } - return new Ulid(); + return new Ulid; } -} \ No newline at end of file +} diff --git a/src/Types/UlidType.php b/src/Types/UlidType.php index dd04859..7c61351 100644 --- a/src/Types/UlidType.php +++ b/src/Types/UlidType.php @@ -6,17 +6,21 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Exception\InvalidType; use Doctrine\DBAL\Types\Exception\ValueNotConvertible; use Doctrine\DBAL\Types\Type; +use InvalidArgumentException; +use Override; use Symfony\Component\Uid\Ulid; +use function is_string; + final class UlidType extends Type { public const NAME = 'ulid'; - /** - * @inheritDoc - */ - public function getSQLDeclaration(array $column, AbstractPlatform $platform): string - { + #[Override] + public function getSQLDeclaration( + array $column, + AbstractPlatform $platform, + ): string { if ($this->hasNativeGuidType($platform)) { return $platform->getGuidTypeDeclarationSQL($column); } @@ -27,26 +31,35 @@ final class UlidType extends Type ]); } - public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Ulid - { + #[Override] + public function convertToPHPValue( + mixed $value, + AbstractPlatform $platform, + ): Ulid|null { if ($value instanceof Ulid || $value === null) { return $value; } - if (!is_string($value)) { + + if (! is_string($value)) { throw InvalidType::new($value, self::NAME, ['null', 'string', self::class]); } + try { return Ulid::fromString($value); - } catch (\InvalidArgumentException $e) { + } catch (InvalidArgumentException $e) { throw ValueNotConvertible::new($value, self::NAME, null, $e); } } - public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string - { - $toStringFunc = fn (Ulid $v) => $v->toRfc4122(); - if (!$this->hasNativeGuidType($platform)) { - $toStringFunc = fn (Ulid $v) => $v->toBinary(); + #[Override] + public function convertToDatabaseValue( + mixed $value, + AbstractPlatform $platform, + ): string|null { + $toStringFunc = static fn(Ulid $v): string => $v->toRfc4122(); + + if (! $this->hasNativeGuidType($platform)) { + $toStringFunc = static fn(Ulid $v): string => $v->toBinary(); } if ($value instanceof Ulid) { @@ -57,18 +70,32 @@ final class UlidType extends Type return null; } - if (!\is_string($value)) { + if (! is_string($value)) { throw InvalidType::new($value, self::NAME, ['null', 'string', self::class]); } + try { return $toStringFunc(Ulid::fromString($value)); - } catch (\InvalidArgumentException $e) { + } catch (InvalidArgumentException $e) { throw ValueNotConvertible::new($value, self::NAME, null, $e); } } private function hasNativeGuidType(AbstractPlatform $platform): bool { - return $platform->getGuidTypeDeclarationSQL([]) !== $platform->getStringTypeDeclarationSQL(['fixed' => true, 'length' => 36]); + return $platform->getGuidTypeDeclarationSQL([]) !== $platform->getStringTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 36, + ]); } -} \ No newline at end of file + + public static function register(): void + { + if (Type::hasType(self::NAME)) { + Type::overrideType(self::NAME, self::class); + return; + } + + Type::addType(self::NAME, self::class); + } +} diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php deleted file mode 100644 index ac5a185..0000000 --- a/tests/Feature/ExampleTest.php +++ /dev/null @@ -1,8 +0,0 @@ -toBeTrue(); - }, -); diff --git a/tests/Pest.php b/tests/Pest.php index 5160657..66d2c57 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,6 @@ in('Feature', 'Unit'); | global functions to help you to reduce the number of lines of code in your test files. | */ -function something(): void +/** + * @template T of object + * @param class-string $className + * @return MockInterface&T + */ +function createMock(string $className): MockInterface { - // .. + return Mockery::mock($className); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 6265b96..d28df0b 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,9 +2,15 @@ namespace Tests; +use Lubiana\DoctrineUlid\Types\UlidType; +use Override; use PHPUnit\Framework\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { - // + #[Override] + protected function setUp(): void + { + UlidType::register(); + } } diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php deleted file mode 100644 index ac5a185..0000000 --- a/tests/Unit/ExampleTest.php +++ /dev/null @@ -1,8 +0,0 @@ -toBeTrue(); - }, -); diff --git a/tests/Unit/UlidGeneratorTest.php b/tests/Unit/UlidGeneratorTest.php new file mode 100644 index 0000000..961c4ef --- /dev/null +++ b/tests/Unit/UlidGeneratorTest.php @@ -0,0 +1,33 @@ +generateId($em, null); + expect($id) + ->toBeInstanceOf(Ulid::class); + }, +); + +test( + 'UlidGenerator constructs with factory and generate Id', + function (): void { + $factory = Mockery::mock(UlidFactory::class); + $ulid = new Ulid; + $factory->shouldReceive('create') + ->andReturn($ulid) + ->once(); + $ulidGenerator = new UlidGenerator($factory); + $em = Mockery::mock(EntityManager::class); + $id = $ulidGenerator->generateId($em, null); + expect($id) + ->toBe($ulid); + }, +); diff --git a/tests/Unit/UlidTypeTest.php b/tests/Unit/UlidTypeTest.php new file mode 100644 index 0000000..ecf2abf --- /dev/null +++ b/tests/Unit/UlidTypeTest.php @@ -0,0 +1,148 @@ +getSQLDeclaration([], $platform); + expect($declaration) + ->toBe('UUID'); + }, +); + +it( + 'returns correct sql declaration on sqlite', + function (): void { + $ulidType = Type::getType(UlidType::NAME); + $platform = new SqlitePlatform; + + // If platform supports guid type, declaration is guid type. + $declaration = $ulidType->getSQLDeclaration([], $platform); + expect($declaration) + ->toBe('BLOB'); + }, +); + +it( + 'can convert string to a Ulid object', + function (): void { + $ulidType = Type::getType(UlidType::NAME); + $platform = new SQLitePlatform; + $stringUlid = '01FGRM9X8ACP5D6HZG9K31D6CZ'; + $phpValue = $ulidType->convertToPHPValue($stringUlid, $platform); + expect( + $phpValue, + )->toBeInstanceOf(Ulid::class)->and((string) $phpValue)->toBe($stringUlid); + }, +); + +it( + 'throws if value is not string during conversion to PHP Value', + function (): void { + $ulidType = Type::getType(UlidType::NAME); + $platform = new SQLitePlatform; + $nonStringValue = 123; + expect( + static fn(): mixed + => $ulidType->convertToPHPValue($nonStringValue, $platform), + )->toThrow( + InvalidType::class, + ); + }, +); + +it( + 'throws if value is not a valid Ulid string during conversion to PHP Value', + function (): void { + $ulidType = Type::getType(UlidType::NAME); + $platform = new SQLitePlatform; + $invalidUlidString = 'invalid-ulid-string'; + expect( + static fn(): mixed + => $ulidType->convertToPHPValue($invalidUlidString, $platform), + )->toThrow( + ValueNotConvertible::class, + ); + }, +); + +it( + 'can convert Ulid object to database value', + function (): void { + $ulidType = Type::getType(UlidType::NAME); + $platform = new SQLitePlatform; + $ulidObject = new Ulid; + $databaseValue = $ulidType->convertToDatabaseValue($ulidObject, $platform); + expect($databaseValue) + ->toBe($ulidObject->toBinary()); + }, +); + +it( + 'can convert Ulid string to a database value', + function (): void { + $ulidType = Type::getType(UlidType::NAME); + $platform = new SQLitePlatform; + $ulidObject = new Ulid; + $ulidBinary = $ulidObject->toBinary(); + $databaseValue = $ulidType->convertToDatabaseValue( + (string) $ulidObject, + $platform, + ); + expect($databaseValue) + ->toBe($ulidBinary); + }, +); + +it( + 'will return null when value is null', + function (): void { + $ulidType = Type::getType(UlidType::NAME); + $platform = new SQLitePlatform; + $nullValue = null; + $databaseValue = $ulidType->convertToDatabaseValue($nullValue, $platform); + expect($databaseValue) + ->toBe(null); + }, +); + +it( + 'throws if value is not string during conversion to Database Value', + function (): void { + $ulidType = Type::getType(UlidType::NAME); + $platform = new SQLitePlatform; + $nonStringValue = 123; + expect( + static fn(): mixed + => $ulidType->convertToDatabaseValue($nonStringValue, $platform), + )->toThrow( + InvalidType::class, + ); + }, +); + +it( + 'throws if value is not a valid Ulid string during conversion to Database Value', + function (): void { + $ulidType = Type::getType(UlidType::NAME); + $platform = new SQLitePlatform; + $invalidUlidString = 'invalid-ulid-string'; + expect( + static fn(): mixed + => $ulidType->convertToDatabaseValue($invalidUlidString, $platform), + )->toThrow( + ValueNotConvertible::class, + ); + }, +);