Passed
Push — master ( a65783...53adb5 )
by Povilas
02:44
created

ListQueryBuilderTest::testBuildQueryApplySelects()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 76
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 63
dl 0
loc 76
rs 8.8072
c 0
b 0
f 0
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
namespace Povs\ListerBundle\Service;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\ORM\EntityManagerInterface;
7
use Doctrine\ORM\QueryBuilder;
8
use PHPUnit\Framework\TestCase;
9
use Povs\ListerBundle\Declaration\ListInterface;
10
use Povs\ListerBundle\Declaration\ListValueInterface;
11
use Povs\ListerBundle\DependencyInjection\Locator\QueryTypeLocator;
12
use Povs\ListerBundle\DependencyInjection\Locator\SelectorTypeLocator;
13
use Povs\ListerBundle\Exception\ListFieldException;
14
use Povs\ListerBundle\Mapper\FilterField;
15
use Povs\ListerBundle\Mapper\FilterMapper;
16
use Povs\ListerBundle\Mapper\JoinField;
17
use Povs\ListerBundle\Mapper\JoinMapper;
18
use Povs\ListerBundle\Mapper\ListField;
19
use Povs\ListerBundle\Mapper\ListMapper;
20
use Povs\ListerBundle\Type\QueryType\QueryTypeInterface;
21
use Povs\ListerBundle\Type\SelectorType\SelectorTypeInterface;
22
23
/**
24
 * @author Povilas Margaiatis <[email protected]>
25
 */
