Completed
Pull Request — master (#3588)
by Andrej
14:44
created

ConnectionTest::testRollackReturnFalse()   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
    public function testCommitReturnTrue() : void
302
    {
303
        $driverMock = $this->createMock(Driver::class);
304
        $driverMock->expects($this->any())
305
            ->method('connect')
306
            ->will($this->returnValue(
307
                $this->createMock(DriverConnection::class)
308
            ));
309
        $pdo = $this->createMock(PDO::class);
310
        $pdo->expects($this->once())
311
            ->method('commit')->willReturn(true);
312
313
        $conn = new Connection(['pdo' => $pdo], $driverMock);
314
315
        $conn->connect();
316
        $conn->beginTransaction();
317
318
        self::assertTrue($conn->commit());
319
    }
320
321
    public function testCommitReturnFalse() : void
322
    {
323
        $driverMock = $this->createMock(Driver::class);
324
        $driverMock->expects($this->any())
325
            ->method('connect')
326
            ->will($this->returnValue(
327
                $this->createMock(DriverConnection::class)
328
            ));
329
        $pdo = $this->createMock(PDO::class);
330
        $pdo->expects($this->once())
331
            ->method('commit')->willReturn(false);
332
333
        $conn = new Connection(['pdo' => $pdo], $driverMock);
334
335
        $conn->connect();
336
        $conn->beginTransaction();
337
338
        self::assertFalse($conn->commit());
339
    }
340
341
    public function testRollackReturnTrue() : void
342
    {
343
        $driverMock = $this->createMock(Driver::class);
344
        $driverMock->expects($this->any())
345
            ->method('connect')
346
            ->will($this->returnValue(
347
                $this->createMock(DriverConnection::class)
348
            ));
349
        $pdo = $this->createMock(PDO::class);
350
        $pdo->expects($this->once())
351
            ->method('rollback')->willReturn(true);
352
353
        $conn = new Connection(['pdo' => $pdo], $driverMock);
354
355
        $conn->connect();
356
        $conn->beginTransaction();
357
358
        self::assertTrue($conn->rollback());
359
    }
360
361
    public function testRollackReturnFalse() : void
362
    {
363
        $driverMock = $this->createMock(Driver::class);
364
        $driverMock->expects($this->any())
365
            ->method('connect')
366
            ->will($this->returnValue(
367
                $this->createMock(DriverConnection::class)
368
            ));
369
        $pdo = $this->createMock(PDO::class);
370
        $pdo->expects($this->once())
371
            ->method('rollback')->willReturn(false);
372
373
        $conn = new Connection(['pdo' => $pdo], $driverMock);
374
375
        $conn->connect();
376
        $conn->beginTransaction();
377
378
        self::assertFalse($conn->rollback());
379
    }
380
381
    /**
382
     * @group DBAL-81
383
     */
384
    public function testRollBackStartsTransactionInNoAutoCommitMode() : void
385
    {
386
        $driverMock = $this->createMock(Driver::class);
387
        $driverMock->expects($this->any())
388
            ->method('connect')
389
            ->will($this->returnValue(
390
                $this->createMock(DriverConnection::class)
391
            ));
392
        $conn = new Connection([], $driverMock);
393
394
        $conn->setAutoCommit(false);
395
        $conn->connect();
396
        $conn->rollBack();
397
398
        self::assertTrue($conn->isTransactionActive());
399
    }
400
401
    /**
402
     * @group DBAL-81
403
     */
404
    public function testSwitchingAutoCommitModeCommitsAllCurrentTransactions() : void
405
    {
406
        $driverMock = $this->createMock(Driver::class);
407
        $driverMock->expects($this->any())
408
            ->method('connect')
409
            ->will($this->returnValue(
410
                $this->createMock(DriverConnection::class)
411
            ));
412
        $conn = new Connection([], $driverMock);
413
414
        $conn->connect();
415
        $conn->beginTransaction();
416
        $conn->beginTransaction();
417
        $conn->setAutoCommit(false);
418
419
        self::assertSame(1, $conn->getTransactionNestingLevel());
420
421
        $conn->beginTransaction();
422
        $conn->beginTransaction();
423
        $conn->setAutoCommit(true);
424
425
        self::assertFalse($conn->isTransactionActive());
426
    }
427
428
    public function testEmptyInsert() : void
429
    {
430
        $conn = $this->getExecuteUpdateMockConnection();
431
432
        $conn->expects($this->once())
433
            ->method('executeUpdate')
434
            ->with('INSERT INTO footable () VALUES ()');
435
436
        $conn->insert('footable', []);
437
    }
438
439
    /**
440
     * @group DBAL-2511
441
     */
442
    public function testUpdateWithDifferentColumnsInDataAndIdentifiers() : void
443
    {
444
        $conn = $this->getExecuteUpdateMockConnection();
445
446
        $conn->expects($this->once())
447
            ->method('executeUpdate')
448
            ->with(
449
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND name = ?',
450
                [
451
                    'some text',
452
                    true,
453
                    1,
454
                    'foo',
455
                ],
456
                [
457
                    'string',
458
                    'boolean',
459
                    'integer',
460
                    'string',
461
                ]
462
            );
463
464
        $conn->update(
465
            'TestTable',
466
            [
467
                'text' => 'some text',
468
                'is_edited' => true,
469
            ],
470
            [
471
                'id' => 1,
472
                'name' => 'foo',
473
            ],
474
            [
475
                'text' => 'string',
476
                'is_edited' => 'boolean',
477
                'id' => 'integer',
478
                'name' => 'string',
479
            ]
480
        );
481
    }
482
483
    /**
484
     * @group DBAL-2511
485
     */
486
    public function testUpdateWithSameColumnInDataAndIdentifiers() : void
487
    {
488
        $conn = $this->getExecuteUpdateMockConnection();
489
490
        $conn->expects($this->once())
491
            ->method('executeUpdate')
492
            ->with(
493
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND is_edited = ?',
494
                [
495
                    'some text',
496
                    true,
497
                    1,
498
                    false,
499
                ],
500
                [
501
                    'string',
502
                    'boolean',
503
                    'integer',
504
                    'boolean',
505
                ]
506
            );
507
508
        $conn->update(
509
            'TestTable',
510
            [
511
                'text' => 'some text',
512
                'is_edited' => true,
513
            ],
514
            [
515
                'id' => 1,
516
                'is_edited' => false,
517
            ],
518
            [
519
                'text' => 'string',
520
                'is_edited' => 'boolean',
521
                'id' => 'integer',
522
            ]
523
        );
524
    }
525
526
    /**
527
     * @group DBAL-2688
528
     */
529
    public function testUpdateWithIsNull() : void
530
    {
531
        $conn = $this->getExecuteUpdateMockConnection();
532
533
        $conn->expects($this->once())
534
            ->method('executeUpdate')
535
            ->with(
536
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id IS NULL AND name = ?',
537
                [
538
                    'some text',
539
                    null,
540
                    'foo',
541
                ],
542
                [
543
                    'string',
544
                    'boolean',
545
                    'string',
546
                ]
547
            );
548
549
        $conn->update(
550
            'TestTable',
551
            [
552
                'text' => 'some text',
553
                'is_edited' => null,
554
            ],
555
            [
556
                'id' => null,
557
                'name' => 'foo',
558
            ],
559
            [
560
                'text' => 'string',
561
                'is_edited' => 'boolean',
562
                'id' => 'integer',
563
                'name' => 'string',
564
            ]
565
        );
566
    }
567
568
    /**
569
     * @group DBAL-2688
570
     */
571
    public function testDeleteWithIsNull() : void
572
    {
573
        $conn = $this->getExecuteUpdateMockConnection();
574
575
        $conn->expects($this->once())
576
            ->method('executeUpdate')
577
            ->with(
578
                'DELETE FROM TestTable WHERE id IS NULL AND name = ?',
579
                ['foo'],
580
                ['string']
581
            );
582
583
        $conn->delete(
584
            'TestTable',
585
            [
586
                'id' => null,
587
                'name' => 'foo',
588
            ],
589
            [
590
                'id' => 'integer',
591
                'name' => 'string',
592
            ]
593
        );
594
    }
595
596
    public function testFetchAssoc() : void
597
    {
598
        $statement = 'SELECT * FROM foo WHERE bar = ?';
599
        $params    = [666];
600
        $types     = [ParameterType::INTEGER];
601
        $result    = [];
602
603
        $driverMock = $this->createMock(Driver::class);
604
605
        $driverMock->expects($this->any())
606
            ->method('connect')
607
            ->will($this->returnValue(
608
                $this->createMock(DriverConnection::class)
609
            ));
610
611
        $driverStatementMock = $this->createMock(Statement::class);
612
613
        $driverStatementMock->expects($this->once())
614
            ->method('fetch')
615
            ->with(FetchMode::ASSOCIATIVE)
616
            ->will($this->returnValue($result));
617
618
        /** @var Connection|MockObject $conn */
619
        $conn = $this->getMockBuilder(Connection::class)
620
            ->setMethods(['executeQuery'])
621
            ->setConstructorArgs([[], $driverMock])
622
            ->getMock();
623
624
        $conn->expects($this->once())
625
            ->method('executeQuery')
626
            ->with($statement, $params, $types)
627
            ->will($this->returnValue($driverStatementMock));
628
629
        self::assertSame($result, $conn->fetchAssoc($statement, $params, $types));
630
    }
631
632
    public function testFetchArray() : void
633
    {
634
        $statement = 'SELECT * FROM foo WHERE bar = ?';
635
        $params    = [666];
636
        $types     = [ParameterType::INTEGER];
637
        $result    = [];
638
639
        $driverMock = $this->createMock(Driver::class);
640
641
        $driverMock->expects($this->any())
642
            ->method('connect')
643
            ->will($this->returnValue(
644
                $this->createMock(DriverConnection::class)
645
            ));
646
647
        $driverStatementMock = $this->createMock(Statement::class);
648
649
        $driverStatementMock->expects($this->once())
650
            ->method('fetch')
651
            ->with(FetchMode::NUMERIC)
652
            ->will($this->returnValue($result));
653
654
        /** @var Connection|MockObject $conn */
655
        $conn = $this->getMockBuilder(Connection::class)
656
            ->setMethods(['executeQuery'])
657
            ->setConstructorArgs([[], $driverMock])
658
            ->getMock();
659
660
        $conn->expects($this->once())
661
            ->method('executeQuery')
662
            ->with($statement, $params, $types)
663
            ->will($this->returnValue($driverStatementMock));
664
665
        self::assertSame($result, $conn->fetchArray($statement, $params, $types));
666
    }
667
668
    public function testFetchColumn() : void
669
    {
670
        $statement = 'SELECT * FROM foo WHERE bar = ?';
671
        $params    = [666];
672
        $types     = [ParameterType::INTEGER];
673
        $column    = 0;
674
        $result    = [];
675
676
        $driverMock = $this->createMock(Driver::class);
677
678
        $driverMock->expects($this->any())
679
            ->method('connect')
680
            ->will($this->returnValue(
681
                $this->createMock(DriverConnection::class)
682
            ));
683
684
        $driverStatementMock = $this->createMock(Statement::class);
685
686
        $driverStatementMock->expects($this->once())
687
            ->method('fetchColumn')
688
            ->with($column)
689
            ->will($this->returnValue($result));
690
691
        /** @var Connection|MockObject $conn */
692
        $conn = $this->getMockBuilder(Connection::class)
693
            ->setMethods(['executeQuery'])
694
            ->setConstructorArgs([[], $driverMock])
695
            ->getMock();
696
697
        $conn->expects($this->once())
698
            ->method('executeQuery')
699
            ->with($statement, $params, $types)
700
            ->will($this->returnValue($driverStatementMock));
701
702
        self::assertSame($result, $conn->fetchColumn($statement, $params, $column, $types));
703
    }
704
705
    public function testFetchAll() : void
706
    {
707
        $statement = 'SELECT * FROM foo WHERE bar = ?';
708
        $params    = [666];
709
        $types     = [ParameterType::INTEGER];
710
        $result    = [];
711
712
        $driverMock = $this->createMock(Driver::class);
713
714
        $driverMock->expects($this->any())
715
            ->method('connect')
716
            ->will($this->returnValue(
717
                $this->createMock(DriverConnection::class)
718
            ));
719
720
        $driverStatementMock = $this->createMock(Statement::class);
721
722
        $driverStatementMock->expects($this->once())
723
            ->method('fetchAll')
724
            ->will($this->returnValue($result));
725
726
        /** @var Connection|MockObject $conn */
727
        $conn = $this->getMockBuilder(Connection::class)
728
            ->setMethods(['executeQuery'])
729
            ->setConstructorArgs([[], $driverMock])
730
            ->getMock();
731
732
        $conn->expects($this->once())
733
            ->method('executeQuery')
734
            ->with($statement, $params, $types)
735
            ->will($this->returnValue($driverStatementMock));
736
737
        self::assertSame($result, $conn->fetchAll($statement, $params, $types));
738
    }
739
740
    public function testConnectionDoesNotMaintainTwoReferencesToExternalPDO() : void
741
    {
742
        $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...
743
744
        $driverMock = $this->createMock(Driver::class);
745
746
        $conn = new Connection($params, $driverMock);
747
748
        self::assertArrayNotHasKey('pdo', $conn->getParams(), 'Connection is maintaining additional reference to the PDO connection');
749
    }
750
751
    public function testPassingExternalPDOMeansConnectionIsConnected() : void
752
    {
753
        $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...
754
755
        $driverMock = $this->createMock(Driver::class);
756
757
        $conn = new Connection($params, $driverMock);
758
759
        self::assertTrue($conn->isConnected(), 'Connection is not connected after passing external PDO');
760
    }
761
762
    public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException() : void
763
    {
764
        /** @var Driver $driver */
765
        $driver  = $this->createMock(Driver::class);
766
        $pdoMock = $this->createMock(\Doctrine\DBAL\Driver\Connection::class);
767
768
        // should never execute queries with invalid arguments
769
        $pdoMock->expects($this->never())->method('exec');
770
        $pdoMock->expects($this->never())->method('prepare');
771
772
        $conn = new Connection(['pdo' => $pdoMock], $driver);
773
774
        $this->expectException(InvalidArgumentException::class);
775
        $conn->delete('kittens', []);
776
    }
777
778
    /**
779
     * @return array<int, array<int, mixed>>
780
     */
781
    public static function dataCallConnectOnce() : iterable
782
    {
783
        return [
784
            ['delete', ['tbl', ['id' => 12345]]],
785
            ['insert', ['tbl', ['data' => 'foo']]],
786
            ['update', ['tbl', ['data' => 'bar'], ['id' => 12345]]],
787
            ['prepare', ['select * from dual']],
788
            ['executeUpdate', ['insert into tbl (id) values (?)'], [123]],
789
        ];
790
    }
791
792
    /**
793
     * @param array<int, mixed> $params
794
     *
795
     * @dataProvider dataCallConnectOnce
796
     */
797
    public function testCallConnectOnce(string $method, array $params) : void
798
    {
799
        $driverMock   = $this->createMock(Driver::class);
800
        $pdoMock      = $this->createMock(Connection::class);
801
        $platformMock = $this->createMock(AbstractPlatform::class);
802
        $stmtMock     = $this->createMock(Statement::class);
803
804
        $pdoMock->expects($this->any())
805
            ->method('prepare')
806
            ->will($this->returnValue($stmtMock));
807
808
        $conn = $this->getMockBuilder(Connection::class)
809
            ->setConstructorArgs([['pdo' => $pdoMock, 'platform' => $platformMock], $driverMock])
810
            ->setMethods(['connect'])
811
            ->getMock();
812
813
        $conn->expects($this->once())->method('connect');
814
815
        call_user_func_array([$conn, $method], $params);
816
    }
817
818
    /**
819
     * @group DBAL-1127
820
     */
821
    public function testPlatformDetectionIsTriggerOnlyOnceOnRetrievingPlatform() : void
822
    {
823
        /** @var Driver|VersionAwarePlatformDriver|MockObject $driverMock */
824
        $driverMock = $this->createMock([Driver::class, VersionAwarePlatformDriver::class]);
825
826
        /** @var ServerInfoAwareConnection|MockObject $driverConnectionMock */
827
        $driverConnectionMock = $this->createMock(ServerInfoAwareConnection::class);
828
829
        /** @var AbstractPlatform|MockObject $platformMock */
830
        $platformMock = $this->getMockForAbstractClass(AbstractPlatform::class);
831
832
        $connection = new Connection([], $driverMock);
833
834
        $driverMock->expects($this->once())
835
            ->method('connect')
836
            ->will($this->returnValue($driverConnectionMock));
837
838
        $driverConnectionMock->expects($this->once())
839
            ->method('requiresQueryForServerVersion')
840
            ->will($this->returnValue(false));
841
842
        $driverConnectionMock->expects($this->once())
843
            ->method('getServerVersion')
844
            ->will($this->returnValue('6.6.6'));
845
846
        $driverMock->expects($this->once())
847
            ->method('createDatabasePlatformForVersion')
848
            ->with('6.6.6')
849
            ->will($this->returnValue($platformMock));
850
851
        self::assertSame($platformMock, $connection->getDatabasePlatform());
852
    }
853
854
    public function testConnectionParamsArePassedToTheQueryCacheProfileInExecuteCacheQuery() : void
855
    {
856
        $resultCacheDriverMock = $this->createMock(Cache::class);
857
858
        $resultCacheDriverMock
859
            ->expects($this->atLeastOnce())
860
            ->method('fetch')
861
            ->with('cacheKey')
862
            ->will($this->returnValue(['realKey' => []]));
863
864
        $query  = 'SELECT * FROM foo WHERE bar = ?';
865
        $params = [666];
866
        $types  = [ParameterType::INTEGER];
867
868
        /** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
869
        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
870
871
        $queryCacheProfileMock
872
            ->expects($this->any())
873
            ->method('getResultCacheDriver')
874
            ->will($this->returnValue($resultCacheDriverMock));
875
876
        // This is our main expectation
877
        $queryCacheProfileMock
878
            ->expects($this->once())
879
            ->method('generateCacheKeys')
880
            ->with($query, $params, $types, $this->params)
881
            ->will($this->returnValue(['cacheKey', 'realKey']));
882
883
        /** @var Driver $driver */
884
        $driver = $this->createMock(Driver::class);
885
886
        self::assertInstanceOf(
887
            ArrayStatement::class,
888
            (new Connection($this->params, $driver))->executeCacheQuery($query, $params, $types, $queryCacheProfileMock)
889
        );
890
    }
891
892
    /**
893
     * @group #2821
894
     */
895
    public function testShouldNotPassPlatformInParamsToTheQueryCacheProfileInExecuteCacheQuery() : void
896
    {
897
        $resultCacheDriverMock = $this->createMock(Cache::class);
898
899
        $resultCacheDriverMock
900
            ->expects($this->atLeastOnce())
901
            ->method('fetch')
902
            ->with('cacheKey')
903
            ->will($this->returnValue(['realKey' => []]));
904
905
        /** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
906
        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
907
908
        $queryCacheProfileMock
909
            ->expects($this->any())
910
            ->method('getResultCacheDriver')
911
            ->will($this->returnValue($resultCacheDriverMock));
912
913
        $query = 'SELECT 1';
914
915
        $connectionParams = $this->params;
916
917
        $queryCacheProfileMock
918
            ->expects($this->once())
919
            ->method('generateCacheKeys')
920
            ->with($query, [], [], $connectionParams)
921
            ->will($this->returnValue(['cacheKey', 'realKey']));
922
923
        $connectionParams['platform'] = $this->createMock(AbstractPlatform::class);
924
925
        /** @var Driver $driver */
926
        $driver = $this->createMock(Driver::class);
927
928
        (new Connection($connectionParams, $driver))->executeCacheQuery($query, [], [], $queryCacheProfileMock);
929
    }
930
931
    /**
932
     * @group #2821
933
     */
934
    public function testThrowsExceptionWhenInValidPlatformSpecified() : void
935
    {
936
        $connectionParams             = $this->params;
937
        $connectionParams['platform'] = new stdClass();
938
939
        /** @var Driver $driver */
940
        $driver = $this->createMock(Driver::class);
941
942
        $this->expectException(DBALException::class);
943
944
        new Connection($connectionParams, $driver);
945
    }
946
947
    /**
948
     * @group DBAL-990
949
     */
950
    public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnectingToNonExistentDatabase() : void
951
    {
952
        /** @var Driver|VersionAwarePlatformDriver|MockObject $driverMock */
953
        $driverMock = $this->createMock([Driver::class, VersionAwarePlatformDriver::class]);
954
955
        $connection        = new Connection(['dbname' => 'foo'], $driverMock);
956
        $originalException = new Exception('Original exception');
957
        $fallbackException = new Exception('Fallback exception');
958
959
        $driverMock->expects($this->at(0))
960
            ->method('connect')
961
            ->willThrowException($originalException);
962
963
        $driverMock->expects($this->at(1))
964
            ->method('connect')
965
            ->willThrowException($fallbackException);
966
967
        $this->expectExceptionMessage($originalException->getMessage());
968
969
        $connection->getDatabasePlatform();
970
    }
971
972
    /**
973
     * @group #3194
974
     */
975
    public function testExecuteCacheQueryStripsPlatformFromConnectionParamsBeforeGeneratingCacheKeys() : void
976
    {
977
        /** @var Driver|MockObject $driver */
978
        $driver = $this->createMock(Driver::class);
979
980
        /** @var AbstractPlatform|MockObject $platform */
981
        $platform = $this->createMock(AbstractPlatform::class);
982
983
        /** @var QueryCacheProfile|MockObject $queryCacheProfile */
984
        $queryCacheProfile = $this->createMock(QueryCacheProfile::class);
985
986
        /** @var Cache|MockObject $resultCacheDriver */
987
        $resultCacheDriver = $this->createMock(Cache::class);
988
989
        $queryCacheProfile
990
            ->expects($this->any())
991
            ->method('getResultCacheDriver')
992
            ->will($this->returnValue($resultCacheDriver));
993
994
        $resultCacheDriver
995
            ->expects($this->atLeastOnce())
996
            ->method('fetch')
997
            ->with('cacheKey')
998
            ->will($this->returnValue(['realKey' => []]));
999
1000
        $query = 'SELECT 1';
1001
1002
        $params = [
1003
            'dbname' => 'foo',
1004
            'platform' => $platform,
1005
        ];
1006
1007
        $paramsWithoutPlatform = $params;
1008
        unset($paramsWithoutPlatform['platform']);
1009
1010
        $queryCacheProfile
1011
            ->expects($this->once())
1012
            ->method('generateCacheKeys')
1013
            ->with($query, [], [], $paramsWithoutPlatform)
1014
            ->will($this->returnValue(['cacheKey', 'realKey']));
1015
1016
        $connection = new Connection($params, $driver);
1017
1018
        self::assertSame($params, $connection->getParams());
1019
1020
        $connection->executeCacheQuery($query, [], [], $queryCacheProfile);
1021
    }
1022
}
1023