Passed
Push — master ( 98e9a8...515b37 )
by Alex
43s queued 10s
created

testMissingFilterNameWillThrowQueryFilterManagerException()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 35
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 35
rs 9.6666
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ArpTest\DoctrineQueryFilter;
6
7
use Arp\DoctrineQueryFilter\Exception\QueryFilterManagerException;
8
use Arp\DoctrineQueryFilter\Filter\Exception\FilterException;
9
use Arp\DoctrineQueryFilter\Filter\Exception\FilterFactoryException;
10
use Arp\DoctrineQueryFilter\Filter\FilterFactoryInterface;
11
use Arp\DoctrineQueryFilter\Filter\FilterInterface;
12
use Arp\DoctrineQueryFilter\Filter\IsEqual;
13
use Arp\DoctrineQueryFilter\Filter\IsNotEqual;
14
use Arp\DoctrineQueryFilter\Metadata\MetadataInterface;
15
use Arp\DoctrineQueryFilter\QueryBuilderInterface;
16
use Arp\DoctrineQueryFilter\QueryFilterManager;
17
use Arp\DoctrineQueryFilter\QueryFilterManagerInterface;
18
use Doctrine\ORM\EntityManager;
19
use Doctrine\ORM\Mapping\ClassMetadata;
20
use Doctrine\ORM\QueryBuilder as DoctrineQueryBuilder;
21
use PHPUnit\Framework\MockObject\MockObject;
22
use PHPUnit\Framework\TestCase;
23
24
/**
25
 * @covers  \Arp\DoctrineQueryFilter\QueryFilterManager
26
 *
27
 * @author  Alex Patterson <[email protected]>
28
 * @package ArpTest\LaminasDoctrine
29
 */