26
class ListQueryBuilderTest extends TestCase
27
{
28
    private $emMock;
29
    private $queryTypeLocatorMock;
30
    private $selectorTypeLocatorMock;
31
    private $selectorTypeMock;
32
    private $configMock;
33
    private $listMock;
34
    private $joinMapperMock;
35
    private $listMapperMock;
36
    private $filterMapperMock;
37
    private $listValueMock;
38
    private $queryBuilderMock;
39
40
    public function setUp()
41
    {
42
        $this->emMock = $this->createMock(EntityManagerInterface::class);
43
        $this->queryTypeLocatorMock = $this->createMock(QueryTypeLocator::class);
44
        $this->selectorTypeLocatorMock = $this->createMock(SelectorTypeLocator::class);
45
        $this->selectorTypeMock = $this->createMock(SelectorTypeInterface::class);
46
        $this->configMock = $this->createMock(ConfigurationResolver::class);
47
        $this->listMock = $this->createMock(ListInterface::class);
48
        $this->joinMapperMock = $this->createMock(JoinMapper::class);
49
        $this->listMapperMock = $this->createMock(ListMapper::class);
50
        $this->filterMapperMock = $this->createMock(FilterMapper::class);
51
        $this->listValueMock = $this->createMock(ListValueInterface::class);
52
        $this->queryBuilderMock = $this->createMock(QueryBuilder::class);
53
    }
54
55
    public function testBuildQueryJoins(): void
56
    {
57
        $fieldsData = [
58
            ['ent1', 'INNER', 'al1'],
59
            ['al1.ent2', 'LEFT', 'al2'],
60
        ];
61
        $fields = [];
62
63
        foreach ($fieldsData as $datum) {
64
            $field = $this->createMock(JoinField::class);
65
            $field->expects($this->once())
66
                ->method('getJoinPath')
67
                ->with('alias')
68
                ->willReturn($datum[0]);
69
            $field->expects($this->once())
70
                ->method('getOption')
71
                ->with('join_type')
72
                ->willReturn($datum[1]);
73
            $field->expects($this->once())
74
                ->method('getAlias')
75
                ->willReturn($datum[2]);
76
            $fields[] = $field;
77
        }
78
79
        $this->setCalls(false, true, true);
80
        $this->joinMapperMock->expects($this->once())
81
            ->method('getFields')
82
            ->willReturn(new ArrayCollection($fields));
83
        $this->queryBuilderMock->expects($this->once())
84
            ->method('innerJoin')
85
            ->with('ent1', 'al1');
86
        $this->queryBuilderMock->expects($this->once())
87
            ->method('leftJoin')
88
            ->with('al1.ent2', 'al2');
89
90
        $this->executeBuildQuery();
91
    }
92
93
    public function testBuildQueryApplySelects(): void
94
    {
95
        $fieldsData = [
96
            ['id1', ['foo'], 'selector', true, 'ASC', 'sort.path'],
97
            ['id2', ['ent1.bar'], 'selector', true, 'DESC', null],
98
        ];
99
        $fields = [];
100
101
        foreach ($fieldsData as $datum) {
102
            $field = $this->createMock(ListField::class);
103
            $field->method('getId')
104
                ->willReturn($datum[0]);
105
            $field->expects($this->once())
106
                ->method('getPaths')
107
                ->willReturn($datum[1]);
108
            $field->method('getOption')
109
                ->willReturnMap([
110
                   ['selector', null, $datum[2]],
111
                   ['sortable', null, $datum[3]],
112
                   ['sort_value', null, $datum[4]],
113
                   ['sort_path', null, $datum[5]]
114
                ]);
115
            $fields[] = $field;
116
        }
117
118
        $this->setCalls(true, false, true);
119
        $this->listMapperMock->expects($this->once())
120
            ->method('getFields')
121
            ->with(false)
122
            ->willReturn(new ArrayCollection($fields));
123
        $this->selectorTypeLocatorMock->expects($this->exactly(2))
124
            ->method('has')
125
            ->willReturn(true);
126
        $this->selectorTypeMock->expects($this->exactly(2))
127
            ->method('hasAggregation')
128
            ->willReturnOnConsecutiveCalls(true, false);
129
        $this->selectorTypeLocatorMock
130
            ->expects($this->exactly(2))
131
            ->method('get')
132
            ->willReturn($this->selectorTypeMock);
133
        $joinFieldMock = $this->createMock(JoinField::class);
134
        $joinFieldMock2 = $this->createMock(JoinField::class);
135
        $joinFieldMock->expects($this->once())
136
            ->method('getAlias')
137
            ->willReturn('ent1');
138
        $joinFieldMock2->expects($this->once())
139
            ->method('getAlias')
140
            ->willReturn('sort');
141
142
        $this->joinMapperMock->expects($this->exactly(2))
143
            ->method('getByPath')
144
            ->willReturnMap([
145
                ['ent1', false, $joinFieldMock],
146
                ['sort', false, $joinFieldMock2]
147
            ]);
148
        $this->selectorTypeMock
149
            ->expects($this->exactly(2))
150
            ->method('apply')
151
            ->withConsecutive(
152
                [$this->queryBuilderMock, ['alias.foo'], 'id1'],
153
                [$this->queryBuilderMock, ['ent1.bar'], 'id2']
154
            );
155
156
        $this->selectorTypeMock->expects($this->once())
157
            ->method('getSortPath')
158
            ->with('id2')
159
            ->willReturn('field_id2');
160
161
        $this->queryBuilderMock->expects($this->exactly(2))
162
            ->method('addOrderBy')
163
            ->withConsecutive(
164
                ['sort.path', 'ASC'],
165
                ['field_id2', 'DESC']
166
            )->willReturnSelf();
167
168
        $this->executeBuildQuery();
169
    }
170
171
    public function testBuildQueryApplyFilter(): void
172
    {
173
        $fieldsData = [
174
            ['id1', ['foo'], 'value1'],
175
            ['id2', ['ent1.bar'],'value2'],
176
            ['id3', ['ent2.bar'], null],
177
        ];
178
        $fields = [];
179
180
        foreach ($fieldsData as $datum) {
181
            $field = $this->createMock(FilterField::class);
182
            if ($datum[2]) {
183
                $field->expects($this->once())
184
                    ->method('getValue')
185
                    ->willReturn($datum[2]);
186
                $field->expects($this->once())
187
                    ->method('hasValue')
188
                    ->willReturn(true);
189
                $field->expects($this->once())
190
                    ->method('getId')
191
                    ->willReturn($datum[0]);
192
                $field->expects($this->once())
193
                    ->method('getPaths')
194
                    ->willReturn($datum[1]);
195
                $field->expects($this->exactly(3))
196
                    ->method('getOption')
197
                    ->willReturnMap([
198
                        ['query_type', null, 'query_type'],
199
                        ['property_delimiter', null, 'delimiter'],
200
                        ['query_options', null, []],
201
                        ['mapped', null, true],
202
                    ]);
203
            } else {
204
                $field->expects($this->once())
205
                    ->method('hasValue')
206
                    ->willReturn(false);
207
            }
208
209
            $fields[] = $field;
210
        }
211
212
        $this->setCalls(true, true, false);
213
        $this->filterMapperMock->expects($this->once())
214
            ->method('getFields')
215
            ->willReturn(new ArrayCollection($fields));
216
        $joinFieldMock = $this->createMock(JoinField::class);
217
        $joinFieldMock->expects($this->once())
218
            ->method('getAlias')
219
            ->willReturn('ent1');
220
        $this->joinMapperMock->expects($this->once())
221
            ->method('getByPath')
222
            ->willReturn($joinFieldMock);
223
        $queryTypeMock = $this->createMock(QueryTypeInterface::class);
224
        $this->queryTypeLocatorMock->expects($this->exactly(2))
225
            ->method('has')
226
            ->willReturn(true);
227
        $this->queryTypeLocatorMock->expects($this->exactly(2))
228
            ->method('get')
229
            ->willReturn($queryTypeMock);
230
231
        $queryTypeMock->expects($this->exactly(2))
232
            ->method('configureOptions');
233
        $queryTypeMock->expects($this->exactly(2))
234
            ->method('setOptions')
235
            ->with([]);
236
        $queryTypeMock->expects($this->exactly(2))
237
            ->method('hasAggregation')
238
            ->willReturnOnConsecutiveCalls(true, false);
239
        $queryTypeMock->expects($this->exactly(2))
240
            ->method('filter')
241
            ->withConsecutive(
242
                [$this->queryBuilderMock, ['alias.foo'], 'id1', 'value1'],
243
                [$this->queryBuilderMock, ['ent1.bar'], 'id2', 'value2']
244
            );
245
246
        $this->executeBuildQuery();
247
    }
248
249
    public function testApplySelectsThrowsExceptionOnInvalidSelectorType(): void
250
    {
251
        $this->expectException(ListFieldException::class);
252
        $this->expectExceptionCode(500);
253
        $this->expectExceptionMessage(sprintf('Type "invalid_type" does not exist or does not implement %s', SelectorTypeInterface::class));
254
        $listFieldMock = $this->createMock(ListField::class);
255
        $listFieldMock->expects($this->once())
256
            ->method('getId')
257
            ->willReturn('id');
258
        $listFieldMock->expects($this->once())
259
            ->method('getOption')
260
            ->willReturn('invalid_type');
261
        $this->setCalls(true, false, false);
262
        $this->selectorTypeLocatorMock->method('has')
263
            ->with('invalid_type')
264
            ->willReturn(false);
265
        $this->listMapperMock->expects($this->once())
266
            ->method('getFields')
267
            ->willReturn(new ArrayCollection([$listFieldMock]));
268
        $this->executeBuildQuery();
269
    }
270
271
    public function testApplyFilterThrowsExceptionOnInvalidQueryType(): void
272
    {
273
        $this->expectException(ListFieldException::class);
274
        $this->expectExceptionCode(500);
275
        $this->expectExceptionMessage(sprintf('Type "invalid_type" does not exist or does not implement %s', QueryTypeInterface::class));
276
        $filterFieldMock = $this->createMock(FilterField::class);
277
        $filterFieldMock->expects($this->exactly(2))
278
            ->method('getOption')
279
            ->willReturnMap([
280
                ['query_type', null, 'invalid_type'],
281
                ['mapped', null, true],
282
            ]);
283
        $filterFieldMock->expects($this->once())
284
            ->method('hasValue')
285
            ->willReturn(true);
286
        $this->setCalls(true, true, false);
287
        $this->selectorTypeLocatorMock->method('has')
288
            ->with('invalid_type')
289
            ->willReturn(false);
290
        $this->filterMapperMock->expects($this->once())
291
            ->method('getFields')
292
            ->willReturn(new ArrayCollection([$filterFieldMock]));
293
        $this->executeBuildQuery();
294
    }
295
296
    public function testExceptionIsThrownOnInvalidPath(): void
297
    {
298
        $this->expectException(ListFieldException::class);
299
        $this->expectExceptionCode(500);
300
        $this->expectExceptionMessage('Could not find join for path "foo"');
301
        $listFieldMock = $this->createMock(ListField::class);
302
        $listFieldMock->expects($this->once())
303
            ->method('getPaths')
304
            ->willReturn(['foo.bar']);
305
        $this->listMapperMock->expects($this->once())
306
            ->method('getFields')
307
            ->willReturn(new ArrayCollection([$listFieldMock]));
308
        $this->joinMapperMock->expects($this->once())
309
            ->method('getByPath')
310
            ->willReturn(null);
311
        $this->setCalls(true, false, false);
312
        $this->executeBuildQuery();
313
    }
314
315
    public function testBuildLazyQuery(): void
316
    {
317
        $this->setCalls(false, false, false);
318
        $queryBuilder = new ListQueryBuilder(
319
            $this->emMock,
320
            $this->queryTypeLocatorMock,
321
            $this->selectorTypeLocatorMock,
322
            $this->configMock
323
        );
324
325
        $this->joinMapperMock->expects($this->once())
326
            ->method('getFields')
327
            ->with(true)
328
            ->willReturn(new ArrayCollection());
329
330
        $this->listMapperMock->expects($this->exactly(2))
331
            ->method('getFields')
332
            ->with(true)
333
            ->willReturnOnConsecutiveCalls(
334
                new ArrayCollection(['field']),
335
                new ArrayCollection()
336
            );
337
338
        $query = $queryBuilder->buildLazyQuery($this->listMock, $this->joinMapperMock, $this->listMapperMock);
339
340
        $this->assertEquals($this->queryBuilderMock, $query);
341
    }
342
343
    public function testBuildLazyQueryWithoutFields(): void
344
    {
345
        $queryBuilder = new ListQueryBuilder(
346
            $this->emMock,
347
            $this->queryTypeLocatorMock,
348
            $this->selectorTypeLocatorMock,
349
            $this->configMock
350
        );
351
352
        $this->listMapperMock->expects($this->once())
353
            ->method('getFields')
354
            ->with(true)
355
            ->willReturn(new ArrayCollection());
356
357
        $query = $queryBuilder->buildLazyQuery($this->listMock, $this->joinMapperMock, $this->listMapperMock);
358
        $this->assertNull($query);
359
    }
360
361
    private function executeBuildQuery(): void
362
    {
363
        $queryBuilder = new ListQueryBuilder(
364
            $this->emMock,
365
            $this->queryTypeLocatorMock,
366
            $this->selectorTypeLocatorMock,
367
            $this->configMock
368
        );
369
370
        $queryBuilder->buildQuery(
371
            $this->listMock,
372
            $this->joinMapperMock,
373
            $this->listMapperMock,
374
            $this->filterMapperMock,
375
            $this->listValueMock
376
        );
377
    }
378
379
    private function setCalls(bool $setJoinFields, bool $setListFields, bool $setFilterFields): void
380
    {
381
        $this->emMock->expects($this->once())
382
            ->method('createQueryBuilder')
383
            ->willReturn($this->queryBuilderMock);
384
        $this->queryBuilderMock->expects($this->once())
385
            ->method('from')
386
            ->with('data_class', 'alias')
387
            ->willReturnSelf();
388
        $this->listMock->expects($this->once())
389
            ->method('getDataClass')
390
            ->willReturn('data_class');
391
        $this->configMock->method('getAlias')
392
            ->willReturn('alias');
393
394
        if ($setJoinFields) {
395
            $this->joinMapperMock->expects($this->once())
396
                ->method('getFields')
397
                ->with(false)
398
                ->willReturn(new ArrayCollection());
399
        }
400
401
        if ($setListFields) {
402
            $this->listMapperMock->expects($this->once())
403
                ->method('getFields')
404
                ->with(false)
405
                ->willReturn(new ArrayCollection());
406
        }
407
408
        if ($setFilterFields) {
409
            $this->filterMapperMock->expects($this->once())
410
                ->method('getFields')
411
                ->with()
412
                ->willReturn(new ArrayCollection());
413
        }
414
    }
415
}
416