|
|
@@ -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));
|
|
|
+ }
|
|
|
+}
|