Passed
Push — master ( 0dc7c1...a9f887 )
by Chito
01:43
created

QueryTest::testWhere()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 19
c 1
b 1
f 0
dl 0
loc 32
rs 9.6333
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\DateTime;
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 array $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 testorderBy(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->paginate());
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->orderBy([], true);
78
        $query->paginate();
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
            ->orderBy([$expression])
95
            ->paginate();
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 DateTime('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 DateTime('2017-01-01 10:00:00'),
118
                ],
119
            ]
120
        );
121
122
        $actual = $posts->lampager()
123
            ->orderBy([$posts->selectQuery()->expr('modified')])
124
            ->orderBy([$posts->selectQuery()->expr('id')])
125
            ->limit(1)
126
            ->paginate();
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 DateTime('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 DateTime('2017-01-01 10:00:00'),
151
                ],
152
            ]
153
        );
154
155
        $actual = $posts->lampager()
156
            ->orderByAsc('modified')
157
            ->orderByAsc('id')
158
            ->limit($posts->selectQuery()->expr('1'))
159
            ->paginate();
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
            ->orderByAsc('modified')
174
            ->orderByAsc('id')
175
            ->limit($posts->selectQuery()->expr('1 + 1'))
176
            ->paginate();
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 DateTime('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 DateTime('2017-01-01 10:00:00'),
199
                ],
200
            ]
201
        );
202
203
        $actual = $posts->lampager()
204
            ->where(['id >' => 1])
205
            ->orderByAsc('modified')
206
            ->orderByAsc('id')
207
            ->limit(1)
208
            ->paginate();
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
            ->orderByAsc('modified')
223
            ->orderByAsc('id')
224
            ->groupBy('modified')
225
            ->paginate();
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
            ->orderByAsc('modified')
238
            ->orderByAsc('id')
239
            ->union($posts->selectQuery()->select())
240
            ->paginate();
241
    }
242
243
    public function testCall(): void
