diff --git a/.gitignore b/.gitignore index 87d072d..dbf3178 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /.idea /vendor -/composer.lock \ No newline at end of file +/composer.lock +/var +/.phpunit.result.cache \ No newline at end of file diff --git a/composer.json b/composer.json index 2480fbd..0e58d4a 100644 --- a/composer.json +++ b/composer.json @@ -14,12 +14,16 @@ "symfony/property-access": "^6.4 || ^7.0", "symfony/string": "^6.4 || ^7.0", "symfony/security-bundle": "^6.4 || ^7.0", - "symfony/doctrine-bridge": "^7.2", - "symfony/uid": "^6.4 || ^7.0" + "symfony/doctrine-bridge": "^6.4 || ^7.0", + "symfony/uid": "^6.4 || ^7.0", + "doctrine/doctrine-bundle": "^2.14" }, "require-dev": { "phpstan/phpstan": "*", - "symfony/phpunit-bridge": "^6.4 || ^7.0" + "symfony/phpunit-bridge": "^6.4 || ^7.0", + "symfony/test-pack": "^1.1", + "symfony/framework-bundle": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0" }, "license": "MIT", "autoload": { @@ -27,6 +31,11 @@ "Rami\\EntityKitBundle\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "Rami\\EntityKitBundle\\Tests\\": "tests/" + } + }, "authors": [ { "name": "Abdellah Ramadan", diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..9711d38 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,16 @@ + + + + + + + + + + ./tests/ + + + \ No newline at end of file diff --git a/src/EventListener/Authored/AuthoredListener.php b/src/EventListener/Authored/AuthoredListener.php index ef0456f..1073dcf 100644 --- a/src/EventListener/Authored/AuthoredListener.php +++ b/src/EventListener/Authored/AuthoredListener.php @@ -17,10 +17,10 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\User\UserInterface; -readonly class AuthoredListener +class AuthoredListener { public function __construct( - private TokenStorageInterface $tokenStorage, + private ?TokenStorageInterface $tokenStorage = null, ) {} public function prePersist(PrePersistEventArgs $args): void { @@ -54,6 +54,10 @@ public function preUpdate(PreUpdateEventArgs $args): void private function getCurrentUserIdentifier(): string|null { + if (null === $this->tokenStorage) { + return null; + } + $user = $this->tokenStorage->getToken()->getUser(); if (!$user instanceof UserInterface) { diff --git a/src/EventListener/Uuid/UuidListener.php b/src/EventListener/Uuid/UuidListener.php index 2692356..1dc368c 100644 --- a/src/EventListener/Uuid/UuidListener.php +++ b/src/EventListener/Uuid/UuidListener.php @@ -14,7 +14,6 @@ use Doctrine\ORM\Event\PrePersistEventArgs; use Rami\EntityKitBundle\Common\Interfaces\Uuid\UuidInterface; use Symfony\Component\Uid\Uuid; -use Symfony\Component\Uid\UuidV4; class UuidListener { diff --git a/tests/Functional/TimeStamped/TimeStampedEntityTest.php b/tests/Functional/TimeStamped/TimeStampedEntityTest.php new file mode 100644 index 0000000..ccd3b09 --- /dev/null +++ b/tests/Functional/TimeStamped/TimeStampedEntityTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, + * please view the LICENSE file that was distributed with this source code. + */ + +namespace Rami\EntityKitBundle\Tests\Functional\TimeStamped; + +use Rami\EntityKitBundle\Tests\Util\App\BaseKernel; +use Rami\EntityKitBundle\Tests\Util\Entity\TimeStamped\Article; +use Symfony\Component\Filesystem\Filesystem; + +class TimeStampedEntityTest extends BaseKernel +{ + public function setUp(): void + { + parent::setUp(); + } + + public function testTimeStampedEntity(): void + { + $blog = new Article(); + + $blog->setTitle('Blog title'); + $blog->setContent('Blog content'); + + $this->entityManager->persist($blog); + $this->entityManager->flush(); + + $this->assertNotNull($blog->getId()); + $this->assertEquals('Blog title', $blog->getTitle()); + $this->assertEquals('Blog content', $blog->getContent()); + + $this->assertNotNull($blog->getCreatedAt()); + $this->assertNotNull($blog->getUpdatedAt()); + + $this->assertInstanceOf(\DateTimeImmutable::class, $blog->getCreatedAt()); + $this->assertInstanceOf(\DateTimeImmutable::class, $blog->getUpdatedAt()); + + $filesystem = new Filesystem(); + $filesystem->remove('var/database.db3'); + } +} \ No newline at end of file diff --git a/tests/Functional/Uuid/UuidEntityTest.php b/tests/Functional/Uuid/UuidEntityTest.php new file mode 100644 index 0000000..2308d3e --- /dev/null +++ b/tests/Functional/Uuid/UuidEntityTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, + * please view the LICENSE file that was distributed with this source code. + */ + +namespace Rami\EntityKitBundle\Tests\Functional\Uuid; + +use Rami\EntityKitBundle\Tests\Util\App\BaseKernel; +use Rami\EntityKitBundle\Tests\Util\Entity\Uuid\User; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Uid\Uuid; + +class UuidEntityTest extends BaseKernel +{ + + public function setUp(): void + { + parent::setUp(); + } + + public function testUuid() : void + { + $user = new User(); + + $this->entityManager->persist($user); + $this->entityManager->flush(); + + $this->assertNotNull($user->getId()); + $this->assertNotNull($user->getUuid()); + + $this->assertInstanceOf(User::class, $user); + $this->assertInstanceOf(Uuid::class, $user->getUuid()); + + $filesystem = new Filesystem(); + $filesystem->remove('var/database.db3'); + } + +} \ No newline at end of file diff --git a/tests/Unit/UnitTest.php b/tests/Unit/UnitTest.php new file mode 100644 index 0000000..2057a2b --- /dev/null +++ b/tests/Unit/UnitTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, + * please view the LICENSE file that was distributed with this source code. + */ + +namespace Rami\EntityKitBundle\Tests\Unit; + +use PHPUnit\Framework\TestCase; + +class UnitTest extends TestCase +{ + public function testSomething() + { + $this->assertEquals('PersistenceBundleTest', 'PersistenceBundleTest'); + } +} \ No newline at end of file diff --git a/tests/Util/App/BaseKernel.php b/tests/Util/App/BaseKernel.php new file mode 100644 index 0000000..369e137 --- /dev/null +++ b/tests/Util/App/BaseKernel.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, + * please view the LICENSE file that was distributed with this source code. + */ + +namespace Rami\EntityKitBundle\Tests\Util\App; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Tools\SchemaTool; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\DependencyInjection\Container; + +class BaseKernel extends KernelTestCase +{ + protected Container $container; + protected EntityManager $entityManager; + + public function setUp(): void + { + self::bootKernel(); + $this->container = static::getContainer(); + $doctrine = $this->container->get('doctrine'); + $this->entityManager = $doctrine->getManager(); + $metaData = $this->entityManager->getMetadataFactory()->getAllMetadata(); + $schemaTool = new SchemaTool($this->entityManager); + $schemaTool->updateSchema($metaData); + } +} \ No newline at end of file diff --git a/tests/Util/App/Kernel.php b/tests/Util/App/Kernel.php new file mode 100644 index 0000000..ae765b8 --- /dev/null +++ b/tests/Util/App/Kernel.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, + * please view the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Rami\EntityKitBundle\Tests\Util\App; + +use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; +use Rami\EntityKitBundle\EntityKitBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\SecurityBundle\SecurityBundle; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\HttpKernel\Kernel as HttpKernel; + +class Kernel extends HttpKernel +{ + + public function registerBundles(): iterable + { + return [ + new EntityKitBundle(), + new FrameworkBundle(), + new DoctrineBundle(), + new SecurityBundle(), + ]; + } + + public function registerContainerConfiguration(LoaderInterface $loader): void + { + $loader->load(__DIR__ . '/config/config.yaml'); + } + + public function getCacheDir(): string + { + return 'var/cache'; + } + + public function getLogDir(): string + { + return 'var/logs'; + } + + public function getProjectDir(): string + { + return __DIR__.'/../'; + } +} diff --git a/tests/Util/App/config/config.yaml b/tests/Util/App/config/config.yaml new file mode 100644 index 0000000..572d51c --- /dev/null +++ b/tests/Util/App/config/config.yaml @@ -0,0 +1,26 @@ +framework: + secret: "test" + test: ~ + +doctrine: + dbal: + driver: "pdo_sqlite" + path: "%kernel.cache_dir%/../database.db3" + + orm: + mappings: + Test: + dir: '%kernel.project_dir%/' + prefix: 'Rami\EntityKitBundle\Tests\Util\Entity' + +security: + providers: + test_users: + memory: null + + firewalls: + test: + pattern: ^/ + security: false + + access_control: [] \ No newline at end of file diff --git a/tests/Util/Entity/Authored/Blog.php b/tests/Util/Entity/Authored/Blog.php new file mode 100644 index 0000000..48e5020 --- /dev/null +++ b/tests/Util/Entity/Authored/Blog.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, + * please view the LICENSE file that was distributed with this source code. + */ + +namespace Rami\EntityKitBundle\Tests\Util\Entity\Authored; + +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Mapping as ORM; +use Rami\EntityKitBundle\Common\Interfaces\Authored\AuthoredInterface; +use Rami\EntityKitBundle\Entity\Traits\AuthoredTrait; + +#[ORM\Entity] +class Blog implements AuthoredInterface +{ + use AuthoredTrait; + + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] + public ?int $id = null; + + #[ORM\Column(type: Types::STRING, length: 255)] + public ?string $title = null; + #[ORM\Column(type: Types::TEXT)] + public ?string $content = null; + + /** + * @return int|null + */ + public function getId(): ?int + { + return $this->id; + } + + /** + * @return string|null + */ + public function getTitle(): ?string + { + return $this->title; + } + + /** + * @param string|null $title + * @return Blog + */ + public function setTitle(?string $title): Blog + { + $this->title = $title; + return $this; + } + + /** + * @return string|null + */ + public function getContent(): ?string + { + return $this->content; + } + + /** + * @param string|null $content + * @return Blog + */ + public function setContent(?string $content): Blog + { + $this->content = $content; + return $this; + } +} \ No newline at end of file diff --git a/tests/Util/Entity/TimeStamped/Article.php b/tests/Util/Entity/TimeStamped/Article.php new file mode 100644 index 0000000..7eb2a18 --- /dev/null +++ b/tests/Util/Entity/TimeStamped/Article.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, + * please view the LICENSE file that was distributed with this source code. + */ + +namespace Rami\EntityKitBundle\Tests\Util\Entity\TimeStamped; + +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Mapping as ORM; +use Rami\EntityKitBundle\Common\Interfaces\Authored\AuthoredInterface; +use Rami\EntityKitBundle\Common\Interfaces\TimeStamped\TimeStampedInterface; +use Rami\EntityKitBundle\Entity\Traits\AuthoredTrait; +use Rami\EntityKitBundle\Entity\Traits\MappedTimeStampedTrait; +use Rami\EntityKitBundle\Entity\Traits\TimeStampedTrait; + +#[ORM\Entity] +class Article implements TimeStampedInterface +{ + use MappedTimeStampedTrait; + + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] + public ?int $id = null; + + #[ORM\Column(type: Types::STRING, length: 255)] + public ?string $title = null; + #[ORM\Column(type: Types::TEXT)] + public ?string $content = null; + + /** + * @return int|null + */ + public function getId(): ?int + { + return $this->id; + } + + /** + * @return string|null + */ + public function getTitle(): ?string + { + return $this->title; + } + + /** + * @param string|null $title + * @return Article + */ + public function setTitle(?string $title): Article + { + $this->title = $title; + return $this; + } + + /** + * @return string|null + */ + public function getContent(): ?string + { + return $this->content; + } + + /** + * @param string|null $content + * @return Article + */ + public function setContent(?string $content): Article + { + $this->content = $content; + return $this; + } +} \ No newline at end of file diff --git a/tests/Util/Entity/Uuid/User.php b/tests/Util/Entity/Uuid/User.php new file mode 100644 index 0000000..82ae692 --- /dev/null +++ b/tests/Util/Entity/Uuid/User.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, + * please view the LICENSE file that was distributed with this source code. + */ + +namespace Rami\EntityKitBundle\Tests\Util\Entity\Uuid; + +use Doctrine\ORM\Mapping as ORM; +use Rami\EntityKitBundle\Common\Interfaces\Uuid\UuidInterface; +use Rami\EntityKitBundle\Entity\Traits\UuidTrait; +use Symfony\Component\Security\Core\User\UserInterface; + +#[ORM\Entity()] +#[ORM\Table(name: 'user')] +class User implements UuidInterface, UserInterface +{ + use UuidTrait; + + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: 'integer')] + public ?int $id = null; + + /** + * @return int|null + */ + public function getId(): ?int + { + return $this->id; + } + + public function getRoles(): array + { + return ['ROLE_USER']; + } + + public function eraseCredentials(): void + { + // TODO: Implement eraseCredentials() method. + } + + public function getUserIdentifier(): string + { + return (string) $this->id; + } +} \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..e69de29