Passed
Pull Request — master (#3588)
by Andrej
11:38
created

ConnectionTest::resultProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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