Passed
Push — master ( 676de8...b86364 )
by Alex
57s queued 12s
created

testFilterApplyArrayFilterCriteria()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 60
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 34
c 2
b 0
f 0
dl 0
loc 60
rs 9.376
cc 2
nc 2
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 QueryBuilderInterface|MockObject $queryBuilder */
65
        $queryBuilder = $this->createMock(QueryBuilderInterface::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 apply the filters in applyFilter().
241
     *
242
     * @throws QueryFilterManagerException
243
     */
244
    public function testFailureToApplyFilterWillResultInQueryFilterManagerException(): void
245
    {
246
        /** @var FilterInterface[]|MockObject[] $filters */
247
        $filters = [
248
            $this->createMock(FilterInterface::class),
249
            $this->createMock(FilterInterface::class),
250
        ];
251
252
        $manager = new QueryFilterManager($this->filterFactory);
253
254
        /** @var QueryBuilderInterface|MockObject $queryBuilder */
255
        $queryBuilder = $this->createMock(QueryBuilderInterface::class);
256
257
        $entityName = 'TestClass';
258
        $criteria = [
259
            'filters' => $filters,
260
        ];
261
262
        /** @var EntityManager|MockObject $entityManager */
263
        $entityManager = $this->createMock(EntityManager::class);
264
265
        $queryBuilder->expects($this->once())
266
            ->method('getEntityManager')
267
            ->willReturn($entityManager);
268
269
        /** @var ClassMetadata|MockObject $metadata */
270
        $metadata = $this->createMock(ClassMetadata::class);
271
272
        $entityManager->expects($this->once())
273
            ->method('getClassMetadata')
274
            ->with($entityName)
275
            ->willReturn($metadata);
276
277
        $exceptionMessage = 'This is a test filter exception message';
278
        $exceptionCode = 999;
279
        $filterException = new FilterException($exceptionMessage, $exceptionCode);
280
281
        $filters[0]->expects($this->once())
282
            ->method('filter')
283
            ->with($queryBuilder, $this->isInstanceOf(MetadataInterface::class), [])
284
            ->willThrowException($filterException);
285
286
        $metadata->expects($this->once())
287
            ->method('getName')
288
            ->willReturn($entityName);
289
290
        $this->expectException(QueryFilterManagerException::class);
291
        $this->expectExceptionCode($exceptionCode);
292
        $this->expectExceptionMessage(
293
            sprintf('Failed to apply query filter for entity \'%s\': %s', $entityName, $exceptionMessage)
294
        );
295
296
        $manager->filter($queryBuilder, $entityName, $criteria);
297
    }
298
299
    /**
300
     * Assert that the expected $criteria filters will be applyed when calling filter()
301
     *
302
     * @throws QueryFilterManagerException
303
     */
304
    public function testFilterApplyArrayFilterCriteria(): void
305
    {
306
        $filterData = [
307
            [
308
                'name'  => IsEqual::class,
309
                'field' => 'test',
310
                'value' => 123,
311
            ],
312
            [
313
                'name'  => IsNotEqual::class,
314
                'field' => 'test2',
315
                'value' => 'Hello World!',
316
            ],
317
        ];
318
319
        $manager = new QueryFilterManager($this->filterFactory);
320
321
        /** @var QueryBuilderInterface|MockObject $queryBuilder */
322
        $queryBuilder = $this->createMock(QueryBuilderInterface::class);
323
324
        $entityName = 'TestClass';
325
        $criteria = [
326
            'filters' => $filterData,
327
        ];
328
329
        /** @var EntityManager|MockObject $entityManager */
330
        $entityManager = $this->createMock(EntityManager::class);
331
332
        $queryBuilder->expects($this->once())
333
            ->method('getEntityManager')
334
            ->willReturn($entityManager);
335
336
        /** @var ClassMetadata|MockObject $metadata */
337
        $metadata = $this->createMock(ClassMetadata::class);
338
339
        $entityManager->expects($this->once())
340
            ->method('getClassMetadata')
341
            ->with($entityName)
342
            ->willReturn($metadata);
343
344
        $factoryArgs = $createdFilters = [];
345
        foreach ($filterData as $data) {
346
            /** @var FilterInterface|MockObject $createdFilter */
347
            $createdFilter = $this->createMock(FilterInterface::class);
348
349
            $factoryArgs[] = [$manager, $data['name'], $data['options'] ?? []];
350
351
            $createdFilter->expects($this->once())
352
                ->method('filter')
353
                ->with($queryBuilder, $this->isInstanceOf(MetadataInterface::class), $data);
354
355
            $createdFilters[] = $createdFilter;
356
        }
357
358
        $this->filterFactory->expects($this->exactly(count($filterData)))
359
            ->method('create')
360
            ->withConsecutive(...$factoryArgs)
361
            ->willReturnOnConsecutiveCalls(...$createdFilters);
362
363
        $manager->filter($queryBuilder, $entityName, $criteria);
364
    }
365
366
    /**
367
     * Assert that a QueryFilterManagerException is thrown when createFilter is unable to create $name
368
     *
369
     * @throws QueryFilterManagerException
370
     */
371
    public function testCreateFilterThrowsQueryFilterManagerExceptionIfUnableToCreateFilter(): void
372
    {
373
        $manager = new QueryFilterManager($this->filterFactory);
374
375
        $name = 'FooFilterName';
376
        $options = [
377
            'foo' => 123,
378
            'bar' => true,
379
        ];
380
381
        $exceptionMessage = 'This is a test exception message';
382
        $exceptionCode = 123;
383
        $exception = new FilterFactoryException($exceptionMessage, $exceptionCode);
384
385
        $this->filterFactory->expects($this->once())
386
            ->method('create')
387
            ->with($manager, $name, $options)
388
            ->willThrowException($exception);
389
390
        $this->expectException(QueryFilterManagerException::class);
391
        $this->expectExceptionCode($exceptionCode);
392
        $this->expectExceptionMessage(
393
            sprintf('Failed to create filter \'%s\': %s', $name, $exceptionMessage)
394
        );
395
396
        $manager->createFilter($name, $options);
397
    }
398
}
399