QueryBuilderTest   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 475
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 1
Metric Value
eloc 172
dl 0
loc 475
rs 10
c 8
b 0
f 1
wmc 25

23 Methods

Rating   Name   Duplication   Size   Complexity  
A testGetParametersWillProxyToInternalQueryBuilder() 0 12 1
A testGetWrappedQueryBuilderWillReturnTheDoctrineQueryBuilder() 0 5 1
A setUp() 0 3 1
A testGetQueryBuilderWillReturnTheConfiguredExprInstance() 0 12 1
A testInstanceOfQueryBuilderInterface() 0 5 1
A getWhereWillProxyToInternalQueryBuilderData() 0 21 1
A testInnerJoinWillProxyToInternalQueryBuilder() 0 15 1
A testGetEntityManagerWillReturnTheConfiguredEntityManagerInstance() 0 12 1
A getOrderByWillProxyToInternalQueryBuilderData() 0 19 1
A testSetParameterWillProxyToInternalQueryBuilder() 0 13 1
A testOrderByWillProxyToInternalQueryBuilder() 0 7 1
A testAndWhereWillProxyToInternalQueryBuilder() 0 9 1
A getGetRootAliasWillReturnQueryBuilderAliasAtIndexZeroData() 0 16 1
A testGetEntityManagerWillReturnTheConfiguredExprInstance() 0 13 1
A testLeftJoinWillProxyToInternalQueryBuilder() 0 15 1
A testSetParametersWillProxyToInternalQueryBuilder() 0 12 1
A testOrWhereWillProxyToInternalQueryBuilder() 0 9 1
A testCreateQueryBuilderWillReturnAnewInstanceOfItself() 0 19 1
A testAddOrderByWillProxyToInternalQueryBuilder() 0 7 1
A testGetRootAliasWillReturnQueryBuilderAliasAtIndexZero() 0 15 2
A testSetAndGetRootAlias() 0 15 1
A testMergeParametersWillProxyToInternalQueryBuilder() 0 42 2
A testGetQuery() 0 29 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ArpTest\DoctrineQueryFilter;
6
7
use Arp\DoctrineQueryFilter\Enum\JoinConditionType;
8
use Arp\DoctrineQueryFilter\Enum\OrderByDirection;
9
use Arp\DoctrineQueryFilter\QueryBuilder;
10
use Arp\DoctrineQueryFilter\QueryBuilderInterface;
11
use Doctrine\Common\Collections\ArrayCollection;
12
use Doctrine\DBAL\Types\Types;
13
use Doctrine\ORM\Configuration;
14
use Doctrine\ORM\EntityManager;
15
use Doctrine\ORM\EntityManagerInterface;
16
use Doctrine\ORM\Query;
17
use Doctrine\ORM\Query\Expr;
18
use Doctrine\ORM\Query\Parameter;
19
use Doctrine\ORM\QueryBuilder as DoctrineQueryBuilder;
20
use PHPUnit\Framework\MockObject\MockObject;
21
use PHPUnit\Framework\TestCase;
22
23
/**
24
 * @author  Alex Patterson <[email protected]>
25
 * @package ArpTest\DoctrineQueryFilter
26
 */
