فهرست منبع

add unit tests for AbstractTimeFilter

Olivier Massot 2 سال پیش
والد
کامیت
31c12038d3

+ 3 - 8
src/Filter/Doctrine/TimeConstraint/AbstractTimeFilter.php

@@ -7,7 +7,6 @@ use App\Service\Constraint\AbstractTimeConstraintUtils;
 use App\Service\Constraint\ActivityYearConstraint;
 use App\Service\Constraint\DateTimeConstraint;
 use App\Service\Constraint\TimeConstraintInterface;
-use Doctrine\ORM\EntityManagerInterface;
 use Doctrine\ORM\Query\Filter\SQLFilter;
 use Symfony\Bundle\SecurityBundle\Security;
 use Symfony\Component\HttpFoundation\RequestStack;
@@ -42,13 +41,6 @@ abstract class AbstractTimeFilter extends SQLFilter
      */
     protected static ?string $annotationEndField = null;
 
-    // <--- Dependency injections
-    protected EntityManagerInterface $entityManager;
-
-    #[Required]
-    public function setEntityManagerInterface(EntityManagerInterface $entityManager): void { $this->entityManager = $entityManager;}
-    // --->
-
     public function setAccessId(int $accessId): void {
         $this->accessId = $accessId;
     }
@@ -98,6 +90,9 @@ abstract class AbstractTimeFilter extends SQLFilter
         if (static::$constraintAnnotation === null) {
             throw new \RuntimeException('Constraint annotation has not been set');
         }
