Completed
Pull Request — master (#3588)
by Andrej
12:04
created

testSetRollbackOnlyNoActiveTransactionThrowsException()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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