27
final class QueryBuilderTest extends TestCase
28
{
29
    /**
30
     * @var DoctrineQueryBuilder&MockObject
31
     */
32
    private DoctrineQueryBuilder $doctrineQueryBuilder;
33
34
    /**
35
     * Prepare the test case dependencies
36
     */
37
    public function setUp(): void
38
    {
39
        $this->doctrineQueryBuilder = $this->createMock(DoctrineQueryBuilder::class);
40
    }
41
42
    /**
43
     * Assert the class implement QueryBuilderInterface
44
     */
45
    public function testInstanceOfQueryBuilderInterface(): void
46
    {
47
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
48
49
        $this->assertInstanceOf(QueryBuilderInterface::class, $queryBuilder);
50
    }
51
52
    /**
53
     * Assert calls to createQueryBuilder will return a new instance of itself
54
     */
55
    public function testCreateQueryBuilderWillReturnAnewInstanceOfItself(): void
56
    {
57
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
58
59
        /** @var EntityManager&MockObject $entityManager */
60
        $entityManager = $this->createMock(EntityManager::class);
61
62
        /** @var DoctrineQueryBuilder&MockObject $newDoctrineQueryBuilder */
63
        $newDoctrineQueryBuilder = $this->createMock(DoctrineQueryBuilder::class);
64
65
        $this->doctrineQueryBuilder->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Doctrine\ORM\QueryBuilder. ( Ignorable by Annotation )

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

65
        $this->doctrineQueryBuilder->/** @scrutinizer ignore-call */ 
66
                                     expects($this->once())

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
66
            ->method('getEntityManager')
67
            ->willReturn($entityManager);
68
69
        $entityManager->expects($this->once())
70
            ->method('createQueryBuilder')
71
            ->willReturn($newDoctrineQueryBuilder);
72
73
        $this->assertInstanceOf(QueryBuilder::class, $queryBuilder->createQueryBuilder());
74
    }
75
76
    /**
77
     * Assert that calls to getEntityManager will return the internal query builder's entity manager instance
78
     */
79
    public function testGetEntityManagerWillReturnTheConfiguredEntityManagerInstance(): void
80
    {
81
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
82
83
        /** @var EntityManager&MockObject $entityManager */
84
        $entityManager = $this->createMock(EntityManager::class);
85
86
        $this->doctrineQueryBuilder->expects($this->once())
87
            ->method('getEntityManager')
88
            ->willReturn($entityManager);
89
90
        $this->assertSame($entityManager, $queryBuilder->getEntityManager());
91
    }
92
93
    /**
94
     * Assert that the root alias can be set and fetched from setRootAlias() and getRootAlias()
95
     */
96
    public function testSetAndGetRootAlias(): void
97
    {
98
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
99
100
        $alias = 'test';
101
102
        $this->doctrineQueryBuilder->expects($this->once())
103
            ->method('getRootAliases')
104
            ->willReturn([]);
105
106
        $this->assertSame('', $queryBuilder->getRootAlias());
107
108
        $queryBuilder->setRootAlias($alias);
109
110
        $this->assertSame($alias, $queryBuilder->getRootAlias());
111
    }
112
113
    /**
114
     * Assert that the root alias returned from calls to getRootAlias()
115
     *
116
     * @dataProvider getGetRootAliasWillReturnQueryBuilderAliasAtIndexZeroData
117
     *
118
     * @param array<mixed> $aliases
119
     */
120
    public function testGetRootAliasWillReturnQueryBuilderAliasAtIndexZero(array $aliases = []): void
121
    {
122
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
123
124
        if (empty($aliases[0])) {
125
            $expected = '';
126
        } else {
127
            $expected = $aliases[0];
128
        }
129
130
        $this->doctrineQueryBuilder->expects($this->once())
131
            ->method('getRootAliases')
132
            ->willReturn($aliases);
133
134
        $this->assertSame($expected, $queryBuilder->getRootAlias());
135
    }
136
137
    /**
138
     * @return array<mixed>
139
     */
140
    public function getGetRootAliasWillReturnQueryBuilderAliasAtIndexZeroData(): array
141
    {
142
        return [
143
            [
144
                [],
145
            ],
146
            [
147
                [
148
                    'foo',
149
                ],
150
            ],
151
            [
152
                [
153
                    'baz',
154
                    'foo',
155
                    'bar',
156
                ],
157
            ],
158
        ];
159
    }
160
161
    /**
162
     * Assert that calls to getQuery() will proxy and return the wrapped query builders query instance
163
     */
164
    public function testGetWrappedQueryBuilderWillReturnTheDoctrineQueryBuilder(): void
165
    {
166
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
167
168
        $this->assertSame($this->doctrineQueryBuilder, $queryBuilder->getWrappedQueryBuilder());
169
    }
170
171
    /**
172
     * Assert that calls to expr() will return the internal query builder's Expr instance
173
     */
174
    public function testGetQueryBuilderWillReturnTheConfiguredExprInstance(): void
175
    {
176
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
177
178
        /** @var Expr&MockObject $expr */
179
        $expr = $this->createMock(Expr::class);
180
181
        $this->doctrineQueryBuilder->expects($this->once())
182
            ->method('expr')
183
            ->willReturn($expr);
184
185
        $queryBuilder->expr();
186
    }
187
188
    /**
189
     * Assert that calls to expr() will return the internal query builder's DQL parts
190
     */
191
    public function testGetEntityManagerWillReturnTheConfiguredExprInstance(): void
192
    {
193
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
194
195
        $parts = [
196
            'foo' => 'bar',
197
        ];
198
199
        $this->doctrineQueryBuilder->expects($this->once())
200
            ->method('getDQLParts')
201
            ->willReturn($parts);
202
203
        $this->assertSame($parts, $queryBuilder->getQueryParts());
204
    }
205
206
    /**
207
     * Assert the Query instance is returned from getQuery()
208
     */
209
    public function testGetQuery(): void
210
    {
211
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
212
213
        /** @var EntityManagerInterface&MockObject $entityManager */
214
        $entityManager = $this->createMock(EntityManagerInterface::class);
215
216
        /** @var Configuration&MockObject $configuration */
217
        $configuration = $this->createMock(Configuration::class);
218
219
        $entityManager->expects($this->exactly(2))
220
            ->method('getConfiguration')
221
            ->willReturn($configuration);
222
223
        $configuration->expects($this->once())
224
            ->method('getDefaultQueryHints')
225
            ->willReturn([]);
226
227
        $configuration->expects($this->once())
228
            ->method('isSecondLevelCacheEnabled')
229
            ->willReturn(false);
230
231
        $query = new Query($entityManager);
232
233
        $this->doctrineQueryBuilder->expects($this->once())
234
            ->method('getQuery')
235
            ->willReturn($query);
236
237
        $this->assertSame($query, $queryBuilder->getQuery());
238
    }
239
240
    /**
241
     * Assert that arguments passed to orWhere() will proxy to the internal query builder's orWhere() method
242
     *
243
     * @param array<mixed> $orWhere
244
     *
245
     * @dataProvider getWhereWillProxyToInternalQueryBuilderData
246
     */
247
    public function testOrWhereWillProxyToInternalQueryBuilder(array $orWhere): void
248
    {
249
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
250
251
        $this->doctrineQueryBuilder->expects($this->once())
252
            ->method('orWhere')
253
            ->with($orWhere);
254
255
        $this->assertSame($queryBuilder, $queryBuilder->orWhere($orWhere));
256
    }
257
258
    /**
259
     * Assert that arguments passed to andWhere() will proxy to the internal query builder's andWhere() method
260
     *
261
     * @param array<mixed> $orWhere
262
     *
263
     * @dataProvider getWhereWillProxyToInternalQueryBuilderData
264
     */
265
    public function testAndWhereWillProxyToInternalQueryBuilder(array $orWhere): void
266
    {
267
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
268
269
        $this->doctrineQueryBuilder->expects($this->once())
270
            ->method('andWhere')
271
            ->with($orWhere);
272
273
        $this->assertSame($queryBuilder, $queryBuilder->andWhere($orWhere));
274
    }
275
276
    /**
277
     * @return array<mixed>
278
     */
279
    public function getWhereWillProxyToInternalQueryBuilderData(): array
280
    {
281
        $expr = new Expr();
282
283
        return [
284
            [
285
                [
286
                    '2 = 2',
287
                ],
288
            ],
289
            [
290
                [
291
                    'a.bar = :bar',
292
                    'a.test != :test AND b.foo = :foo',
293
                ],
294
            ],
295
            [
296
                [
297
                    $expr->eq('t.foo', ':foo'),
298
                    't.bar = 123',
299
                    $expr->eq('b.baz', ':baz'),
300
                ],
301
            ],
302
        ];
303
    }
304
305
    /**
306
     * Assert that calls to innerJoin() will proxy to the internal query builder
307
     */
308
    public function testInnerJoinWillProxyToInternalQueryBuilder(): void
309
    {
310
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
311
312
        $name = 'foo';
313
        $alias = 'a';
314
        $conditionType = JoinConditionType::ON;
315
        $condition = '1 = 1';
316
        $indexBy = null;
317
318
        $this->doctrineQueryBuilder->expects($this->once())
319
            ->method('innerJoin')
320
            ->with($name, $alias, $conditionType->value, $condition, $indexBy);
321
322
        $this->assertSame($queryBuilder, $queryBuilder->innerJoin($name, $alias, $conditionType, $condition, $indexBy));
323
    }
324
325
    /**
326
     * Assert that calls to leftJoin() will proxy to the internal query builder
327
     */
328
    public function testLeftJoinWillProxyToInternalQueryBuilder(): void
329
    {
330
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
331
332
        $name = 'bar';
333
        $alias = 'b';
334
        $conditionType = JoinConditionType::WITH;
335
        $condition = 'a.test = b.hello';
336
        $indexBy = 'a.name';
337
338
        $this->doctrineQueryBuilder->expects($this->once())
339
            ->method('leftJoin')
340
            ->with($name, $alias, $conditionType->value, $condition, $indexBy);
341
342
        $this->assertSame($queryBuilder, $queryBuilder->leftJoin($name, $alias, $conditionType, $condition, $indexBy));
343
    }
344
345
    /**
346
     * Assert that calls to orderBy() will proxy to the internal query builder instance
347
     *
348
     * @param Expr\OrderBy|string $sort
349
     * @param OrderByDirection|null $direction
350
     *
351
     * @dataProvider getOrderByWillProxyToInternalQueryBuilderData
352
     */
353
    public function testOrderByWillProxyToInternalQueryBuilder(
354
        Expr\OrderBy|string $sort,
355
        ?OrderByDirection $direction = null
356
    ): void {
357
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
358
359
        $this->assertSame($queryBuilder, $queryBuilder->orderBy($sort, $direction));
360
    }
361
362
    /**
363
     * Assert that calls to addOrderBy() will proxy to the internal query builder instance
364
     *
365
     * @param Expr\OrderBy|string $sort
366
     * @param OrderByDirection|null $direction
367
     *
368
     * @dataProvider getOrderByWillProxyToInternalQueryBuilderData
369
     */
370
    public function testAddOrderByWillProxyToInternalQueryBuilder(
371
        Expr\OrderBy|string $sort,
372
        ?OrderByDirection $direction = null
373
    ): void {
374
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
375
376
        $this->assertSame($queryBuilder, $queryBuilder->addOrderBy($sort, $direction));
377
    }
378
379
    /**
380
     * @return array<mixed>
381
     */
382
    public function getOrderByWillProxyToInternalQueryBuilderData(): array
383
    {
384
        $expr = new Expr();
385
386
        return [
387
            [
388
                'a.test',
389
                OrderByDirection::DESC,
390
            ],
391
            [
392
                'b.foo',
393
                null,
394
            ],
395
            [
396
                $expr->asc('a.bar'),
397
            ],
398
            [
399
                $expr->desc('a.baz'),
400
                null,
401
            ],
402
        ];
403
    }
404
405
    /**
406
     * Assert that calls to getParameters() will proxy to the internal query builder
407
     */
408
    public function testGetParametersWillProxyToInternalQueryBuilder(): void
409
    {
410
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
411
412
        /** @var ArrayCollection<int, Parameter>&MockObject $parameters */
413
        $parameters = $this->createMock(ArrayCollection::class);
414
415
        $this->doctrineQueryBuilder->expects($this->once())
416
            ->method('getParameters')
417
            ->willReturn($parameters);
418
419
        $this->assertSame($parameters, $queryBuilder->getParameters());
420
    }
421
422
    /**
423
     * Assert that calls to setParameters() will proxy to the internal query builder
424
     */
425
    public function testSetParametersWillProxyToInternalQueryBuilder(): void
426
    {
427
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
428
429
        /** @var ArrayCollection<int, Parameter>&MockObject $parameters */
430
        $parameters = $this->createMock(ArrayCollection::class);
431
432
        $this->doctrineQueryBuilder->expects($this->once())
433
            ->method('setParameters')
434
            ->with($parameters);
435
436
        $this->assertSame($queryBuilder, $queryBuilder->setParameters($parameters));
437
    }
438
439
    /**
440
     * Assert that calls to setParameter() will proxy to the internal query builder
441
     */
442
    public function testSetParameterWillProxyToInternalQueryBuilder(): void
443
    {
444
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
445
446
        $name = 'Foo';
447
        $value = 'This is a test value';
448
        $type = Types::STRING;
449
450
        $this->doctrineQueryBuilder->expects($this->once())
451
            ->method('setParameter')
452
            ->with($name, $value, $type);
453
454
        $this->assertSame($queryBuilder, $queryBuilder->setParameter($name, $value, $type));
455
    }
456
457
    /**
458
     * Assert that calls to mergeParameters() will proxy to the internal query builder
459
     */
460
    public function testMergeParametersWillProxyToInternalQueryBuilder(): void
461
    {
462
        $queryBuilder = new QueryBuilder($this->doctrineQueryBuilder);
463
464
        $bParams = $addArgs = [];
465
        $b = [
466
            'bar'  => 456,
467
            'test' => 'This is value from B',
468
        ];
469
        $count = count($b);
470
        for ($x = 1; $x < $count; $x++) {
471
            /** @var Parameter&MockObject $parameter */
472
            $parameter = $this->createMock(Parameter::class);
473
            $bParams[] = $parameter;
474
            $addArgs[] = [$parameter];
475
        }
476
477
        /** @var ArrayCollection<int, Parameter>&MockObject $params */
478
        $params = $this->createMock(ArrayCollection::class);
479
        $this->doctrineQueryBuilder->expects($this->once())
480
            ->method('getParameters')
481
            ->willReturn($params);
482
483
        /** @var QueryBuilderInterface&MockObject $newQueryBuilder */
484
        $newQueryBuilder = $this->createMock(QueryBuilderInterface::class);
485
486
        /** @var ArrayCollection<int, Parameter>&MockObject $newParams */
487
        $newParams = $this->createMock(ArrayCollection::class);
488
489
        $newQueryBuilder->expects($this->once())
490
            ->method('getParameters')
491
            ->willReturn($newParams);
492
493
        $newParams->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Doctrine\Common\Collections\ArrayCollection. ( Ignorable by Annotation )

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

493
        $newParams->/** @scrutinizer ignore-call */ 
494
                    expects($this->once())

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
494
            ->method('getIterator')
495
            ->willReturn(new \ArrayIterator($bParams));
496
497
        $params->expects($this->exactly(count($bParams)))
498
            ->method('add')
499
            ->withConsecutive(...$addArgs);
500
501
        $this->assertSame($queryBuilder, $queryBuilder->mergeParameters($newQueryBuilder));
502
    }
503
}
504