+        if ($this->accessId === null) {
+            throw new \RuntimeException('AccessId has not been set');
+        }
 
         $constraintAnnotation = $targetEntity->getReflectionClass()->getAttributes(static::$constraintAnnotation)[0] ?? null;
         if (!$constraintAnnotation) {

+ 415 - 0
tests/Unit/Filter/Doctrine/TimeConstraint/AbstractTimeFilterTest.php

@@ -0,0 +1,415 @@
+<?php
+namespace App\Tests\Unit\Filter\TimeConstraint;
+
+use App\Attribute\DateTimeConstraintAware;
+use App\Filter\Doctrine\TimeConstraint\AbstractTimeFilter;
+use App\Filter\Doctrine\TimeConstraint\DatetimeFilter;
+use App\Service\Constraint\DateTimeConstraint;
+use App\Service\Constraint\TimeConstraintInterface;
+use App\Tests\Unit\TestToolsTrait;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\Mapping\ClassMetadata;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+
+class TestableAbstractTimeFilter extends AbstractTimeFilter {
+    protected static ?string $annotationStartField = 'startFieldName';
+    protected static ?string $annotationEndField = 'endFieldName';
+    public function getAccessId() { return $this->accessId; }
+    public function getTimeConstraint() { return $this->timeConstraint; }
+    public function getStartAndEndFields(array $arguments): array { return parent::getStartAndEndFields($arguments); }
+    public function getConstraints(int $accessId): array { return parent::getConstraints($accessId); }
+    public function constructQuery(array $constraints, string $targetTableAlias, array $fields): string { return parent::constructQuery($constraints, $targetTableAlias, $fields); }
+    public function getArithmeticValue(int $condition): ?string { return parent::getArithmeticValue($condition); }
+    public static function setConstraintAnnotation(?string $constraintAnnotation) { static::$constraintAnnotation = $constraintAnnotation; }
+    public static function setAnnotationStartField(?string $startField) { static::$annotationStartField = $startField; }
+    public static function setAnnotationEndField(?string $endField) { static::$annotationEndField = $endField; }
+}
+
+#[\Attribute(\Attribute::TARGET_CLASS)]
+class TestableConstraintAware
+{
+    public string $startDateFieldName;
+    public string $endDateFieldName;
+
+    public function __construct(
+        string $startDateFieldName,
+        string $endDateFieldName
+    ){
+        $this->startDateFieldName = $startDateFieldName;
+        $this->endDateFieldName = $endDateFieldName;
+    }
+
+    public function getArguments() {
+        return null;
+    }
+}
+
+class AbstractTimeFilterTest extends TestCase
+{
+    private MockObject | EntityManagerInterface $em;
+
+    public function setUp(): void
+    {
+        $this->em = $this->getMockBuilder(EntityManagerInterface::class)->disableOriginalConstructor()->getMock();
+
+        // reset static values
+        TestableAbstractTimeFilter::setConstraintAnnotation(null);
+        TestableAbstractTimeFilter::setAnnotationStartField('startFieldName');
+        TestableAbstractTimeFilter::setAnnotationEndField('endFieldName');
+    }
+
+
+    private function getAbstractTimeFilterMockFor(array $methodNames): TestableAbstractTimeFilter | MockObject {
+
+        return $this
+            ->getMockBuilder(TestableAbstractTimeFilter::class)
+            ->setConstructorArgs([$this->em])
+            ->setMethodsExcept(array_merge(['setEntityManager'], $methodNames))
+            ->getMock();
+    }
+
+    public function testSetAccessId(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['setAccessId', 'getAccessId']);
+        $filter->setAccessId(123);
+
+        $this->assertEquals(123, $filter->getAccessId());
+    }
+
+    public function testSetTimeConstraint(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['setTimeConstraint', 'getTimeConstraint']);
+
+        $timeConstraint = $this->getMockBuilder(DateTimeConstraint::class)->disableOriginalConstructor()->getMock();
+        $filter->setTimeConstraint($timeConstraint);
+
+        $this->assertEquals($timeConstraint, $filter->getTimeConstraint());
+    }
+
+    public function testGetStartAndEndFields(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['getStartAndEndFields']);
+
+        $this->assertEquals(
+            ['startDate', 'endDate'],
+            $filter->getStartAndEndFields(['startFieldName' => 'startDate', 'endFieldName' => 'endDate'])
+        );
+    }
+
+    public function testGetStartAndEndFieldsNoValue(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['getStartAndEndFields']);
+
+        $this->assertEquals(
+            ['startDate', null],
+            $filter->getStartAndEndFields(['startFieldName' => 'startDate'])
+        );
+
+        $this->assertEquals(
+            [null, 'endDate'],
+            $filter->getStartAndEndFields(['endFieldName' => 'endDate'])
+        );
+
+        $this->assertEquals(
+            [null, null],
+            $filter->getStartAndEndFields([])
+        );
+    }
+
+    public function testGetStartAndEndFieldsMissingAnnotationStartField(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['getStartAndEndFields']);
+
+        TestableAbstractTimeFilter::setAnnotationStartField(null);
+
+        $this->expectException(\RuntimeException::class);
+
+        $filter->getStartAndEndFields([]);
+    }
+
+    public function testGetStartAndEndFieldsMissingAnnotationEndField(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['getStartAndEndFields']);
+
+        TestableAbstractTimeFilter::setAnnotationEndField(null);
+
+        $this->expectException(\RuntimeException::class);
+
+        $filter->getStartAndEndFields([]);
+    }
+
+    public function testGetConstraints(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['getConstraints', 'setTimeConstraint']);
+
+        $timeConstraint = $this->getMockBuilder(DateTimeConstraint::class)->disableOriginalConstructor()->getMock();
+        $filter->setTimeConstraint($timeConstraint);
+
+        $accessId = 123;
+        $constraints = ['a' => ['b' => [4, 5, 6]]];
+
+        $timeConstraint->expects(self::once())->method('invoke')->with($accessId)->willReturn($constraints);
+
+        $this->assertEquals(
+            $constraints,
+            $filter->getConstraints($accessId)
+        );
+    }
+
+    public function testAddFilterConstraint(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['addFilterConstraint', 'setAccessId']);
+
+        $targetEntity = $this->getMockBuilder(ClassMetadata::class)->disableOriginalConstructor()->getMock();
+        $targetTableAlias = 'abc';
+
+        $constraintAnnotationClass = DateTimeConstraintAware::class;
+        TestableAbstractTimeFilter::setConstraintAnnotation($constraintAnnotationClass);
+
+        $startFieldName = 'startDate';
+        $endFieldName = 'endDate';
+
+        $constraintAnnotation = $this
+            ->getMockBuilder(TestableConstraintAware::class)
+            ->setConstructorArgs([$startFieldName, $endFieldName])
+            ->getMock();
+
+        $constraintAnnotation->method('getArguments')->willReturn([$startFieldName, $endFieldName]);
+
+        $reflectionClass = $this->getMockBuilder(\ReflectionClass::class)->disableOriginalConstructor()->getMock();
+        $reflectionClass
+            ->expects(self::once())
+            ->method('getAttributes')
+            ->with(DateTimeConstraintAware::class)
+            ->willReturn([$constraintAnnotation]);
+
+        $targetEntity->expects(self::once())->method('getReflectionClass')->willReturn($reflectionClass);
+
+        $fields = ['startDate', 'endDate'];
+
+        $filter->expects(self::once())->method('getStartAndEndFields')->with([$startFieldName, $endFieldName])->willReturn($fields);
+
+        $accessId = 123;
+        $filter->setAccessId(123);
+
+        $constraints = [
+            'start' => ['2021-12-20' => [4]],
+            'end' => ['2021-12-20' => [5], 'NULL' => [0]]
+        ];
+
+        $filter->expects(self::once())->method('getConstraints')->with($accessId)->willReturn($constraints);
+
+        $formattedFields = ['start' => 'startDate', 'end' => 'endDate'];
+
+        $generatedSql = 'azerty=1';
+
+        $filter
+            ->expects(self::once())
+            ->method('constructQuery')
+            ->with($constraints, $targetTableAlias, $formattedFields)
+            ->willReturn($generatedSql);
+
+        $this->assertEquals(
+            $generatedSql,
+            $filter->addFilterConstraint($targetEntity, $targetTableAlias)
+        );
+    }
+
+    public function testAddFilterConstraintMissingConstraintAnnotation(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['addFilterConstraint']);
+
+        $targetEntity = $this->getMockBuilder(ClassMetadata::class)->disableOriginalConstructor()->getMock();
+        $targetTableAlias = 'abc';
+
+        TestableAbstractTimeFilter::setConstraintAnnotation(null);
+
+        $this->expectException(\RuntimeException::class);
+
+        $filter->addFilterConstraint($targetEntity, $targetTableAlias);
+    }
+
+    public function testAddFilterConstraintMissingAccessId(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['addFilterConstraint']);
+
+        $targetEntity = $this->getMockBuilder(ClassMetadata::class)->disableOriginalConstructor()->getMock();
+        $targetTableAlias = 'abc';
+
+        $constraintAnnotationClass = DateTimeConstraintAware::class;
+        TestableAbstractTimeFilter::setConstraintAnnotation($constraintAnnotationClass);
+
+        $this->expectException(\RuntimeException::class);
+
+        $filter->addFilterConstraint($targetEntity, $targetTableAlias);
+    }
+
+    public function testAddFilterConstraintMissingTargetHasNoAnnotation(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['addFilterConstraint', 'setAccessId']);
+
+        $targetEntity = $this->getMockBuilder(ClassMetadata::class)->disableOriginalConstructor()->getMock();
+        $targetTableAlias = 'abc';
+
+        $constraintAnnotationClass = DateTimeConstraintAware::class;
+        TestableAbstractTimeFilter::setConstraintAnnotation($constraintAnnotationClass);
+
+        $accessId = 123;
+        $filter->setAccessId(123);
+
+        $reflectionClass = $this->getMockBuilder(\ReflectionClass::class)->disableOriginalConstructor()->getMock();
+        $reflectionClass
+            ->expects(self::once())
+            ->method('getAttributes')
+            ->with(DateTimeConstraintAware::class)
+            ->willReturn([]);
+
+        $targetEntity->expects(self::once())->method('getReflectionClass')->willReturn($reflectionClass);
+
+        $this->assertEquals(
+            '',
+            $filter->addFilterConstraint($targetEntity, $targetTableAlias)
+        );
+    }
+
+    public function testAddFilterConstraintAnnotationMissingStartDate(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['addFilterConstraint', 'setAccessId']);
+
+        $targetEntity = $this->getMockBuilder(ClassMetadata::class)->disableOriginalConstructor()->getMock();
+        $targetTableAlias = 'abc';
+
+        $constraintAnnotationClass = DateTimeConstraintAware::class;
+        TestableAbstractTimeFilter::setConstraintAnnotation($constraintAnnotationClass);
+
+        $accessId = 123;
+        $filter->setAccessId(123);
+
+        $startFieldName = 'startDate';
+        $endFieldName = 'endDate';
+
+        $constraintAnnotation = $this
+            ->getMockBuilder(TestableConstraintAware::class)
+            ->setConstructorArgs(['', $endFieldName])
+            ->getMock();
+
+        $constraintAnnotation->method('getArguments')->willReturn([$startFieldName, $endFieldName]);
+
+        $reflectionClass = $this->getMockBuilder(\ReflectionClass::class)->disableOriginalConstructor()->getMock();
+        $reflectionClass
+            ->expects(self::once())
+            ->method('getAttributes')
+            ->with(DateTimeConstraintAware::class)
+            ->willReturn([$constraintAnnotation]);
+
+        $targetEntity->expects(self::once())->method('getReflectionClass')->willReturn($reflectionClass);
+
+        $fields = ['startDate', 'endDate'];
+
+        $filter->expects(self::once())->method('getStartAndEndFields')->willReturn(['', $endFieldName]);
+
+        $this->expectException(\RuntimeException::class);
+
+        $filter->addFilterConstraint($targetEntity, $targetTableAlias);
+    }
+
+    public function testAddFilterConstraintAnnotationMissingEndDate(): void {
+        $filter = $this->getAbstractTimeFilterMockFor(['addFilterConstraint', 'setAccessId']);
+
+        $targetEntity = $this->getMockBuilder(ClassMetadata::class)->disableOriginalConstructor()->getMock();
+        $targetTableAlias = 'abc';
+
+        $constraintAnnotationClass = DateTimeConstraintAware::class;
+        TestableAbstractTimeFilter::setConstraintAnnotation($constraintAnnotationClass);
+
+        $accessId = 123;
+        $filter->setAccessId(123);
+
+        $startFieldName = 'startDate';
+        $endFieldName = 'endDate';
+
+        $constraintAnnotation = $this
+            ->getMockBuilder(TestableConstraintAware::class)
+            ->setConstructorArgs([$startFieldName, ''])
+            ->getMock();
+
+        $constraintAnnotation->method('getArguments')->willReturn([$startFieldName, $endFieldName]);
+
+        $reflectionClass = $this->getMockBuilder(\ReflectionClass::class)->disableOriginalConstructor()->getMock();
+        $reflectionClass
+            ->expects(self::once())
+            ->method('getAttributes')
+            ->with(DateTimeConstraintAware::class)
+            ->willReturn([$constraintAnnotation]);
+
+        $targetEntity->expects(self::once())->method('getReflectionClass')->willReturn($reflectionClass);
+
+        $fields = ['startDate', 'endDate'];
+
+        $filter->expects(self::once())->method('getStartAndEndFields')->willReturn([$startFieldName, '']);
+
+        $this->expectException(\RuntimeException::class);
+
+        $filter->addFilterConstraint($targetEntity, $targetTableAlias);
+    }
+
+    /**
+     * @see DateTimeFilter::constructQuery()
+     */
+    public function testConstructQuery(): void
+    {
+        // Exceptionnellement on ne mock jamais une méthode (getArithmeticValue), il n'y a aucune plus value à le faire et ça alourdirait beaucoup les tests
+        $filter = $this->getAbstractTimeFilterMockFor(['constructQuery', 'getArithmeticValue']);
+
+        $constraints = [
+            'start' => ['2021-12-20' => [4]],
+            'end' => ['2021-12-20' => [5], 'NULL' => [0]]
+        ];
+
+        $tableAlias = 'o';
+
+        $fields = [
+            'start' => 'startDate',
+            'end' => 'endDate'
+        ];
+
+        $queryExpected = "(o.startDate <= '2021-12-20') AND (o.endDate > '2021-12-20' OR o.endDate IS NULL)";
+
+        $this->assertEquals(
+            $queryExpected,
+            $filter->constructQuery($constraints, $tableAlias, $fields)
+        );
+    }
+
+    /**
+     * @see DateTimeFilter::constructQuery()
+     */
+    public function testConstructQueryWithOr(): void
+    {
+        // Exceptionnellement on ne mock jamais une méthode (getArithmeticValue), il n'y a aucune plus value à le faire et ça alourdirait beaucoup les tests
+        $filter = $this->getAbstractTimeFilterMockFor(['constructQuery', 'getArithmeticValue']);
+
+        $constraints = [
+            'start' => ['2021-12-20' => [1], '2021-12-30' => [5]],
+            'end' => ['2022-01-20' => [1]]
+        ];
+
+        $tableAlias = 'o';
+
+        $fields = [
+            'start' => 'startDate',
+            'end' => 'endDate'
+        ];
+
+        $queryExpected = "(o.startDate < '2021-12-20' OR o.startDate > '2021-12-30') AND (o.endDate < '2022-01-20')";
+
+        $this->assertEquals(
+            $queryExpected,
+            $filter->constructQuery($constraints, $tableAlias, $fields)
+        );
+    }
+
+    /**
+     * @see DateTimeFilter::getArithmeticValue()
+     */
+    public function testGetArithmeticValue(): void
+    {
+        $filter = $this->getAbstractTimeFilterMockFor(['getArithmeticValue']);
+
+        $this->assertEquals('<', $filter->getArithmeticValue(1));
+        $this->assertEquals('<=', $filter->getArithmeticValue(4));
+        $this->assertEquals('=', $filter->getArithmeticValue(3));
+        $this->assertEquals('>=', $filter->getArithmeticValue(8));
+        $this->assertEquals('>', $filter->getArithmeticValue(5));
+        $this->assertEquals(null, $filter->getArithmeticValue(0));
+    }
+}