30
final class QueryFilterManagerTest extends TestCase
31
{
32
    /**
33
     * @var FilterFactoryInterface|MockObject
34
     */
35
    private $filterFactory;
36
37
    /**
38
     * Prepare the test case dependencies
39
     */
40
    public function setUp(): void
41
    {
42
        $this->filterFactory = $this->createMock(FilterFactoryInterface::class);
43
    }
44
45
    /**
46
     * Assert that the manager implements QueryFilterManagerInterface
47
     */
48
    public function testImplementsQueryFilterManagerInterface(): void
49
    {
50
        $manager = new QueryFilterManager($this->filterFactory);
51
52
        $this->assertInstanceOf(QueryFilterManagerInterface::class, $manager);
53
    }
54
55
    /**
56
     * Assert no filtering will be applied if filter() is provided configuration without the required 'filters' key
57
     *
58
     * @throws QueryFilterManagerException
59
     */
60
    public function testFilterWillNotPerformFilteringWithoutFilterKey(): void
61
    {
62
        $manager = new QueryFilterManager($this->filterFactory);
63
64
        /** @var DoctrineQueryBuilder|MockObject $doctrineQueryBuilder */
65
        $queryBuilder = $this->createMock(DoctrineQueryBuilder::class);
66
67
        $queryBuilder->expects($this->never())->method('getEntityManager');
68
69
        $this->assertSame($queryBuilder, $manager->filter($queryBuilder, 'Foo', []));
70
    }
71
72
    /**
73
     * Assert that failure to create a entity metadata instance will result in a
74
     * QueryFilterManagerException being thrown
75
     *
76
     * @throws QueryFilterManagerException
77
     */
78
    public function testFilterWillThrowQueryFilterManagerExceptionIfProvidedWithAnInvalidEntityName(): void
79
    {
80
        $entityName = 'Test';
81
        $manager = new QueryFilterManager($this->filterFactory);
82
83
        $criteria = [
84
            'filters' => [
85
                [
86
                    'name' => 'foo',
87
                ],
88
            ],
89
        ];
90
91
        /** @var DoctrineQueryBuilder|MockObject $queryBuilder */
92
        $queryBuilder = $this->createMock(DoctrineQueryBuilder::class);
93
94
        /** @var EntityManager|MockObject $entityManager */
95
        $entityManager = $this->createMock(EntityManager::class);
96
97
        $queryBuilder->expects($this->once())
98
            ->method('getEntityManager')
99
            ->willReturn($entityManager);
100
101
        $exceptionMessage = 'This is an exception message';
102
        $exceptionCode = 123;
103
        $exception = new \Exception($exceptionMessage, $exceptionCode);
104
105
        $entityManager->expects($this->once())
106
            ->method('getClassMetadata')
107
            ->with($entityName)
108
            ->willThrowException($exception);
109
110
        $this->expectException(QueryFilterManagerException::class);
111
        $this->expectExceptionCode($exceptionCode);
112
        $this->expectExceptionMessage(
113
            sprintf('Failed to fetch entity metadata for class \'%s\': %s', $entityName, $exceptionMessage),
114
        );
115
116
        $manager->filter($queryBuilder, $entityName, $criteria);
117
    }
118
119
    /**
120
     * Assert that a QueryFilterManagerException is thrown if providing an invalid QueryBuilder instance to filter()
121
     *
122
     * @throws QueryFilterManagerException
123
     */
124
    public function testQueryFilterManagerExceptionIsThrownWhenProvidingAnInvalidQueryBuilderToFilter(): void
125
    {
126
        $manager = new QueryFilterManager($this->filterFactory);
127
128
        $invalidQueryBuilder = new \stdClass();
129
        $entityName = 'EntityTestName';
130
        $criteria = [];
131
132
        $this->expectException(QueryFilterManagerException::class);
133
        $this->expectExceptionMessage(
134
            sprintf(
135
                'The \'queryBuilder\' argument must be an object of type \'%s\' or \'%s\'; '
136
                . '\'%s\' provided in \'%s\'',
137
                QueryBuilderInterface::class,
138
                DoctrineQueryBuilder::class,
139
                is_object($invalidQueryBuilder) ? get_class($invalidQueryBuilder) : gettype($invalidQueryBuilder),
140
                QueryFilterManager::class
141
            )
142
        );
143
144
        /** @noinspection PhpParamsInspection */
145
        $manager->filter($invalidQueryBuilder, $entityName, $criteria);
0 ignored issues
show
Bug introduced by
$invalidQueryBuilder of type stdClass is incompatible with the type Arp\DoctrineQueryFilter\...ctrine\ORM\QueryBuilder expected by parameter $queryBuilder of Arp\DoctrineQueryFilter\...FilterManager::filter(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

145
        $manager->filter(/** @scrutinizer ignore-type */ $invalidQueryBuilder, $entityName, $criteria);
Loading history...
146
    }
147
148
    /**
149
     * Assert that a QueryFilterManagerException if thrown when providing an array query filter specification that
150
     * does not contain a 'name' property
151
     *
152
     * @throws QueryFilterManagerException
153
     */
154
    public function testMissingFilterNameWillThrowQueryFilterManagerException(): void
155
    {
156
        $manager = new QueryFilterManager($this->filterFactory);
157
158
        /** @var QueryBuilderInterface|MockObject $queryBuilder */
159
        $queryBuilder = $this->createMock(QueryBuilderInterface::class);
160
161
        $entityName = 'TestClass';
162
        $criteria = [
163
            'filters' => [
164
                [],
165
            ],
166
        ];
167
168
        /** @var EntityManager|MockObject $entityManager */
169
        $entityManager = $this->createMock(EntityManager::class);
170
171
        $queryBuilder->expects($this->once())
172
            ->method('getEntityManager')
173
            ->willReturn($entityManager);
174
175
        /** @var ClassMetadata|MockObject $metadata */
176
        $metadata = $this->createMock(ClassMetadata::class);
177
178
        $entityManager->expects($this->once())
179
            ->method('getClassMetadata')
180
            ->with($entityName)
181
            ->willReturn($metadata);
182
183
        $this->expectException(QueryFilterManagerException::class);
184
        $this->expectExceptionMessage(
185
            sprintf('The required \'name\' configuration option is missing in \'%s\'', QueryFilterManager::class)
186
        );
187
188
        $manager->filter($queryBuilder, $entityName, $criteria);
189
    }
190
191
    /**
192
     * Assert that a QueryFilterManagerException if thrown when providing invalid criteria filters to filter()
193
     *
194
     * @throws QueryFilterManagerException
195
     */
196
    public function testInvalidFilterWillThrowQueryFilterManagerException(): void
197
    {
198
        $manager = new QueryFilterManager($this->filterFactory);
199
200
        /** @var QueryBuilderInterface|MockObject $queryBuilder */
201
        $queryBuilder = $this->createMock(QueryBuilderInterface::class);
202
203
        $filter = new \stdClass();
204
        $entityName = 'TestClass';
205
        $criteria = [
206
            'filters' => [
207
                $filter,
208
            ],
209
        ];
210
211
        /** @var EntityManager|MockObject $entityManager */
212
        $entityManager = $this->createMock(EntityManager::class);
213
214
        $queryBuilder->expects($this->once())
215
            ->method('getEntityManager')
216
            ->willReturn($entityManager);
217
218
        /** @var ClassMetadata|MockObject $metadata */
219
        $metadata = $this->createMock(ClassMetadata::class);
220
221
        $entityManager->expects($this->once())
222
            ->method('getClassMetadata')
223
            ->with($entityName)
224
            ->willReturn($metadata);
225
226
        $this->expectException(QueryFilterManagerException::class);
227
        $this->expectExceptionMessage(
228
            sprintf(
229
                'The \'data\' argument must be an \'array\' or object of type \'%s\'; \'%s\' provided in \'%s\'',
230
                FilterInterface::class,
231
                is_object($filter) ? get_class($filter) : gettype($filter),
232
                QueryFilterManager::class
233
            )
234
        );
235
236
        $manager->filter($queryBuilder, $entityName, $criteria);
237
    }
238
239
    /**
240
     * Assert that a QueryFilterManagerException is thrown when unable to create the filters in createFilter().
241
     *
242
     * @throws QueryFilterManagerException
243
     */
244
    public function testFailureToCreateFilterWillResultInQueryFilterManagerException(): void
245
    {
246
        $filterName = 'eq';
247
        $filterOptions = [
248
            'testing' => 123,
249
            'hello' => 'world!',
250
        ];
251
252
        /** @var FilterInterface[]|MockObject[] $filters */
253
        $filters = [
254
            [
255
                'name' => $filterName,
256
                'options' => $filterOptions, // Creation options
257
            ],
258
        ];
259
260
        $manager = new QueryFilterManager($this->filterFactory);
261
262
        /** @var QueryBuilderInterface|MockObject $queryBuilder */
263
        $queryBuilder = $this->createMock(QueryBuilderInterface::class);
264
265
        $entityName = 'TestClass';
266
        $criteria = [
267
            'filters' => $filters,
268
        ];
269
270
        /** @var EntityManager|MockObject $entityManager */
271
        $entityManager = $this->createMock(EntityManager::class);
272
273
        $queryBuilder->expects($this->once())
274
            ->method('getEntityManager')
275
            ->willReturn($entityManager);
276
277
        /** @var ClassMetadata|MockObject $metadata */
278
        $metadata = $this->createMock(ClassMetadata::class);
279
280
        $entityManager->expects($this->once())
281
            ->method('getClassMetadata')
282
            ->with($entityName)
283
            ->willReturn($metadata);
284
285
        $exceptionMessage = 'This is a test filter factory exception message';
286
        $exceptionCode = 456;
287
        $filterException = new FilterFactoryException($exceptionMessage, $exceptionCode);
288
289
        $this->filterFactory->expects($this->once())
290
            ->method('create')
291
            ->with($manager, $filterName, $filterOptions)
292
            ->willThrowException($filterException);
293
294
        $this->expectException(QueryFilterManagerException::class);
295
        $this->expectExceptionCode($exceptionCode);
296
        $this->expectExceptionMessage(
297
            sprintf('Failed to create filter \'%s\': %s', $filterName, $exceptionMessage),
298
        );
299
300
        $manager->filter($queryBuilder, $entityName, $criteria);
301
    }
302
303
    /**
304
     * Assert that a QueryFilterManagerException is thrown when unable to apply the filters in applyFilter().
305
     *
306
     * @throws QueryFilterManagerException
307
     */
308
    public function testFailureToApplyFilterWillResultInQueryFilterManagerException(): void
309
    {
310
        /** @var FilterInterface[]|MockObject[] $filters */
311
        $filters = [
312
            $this->createMock(FilterInterface::class),
313
            $this->createMock(FilterInterface::class),
314
        ];
315
316
        $manager = new QueryFilterManager($this->filterFactory);
317
318
        /** @var QueryBuilderInterface|MockObject $queryBuilder */
319
        $queryBuilder = $this->createMock(QueryBuilderInterface::class);
320
321
        $entityName = 'TestClass';
322
        $criteria = [
323
            'filters' => $filters,
324
        ];
325
326
        /** @var EntityManager|MockObject $entityManager */
327
        $entityManager = $this->createMock(EntityManager::class);
328
329
        $queryBuilder->expects($this->once())
330
            ->method('getEntityManager')
331
            ->willReturn($entityManager);
332
333
        /** @var ClassMetadata|MockObject $metadata */
334
        $metadata = $this->createMock(ClassMetadata::class);
335
336
        $entityManager->expects($this->once())
337
            ->method('getClassMetadata')
338
            ->with($entityName)
339
            ->willReturn($metadata);
340
341
        $exceptionMessage = 'This is a test filter exception message';
342
        $exceptionCode = 999;
343
        $filterException = new FilterException($exceptionMessage, $exceptionCode);
344
345
        $filters[0]->expects($this->once())
346
            ->method('filter')
347
            ->with($queryBuilder, $this->isInstanceOf(MetadataInterface::class), [])
348
            ->willThrowException($filterException);
349
350
        $metadata->expects($this->once())
351
            ->method('getName')
352
            ->willReturn($entityName);
353
354
        $this->expectException(QueryFilterManagerException::class);
355
        $this->expectExceptionCode($exceptionCode);
356
        $this->expectExceptionMessage(
357
            sprintf('Failed to apply query filter for entity \'%s\': %s', $entityName, $exceptionMessage)
358
        );
359
360
        $manager->filter($queryBuilder, $entityName, $criteria);
361
    }
362
363
    /**
364
     * Assert that the expected $criteria filters will be applied when calling filter()
365
     *
366
     * @throws QueryFilterManagerException
367
     */
368
    public function testFilterApplyArrayFilterCriteria(): void
369
    {
370
        $filterData = [
371
            [
372
                'name'  => IsEqual::class,
373
                'field' => 'test',
374
                'value' => 123,
375
            ],
376
            [
377
                'name'  => IsNotEqual::class,
378
                'field' => 'test2',
379
                'value' => 'Hello World!',
380
            ],
381
        ];
382
383
        $manager = new QueryFilterManager($this->filterFactory);
384
385
        /** @var QueryBuilderInterface|MockObject $queryBuilder */
386
        $queryBuilder = $this->createMock(QueryBuilderInterface::class);
387
388
        $entityName = 'TestClass';
389
        $criteria = [
390
            'filters' => $filterData,
391
        ];
392
393
        /** @var EntityManager|MockObject $entityManager */
394
        $entityManager = $this->createMock(EntityManager::class);
395
396
        $queryBuilder->expects($this->once())
397
            ->method('getEntityManager')
398
            ->willReturn($entityManager);
399
400
        /** @var ClassMetadata|MockObject $metadata */
401
        $metadata = $this->createMock(ClassMetadata::class);
402
403
        $entityManager->expects($this->once())
404
            ->method('getClassMetadata')
405
            ->with($entityName)
406
            ->willReturn($metadata);
407
408
        $factoryArgs = $createdFilters = [];
409
        foreach ($filterData as $data) {
410
            /** @var FilterInterface|MockObject $createdFilter */
411
            $createdFilter = $this->createMock(FilterInterface::class);
412
413
            $factoryArgs[] = [$manager, $data['name'], $data['options'] ?? []];
414
415
            $createdFilter->expects($this->once())
416
                ->method('filter')
417
                ->with($queryBuilder, $this->isInstanceOf(MetadataInterface::class), $data);
418
419
            $createdFilters[] = $createdFilter;
420
        }
421
422
        $this->filterFactory->expects($this->exactly(count($filterData)))
423
            ->method('create')
424
            ->withConsecutive(...$factoryArgs)
425
            ->willReturnOnConsecutiveCalls(...$createdFilters);
426
427
        $manager->filter($queryBuilder, $entityName, $criteria);
428
    }
429
}
430