Passed
Push — master ( fe4278...124d43 )
by Chito
02:06
created

QueryTest::testDebugInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 22
c 1
b 1
f 0
dl 0
loc 28
rs 9.568
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Lampager\Cake\Test\TestCase\ORM;
6
7
use Cake\Database\Expression\OrderClauseExpression;
8
use Cake\I18n\FrozenTime;
9
use Cake\ORM\Entity;
10
use Cake\ORM\Table;
11
use Cake\ORM\TableRegistry;
12
use Generator;
13
use Lampager\Cake\Model\Behavior\LampagerBehavior;
14
use Lampager\Cake\ORM\Query;
15
use Lampager\Cake\PaginationResult;
16
use Lampager\Cake\Paginator;
17
use Lampager\Cake\Test\TestCase\TestCase;
18
use Lampager\Contracts\Exceptions\LampagerException;
19
use Lampager\Exceptions\Query\BadKeywordException;
20
use Lampager\Exceptions\Query\InsufficientConstraintsException;
21
use Lampager\Exceptions\Query\LimitParameterException;
22
use PHPUnit\Framework\MockObject\MockObject;
23
24
class QueryTest extends TestCase
25
{
26
    public $fixtures = [
27
        'plugin.Lampager\\Cake.Posts',
28
    ];
29
30
    public function setUp(): void
31
    {
32
        parent::setUp();
33
34
        set_error_handler(
35
            static function ($errno, $errstr, $errfile, $errline) {
36
                throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
37
            },
38
            E_ALL
39
        );
40
    }
41
42
    public function tearDown(): void
43
    {
44
        restore_error_handler();
45
46
        parent::tearDown();
47
    }
48
49
    /**
50
     * @dataProvider orderProvider
51
     */
52
    public function testOrder(callable $factory, PaginationResult $expected): void
53
    {
54
        /** @var LampagerBehavior&Table $posts */
55
        $posts = TableRegistry::getTableLocator()->get('Posts');
56
        $posts->addBehavior(LampagerBehavior::class);
57
58
        /** @var Query $query */
59
        $query = $factory($posts);
60
        $this->assertJsonEquals($expected, $query->all());
61
    }
62
63
    /**
64
     * @dataProvider orderProvider
65
     */
66
    public function testOrderClear(callable $factory): void
67
    {
68
        $this->expectException(LampagerException::class);
69
        $this->expectExceptionMessage('At least one order constraint required');
70
71
        /** @var LampagerBehavior&Table $posts */
72
        $posts = TableRegistry::getTableLocator()->get('Posts');
73
        $posts->addBehavior(LampagerBehavior::class);
74
75
        /** @var Query $query */
76
        $query = $factory($posts);
77
        $query->order([], true);
78
        $query->all();
79
    }
80
81
    public function testOrderIllegal(): void
82
    {
83
        $this->expectException(BadKeywordException::class);
84
        $this->expectExceptionMessage('OrderClauseExpression does not have direction');
85
86
        /** @var MockObject&OrderClauseExpression $expression */
87
        $expression = $this->getMockBuilder(OrderClauseExpression::class)->disableOriginalConstructor()->getMock();
88
        $expression->method('sql')->willReturn('modified');
89
90
        /** @var LampagerBehavior&Table $posts */
91
        $posts = TableRegistry::getTableLocator()->get('Posts');
92
        $posts->addBehavior(LampagerBehavior::class);
93
        $posts->lampager()
94
            ->order([$expression])
95
            ->all();
96
    }
97
98
    public function testOrderQueryExpression(): void
99
    {
100
        /** @var LampagerBehavior&Table $posts */
101
        $posts = TableRegistry::getTableLocator()->get('Posts');
102
        $posts->addBehavior(LampagerBehavior::class);
103
104
        $expected = new PaginationResult(
105
            [
106
                new Entity([
107
                    'id' => 1,
108
                    'modified' => new FrozenTime('2017-01-01 10:00:00'),
109
                ]),
110
            ],
111
            [
112
                'hasPrevious' => null,
113
                'previousCursor' => null,
114
                'hasNext' => true,
115
                'nextCursor' => [
116
                    'id' => 3,
117
                    'modified' => new FrozenTime('2017-01-01 10:00:00'),
118
                ],
119
            ]
120
        );
121
122
        $actual = $posts->lampager()
123
            ->order([$posts->query()->newExpr(['modified'])])
124
            ->order([$posts->query()->newExpr(['id'])])
125
            ->limit(1)
126
            ->all();
127
128
        $this->assertJsonEquals($expected, $actual);
129
    }
130
131
    public function testLimitQueryExpression(): void
132
    {
133
        /** @var LampagerBehavior&Table $posts */
134
        $posts = TableRegistry::getTableLocator()->get('Posts');
135
        $posts->addBehavior(LampagerBehavior::class);
136
137
        $expected = new PaginationResult(
138
            [
139
                new Entity([
140
                    'id' => 1,
141
                    'modified' => new FrozenTime('2017-01-01 10:00:00'),
142
                ]),
143
            ],
144
            [
145
                'hasPrevious' => null,
146
                'previousCursor' => null,
147
                'hasNext' => true,
148
                'nextCursor' => [
149
                    'id' => 3,
150
                    'modified' => new FrozenTime('2017-01-01 10:00:00'),
151
                ],
152
            ]
153
        );
154
155
        $actual = $posts->lampager()
156
            ->orderAsc('modified')
157
            ->orderAsc('id')
158
            ->limit($posts->query()->newExpr(['1']))
159
            ->all();
160
161
        $this->assertJsonEquals($expected, $actual);
162
    }
163
164
    public function testLimitIllegalQueryExpression(): void
165
    {
166
        $this->expectException(LimitParameterException::class);
167
        $this->expectExceptionMessage('Limit must be positive integer');
168
169
        /** @var LampagerBehavior&Table $posts */
170
        $posts = TableRegistry::getTableLocator()->get('Posts');
171
        $posts->addBehavior(LampagerBehavior::class);
172
        $posts->lampager()
173
            ->orderAsc('modified')
174
            ->orderAsc('id')
175
            ->limit($posts->query()->newExpr(['1 + 1']))
176
            ->all();
177
    }
178
179
    public function testWhere(): void
180
    {
181
        /** @var LampagerBehavior&Table $posts */
182
        $posts = TableRegistry::getTableLocator()->get('Posts');
183
        $posts->addBehavior(LampagerBehavior::class);
184
185
        $expected = new PaginationResult(
186
            [
187
                new Entity([
188
                    'id' => 3,
189
                    'modified' => new FrozenTime('2017-01-01 10:00:00'),
190
                ]),
191
            ],
192
            [
193
                'hasPrevious' => null,
194
                'previousCursor' => null,
195
                'hasNext' => true,
196
                'nextCursor' => [
197
                    'id' => 5,
198
                    'modified' => new FrozenTime('2017-01-01 10:00:00'),
199
                ],
200
            ]
201
        );
202
203
        $actual = $posts->lampager()
204
            ->where(['id >' => 1])
205
            ->orderAsc('modified')
206
            ->orderAsc('id')
207
            ->limit(1)
208
            ->all();
209
210
        $this->assertJsonEquals($expected, $actual);
211
    }
212
213
    public function testGroup(): void
214
    {
215
        $this->expectException(InsufficientConstraintsException::class);
216
        $this->expectExceptionMessage('group()/union() are not supported');
217
218
        /** @var LampagerBehavior&Table $posts */
219
        $posts = TableRegistry::getTableLocator()->get('Posts');
220
        $posts->addBehavior(LampagerBehavior::class);
221
        $posts->lampager()
222
            ->orderAsc('modified')
223
            ->orderAsc('id')
224
            ->group('modified')
225
            ->all();
226
    }
227
228
    public function testUnion(): void
229
    {
230
        $this->expectException(InsufficientConstraintsException::class);
231
        $this->expectExceptionMessage('group()/union() are not supported');
232
233
        /** @var LampagerBehavior&Table $posts */
234
        $posts = TableRegistry::getTableLocator()->get('Posts');
235
        $posts->addBehavior(LampagerBehavior::class);
236
        $posts->lampager()
237
            ->orderAsc('modified')
238
            ->orderAsc('id')
239
            ->union($posts->query()->select())
240
            ->all();
241
    }
242
243
    public function testCall(): void
244
    {
245
        $this->expectException(\ErrorException::class);
246
        $this->expectExceptionMessage('You must call `all()` first');
247
248
        /** @var LampagerBehavior&Table $posts */
249
        $posts = TableRegistry::getTableLocator()->get('Posts');
250
        $posts->addBehavior(LampagerBehavior::class);
251
        $posts->lampager()
252
            ->orderAsc('id')
253
            ->take();
254
    }
255
256
    public function testDebugInfo(): void
257
    {
258
        /** @var LampagerBehavior&Table $posts */
259
        $posts = TableRegistry::getTableLocator()->get('Posts');
260
        $posts->addBehavior(LampagerBehavior::class);
261
262
        $actual = $posts->lampager()
263
            ->orderAsc('modified')
264
            ->orderAsc('id')
265
            ->limit(3)
266
            ->__debugInfo();
267
268
        $this->assertSame('This is a Lampager Query object to get the paginated results.', $actual['(help)']);
269
        $this->assertInstanceOf(Paginator::class, $actual['paginator']);
270
        $this->assertTextStartsWith('SELECT ', $actual['sql']);
271
        $this->assertSame($posts, $actual['repository']);
272
273
        $this->assertArrayHasKey('params', $actual);
274
        $this->assertArrayHasKey('defaultTypes', $actual);
275
        $this->assertArrayHasKey('decorators', $actual);
276
        $this->assertArrayHasKey('executed', $actual);
277
        $this->assertArrayHasKey('hydrate', $actual);
278
        $this->assertArrayHasKey('buffered', $actual);
279
        $this->assertArrayHasKey('formatters', $actual);
280
        $this->assertArrayHasKey('mapReducers', $actual);
281
        $this->assertArrayHasKey('contain', $actual);
282
        $this->assertArrayHasKey('matching', $actual);
283
        $this->assertArrayHasKey('extraOptions', $actual);
284
    }
285
286
    public function testDebugInfoIncomplete(): void
287
    {
288
        /** @var LampagerBehavior&Table $posts */
289
        $posts = TableRegistry::getTableLocator()->get('Posts');
290
        $posts->addBehavior(LampagerBehavior::class);
291
292
        $actual = $posts->lampager()
293
            ->limit(3)
294
            ->__debugInfo();
295
296
        $this->assertSame('This is a Lampager Query object to get the paginated results.', $actual['(help)']);
297
        $this->assertInstanceOf(Paginator::class, $actual['paginator']);
298
        $this->assertSame('SQL could not be generated for this query as it is incomplete: At least one order constraint required', $actual['sql']);
299
        $this->assertSame($posts, $actual['repository']);
300
301
        $this->assertArrayHasKey('params', $actual);
302
        $this->assertArrayHasKey('defaultTypes', $actual);
303
        $this->assertArrayHasKey('decorators', $actual);
304
        $this->assertArrayHasKey('executed', $actual);
305
        $this->assertArrayHasKey('hydrate', $actual);
306
        $this->assertArrayHasKey('buffered', $actual);
307
        $this->assertArrayHasKey('formatters', $actual);
308
        $this->assertArrayHasKey('mapReducers', $actual);
309
        $this->assertArrayHasKey('contain', $actual);
310
        $this->assertArrayHasKey('matching', $actual);
311
        $this->assertArrayHasKey('extraOptions', $actual);
312
    }
313
314
    /**
315
     * @dataProvider countProvider
316
     */
317
    public function testCount(callable $factory, int $expected): void
318
    {
319
        /** @var LampagerBehavior&Table $posts */
320
        $posts = TableRegistry::getTableLocator()->get('Posts');
321
        $posts->addBehavior(LampagerBehavior::class);
322
323
        $this->assertSame($expected, $factory($posts));
324
    }
325
326
    public function orderProvider(): Generator
327
    {
328
        yield 'Ascending and ascending' => [
329
            function (Table $posts) {
330
                /** @var LampagerBehavior&Table $posts */
331
                return $posts->lampager()
332
                    ->forward()
333
                    ->seekable()
334
                    ->limit(3)
335
                    ->order([
336
                        'modified' => 'asc',
337
                        'id' => 'asc',
338
                    ]);
339
            },
340
            new PaginationResult(
341
                [
342
                    new Entity([
343
                        'id' => 1,
344
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
345
                    ]),
346
                    new Entity([
347
                        'id' => 3,
348
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
349
                    ]),
350
                    new Entity([
351
                        'id' => 5,
352
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
353
                    ]),
354
                ],
355
                [
356
                    'hasPrevious' => null,
357
                    'previousCursor' => null,
358
                    'hasNext' => true,
359
                    'nextCursor' => [
360
                        'id' => 2,
361
                        'modified' => new FrozenTime('2017-01-01 11:00:00'),
362
                    ],
363
                ]
364
            ),
365
        ];
366
367
        yield 'Descending and descending' => [
368
            function (Table $posts) {
369
                /** @var LampagerBehavior&Table $posts */
370
                return $posts->lampager()
371
                    ->forward()
372
                    ->seekable()
373
                    ->limit(3)
374
                    ->order([
375
                        'modified' => 'desc',
376
                        'id' => 'desc',
377
                    ]);
378
            },
379
            new PaginationResult(
380
                [
381
                    new Entity([
382
                        'id' => 4,
383
                        'modified' => new FrozenTime('2017-01-01 11:00:00'),
384
                    ]),
385
                    new Entity([
386
                        'id' => 2,
387
                        'modified' => new FrozenTime('2017-01-01 11:00:00'),
388
                    ]),
389
                    new Entity([
390
                        'id' => 5,
391
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
392
                    ]),
393
                ],
394
                [
395
                    'hasPrevious' => null,
396
                    'previousCursor' => null,
397
                    'hasNext' => true,
398
                    'nextCursor' => [
399
                        'id' => 3,
400
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
401
                    ],
402
                ]
403
            ),
404
        ];
405
    }
406
407
    public function countProvider(): Generator
408
    {
409
        yield 'Ascending forward start inclusive' => [
410
            function (Table $posts) {
411
                /** @var LampagerBehavior&Table $posts */
412
                return $posts->lampager()
413
                    ->forward()
414
                    ->seekable()
415
                    ->limit(3)
416
                    ->orderAsc('modified')
417
                    ->orderAsc('id')
418
                    ->count();
419
            },
420
            3,
421
        ];
422
423
        yield 'Ascending forward start exclusive' => [
424
            function (Table $posts) {
425
                /** @var LampagerBehavior&Table $posts */
426
                return $posts->lampager()
427
                    ->forward()
428
                    ->seekable()
429
                    ->exclusive()
430
                    ->limit(3)
431
                    ->orderAsc('modified')
432
                    ->orderAsc('id')
433
                    ->count();
434
            },
435
            3,
436
        ];
437
438
        yield 'Ascending forward inclusive' => [
439
            function (Table $posts) {
440
                /** @var LampagerBehavior&Table $posts */
441
                return $posts->lampager()
442
                    ->forward()
443
                    ->seekable()
444
                    ->limit(3)
445
                    ->orderAsc('modified')
446
                    ->orderAsc('id')
447
                    ->cursor([
448
                        'id' => 3,
449
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
450
                    ])
451
                    ->count();
452
            },
453
            3,
454
        ];
455
456
        yield 'Ascending forward exclusive' => [
457
            function (Table $posts) {
458
                /** @var LampagerBehavior&Table $posts */
459
                return $posts->lampager()
460
                    ->forward()
461
                    ->seekable()
462
                    ->exclusive()
463
                    ->limit(3)
464
                    ->orderAsc('modified')
465
                    ->orderAsc('id')
466
                    ->cursor([
467
                        'id' => 3,
468
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
469
                    ])
470
                    ->count();
471
            },
472
            3,
473
        ];
474
475
        yield 'Ascending backward start inclusive' => [
476
            function (Table $posts) {
477
                /** @var LampagerBehavior&Table $posts */
478
                return $posts->lampager()
479
                    ->backward()
480
                    ->seekable()
481
                    ->limit(3)
482
                    ->orderAsc('modified')
483
                    ->orderAsc('id')
484
                    ->count();
485
            },
486
            3,
487
        ];
488
489
        yield 'Ascending backward start exclusive' => [
490
            function (Table $posts) {
491
                /** @var LampagerBehavior&Table $posts */
492
                return $posts->lampager()
493
                    ->backward()
494
                    ->seekable()
495
                    ->exclusive()
496
                    ->limit(3)
497
                    ->orderAsc('modified')
498
                    ->orderAsc('id')
499
                    ->count();
500
            },
501
            3,
502
        ];
503
504
        yield 'Ascending backward inclusive' => [
505
            function (Table $posts) {
506
                /** @var LampagerBehavior&Table $posts */
507
                return $posts->lampager()
508
                    ->backward()
509
                    ->seekable()
510
                    ->limit(3)
511
                    ->orderAsc('modified')
512
                    ->orderAsc('id')
513
                    ->cursor([
514
                        'id' => 3,
515
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
516
                    ])
517
                    ->count();
518
            },
519
            2,
520
        ];
521
522
        yield 'Ascending backward exclusive' => [
523
            function (Table $posts) {
524
                /** @var LampagerBehavior&Table $posts */
525
                return $posts->lampager()
526
                    ->backward()
527
                    ->seekable()
528
                    ->exclusive()
529
                    ->limit(3)
530
                    ->orderAsc('modified')
531
                    ->orderAsc('id')
532
                    ->cursor([
533
                        'id' => 3,
534
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
535
                    ])
536
                    ->count();
537
            },
538
            1,
539
        ];
540
541
        yield 'Descending forward start inclusive' => [
542
            function (Table $posts) {
543
                /** @var LampagerBehavior&Table $posts */
544
                return $posts->lampager()
545
                    ->forward()
546
                    ->seekable()
547
                    ->limit(3)
548
                    ->orderDesc('modified')
549
                    ->orderDesc('id')
550
                    ->count();
551
            },
552
            3,
553
        ];
554
555
        yield 'Descending forward start exclusive' => [
556
            function (Table $posts) {
557
                /** @var LampagerBehavior&Table $posts */
558
                return $posts->lampager()
559
                    ->forward()
560
                    ->seekable()
561
                    ->exclusive()
562
                    ->limit(3)
563
                    ->orderDesc('modified')
564
                    ->orderDesc('id')
565
                    ->count();
566
            },
567
            3,
568
        ];
569
570
        yield 'Descending forward inclusive' => [
571
            function (Table $posts) {
572
                /** @var LampagerBehavior&Table $posts */
573
                return $posts->lampager()
574
                    ->forward()
575
                    ->seekable()
576
                    ->limit(3)
577
                    ->orderDesc('modified')
578
                    ->orderDesc('id')
579
                    ->cursor([
580
                        'id' => 3,
581
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
582
                    ])
583
                    ->count();
584
            },
585
            2,
586
        ];
587
588
        yield 'Descending forward exclusive' => [
589
            function (Table $posts) {
590
                /** @var LampagerBehavior&Table $posts */
591
                return $posts->lampager()
592
                    ->forward()
593
                    ->seekable()
594
                    ->exclusive()
595
                    ->limit(3)
596
                    ->orderDesc('modified')
597
                    ->orderDesc('id')
598
                    ->cursor([
599
                        'id' => 3,
600
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
601
                    ])
602
                    ->count();
603
            },
604
            1,
605
        ];
606
607
        yield 'Descending backward start inclusive' => [
608
            function (Table $posts) {
609
                /** @var LampagerBehavior&Table $posts */
610
                return $posts->lampager()
611
                    ->backward()
612
                    ->seekable()
613
                    ->limit(3)
614
                    ->orderDesc('modified')
615
                    ->orderDesc('id')
616
                    ->count();
617
            },
618
            3,
619
        ];
620
621
        yield 'Descending backward start exclusive' => [
622
            function (Table $posts) {
623
                /** @var LampagerBehavior&Table $posts */
624
                return $posts->lampager()
625
                    ->backward()
626
                    ->seekable()
627
                    ->exclusive()
628
                    ->limit(3)
629
                    ->orderDesc('modified')
630
                    ->orderDesc('id')
631
                    ->count();
632
            },
633
            3,
634
        ];
635
636
        yield 'Descending backward inclusive' => [
637
            function (Table $posts) {
638
                /** @var LampagerBehavior&Table $posts */
639
                return $posts->lampager()
640
                    ->backward()
641
                    ->seekable()
642
                    ->limit(3)
643
                    ->orderDesc('modified')
644
                    ->orderDesc('id')
645
                    ->cursor([
646
                        'id' => 3,
647
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
648
                    ])
649
                    ->count();
650
            },
651
            3,
652
        ];
653
654
        yield 'Descending backward exclusive' => [
655
            function (Table $posts) {
656
                /** @var LampagerBehavior&Table $posts */
657
                return $posts->lampager()
658
                    ->backward()
659
                    ->seekable()
660
                    ->exclusive()
661
                    ->limit(3)
662
                    ->orderDesc('modified')
663
                    ->orderDesc('id')
664
                    ->cursor([
665
                        'id' => 3,
666
                        'modified' => new FrozenTime('2017-01-01 10:00:00'),
667
                    ])
668
                    ->count();
669
            },
670
            3,
671
        ];
672
    }
673
}
674