+ 0 - 76
tests/Unit/Filter/DoctrineFilter/DateTimeFilterTest.php

@@ -1,76 +0,0 @@
-<?php
-namespace App\Tests\Unit\Filter\DoctrineFilter;
-
-use App\Filter\DoctrineFilter\DateTimeFilter;
-use App\Tests\Unit\TestToolsTrait;
-use Doctrine\ORM\EntityManagerInterface;
-use PHPUnit\Framework\TestCase;
-
-
-class DateTimeFilterTest extends TestCase
-{
-    use TestToolsTrait;
-
-    private DateTimeFilter $dateTimeFilter;
-
-    public function setUp(): void
-    {
-        $em = $this->getMockBuilder(EntityManagerInterface::class)->disableOriginalConstructor()->getMock();
-        $this->dateTimeFilter = new DateTimeFilter($em);
-    }
-
-    /**
-     * @see DateTimeFilter::constructQuery()
-     */
-    public function testConstructQuery():void
-    {
-        $queryExpected = "(o.startDate <= '2021-12-20') AND (o.endDate > '2021-12-20' OR o.endDate IS NULL)";
-        $this->assertEquals($queryExpected, $this->invokeMethod($this->dateTimeFilter, 'constructQuery', [
-            [
-                'start' => [
-                    '2021-12-20' => [4]
-                ],
-                'end' => [
-                    '2021-12-20' => [5],
-                    'NULL' => [0]
-                ]
-            ],
-            'o',
-            [
-                'start' => 'startDate',
-                'end' => 'endDate'
-            ]
-        ]));
-
-        $queryExpected = "(o.startDate < '2021-12-20' OR o.startDate > '2021-12-30') AND (o.endDate < '2022-01-20')";
-        $this->assertEquals($queryExpected, $this->invokeMethod($this->dateTimeFilter, 'constructQuery', [
-            [
-                'start' => [
-                    '2021-12-20' => [1],
-                    '2021-12-30' => [5]
-                ],
-                'end' => [
-                    '2022-01-20' => [1]
-                ]
-            ],
-            'o',
-            [
-                'start' => 'startDate',
-                'end' => 'endDate'
-            ]
-        ]));
-    }
-
-    /**
-     * @see DateTimeFilter::getArithmeticValue()
-     */
-    public function testGetArithmeticValue():void
-    {
-        $this->assertEquals('<', $this->invokeMethod($this->dateTimeFilter, 'getArithmeticValue', [1]));
-        $this->assertEquals('<=', $this->invokeMethod($this->dateTimeFilter, 'getArithmeticValue', [4]));
-        $this->assertEquals('=', $this->invokeMethod($this->dateTimeFilter, 'getArithmeticValue', [3]));
-        $this->assertEquals('>=', $this->invokeMethod($this->dateTimeFilter, 'getArithmeticValue', [8]));
-        $this->assertEquals('>', $this->invokeMethod($this->dateTimeFilter, 'getArithmeticValue', [5]));
-        $this->assertEquals(null, $this->invokeMethod($this->dateTimeFilter, 'getArithmeticValue', [0]));
-    }
-}