|
|
@@ -0,0 +1,367 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+declare(strict_types=1);
|
|
|
+
|
|
|
+namespace App\Tests\Unit\Service\State\Provider;
|
|
|
+
|
|
|
+use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
|
|
|
+use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
|
|
|
+use ApiPlatform\Metadata\Operation;
|
|
|
+use ApiPlatform\State\Pagination\Pagination;
|
|
|
+use ApiPlatform\State\Pagination\TraversablePaginator;
|
|
|
+use App\Service\State\Provider\ProviderUtils;
|
|
|
+use Doctrine\DBAL\Connection;
|
|
|
+use Doctrine\DBAL\Platforms\AbstractPlatform;
|
|
|
+use Doctrine\ORM\EntityManagerInterface;
|
|
|
+use Doctrine\ORM\EntityRepository;
|
|
|
+use Doctrine\ORM\Query;
|
|
|
+use Doctrine\ORM\QueryBuilder;
|
|
|
+use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator;
|
|
|
+use PHPUnit\Framework\MockObject\MockObject;
|
|
|
+use PHPUnit\Framework\TestCase;
|
|
|
+
|
|
|
+class ProviderUtilsTest extends TestCase
|
|
|
+{
|
|
|
+ private ProviderUtils $providerUtils;
|
|
|
+ private EntityManagerInterface|MockObject $entityManager;
|
|
|
+ private Pagination $pagination;
|
|
|
+ private QueryCollectionExtensionInterface|MockObject $extension1;
|
|
|
+ private QueryCollectionExtensionInterface|MockObject $extension2;
|
|
|
+
|
|
|
+ protected function setUp(): void
|
|
|
+ {
|
|
|
+ $this->entityManager = $this->createMock(EntityManagerInterface::class);
|
|
|
+ $this->pagination = new Pagination(['enabled' => true]);
|
|
|
+ $this->extension1 = $this->createMock(QueryCollectionExtensionInterface::class);
|
|
|
+ $this->extension2 = $this->createMock(QueryCollectionExtensionInterface::class);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function getProviderUtilsMockForMethod(string $methodName): ProviderUtils|MockObject
|
|
|
+ {
|
|
|
+ $providerUtils = $this
|
|
|
+ ->getMockBuilder(ProviderUtils::class)
|
|
|
+ ->setConstructorArgs([
|
|
|
+ $this->entityManager,
|
|
|
+ [$this->extension1, $this->extension2],
|
|
|
+ $this->pagination
|
|
|
+ ])
|
|
|
+ ->setMethodsExcept([$methodName])
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ return $providerUtils;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function testApplyCollectionExtensionsAndPagination(): void
|
|
|
+ {
|
|
|
+ $providerUtils = $this->getProviderUtilsMockForMethod('applyCollectionExtensionsAndPagination');
|
|
|
+
|
|
|
+ $entityClass = 'App\Entity\Test\TestEntity';
|
|
|
+ $operation = $this->createMock(Operation::class);
|
|
|
+ $context = ['filters' => ['name' => 'test']];
|
|
|
+
|
|
|
+ $platform = $this->createMock(AbstractPlatform::class);
|
|
|
+ $connection = $this->createMock(Connection::class);
|
|
|
+ $connection->expects(self::any())->method('getDatabasePlatform')->willReturn($platform);
|
|
|
+
|
|
|
+ $query = $this->createMock(Query::class);
|
|
|
+ $query->method('getEntityManager')->willReturn($this->entityManager);
|
|
|
+
|
|
|
+ $queryBuilder = $this->createMock(QueryBuilder::class);
|
|
|
+ $queryBuilder->method('getQuery')->willReturn($query);
|
|
|
+ $queryBuilder->method('getEntityManager')->willReturn($this->entityManager);
|
|
|
+
|
|
|
+ $repository = $this->createMock(EntityRepository::class);
|
|
|
+
|
|
|
+ $repository
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('createQueryBuilder')
|
|
|
+ ->with('o')
|
|
|
+ ->willReturn($queryBuilder);
|
|
|
+
|
|
|
+ $this->entityManager
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('getRepository')
|
|
|
+ ->with($entityClass)
|
|
|
+ ->willReturn($repository);
|
|
|
+
|
|
|
+ $this->entityManager
|
|
|
+ ->method('getConnection')
|
|
|
+ ->willReturn($connection);
|
|
|
+
|
|
|
+ // Mock extensions applying to the query
|
|
|
+ $this->extension1
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('applyToCollection')
|
|
|
+ ->with(
|
|
|
+ $queryBuilder,
|
|
|
+ self::isInstanceOf(QueryNameGenerator::class),
|
|
|
+ $entityClass,
|
|
|
+ $operation,
|
|
|
+ $context
|
|
|
+ );
|
|
|
+
|
|
|
+ $this->extension2
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('applyToCollection')
|
|
|
+ ->with(
|
|
|
+ $queryBuilder,
|
|
|
+ self::isInstanceOf(QueryNameGenerator::class),
|
|
|
+ $entityClass,
|
|
|
+ $operation,
|
|
|
+ $context
|
|
|
+ );
|
|
|
+
|
|
|
+ // Real pagination will be used
|
|
|
+
|
|
|
+ $result = $providerUtils->applyCollectionExtensionsAndPagination($entityClass, $operation, $context);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(TraversablePaginator::class, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function testApplyCollectionExtensionsAndPaginationWithoutContext(): void
|
|
|
+ {
|
|
|
+ $providerUtils = $this->getProviderUtilsMockForMethod('applyCollectionExtensionsAndPagination');
|
|
|
+
|
|
|
+ $entityClass = 'App\Entity\Test\TestEntity';
|
|
|
+ $operation = $this->createMock(Operation::class);
|
|
|
+
|
|
|
+ $platform = $this->createMock(AbstractPlatform::class);
|
|
|
+ $connection = $this->createMock(Connection::class);
|
|
|
+ $connection->expects(self::any())->method('getDatabasePlatform')->willReturn($platform);
|
|
|
+
|
|
|
+ $query = $this->createMock(Query::class);
|
|
|
+ $query->method('getEntityManager')->willReturn($this->entityManager);
|
|
|
+
|
|
|
+ $queryBuilder = $this->createMock(QueryBuilder::class);
|
|
|
+ $queryBuilder->method('getQuery')->willReturn($query);
|
|
|
+ $queryBuilder->method('getEntityManager')->willReturn($this->entityManager);
|
|
|
+
|
|
|
+ $repository = $this->createMock(EntityRepository::class);
|
|
|
+
|
|
|
+ $repository
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('createQueryBuilder')
|
|
|
+ ->with('o')
|
|
|
+ ->willReturn($queryBuilder);
|
|
|
+
|
|
|
+ $this->entityManager
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('getRepository')
|
|
|
+ ->with($entityClass)
|
|
|
+ ->willReturn($repository);
|
|
|
+
|
|
|
+ $this->entityManager
|
|
|
+ ->method('getConnection')
|
|
|
+ ->willReturn($connection);
|
|
|
+
|
|
|
+ // Extensions should still be applied with empty context
|
|
|
+ $this->extension1
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('applyToCollection')
|
|
|
+ ->with(
|
|
|
+ $queryBuilder,
|
|
|
+ self::isInstanceOf(QueryNameGenerator::class),
|
|
|
+ $entityClass,
|
|
|
+ $operation,
|
|
|
+ []
|
|
|
+ );
|
|
|
+
|
|
|
+ $this->extension2
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('applyToCollection')
|
|
|
+ ->with(
|
|
|
+ $queryBuilder,
|
|
|
+ self::isInstanceOf(QueryNameGenerator::class),
|
|
|
+ $entityClass,
|
|
|
+ $operation,
|
|
|
+ []
|
|
|
+ );
|
|
|
+
|
|
|
+ $result = $providerUtils->applyCollectionExtensionsAndPagination($entityClass, $operation);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(TraversablePaginator::class, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function testApplyCollectionExtensionsAndPaginationWithNonQueryCollectionExtension(): void
|
|
|
+ {
|
|
|
+ $entityClass = 'App\Entity\Test\TestEntity';
|
|
|
+ $operation = $this->createMock(Operation::class);
|
|
|
+
|
|
|
+ $nonQueryExtension = new class {};
|
|
|
+
|
|
|
+ $providerUtils = $this
|
|
|
+ ->getMockBuilder(ProviderUtils::class)
|
|
|
+ ->setConstructorArgs([
|
|
|
+ $this->entityManager,
|
|
|
+ [$this->extension1, $nonQueryExtension, $this->extension2],
|
|
|
+ $this->pagination
|
|
|
+ ])
|
|
|
+ ->setMethodsExcept(['applyCollectionExtensionsAndPagination'])
|
|
|
+ ->getMock();
|
|
|
+
|
|
|
+ $platform = $this->createMock(AbstractPlatform::class);
|
|
|
+ $connection = $this->createMock(Connection::class);
|
|
|
+ $connection->expects(self::any())->method('getDatabasePlatform')->willReturn($platform);
|
|
|
+
|
|
|
+ $query = $this->createMock(Query::class);
|
|
|
+ $query->method('getEntityManager')->willReturn($this->entityManager);
|
|
|
+
|
|
|
+ $queryBuilder = $this->createMock(QueryBuilder::class);
|
|
|
+ $queryBuilder->method('getQuery')->willReturn($query);
|
|
|
+ $queryBuilder->method('getEntityManager')->willReturn($this->entityManager);
|
|
|
+
|
|
|
+ $repository = $this->createMock(EntityRepository::class);
|
|
|
+
|
|
|
+ $repository
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('createQueryBuilder')
|
|
|
+ ->with('o')
|
|
|
+ ->willReturn($queryBuilder);
|
|
|
+
|
|
|
+ $this->entityManager
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('getRepository')
|
|
|
+ ->with($entityClass)
|
|
|
+ ->willReturn($repository);
|
|
|
+
|
|
|
+ $this->entityManager
|
|
|
+ ->method('getConnection')
|
|
|
+ ->willReturn($connection);
|
|
|
+
|
|
|
+ $this->extension1
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('applyToCollection');
|
|
|
+
|
|
|
+ $this->extension2
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('applyToCollection');
|
|
|
+
|
|
|
+ $result = $providerUtils->applyCollectionExtensionsAndPagination($entityClass, $operation);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(TraversablePaginator::class, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function testApplyCollectionExtensionsAndPaginationCreatesCorrectDoctrinePaginator(): void
|
|
|
+ {
|
|
|
+ $providerUtils = $this->getProviderUtilsMockForMethod('applyCollectionExtensionsAndPagination');
|
|
|
+
|
|
|
+ $entityClass = 'App\Entity\Test\TestEntity';
|
|
|
+ $operation = $this->createMock(Operation::class);
|
|
|
+
|
|
|
+ $platform = $this->createMock(AbstractPlatform::class);
|
|
|
+ $connection = $this->createMock(Connection::class);
|
|
|
+ $connection->expects(self::any())->method('getDatabasePlatform')->willReturn($platform);
|
|
|
+
|
|
|
+ $query = $this->createMock(Query::class);
|
|
|
+ $query->method('getEntityManager')->willReturn($this->entityManager);
|
|
|
+
|
|
|
+ $queryBuilder = $this->createMock(QueryBuilder::class);
|
|
|
+ $queryBuilder->method('getQuery')->willReturn($query);
|
|
|
+ $queryBuilder->method('getEntityManager')->willReturn($this->entityManager);
|
|
|
+
|
|
|
+ $repository = $this->createMock(EntityRepository::class);
|
|
|
+
|
|
|
+ $repository
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('createQueryBuilder')
|
|
|
+ ->with('o')
|
|
|
+ ->willReturn($queryBuilder);
|
|
|
+
|
|
|
+ $this->entityManager
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('getRepository')
|
|
|
+ ->with($entityClass)
|
|
|
+ ->willReturn($repository);
|
|
|
+
|
|
|
+ $this->entityManager
|
|
|
+ ->method('getConnection')
|
|
|
+ ->willReturn($connection);
|
|
|
+
|
|
|
+ $this->extension1
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('applyToCollection');
|
|
|
+
|
|
|
+ $this->extension2
|
|
|
+ ->expects(self::once())
|
|
|
+ ->method('applyToCollection');
|
|
|
+
|
|
|
+ // Real pagination will be used
|
|
|
+
|
|
|
+ $result = $providerUtils->applyCollectionExtensionsAndPagination($entityClass, $operation);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(TraversablePaginator::class, $result);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function testGetTraversablePaginator(): void
|
|
|
+ {
|
|
|
+ $providerUtils = $this->getProviderUtilsMockForMethod('getTraversablePaginator');
|
|
|
+
|
|
|
+ $mappedItems = ['item1', 'item2', 'item3'];
|
|
|
+ $originalPaginator = new TraversablePaginator(
|
|
|
+ new \ArrayIterator(['original1', 'original2']),
|
|
|
+ 2,
|
|
|
+ 10,
|
|
|
+ 50
|
|
|
+ );
|
|
|
+
|
|
|
+ $result = $providerUtils->getTraversablePaginator($mappedItems, $originalPaginator);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(TraversablePaginator::class, $result);
|
|
|
+ $this->assertEquals(2, $result->getCurrentPage());
|
|
|
+ $this->assertEquals(10, $result->getItemsPerPage());
|
|
|
+ $this->assertEquals(50, $result->getTotalItems());
|
|
|
+
|
|
|
+ // Test that the iterator contains the mapped items
|
|
|
+ $resultItems = iterator_to_array($result->getIterator());
|
|
|
+ $this->assertSame($mappedItems, $resultItems);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function testGetTraversablePaginatorWithEmptyMappedItems(): void
|
|
|
+ {
|
|
|
+ $providerUtils = $this->getProviderUtilsMockForMethod('getTraversablePaginator');
|
|
|
+
|
|
|
+ $mappedItems = [];
|
|
|
+ $originalPaginator = new TraversablePaginator(
|
|
|
+ new \ArrayIterator([]),
|
|
|
+ 1,
|
|
|
+ 30,
|
|
|
+ 0
|
|
|
+ );
|
|
|
+
|
|
|
+ $result = $providerUtils->getTraversablePaginator($mappedItems, $originalPaginator);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(TraversablePaginator::class, $result);
|
|
|
+ $this->assertEquals(1, $result->getCurrentPage());
|
|
|
+ $this->assertEquals(30, $result->getItemsPerPage());
|
|
|
+ $this->assertEquals(0, $result->getTotalItems());
|
|
|
+ $this->assertEmpty(iterator_to_array($result->getIterator()));
|
|
|
+ }
|
|
|
+
|
|
|
+ public function testGetTraversablePaginatorWithComplexMappedItems(): void
|
|
|
+ {
|
|
|
+ $providerUtils = $this->getProviderUtilsMockForMethod('getTraversablePaginator');
|
|
|
+
|
|
|
+ $mappedItems = [
|
|
|
+ ['id' => 1, 'name' => 'Item 1'],
|
|
|
+ ['id' => 2, 'name' => 'Item 2'],
|
|
|
+ (object)['id' => 3, 'name' => 'Item 3'],
|
|
|
+ ];
|
|
|
+ $originalPaginator = new TraversablePaginator(
|
|
|
+ new \ArrayIterator(['original1', 'original2', 'original3']),
|
|
|
+ 3,
|
|
|
+ 5,
|
|
|
+ 100
|
|
|
+ );
|
|
|
+
|
|
|
+ $result = $providerUtils->getTraversablePaginator($mappedItems, $originalPaginator);
|
|
|
+
|
|
|
+ $this->assertInstanceOf(TraversablePaginator::class, $result);
|
|
|
+ $this->assertEquals(3, $result->getCurrentPage());
|
|
|
+ $this->assertEquals(5, $result->getItemsPerPage());
|
|
|
+ $this->assertEquals(100, $result->getTotalItems());
|
|
|
+
|
|
|
+ $resultItems = iterator_to_array($result->getIterator());
|
|
|
+ $this->assertCount(3, $resultItems);
|
|
|
+ $this->assertSame($mappedItems, $resultItems);
|
|
|
+ }
|
|
|
+}
|