244
    {
245
        $this->expectException(\BadMethodCallException::class);
246
        $this->expectExceptionMessage('Method Lampager\Cake\ORM\Query::take does not exist');
247
248
        /** @var LampagerBehavior&Table $posts */
249
        $posts = TableRegistry::getTableLocator()->get('Posts');
250
        $posts->addBehavior(LampagerBehavior::class);
251
        $posts->lampager()
252
            ->orderByAsc('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
            ->orderByAsc('modified')
264
            ->orderByAsc('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('formatters', $actual);
279
        $this->assertArrayHasKey('mapReducers', $actual);
280
        $this->assertArrayHasKey('contain', $actual);
281
        $this->assertArrayHasKey('matching', $actual);
282
        $this->assertArrayHasKey('extraOptions', $actual);
283
    }
284
285
    public function testDebugInfoIncomplete(): void
286
    {
287
        /** @var LampagerBehavior&Table $posts */
288
        $posts = TableRegistry::getTableLocator()->get('Posts');
289
        $posts->addBehavior(LampagerBehavior::class);
290
291
        $actual = $posts->lampager()
292
            ->limit(3)
293
            ->__debugInfo();
294
295
        $this->assertSame('This is a Lampager Query object to get the paginated results.', $actual['(help)']);
296
        $this->assertInstanceOf(Paginator::class, $actual['paginator']);
297
        $this->assertSame('SQL could not be generated for this query as it is incomplete: At least one order constraint required', $actual['sql']);
298
        $this->assertSame($posts, $actual['repository']);
299
300
        $this->assertArrayHasKey('params', $actual);
301
        $this->assertArrayHasKey('defaultTypes', $actual);
302
        $this->assertArrayHasKey('decorators', $actual);
303
        $this->assertArrayHasKey('executed', $actual);
304
        $this->assertArrayHasKey('hydrate', $actual);
305
        $this->assertArrayHasKey('formatters', $actual);
306
        $this->assertArrayHasKey('mapReducers', $actual);
307
        $this->assertArrayHasKey('contain', $actual);
308
        $this->assertArrayHasKey('matching', $actual);
309
        $this->assertArrayHasKey('extraOptions', $actual);
310
    }
311
312
    /**
313
     * @dataProvider countProvider
314
     */
315
    public function testCount(callable $factory, int $expected): void
316
    {
317
        /** @var LampagerBehavior&Table $posts */
318
        $posts = TableRegistry::getTableLocator()->get('Posts');
319
        $posts->addBehavior(LampagerBehavior::class);
320
321
        $this->assertSame($expected, $factory($posts));
322
    }
323
324
    public static function orderProvider(): Generator
325
    {
326
        yield 'Ascending and ascending' => [
327
            function (Table $posts) {
328
                /** @var LampagerBehavior&Table $posts */
329
                return $posts->lampager()
330
                    ->forward()
331
                    ->seekable()
332
                    ->limit(3)
333
                    ->orderBy([
334
                        'modified' => 'asc',
335
                        'id' => 'asc',
336
                    ]);
337
            },
338
            new PaginationResult(
339
                [
340
                    new Entity([
341
                        'id' => 1,
342
                        'modified' => new DateTime('2017-01-01 10:00:00'),
343
                    ]),
344
                    new Entity([
345
                        'id' => 3,
346
                        'modified' => new DateTime('2017-01-01 10:00:00'),
347
                    ]),
348
                    new Entity([
349
                        'id' => 5,
350
                        'modified' => new DateTime('2017-01-01 10:00:00'),
351
                    ]),
352
                ],
353
                [
354
                    'hasPrevious' => null,
355
                    'previousCursor' => null,
356
                    'hasNext' => true,
357
                    'nextCursor' => [
358
                        'id' => 2,
359
                        'modified' => new DateTime('2017-01-01 11:00:00'),
360
                    ],
361
                ]
362
            ),
363
        ];
364
365
        yield 'Descending and descending' => [
366
            function (Table $posts) {
367
                /** @var LampagerBehavior&Table $posts */
368
                return $posts->lampager()
369
                    ->forward()
370
                    ->seekable()
371
                    ->limit(3)
372
                    ->orderBy([
373
                        'modified' => 'desc',
374
                        'id' => 'desc',
375
                    ]);
376
            },
377
            new PaginationResult(
378
                [
379
                    new Entity([
380
                        'id' => 4,
381
                        'modified' => new DateTime('2017-01-01 11:00:00'),
382
                    ]),
383
                    new Entity([
384
                        'id' => 2,
385
                        'modified' => new DateTime('2017-01-01 11:00:00'),
386
                    ]),
387
                    new Entity([
388
                        'id' => 5,
389
                        'modified' => new DateTime('2017-01-01 10:00:00'),
390
                    ]),
391
                ],
392
                [
393
                    'hasPrevious' => null,
394
                    'previousCursor' => null,
395
                    'hasNext' => true,
396
                    'nextCursor' => [
397
                        'id' => 3,
398
                        'modified' => new DateTime('2017-01-01 10:00:00'),
399
                    ],
400
                ]
401
            ),
402
        ];
403
    }
404
405
    public static function countProvider(): Generator
406
    {
407
        yield 'Ascending forward start inclusive' => [
408
            function (Table $posts) {
409
                /** @var LampagerBehavior&Table $posts */
410
                return $posts->lampager()
411
                    ->forward()
412
                    ->seekable()
413
                    ->limit(3)
414
                    ->orderByAsc('modified')
415
                    ->orderByAsc('id')
416
                    ->count();
417
            },
418
            3,
419
        ];
420
421
        yield 'Ascending forward start exclusive' => [
422
            function (Table $posts) {
423
                /** @var LampagerBehavior&Table $posts */
424
                return $posts->lampager()
425
                    ->forward()
426
                    ->seekable()
427
                    ->exclusive()
428
                    ->limit(3)
429
                    ->orderByAsc('modified')
430
                    ->orderByAsc('id')
431
                    ->count();
432
            },
433
            3,
434
        ];
435
436
        yield 'Ascending forward inclusive' => [
437
            function (Table $posts) {
438
                /** @var LampagerBehavior&Table $posts */
439
                return $posts->lampager()
440
                    ->forward()
441
                    ->seekable()
442
                    ->limit(3)
443
                    ->orderByAsc('modified')
444
                    ->orderByAsc('id')
445
                    ->cursor([
446
                        'id' => 3,
447
                        'modified' => new DateTime('2017-01-01 10:00:00'),
448
                    ])
449
                    ->count();
450
            },
451
            3,
452
        ];
453
454
        yield 'Ascending forward exclusive' => [
455
            function (Table $posts) {
456
                /** @var LampagerBehavior&Table $posts */
457
                return $posts->lampager()
458
                    ->forward()
459
                    ->seekable()
460
                    ->exclusive()
461
                    ->limit(3)
462
                    ->orderByAsc('modified')
463
                    ->orderByAsc('id')
464
                    ->cursor([
465
                        'id' => 3,
466
                        'modified' => new DateTime('2017-01-01 10:00:00'),
467
                    ])
468
                    ->count();
469
            },
470
            3,
471
        ];
472
473
        yield 'Ascending backward start inclusive' => [
474
            function (Table $posts) {
475
                /** @var LampagerBehavior&Table $posts */
476
                return $posts->lampager()
477
                    ->backward()
478
                    ->seekable()
479
                    ->limit(3)
480
                    ->orderByAsc('modified')
481
                    ->orderByAsc('id')
482
                    ->count();
483
            },
484
            3,
485
        ];
486
487
        yield 'Ascending backward start exclusive' => [
488
            function (Table $posts) {
489
                /** @var LampagerBehavior&Table $posts */
490
                return $posts->lampager()
491
                    ->backward()
492
                    ->seekable()
493
                    ->exclusive()
494
                    ->limit(3)
495
                    ->orderByAsc('modified')
496
                    ->orderByAsc('id')
497
                    ->count();
498
            },
499
            3,
500
        ];
501
502
        yield 'Ascending backward inclusive' => [
503
            function (Table $posts) {
504
                /** @var LampagerBehavior&Table $posts */
505
                return $posts->lampager()
506
                    ->backward()
507
                    ->seekable()
508
                    ->limit(3)
509
                    ->orderByAsc('modified')
510
                    ->orderByAsc('id')
511
                    ->cursor([
512
                        'id' => 3,
513
                        'modified' => new DateTime('2017-01-01 10:00:00'),
514
                    ])
515
                    ->count();
516
            },
517
            2,
518
        ];
519
520
        yield 'Ascending backward exclusive' => [
521
            function (Table $posts) {
522
                /** @var LampagerBehavior&Table $posts */
523
                return $posts->lampager()
524
                    ->backward()
525
                    ->seekable()
526
                    ->exclusive()
527
                    ->limit(3)
528
                    ->orderByAsc('modified')
529
                    ->orderByAsc('id')
530
                    ->cursor([
531
                        'id' => 3,
532
                        'modified' => new DateTime('2017-01-01 10:00:00'),
533
                    ])
534
                    ->count();
535
            },
536
            1,
537
        ];
538
539
        yield 'Descending forward start inclusive' => [
540
            function (Table $posts) {
541
                /** @var LampagerBehavior&Table $posts */
542
                return $posts->lampager()
543
                    ->forward()
544
                    ->seekable()
545
                    ->limit(3)
546
                    ->orderByDesc('modified')
547
                    ->orderByDesc('id')
548
                    ->count();
549
            },
550
            3,
551
        ];
552
553
        yield 'Descending forward start exclusive' => [
554
            function (Table $posts) {
555
                /** @var LampagerBehavior&Table $posts */
556
                return $posts->lampager()
557
                    ->forward()
558
                    ->seekable()
559
                    ->exclusive()
560
                    ->limit(3)
561
                    ->orderByDesc('modified')
562
                    ->orderByDesc('id')
563
                    ->count();
564
            },
565
            3,
566
        ];
567
568
        yield 'Descending forward inclusive' => [
569
            function (Table $posts) {
570
                /** @var LampagerBehavior&Table $posts */
571
                return $posts->lampager()
572
                    ->forward()
573
                    ->seekable()
574
                    ->limit(3)
575
                    ->orderByDesc('modified')
576
                    ->orderByDesc('id')
577
                    ->cursor([
578
                        'id' => 3,
579
                        'modified' => new DateTime('2017-01-01 10:00:00'),
580
                    ])
581
                    ->count();
582
            },
583
            2,
584
        ];
585
586
        yield 'Descending forward exclusive' => [
587
            function (Table $posts) {
588
                /** @var LampagerBehavior&Table $posts */
589
                return $posts->lampager()
590
                    ->forward()
591
                    ->seekable()
592
                    ->exclusive()
593
                    ->limit(3)
594
                    ->orderByDesc('modified')
595
                    ->orderByDesc('id')
596
                    ->cursor([
597
                        'id' => 3,
598
                        'modified' => new DateTime('2017-01-01 10:00:00'),
599
                    ])
600
                    ->count();
601
            },
602
            1,
603
        ];
604
605
        yield 'Descending backward start inclusive' => [
606
            function (Table $posts) {
607
                /** @var LampagerBehavior&Table $posts */
608
                return $posts->lampager()
609
                    ->backward()
610
                    ->seekable()
611
                    ->limit(3)
612
                    ->orderByDesc('modified')
613
                    ->orderByDesc('id')
614
                    ->count();
615
            },
616
            3,
617
        ];
618
619
        yield 'Descending backward start exclusive' => [
620
            function (Table $posts) {
621
                /** @var LampagerBehavior&Table $posts */
622
                return $posts->lampager()
623
                    ->backward()
624
                    ->seekable()
625
                    ->exclusive()
626
                    ->limit(3)
627
                    ->orderByDesc('modified')
628
                    ->orderByDesc('id')
629
                    ->count();
630
            },
631
            3,
632
        ];
633
634
        yield 'Descending backward inclusive' => [
635
            function (Table $posts) {
636
                /** @var LampagerBehavior&Table $posts */
637
                return $posts->lampager()
638
                    ->backward()
639
                    ->seekable()
640
                    ->limit(3)
641
                    ->orderByDesc('modified')
642
                    ->orderByDesc('id')
643
                    ->cursor([
644
                        'id' => 3,
645
                        'modified' => new DateTime('2017-01-01 10:00:00'),
646
                    ])
647
                    ->count();
648
            },
649
            3,
650
        ];
651
652
        yield 'Descending backward exclusive' => [
653
            function (Table $posts) {
654
                /** @var LampagerBehavior&Table $posts */
655
                return $posts->lampager()
656
                    ->backward()
657
                    ->seekable()
658
                    ->exclusive()
659
                    ->limit(3)
660
                    ->orderByDesc('modified')
661
                    ->orderByDesc('id')
662
                    ->cursor([
663
                        'id' => 3,
664
                        'modified' => new DateTime('2017-01-01 10:00:00'),
665
                    ])
666
                    ->count();
667
            },
668
            3,
669
        ];
670
    }
671
}
672