Failed Conditions
Pull Request — 2.10.x (#3983)
by Grégoire
63:38
created

testCommitStartsTransactionInNoAutoCommitMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 15
rs 9.9332
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 PHPUnit\Framework\MockObject\MockObject;
29
use stdClass;
30
use function assert;
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
            ->onlyMethods(['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());
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Connection::getHost() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

119
        self::assertEquals('localhost', /** @scrutinizer ignore-deprecated */ $this->connection->getHost());
Loading history...
120
    }
121
122
    public function testGetPort() : void
123
    {
124
        self::assertEquals('1234', $this->connection->getPort());
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Connection::getPort() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

124
        self::assertEquals('1234', /** @scrutinizer ignore-deprecated */ $this->connection->getPort());
Loading history...
125
    }
126
127
    public function testGetUsername() : void
128
    {
129
        self::assertEquals('root', $this->connection->getUsername());
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Connection::getUsername() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

129
        self::assertEquals('root', /** @scrutinizer ignore-deprecated */ $this->connection->getUsername());
Loading history...
130
    }
131
132
    public function testGetPassword() : void
133
    {
134
        self::assertEquals('password', $this->connection->getPassword());
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Connection::getPassword() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

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