Completed
Pull Request — master (#3588)
by Andrej
19:46
created

ConnectionTest::testRollackReturnTrue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 18
rs 9.8666
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\DBAL;
4
5
use Doctrine\Common\Cache\Cache;
6
use Doctrine\Common\EventManager;
7
use Doctrine\DBAL\Cache\ArrayStatement;
8
use Doctrine\DBAL\Cache\QueryCacheProfile;
9
use Doctrine\DBAL\Configuration;
10
use Doctrine\DBAL\Connection;
11
use Doctrine\DBAL\ConnectionException;
12
use Doctrine\DBAL\DBALException;
13
use Doctrine\DBAL\Driver;
14
use Doctrine\DBAL\Driver\Connection as DriverConnection;
15
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
16
use Doctrine\DBAL\Driver\Statement;
17
use Doctrine\DBAL\DriverManager;
18
use Doctrine\DBAL\Events;
19
use Doctrine\DBAL\Exception\InvalidArgumentException;
20
use Doctrine\DBAL\FetchMode;
21
use Doctrine\DBAL\Logging\DebugStack;
22
use Doctrine\DBAL\Logging\EchoSQLLogger;
23
use Doctrine\DBAL\ParameterType;
24
use Doctrine\DBAL\Platforms\AbstractPlatform;
25
use Doctrine\DBAL\VersionAwarePlatformDriver;
26
use Doctrine\Tests\DbalTestCase;
27
use Exception;
28
use PDO;
29
use PHPUnit\Framework\MockObject\MockObject;
30
use stdClass;
31
use function call_user_func_array;
32
33
/**
34
 * @requires extension pdo_mysql
35
 */
36
class ConnectionTest extends DbalTestCase
37
{
38
    /** @var Connection */
39
    private $connection;
40
41
    /** @var string[] */
42
    protected $params = [
43
        'driver' => 'pdo_mysql',
44
        'host' => 'localhost',
45
        'user' => 'root',
46
        'password' => 'password',
47
        'port' => '1234',
48
    ];
49
50
    protected function setUp() : void
51
    {
52
        $this->connection = DriverManager::getConnection($this->params);
53
    }
54
55
    /**
56
     * @return Connection|MockObject
57
     */
58
    private function getExecuteUpdateMockConnection()
59
    {
60
        $driverMock = $this->createMock(Driver::class);
61
62
        $driverMock->expects($this->any())
63
            ->method('connect')
64
            ->will($this->returnValue(
65
                $this->createMock(DriverConnection::class)
66
            ));
67
68
        $platform = $this->getMockForAbstractClass(AbstractPlatform::class);
69
70
        return $this->getMockBuilder(Connection::class)
71
            ->setMethods(['executeUpdate'])
72
            ->setConstructorArgs([['platform' => $platform], $driverMock])
73
            ->getMock();
74
    }
75
76
    public function testIsConnected() : void
77
    {
78
        self::assertFalse($this->connection->isConnected());
79
    }
80
81
    public function testNoTransactionActiveByDefault() : void
82
    {
83
        self::assertFalse($this->connection->isTransactionActive());
84
    }
85
86
    public function testCommitWithNoActiveTransactionThrowsException() : void
87
    {
88
        $this->expectException(ConnectionException::class);
89
        $this->connection->commit();
90
    }
91
92
    public function testRollbackWithNoActiveTransactionThrowsException() : void
93
    {
94
        $this->expectException(ConnectionException::class);
95
        $this->connection->rollBack();
96
    }
97
98
    public function testSetRollbackOnlyNoActiveTransactionThrowsException() : void
99
    {
100
        $this->expectException(ConnectionException::class);
101
        $this->connection->setRollbackOnly();
102
    }
103
104
    public function testIsRollbackOnlyNoActiveTransactionThrowsException() : void
105
    {
106
        $this->expectException(ConnectionException::class);
107
        $this->connection->isRollbackOnly();
108
    }
109
110
    public function testGetConfiguration() : void
111
    {
112
        $config = $this->connection->getConfiguration();
113
114
        self::assertInstanceOf(Configuration::class, $config);
115
    }
116
117
    public function testGetHost() : void
118
    {
119
        self::assertEquals('localhost', $this->connection->getHost());
120
    }
121
122
    public function testGetPort() : void
123
    {
124
        self::assertEquals('1234', $this->connection->getPort());
125
    }
126
127
    public function testGetUsername() : void
128
    {
129
        self::assertEquals('root', $this->connection->getUsername());
130
    }
131
132
    public function testGetPassword() : void
133
    {
134
        self::assertEquals('password', $this->connection->getPassword());
135
    }
136
137
    public function testGetDriver() : void
138
    {
139
        self::assertInstanceOf(\Doctrine\DBAL\Driver\PDOMySql\Driver::class, $this->connection->getDriver());
140
    }
141
142
    public function testGetEventManager() : void
143
    {
144
        self::assertInstanceOf(EventManager::class, $this->connection->getEventManager());
145
    }
146
147
    public function testConnectDispatchEvent() : void
148
    {
149
        $listenerMock = $this->getMockBuilder('ConnectDispatchEventListener')
150
            ->setMethods(['postConnect'])
151
            ->getMock();
152
        $listenerMock->expects($this->once())->method('postConnect');
153
154
        $eventManager = new EventManager();
155
        $eventManager->addEventListener([Events::postConnect], $listenerMock);
156
157
        $driverMock = $this->createMock(Driver::class);
158
        $driverMock->expects($this->at(0))
159
                   ->method('connect');
160
161
        $conn = new Connection([], $driverMock, new Configuration(), $eventManager);
162
        $conn->connect();
163
    }
164
165
    public function testEventManagerPassedToPlatform() : void
166
    {
167
        $eventManager = new EventManager();
168
169
        /** @var AbstractPlatform|MockObject $driver */
170
        $platform = $this->createMock(AbstractPlatform::class);
171
        $platform->expects($this->once())
172
            ->method('setEventManager')
173
            ->with($eventManager);
174
175
        /** @var Driver|MockObject $driver */
176
        $driver = $this->createMock(Driver::class);
177
        $driver->expects($this->any())
178
            ->method('getDatabasePlatform')
179
            ->willReturn($platform);
180
181
        $connection = new Connection($this->params, $driver, null, $eventManager);
182
        $connection->getDatabasePlatform();
183
    }
184
185
    /**
186
     * @requires extension pdo_sqlite
187
     * @dataProvider getQueryMethods
188
     */
189
    public function testDriverExceptionIsWrapped(string $method) : void
190
    {
191
        $this->expectException(DBALException::class);
192
        $this->expectExceptionMessage("An exception occurred while executing 'MUUHAAAAHAAAA':\n\nSQLSTATE[HY000]: General error: 1 near \"MUUHAAAAHAAAA\"");
193
194
        $connection = DriverManager::getConnection([
195
            'driver' => 'pdo_sqlite',
196
            'memory' => true,
197
        ]);
198
199
        $connection->$method('MUUHAAAAHAAAA');
200
    }
201
202
    /**
203
     * @return array<int, array<int, mixed>>
204
     */
205
    public static function getQueryMethods() : iterable
206
    {
207
        return [
208
            ['exec'],
209
            ['query'],
210
            ['executeQuery'],
211
            ['executeUpdate'],
212
            ['prepare'],
213
        ];
214
    }
215
216
    /**
217
     * Pretty dumb test, however we want to check that the EchoSQLLogger correctly implements the interface.
218
     *
219
     * @group DBAL-11
220
     */
221
    public function testEchoSQLLogger() : void
222
    {
223
        $logger = new EchoSQLLogger();
224
        $this->connection->getConfiguration()->setSQLLogger($logger);
225
        self::assertSame($logger, $this->connection->getConfiguration()->getSQLLogger());
226
    }
227
228
    /**
229
     * Pretty dumb test, however we want to check that the DebugStack correctly implements the interface.
230
     *
231
     * @group DBAL-11
232
     */
233
    public function testDebugSQLStack() : void
234
    {
235
        $logger = new DebugStack();
236
        $this->connection->getConfiguration()->setSQLLogger($logger);
237
        self::assertSame($logger, $this->connection->getConfiguration()->getSQLLogger());
238
    }
239
240
    /**
241
     * @group DBAL-81
242
     */
243
    public function testIsAutoCommit() : void
244
    {
245
        self::assertTrue($this->connection->isAutoCommit());
246
    }
247
248
    /**
249
     * @group DBAL-81
250
     */
251
    public function testSetAutoCommit() : void
252
    {
253
        $this->connection->setAutoCommit(false);
254
        self::assertFalse($this->connection->isAutoCommit());
255
        $this->connection->setAutoCommit(0);
256
        self::assertFalse($this->connection->isAutoCommit());
257
    }
258
259
    /**
260
     * @group DBAL-81
261
     */
262
    public function testConnectStartsTransactionInNoAutoCommitMode() : void
263
    {
264
        $driverMock = $this->createMock(Driver::class);
265
        $driverMock->expects($this->any())
266
            ->method('connect')
267
            ->will($this->returnValue(
268
                $this->createMock(DriverConnection::class)
269
            ));
270
        $conn = new Connection([], $driverMock);
271
272
        $conn->setAutoCommit(false);
273
274
        self::assertFalse($conn->isTransactionActive());
275
276
        $conn->connect();
277
278
        self::assertTrue($conn->isTransactionActive());
279
    }
280
281
    /**
282
     * @group DBAL-81
283
     */
284
    public function testCommitStartsTransactionInNoAutoCommitMode() : void
285
    {
286
        $driverMock = $this->createMock(Driver::class);
287
        $driverMock->expects($this->any())
288
            ->method('connect')
289
            ->will($this->returnValue(
290
                $this->createMock(DriverConnection::class)
291
            ));
292
        $conn = new Connection([], $driverMock);
293
294
        $conn->setAutoCommit(false);
295
        $conn->connect();
296
        $conn->commit();
297
298
        self::assertTrue($conn->isTransactionActive());
299
    }
300
301
    /**
302
     * @group DBAL-81
303
     */
304
    public function testCommitReturnTrue() : void
305
    {
306
        $driverMock = $this->createMock(Driver::class);
307
        $driverMock->expects($this->any())
308
            ->method('connect')
309
            ->will($this->returnValue(
310
                $this->createMock(DriverConnection::class)
311
            ));
312
        $pdo = $this->createMock(PDO::class);
313
        $pdo->expects($this->once())
314
            ->method('commit')->willReturn(true);
315
316
        $conn = new Connection(['pdo' => $pdo], $driverMock);
317
318
        $conn->connect();
319
        $conn->beginTransaction();
320
321
        self::assertTrue($conn->commit());
322
    }
323
324
    /**
325
     * @group DBAL-81
326
     */
327
    public function testCommitReturnFalse() : void
328
    {
329
        $driverMock = $this->createMock(Driver::class);
330
        $driverMock->expects($this->any())
331
            ->method('connect')
332
            ->will($this->returnValue(
333
                $this->createMock(DriverConnection::class)
334
            ));
335
        $pdo = $this->createMock(PDO::class);
336
        $pdo->expects($this->once())
337
            ->method('commit')->willReturn(false);
338
339
        $conn = new Connection(['pdo' => $pdo], $driverMock);
340
341
        $conn->connect();
342
        $conn->beginTransaction();
343
344
        self::assertFalse($conn->commit());
345
    }
346
347
    /**
348
     * @group DBAL-81
349
     */
350
    public function testRollackReturnTrue() : void
351
    {
352
        $driverMock = $this->createMock(Driver::class);
353
        $driverMock->expects($this->any())
354
            ->method('connect')
355
            ->will($this->returnValue(
356
                $this->createMock(DriverConnection::class)
357
            ));
358
        $pdo = $this->createMock(PDO::class);
359
        $pdo->expects($this->once())
360
            ->method('rollback')->willReturn(true);
361
362
        $conn = new Connection(['pdo' => $pdo], $driverMock);
363
364
        $conn->connect();
365
        $conn->beginTransaction();
366
367
        self::assertTrue($conn->rollback());
368
    }
369
370
    /**
371
     * @group DBAL-81
372
     */
373
    public function testRollackReturnFalse() : void
374
    {
375
        $driverMock = $this->createMock(Driver::class);
376
        $driverMock->expects($this->any())
377
            ->method('connect')
378
            ->will($this->returnValue(
379
                $this->createMock(DriverConnection::class)
380
            ));
381
        $pdo = $this->createMock(PDO::class);
382
        $pdo->expects($this->once())
383
            ->method('rollback')->willReturn(false);
384
385
        $conn = new Connection(['pdo' => $pdo], $driverMock);
386
387
        $conn->connect();
388
        $conn->beginTransaction();
389
390
        self::assertFalse($conn->rollback());
391
    }
392
393
    /**
394
     * @group DBAL-81
395
     */
396
    public function testRollBackStartsTransactionInNoAutoCommitMode() : void
397
    {
398
        $driverMock = $this->createMock(Driver::class);
399
        $driverMock->expects($this->any())
400
            ->method('connect')
401
            ->will($this->returnValue(
402
                $this->createMock(DriverConnection::class)
403
            ));
404
        $conn = new Connection([], $driverMock);
405
406
        $conn->setAutoCommit(false);
407
        $conn->connect();
408
        $conn->rollBack();
409
410
        self::assertTrue($conn->isTransactionActive());
411
    }
412
413
    /**
414
     * @group DBAL-81
415
     */
416
    public function testSwitchingAutoCommitModeCommitsAllCurrentTransactions() : void
417
    {
418
        $driverMock = $this->createMock(Driver::class);
419
        $driverMock->expects($this->any())
420
            ->method('connect')
421
            ->will($this->returnValue(
422
                $this->createMock(DriverConnection::class)
423
            ));
424
        $conn = new Connection([], $driverMock);
425
426
        $conn->connect();
427
        $conn->beginTransaction();
428
        $conn->beginTransaction();
429
        $conn->setAutoCommit(false);
430
431
        self::assertSame(1, $conn->getTransactionNestingLevel());
432
433
        $conn->beginTransaction();
434
        $conn->beginTransaction();
435
        $conn->setAutoCommit(true);
436
437
        self::assertFalse($conn->isTransactionActive());
438
    }
439
440
    public function testEmptyInsert() : void
441
    {
442
        $conn = $this->getExecuteUpdateMockConnection();
443
444
        $conn->expects($this->once())
445
            ->method('executeUpdate')
446
            ->with('INSERT INTO footable () VALUES ()');
447
448
        $conn->insert('footable', []);
449
    }
450
451
    /**
452
     * @group DBAL-2511
453
     */
454
    public function testUpdateWithDifferentColumnsInDataAndIdentifiers() : void
455
    {
456
        $conn = $this->getExecuteUpdateMockConnection();
457
458
        $conn->expects($this->once())
459
            ->method('executeUpdate')
460
            ->with(
461
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND name = ?',
462
                [
463
                    'some text',
464
                    true,
465
                    1,
466
                    'foo',
467
                ],
468
                [
469
                    'string',
470
                    'boolean',
471
                    'integer',
472
                    'string',
473
                ]
474
            );
475
476
        $conn->update(
477
            'TestTable',
478
            [
479
                'text' => 'some text',
480
                'is_edited' => true,
481
            ],
482
            [
483
                'id' => 1,
484
                'name' => 'foo',
485
            ],
486
            [
487
                'text' => 'string',
488
                'is_edited' => 'boolean',
489
                'id' => 'integer',
490
                'name' => 'string',
491
            ]
492
        );
493
    }
494
495
    /**
496
     * @group DBAL-2511
497
     */
498
    public function testUpdateWithSameColumnInDataAndIdentifiers() : void
499
    {
500
        $conn = $this->getExecuteUpdateMockConnection();
501
502
        $conn->expects($this->once())
503
            ->method('executeUpdate')
504
            ->with(
505
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND is_edited = ?',
506
                [
507
                    'some text',
508
                    true,
509
                    1,
510
                    false,
511
                ],
512
                [
513
                    'string',
514
                    'boolean',
515
                    'integer',
516
                    'boolean',
517
                ]
518
            );
519
520
        $conn->update(
521
            'TestTable',
522
            [
523
                'text' => 'some text',
524
                'is_edited' => true,
525
            ],
526
            [
527
                'id' => 1,
528
                'is_edited' => false,
529
            ],
530
            [
531
                'text' => 'string',
532
                'is_edited' => 'boolean',
533
                'id' => 'integer',
534
            ]
535
        );
536
    }
537
538
    /**
539
     * @group DBAL-2688
540
     */
541
    public function testUpdateWithIsNull() : void
542
    {
543
        $conn = $this->getExecuteUpdateMockConnection();
544
545
        $conn->expects($this->once())
546
            ->method('executeUpdate')
547
            ->with(
548
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id IS NULL AND name = ?',
549
                [
550
                    'some text',
551
                    null,
552
                    'foo',
553
                ],
554
                [
555
                    'string',
556
                    'boolean',
557
                    'string',
558
                ]
559
            );
560
561
        $conn->update(
562
            'TestTable',
563
            [
564
                'text' => 'some text',
565
                'is_edited' => null,
566
            ],
567
            [
568
                'id' => null,
569
                'name' => 'foo',
570
            ],
571
            [
572
                'text' => 'string',
573
                'is_edited' => 'boolean',
574
                'id' => 'integer',
575
                'name' => 'string',
576
            ]
577
        );
578
    }
579
580
    /**
581
     * @group DBAL-2688
582
     */
583
    public function testDeleteWithIsNull() : void
584
    {
585
        $conn = $this->getExecuteUpdateMockConnection();
586
587
        $conn->expects($this->once())
588
            ->method('executeUpdate')
589
            ->with(
590
                'DELETE FROM TestTable WHERE id IS NULL AND name = ?',
591
                ['foo'],
592
                ['string']
593
            );
594
595
        $conn->delete(
596
            'TestTable',
597
            [
598
                'id' => null,
599
                'name' => 'foo',
600
            ],
601
            [
602
                'id' => 'integer',
603
                'name' => 'string',
604
            ]
605
        );
606
    }
607
608
    public function testFetchAssoc() : void
609
    {
610
        $statement = 'SELECT * FROM foo WHERE bar = ?';
611
        $params    = [666];
612
        $types     = [ParameterType::INTEGER];
613
        $result    = [];
614
615
        $driverMock = $this->createMock(Driver::class);
616
617
        $driverMock->expects($this->any())
618
            ->method('connect')
619
            ->will($this->returnValue(
620
                $this->createMock(DriverConnection::class)
621
            ));
622
623
        $driverStatementMock = $this->createMock(Statement::class);
624
625
        $driverStatementMock->expects($this->once())
626
            ->method('fetch')
627
            ->with(FetchMode::ASSOCIATIVE)
628
            ->will($this->returnValue($result));
629
630
        /** @var Connection|MockObject $conn */
631
        $conn = $this->getMockBuilder(Connection::class)
632
            ->setMethods(['executeQuery'])
633
            ->setConstructorArgs([[], $driverMock])
634
            ->getMock();
635
636
        $conn->expects($this->once())
637
            ->method('executeQuery')
638
            ->with($statement, $params, $types)
639
            ->will($this->returnValue($driverStatementMock));
640
641
        self::assertSame($result, $conn->fetchAssoc($statement, $params, $types));
642
    }
643
644
    public function testFetchArray() : void
645
    {
646
        $statement = 'SELECT * FROM foo WHERE bar = ?';
647
        $params    = [666];
648
        $types     = [ParameterType::INTEGER];
649
        $result    = [];
650
651
        $driverMock = $this->createMock(Driver::class);
652
653
        $driverMock->expects($this->any())
654
            ->method('connect')
655
            ->will($this->returnValue(
656
                $this->createMock(DriverConnection::class)
657
            ));
658
659
        $driverStatementMock = $this->createMock(Statement::class);
660
661
        $driverStatementMock->expects($this->once())
662
            ->method('fetch')
663
            ->with(FetchMode::NUMERIC)
664
            ->will($this->returnValue($result));
665
666
        /** @var Connection|MockObject $conn */
667
        $conn = $this->getMockBuilder(Connection::class)
668
            ->setMethods(['executeQuery'])
669
            ->setConstructorArgs([[], $driverMock])
670
            ->getMock();
671
672
        $conn->expects($this->once())
673
            ->method('executeQuery')
674
            ->with($statement, $params, $types)
675
            ->will($this->returnValue($driverStatementMock));
676
677
        self::assertSame($result, $conn->fetchArray($statement, $params, $types));
678
    }
679
680
    public function testFetchColumn() : void
681
    {
682
        $statement = 'SELECT * FROM foo WHERE bar = ?';
683
        $params    = [666];
684
        $types     = [ParameterType::INTEGER];
685
        $column    = 0;
686
        $result    = [];
687
688
        $driverMock = $this->createMock(Driver::class);
689
690
        $driverMock->expects($this->any())
691
            ->method('connect')
692
            ->will($this->returnValue(
693
                $this->createMock(DriverConnection::class)
694
            ));
695
696
        $driverStatementMock = $this->createMock(Statement::class);
697
698
        $driverStatementMock->expects($this->once())
699
            ->method('fetchColumn')
700
            ->with($column)
701
            ->will($this->returnValue($result));
702
703
        /** @var Connection|MockObject $conn */
704
        $conn = $this->getMockBuilder(Connection::class)
705
            ->setMethods(['executeQuery'])
706
            ->setConstructorArgs([[], $driverMock])
707
            ->getMock();
708
709
        $conn->expects($this->once())
710
            ->method('executeQuery')
711
            ->with($statement, $params, $types)
712
            ->will($this->returnValue($driverStatementMock));
713
714
        self::assertSame($result, $conn->fetchColumn($statement, $params, $column, $types));
715
    }
716
717
    public function testFetchAll() : void
718
    {
719
        $statement = 'SELECT * FROM foo WHERE bar = ?';
720
        $params    = [666];
721
        $types     = [ParameterType::INTEGER];
722
        $result    = [];
723
724
        $driverMock = $this->createMock(Driver::class);
725
726
        $driverMock->expects($this->any())
727
            ->method('connect')
728
            ->will($this->returnValue(
729
                $this->createMock(DriverConnection::class)
730
            ));
731
732
        $driverStatementMock = $this->createMock(Statement::class);
733
734
        $driverStatementMock->expects($this->once())
735
            ->method('fetchAll')
736
            ->will($this->returnValue($result));
737
738
        /** @var Connection|MockObject $conn */
739
        $conn = $this->getMockBuilder(Connection::class)
740
            ->setMethods(['executeQuery'])
741
            ->setConstructorArgs([[], $driverMock])
742
            ->getMock();
743
744
        $conn->expects($this->once())
745
            ->method('executeQuery')
746
            ->with($statement, $params, $types)
747
            ->will($this->returnValue($driverStatementMock));
748
749
        self::assertSame($result, $conn->fetchAll($statement, $params, $types));
750
    }
751
752
    public function testConnectionDoesNotMaintainTwoReferencesToExternalPDO() : void
753
    {
754
        $params['pdo'] = new stdClass();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
755
756
        $driverMock = $this->createMock(Driver::class);
757
758
        $conn = new Connection($params, $driverMock);
759
760
        self::assertArrayNotHasKey('pdo', $conn->getParams(), 'Connection is maintaining additional reference to the PDO connection');
761
    }
762
763
    public function testPassingExternalPDOMeansConnectionIsConnected() : void
764
    {
765
        $params['pdo'] = new stdClass();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
766
767
        $driverMock = $this->createMock(Driver::class);
768
769
        $conn = new Connection($params, $driverMock);
770
771
        self::assertTrue($conn->isConnected(), 'Connection is not connected after passing external PDO');
772
    }
773
774
    public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException() : void
775
    {
776
        /** @var Driver $driver */
777
        $driver  = $this->createMock(Driver::class);
778
        $pdoMock = $this->createMock(\Doctrine\DBAL\Driver\Connection::class);
779
780
        // should never execute queries with invalid arguments
781
        $pdoMock->expects($this->never())->method('exec');
782
        $pdoMock->expects($this->never())->method('prepare');
783
784
        $conn = new Connection(['pdo' => $pdoMock], $driver);
785
786
        $this->expectException(InvalidArgumentException::class);
787
        $conn->delete('kittens', []);
788
    }
789
790
    /**
791
     * @return array<int, array<int, mixed>>
792
     */
793
    public static function dataCallConnectOnce() : iterable
794
    {
795
        return [
796
            ['delete', ['tbl', ['id' => 12345]]],
797
            ['insert', ['tbl', ['data' => 'foo']]],
798
            ['update', ['tbl', ['data' => 'bar'], ['id' => 12345]]],
799
            ['prepare', ['select * from dual']],
800
            ['executeUpdate', ['insert into tbl (id) values (?)'], [123]],
801
        ];
802
    }
803
804
    /**
805
     * @param array<int, mixed> $params
806
     *
807
     * @dataProvider dataCallConnectOnce
808
     */
809
    public function testCallConnectOnce(string $method, array $params) : void
810
    {
811
        $driverMock   = $this->createMock(Driver::class);
812
        $pdoMock      = $this->createMock(Connection::class);
813
        $platformMock = $this->createMock(AbstractPlatform::class);
814
        $stmtMock     = $this->createMock(Statement::class);
815
816
        $pdoMock->expects($this->any())
817
            ->method('prepare')
818
            ->will($this->returnValue($stmtMock));
819
820
        $conn = $this->getMockBuilder(Connection::class)
821
            ->setConstructorArgs([['pdo' => $pdoMock, 'platform' => $platformMock], $driverMock])
822
            ->setMethods(['connect'])
823
            ->getMock();
824
825
        $conn->expects($this->once())->method('connect');
826
827
        call_user_func_array([$conn, $method], $params);
828
    }
829
830
    /**
831
     * @group DBAL-1127
832
     */
833
    public function testPlatformDetectionIsTriggerOnlyOnceOnRetrievingPlatform() : void
834
    {
835
        /** @var Driver|VersionAwarePlatformDriver|MockObject $driverMock */
836
        $driverMock = $this->createMock([Driver::class, VersionAwarePlatformDriver::class]);
837
838
        /** @var ServerInfoAwareConnection|MockObject $driverConnectionMock */
839
        $driverConnectionMock = $this->createMock(ServerInfoAwareConnection::class);
840
841
        /** @var AbstractPlatform|MockObject $platformMock */
842
        $platformMock = $this->getMockForAbstractClass(AbstractPlatform::class);
843
844
        $connection = new Connection([], $driverMock);
845
846
        $driverMock->expects($this->once())
847
            ->method('connect')
848
            ->will($this->returnValue($driverConnectionMock));
849
850
        $driverConnectionMock->expects($this->once())
851
            ->method('requiresQueryForServerVersion')
852
            ->will($this->returnValue(false));
853
854
        $driverConnectionMock->expects($this->once())
855
            ->method('getServerVersion')
856
            ->will($this->returnValue('6.6.6'));
857
858
        $driverMock->expects($this->once())
859
            ->method('createDatabasePlatformForVersion')
860
            ->with('6.6.6')
861
            ->will($this->returnValue($platformMock));
862
863
        self::assertSame($platformMock, $connection->getDatabasePlatform());
864
    }
865
866
    public function testConnectionParamsArePassedToTheQueryCacheProfileInExecuteCacheQuery() : void
867
    {
868
        $resultCacheDriverMock = $this->createMock(Cache::class);
869
870
        $resultCacheDriverMock
871
            ->expects($this->atLeastOnce())
872
            ->method('fetch')
873
            ->with('cacheKey')
874
            ->will($this->returnValue(['realKey' => []]));
875
876
        $query  = 'SELECT * FROM foo WHERE bar = ?';
877
        $params = [666];
878
        $types  = [ParameterType::INTEGER];
879
880
        /** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
881
        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
882
883
        $queryCacheProfileMock
884
            ->expects($this->any())
885
            ->method('getResultCacheDriver')
886
            ->will($this->returnValue($resultCacheDriverMock));
887
888
        // This is our main expectation
889
        $queryCacheProfileMock
890
            ->expects($this->once())
891
            ->method('generateCacheKeys')
892
            ->with($query, $params, $types, $this->params)
893
            ->will($this->returnValue(['cacheKey', 'realKey']));
894
895
        /** @var Driver $driver */
896
        $driver = $this->createMock(Driver::class);
897
898
        self::assertInstanceOf(
899
            ArrayStatement::class,
900
            (new Connection($this->params, $driver))->executeCacheQuery($query, $params, $types, $queryCacheProfileMock)
901
        );
902
    }
903
904
    /**
905
     * @group #2821
906
     */
907
    public function testShouldNotPassPlatformInParamsToTheQueryCacheProfileInExecuteCacheQuery() : void
908
    {
909
        $resultCacheDriverMock = $this->createMock(Cache::class);
910
911
        $resultCacheDriverMock
912
            ->expects($this->atLeastOnce())
913
            ->method('fetch')
914
            ->with('cacheKey')
915
            ->will($this->returnValue(['realKey' => []]));
916
917
        /** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
918
        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
919
920
        $queryCacheProfileMock
921
            ->expects($this->any())
922
            ->method('getResultCacheDriver')
923
            ->will($this->returnValue($resultCacheDriverMock));
924
925
        $query = 'SELECT 1';
926
927
        $connectionParams = $this->params;
928
929
        $queryCacheProfileMock
930
            ->expects($this->once())
931
            ->method('generateCacheKeys')
932
            ->with($query, [], [], $connectionParams)
933
            ->will($this->returnValue(['cacheKey', 'realKey']));
934
935
        $connectionParams['platform'] = $this->createMock(AbstractPlatform::class);
936
937
        /** @var Driver $driver */
938
        $driver = $this->createMock(Driver::class);
939
940
        (new Connection($connectionParams, $driver))->executeCacheQuery($query, [], [], $queryCacheProfileMock);
941
    }
942
943
    /**
944
     * @group #2821
945
     */
946
    public function testThrowsExceptionWhenInValidPlatformSpecified() : void
947
    {
948
        $connectionParams             = $this->params;
949
        $connectionParams['platform'] = new stdClass();
950
951
        /** @var Driver $driver */
952
        $driver = $this->createMock(Driver::class);
953
954
        $this->expectException(DBALException::class);
955
956
        new Connection($connectionParams, $driver);
957
    }
958
959
    /**
960
     * @group DBAL-990
961
     */
962
    public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnectingToNonExistentDatabase() : void
963
    {
964
        /** @var Driver|VersionAwarePlatformDriver|MockObject $driverMock */
965
        $driverMock = $this->createMock([Driver::class, VersionAwarePlatformDriver::class]);
966
967
        $connection        = new Connection(['dbname' => 'foo'], $driverMock);
968
        $originalException = new Exception('Original exception');
969
        $fallbackException = new Exception('Fallback exception');
970
971
        $driverMock->expects($this->at(0))
972
            ->method('connect')
973
            ->willThrowException($originalException);
974
975
        $driverMock->expects($this->at(1))
976
            ->method('connect')
977
            ->willThrowException($fallbackException);
978
979
        $this->expectExceptionMessage($originalException->getMessage());
980
981
        $connection->getDatabasePlatform();
982
    }
983
984
    /**
985
     * @group #3194
986
     */
987
    public function testExecuteCacheQueryStripsPlatformFromConnectionParamsBeforeGeneratingCacheKeys() : void
988
    {
989
        /** @var Driver|MockObject $driver */
990
        $driver = $this->createMock(Driver::class);
991
992
        /** @var AbstractPlatform|MockObject $platform */
993
        $platform = $this->createMock(AbstractPlatform::class);
994
995
        /** @var QueryCacheProfile|MockObject $queryCacheProfile */
996
        $queryCacheProfile = $this->createMock(QueryCacheProfile::class);
997
998
        /** @var Cache|MockObject $resultCacheDriver */
999
        $resultCacheDriver = $this->createMock(Cache::class);
1000
1001
        $queryCacheProfile
1002
            ->expects($this->any())
1003
            ->method('getResultCacheDriver')
1004
            ->will($this->returnValue($resultCacheDriver));
1005
1006
        $resultCacheDriver
1007
            ->expects($this->atLeastOnce())
1008
            ->method('fetch')
1009
            ->with('cacheKey')
1010
            ->will($this->returnValue(['realKey' => []]));
1011
1012
        $query = 'SELECT 1';
1013
1014
        $params = [
1015
            'dbname' => 'foo',
1016
            'platform' => $platform,
1017
        ];
1018
1019
        $paramsWithoutPlatform = $params;
1020
        unset($paramsWithoutPlatform['platform']);
1021
1022
        $queryCacheProfile
1023
            ->expects($this->once())
1024
            ->method('generateCacheKeys')
1025
            ->with($query, [], [], $paramsWithoutPlatform)
1026
            ->will($this->returnValue(['cacheKey', 'realKey']));
1027
1028
        $connection = new Connection($params, $driver);
1029
1030
        self::assertSame($params, $connection->getParams());
1031
1032
        $connection->executeCacheQuery($query, [], [], $queryCacheProfile);
1033
    }
1034
}
1035