ConnectionTest.php 26.3 KB
Newer Older
jwage's avatar
jwage committed
1 2
<?php

3
namespace Doctrine\DBAL\Tests;
jwage's avatar
jwage committed
4

5
use Doctrine\Common\Cache\Cache;
6
use Doctrine\Common\EventManager;
Luís Cobucci's avatar
Luís Cobucci committed
7
use Doctrine\DBAL\Cache\ArrayStatement;
8
use Doctrine\DBAL\Cache\QueryCacheProfile;
9
use Doctrine\DBAL\Configuration;
jeroendedauw's avatar
jeroendedauw committed
10
use Doctrine\DBAL\Connection;
Luís Cobucci's avatar
Luís Cobucci committed
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;
Sergei Morozov's avatar
Sergei Morozov committed
16 17
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\DriverManager;
18
use Doctrine\DBAL\Events;
Luís Cobucci's avatar
Luís Cobucci committed
19
use Doctrine\DBAL\Exception\InvalidArgumentException;
20
use Doctrine\DBAL\FetchMode;
Sergei Morozov's avatar
Sergei Morozov committed
21
use Doctrine\DBAL\Logging\DebugStack;
22
use Doctrine\DBAL\ParameterType;
23
use Doctrine\DBAL\Platforms\AbstractPlatform;
24
use Doctrine\DBAL\VersionAwarePlatformDriver;
Sergei Morozov's avatar
Sergei Morozov committed
25
use Exception;
26
use PHPUnit\Framework\MockObject\MockObject;
27
use PHPUnit\Framework\TestCase;
Sergei Morozov's avatar
Sergei Morozov committed
28
use stdClass;
29

30 31 32
/**
 * @requires extension pdo_mysql
 */
33
class ConnectionTest extends TestCase
jwage's avatar
jwage committed
34
{
Sergei Morozov's avatar
Sergei Morozov committed
35
    /** @var Connection */
Sergei Morozov's avatar
Sergei Morozov committed
36
    private $connection;
37

Sergei Morozov's avatar
Sergei Morozov committed
38 39
    /** @var string[] */
    protected $params = [
40 41 42 43
        'driver' => 'pdo_mysql',
        'host' => 'localhost',
        'user' => 'root',
        'password' => 'password',
Sergei Morozov's avatar
Sergei Morozov committed
44 45
        'port' => '1234',
    ];
46

47
    protected function setUp() : void
jwage's avatar
jwage committed
48
    {
Sergei Morozov's avatar
Sergei Morozov committed
49
        $this->connection = DriverManager::getConnection($this->params);
jwage's avatar
jwage committed
50 51
    }

52 53 54 55
    /**
     * @return Connection|MockObject
     */
    private function getExecuteUpdateMockConnection()
56
    {
Sergei Morozov's avatar
Sergei Morozov committed
57
        $driverMock = $this->createMock(Driver::class);
58

59
        $driverMock->expects(self::any())
60
            ->method('connect')
61
            ->will(self::returnValue(
62 63
                $this->createMock(DriverConnection::class)
            ));
64

65 66
        $platform = $this->getMockForAbstractClass(AbstractPlatform::class);

Sergei Morozov's avatar
Sergei Morozov committed
67
        return $this->getMockBuilder(Connection::class)
68
            ->onlyMethods(['executeUpdate'])
69
            ->setConstructorArgs([['platform' => $platform], $driverMock])
70 71 72
            ->getMock();
    }

73
    public function testIsConnected() : void
74
    {
Sergei Morozov's avatar
Sergei Morozov committed
75
        self::assertFalse($this->connection->isConnected());
76 77
    }

78
    public function testNoTransactionActiveByDefault() : void
79
    {
Sergei Morozov's avatar
Sergei Morozov committed
80
        self::assertFalse($this->connection->isTransactionActive());
81 82
    }

83
    public function testCommitWithNoActiveTransactionThrowsException() : void
84
    {
Luís Cobucci's avatar
Luís Cobucci committed
85
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
86
        $this->connection->commit();
87 88
    }

89
    public function testRollbackWithNoActiveTransactionThrowsException() : void
90
    {
Luís Cobucci's avatar
Luís Cobucci committed
91
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
92
        $this->connection->rollBack();
93 94
    }

95
    public function testSetRollbackOnlyNoActiveTransactionThrowsException() : void
96
    {
Luís Cobucci's avatar
Luís Cobucci committed
97
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
98
        $this->connection->setRollbackOnly();
99 100
    }

101
    public function testIsRollbackOnlyNoActiveTransactionThrowsException() : void
102
    {
Luís Cobucci's avatar
Luís Cobucci committed
103
        $this->expectException(ConnectionException::class);
Sergei Morozov's avatar
Sergei Morozov committed
104
        $this->connection->isRollbackOnly();
105 106
    }

107
    public function testGetConfiguration() : void
108
    {
Sergei Morozov's avatar
Sergei Morozov committed
109
        $config = $this->connection->getConfiguration();
110

Sergei Morozov's avatar
Sergei Morozov committed
111
        self::assertInstanceOf(Configuration::class, $config);
112 113
    }

114
    public function testGetHost() : void
jwage's avatar
jwage committed
115
    {
Sergei Morozov's avatar
Sergei Morozov committed
116
        self::assertEquals('localhost', $this->connection->getHost());
jwage's avatar
jwage committed
117 118
    }

119
    public function testGetPort() : void
jwage's avatar
jwage committed
120
    {
Sergei Morozov's avatar
Sergei Morozov committed
121
        self::assertEquals('1234', $this->connection->getPort());
jwage's avatar
jwage committed
122 123
    }

124
    public function testGetUsername() : void
jwage's avatar
jwage committed
125
    {
Sergei Morozov's avatar
Sergei Morozov committed
126
        self::assertEquals('root', $this->connection->getUsername());
jwage's avatar
jwage committed
127 128
    }

129
    public function testGetPassword() : void
jwage's avatar
jwage committed
130
    {
Sergei Morozov's avatar
Sergei Morozov committed
131
        self::assertEquals('password', $this->connection->getPassword());
jwage's avatar
jwage committed
132 133
    }

134
    public function testGetDriver() : void
jwage's avatar
jwage committed
135
    {
Sergei Morozov's avatar
Sergei Morozov committed
136
        self::assertInstanceOf(\Doctrine\DBAL\Driver\PDOMySql\Driver::class, $this->connection->getDriver());
jwage's avatar
jwage committed
137 138
    }

139
    public function testGetEventManager() : void
jwage's avatar
jwage committed
140
    {
Sergei Morozov's avatar
Sergei Morozov committed
141
        self::assertInstanceOf(EventManager::class, $this->connection->getEventManager());
142 143
    }

144
    public function testConnectDispatchEvent() : void
145
    {
146 147
        $listenerMock = $this->getMockBuilder($this->getMockClass('ConnectDispatchEventListener'))
            ->addMethods(['postConnect'])
148
            ->getMock();
149
        $listenerMock->expects(self::once())->method('postConnect');
150 151

        $eventManager = new EventManager();
Sergei Morozov's avatar
Sergei Morozov committed
152
        $eventManager->addEventListener([Events::postConnect], $listenerMock);
153

Sergei Morozov's avatar
Sergei Morozov committed
154
        $driverMock = $this->createMock(Driver::class);
155
        $driverMock->expects(self::at(0))
156 157
                   ->method('connect');

158
        $conn = new Connection([], $driverMock, new Configuration(), $eventManager);
159
        $conn->connect();
jwage's avatar
jwage committed
160
    }
161

162
    public function testEventManagerPassedToPlatform() : void
163
    {
164 165 166
        $eventManager = new EventManager();

        $platform = $this->createMock(AbstractPlatform::class);
167
        $platform->expects(self::once())
168 169 170 171
            ->method('setEventManager')
            ->with($eventManager);

        $driver = $this->createMock(Driver::class);
172
        $driver->expects(self::any())
173 174 175 176 177
            ->method('getDatabasePlatform')
            ->willReturn($platform);

        $connection = new Connection($this->params, $driver, null, $eventManager);
        $connection->getDatabasePlatform();
178 179
    }

180
    /**
181
     * @requires extension pdo_sqlite
182 183
     * @dataProvider getQueryMethods
     */
184
    public function testDriverExceptionIsWrapped(callable $callback) : void
185
    {
Luís Cobucci's avatar
Luís Cobucci committed
186 187
        $this->expectException(DBALException::class);
        $this->expectExceptionMessage("An exception occurred while executing 'MUUHAAAAHAAAA':\n\nSQLSTATE[HY000]: General error: 1 near \"MUUHAAAAHAAAA\"");
188

Sergei Morozov's avatar
Sergei Morozov committed
189
        $connection = DriverManager::getConnection([
190 191
            'driver' => 'pdo_sqlite',
            'memory' => true,
Sergei Morozov's avatar
Sergei Morozov committed
192
        ]);
193

194
        $callback($connection, 'MUUHAAAAHAAAA');
195 196
    }

197
    /**
198
     * @return iterable<string, array<int, callable>>
199 200
     */
    public static function getQueryMethods() : iterable
201
    {
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
        yield 'exec' => [
            static function (Connection $connection, string $statement) : void {
                $connection->exec($statement);
            },
        ];

        yield 'query' => [
            static function (Connection $connection, string $statement) : void {
                $connection->query($statement);
            },
        ];

        yield 'executeQuery' => [
            static function (Connection $connection, string $statement) : void {
                $connection->executeQuery($statement);
            },
        ];

        yield 'executeUpdate' => [
            static function (Connection $connection, string $statement) : void {
                $connection->executeUpdate($statement);
            },
        ];

        yield 'prepare' => [
            static function (Connection $connection, string $statement) : void {
                $connection->prepare($statement);
            },
Sergei Morozov's avatar
Sergei Morozov committed
230
        ];
231 232
    }

233 234 235 236 237
    /**
     * Pretty dumb test, however we want to check that the DebugStack correctly implements the interface.
     *
     * @group DBAL-11
     */
238
    public function testDebugSQLStack() : void
239
    {
Sergei Morozov's avatar
Sergei Morozov committed
240
        $logger = new DebugStack();
Sergei Morozov's avatar
Sergei Morozov committed
241 242
        $this->connection->getConfiguration()->setSQLLogger($logger);
        self::assertSame($logger, $this->connection->getConfiguration()->getSQLLogger());
243
    }
244 245 246 247

    /**
     * @group DBAL-81
     */
248
    public function testIsAutoCommit() : void
249
    {
Sergei Morozov's avatar
Sergei Morozov committed
250
        self::assertTrue($this->connection->isAutoCommit());
251 252 253 254 255
    }

    /**
     * @group DBAL-81
     */
256
    public function testSetAutoCommit() : void
257
    {
Sergei Morozov's avatar
Sergei Morozov committed
258 259 260 261
        $this->connection->setAutoCommit(false);
        self::assertFalse($this->connection->isAutoCommit());
        $this->connection->setAutoCommit(0);
        self::assertFalse($this->connection->isAutoCommit());
262 263 264 265 266
    }

    /**
     * @group DBAL-81
     */
267
    public function testConnectStartsTransactionInNoAutoCommitMode() : void
268
    {
Sergei Morozov's avatar
Sergei Morozov committed
269
        $driverMock = $this->createMock(Driver::class);
270
        $driverMock->expects(self::any())
271
            ->method('connect')
272
            ->will(self::returnValue(
273 274
                $this->createMock(DriverConnection::class)
            ));
275
        $conn = new Connection([], $driverMock);
276 277 278

        $conn->setAutoCommit(false);

279
        self::assertFalse($conn->isTransactionActive());
280 281 282

        $conn->connect();

283
        self::assertTrue($conn->isTransactionActive());
284 285 286 287 288
    }

    /**
     * @group DBAL-81
     */
289
    public function testCommitStartsTransactionInNoAutoCommitMode() : void
290
    {
Sergei Morozov's avatar
Sergei Morozov committed
291
        $driverMock = $this->createMock(Driver::class);
292
        $driverMock->expects(self::any())
293
            ->method('connect')
294
            ->will(self::returnValue(
295 296
                $this->createMock(DriverConnection::class)
            ));
297
        $conn = new Connection([], $driverMock);
298 299 300 301 302

        $conn->setAutoCommit(false);
        $conn->connect();
        $conn->commit();

303
        self::assertTrue($conn->isTransactionActive());
304 305
    }

306 307 308 309 310 311
    /**
     * @dataProvider resultProvider
     */
    public function testCommitReturn(bool $expectedResult) : void
    {
        $driverConnection = $this->createMock(DriverConnection::class);
312
        $driverConnection->expects(self::once())
313 314 315
            ->method('commit')->willReturn($expectedResult);

        $driverMock = $this->createMock(Driver::class);
316
        $driverMock->expects(self::any())
317
            ->method('connect')
318
            ->will(self::returnValue($driverConnection));
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335

        $conn = new Connection([], $driverMock);

        $conn->connect();
        $conn->beginTransaction();

        self::assertSame($expectedResult, $conn->commit());
    }

    /**
     * @return bool[][]
     */
    public function resultProvider() : array
    {
        return [[true], [false]];
    }

336 337 338
    /**
     * @group DBAL-81
     */
339
    public function testRollBackStartsTransactionInNoAutoCommitMode() : void
340
    {
Sergei Morozov's avatar
Sergei Morozov committed
341
        $driverMock = $this->createMock(Driver::class);
342
        $driverMock->expects(self::any())
343
            ->method('connect')
344
            ->will(self::returnValue(
345 346
                $this->createMock(DriverConnection::class)
            ));
347
        $conn = new Connection([], $driverMock);
348 349 350 351 352

        $conn->setAutoCommit(false);
        $conn->connect();
        $conn->rollBack();

353
        self::assertTrue($conn->isTransactionActive());
354 355 356 357 358
    }

    /**
     * @group DBAL-81
     */
359
    public function testSwitchingAutoCommitModeCommitsAllCurrentTransactions() : void
360
    {
Sergei Morozov's avatar
Sergei Morozov committed
361
        $driverMock = $this->createMock(Driver::class);
362
        $driverMock->expects(self::any())
363
            ->method('connect')
364
            ->will(self::returnValue(
365 366
                $this->createMock(DriverConnection::class)
            ));
367
        $conn = new Connection([], $driverMock);
368 369 370 371 372 373

        $conn->connect();
        $conn->beginTransaction();
        $conn->beginTransaction();
        $conn->setAutoCommit(false);

374
        self::assertSame(1, $conn->getTransactionNestingLevel());
375 376 377 378 379

        $conn->beginTransaction();
        $conn->beginTransaction();
        $conn->setAutoCommit(true);

380
        self::assertFalse($conn->isTransactionActive());
381
    }
382

383
    public function testEmptyInsert() : void
384
    {
385
        $conn = $this->getExecuteUpdateMockConnection();
386

387
        $conn->expects(self::once())
388 389 390
            ->method('executeUpdate')
            ->with('INSERT INTO footable () VALUES ()');

Sergei Morozov's avatar
Sergei Morozov committed
391
        $conn->insert('footable', []);
392
    }
393

394 395 396
    /**
     * @group DBAL-2511
     */
397
    public function testUpdateWithDifferentColumnsInDataAndIdentifiers() : void
398
    {
399
        $conn = $this->getExecuteUpdateMockConnection();
400

401
        $conn->expects(self::once())
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
            ->method('executeUpdate')
            ->with(
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND name = ?',
                [
                    'some text',
                    true,
                    1,
                    'foo',
                ],
                [
                    'string',
                    'boolean',
                    'integer',
                    'string',
                ]
            );

        $conn->update(
            'TestTable',
            [
                'text' => 'some text',
                'is_edited' => true,
            ],
            [
                'id' => 1,
                'name' => 'foo',
            ],
            [
                'text' => 'string',
                'is_edited' => 'boolean',
                'id' => 'integer',
                'name' => 'string',
            ]
        );
    }

    /**
     * @group DBAL-2511
     */
441
    public function testUpdateWithSameColumnInDataAndIdentifiers() : void
442
    {
443
        $conn = $this->getExecuteUpdateMockConnection();
444

445
        $conn->expects(self::once())
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
            ->method('executeUpdate')
            ->with(
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id = ? AND is_edited = ?',
                [
                    'some text',
                    true,
                    1,
                    false,
                ],
                [
                    'string',
                    'boolean',
                    'integer',
                    'boolean',
                ]
            );

        $conn->update(
            'TestTable',
            [
                'text' => 'some text',
                'is_edited' => true,
            ],
            [
                'id' => 1,
                'is_edited' => false,
            ],
            [
                'text' => 'string',
                'is_edited' => 'boolean',
                'id' => 'integer',
            ]
        );
    }

481 482 483
    /**
     * @group DBAL-2688
     */
484
    public function testUpdateWithIsNull() : void
485
    {
486
        $conn = $this->getExecuteUpdateMockConnection();
487

488
        $conn->expects(self::once())
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
            ->method('executeUpdate')
            ->with(
                'UPDATE TestTable SET text = ?, is_edited = ? WHERE id IS NULL AND name = ?',
                [
                    'some text',
                    null,
                    'foo',
                ],
                [
                    'string',
                    'boolean',
                    'string',
                ]
            );

        $conn->update(
            'TestTable',
            [
                'text' => 'some text',
                'is_edited' => null,
            ],
            [
                'id' => null,
                'name' => 'foo',
            ],
            [
                'text' => 'string',
                'is_edited' => 'boolean',
                'id' => 'integer',
                'name' => 'string',
            ]
        );
    }

    /**
     * @group DBAL-2688
     */
526
    public function testDeleteWithIsNull() : void
527
    {
528
        $conn = $this->getExecuteUpdateMockConnection();
529

530
        $conn->expects(self::once())
531 532 533
            ->method('executeUpdate')
            ->with(
                'DELETE FROM TestTable WHERE id IS NULL AND name = ?',
Sergei Morozov's avatar
Sergei Morozov committed
534 535
                ['foo'],
                ['string']
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
            );

        $conn->delete(
            'TestTable',
            [
                'id' => null,
                'name' => 'foo',
            ],
            [
                'id' => 'integer',
                'name' => 'string',
            ]
        );
    }

551
    public function testFetchAssoc() : void
552 553
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
554 555 556
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
557

Sergei Morozov's avatar
Sergei Morozov committed
558
        $driverMock = $this->createMock(Driver::class);
559

560
        $driverMock->expects(self::any())
561
            ->method('connect')
562
            ->will(self::returnValue(
563 564
                $this->createMock(DriverConnection::class)
            ));
565

566
        $driverStatementMock = $this->createMock(Statement::class);
567

568
        $driverStatementMock->expects(self::once())
569
            ->method('fetch')
570
            ->with(FetchMode::ASSOCIATIVE)
571
            ->will(self::returnValue($result));
572

Sergei Morozov's avatar
Sergei Morozov committed
573
        $conn = $this->getMockBuilder(Connection::class)
574
            ->onlyMethods(['executeQuery'])
575
            ->setConstructorArgs([[], $driverMock])
576 577
            ->getMock();

578
        $conn->expects(self::once())
579 580
            ->method('executeQuery')
            ->with($statement, $params, $types)
581
            ->will(self::returnValue($driverStatementMock));
582

583
        self::assertSame($result, $conn->fetchAssoc($statement, $params, $types));
584 585
    }

586
    public function testFetchArray() : void
587 588
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
589 590 591
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
592

Sergei Morozov's avatar
Sergei Morozov committed
593
        $driverMock = $this->createMock(Driver::class);
594

595
        $driverMock->expects(self::any())
596
            ->method('connect')
597
            ->will(self::returnValue(
598 599
                $this->createMock(DriverConnection::class)
            ));
600

601
        $driverStatementMock = $this->createMock(Statement::class);
602

603
        $driverStatementMock->expects(self::once())
604
            ->method('fetch')
605
            ->with(FetchMode::NUMERIC)
606
            ->will(self::returnValue($result));
607

Sergei Morozov's avatar
Sergei Morozov committed
608
        $conn = $this->getMockBuilder(Connection::class)
609
            ->onlyMethods(['executeQuery'])
610
            ->setConstructorArgs([[], $driverMock])
611 612
            ->getMock();

613
        $conn->expects(self::once())
614 615
            ->method('executeQuery')
            ->with($statement, $params, $types)
616
            ->will(self::returnValue($driverStatementMock));
617

618
        self::assertSame($result, $conn->fetchArray($statement, $params, $types));
619 620
    }

621
    public function testFetchColumn() : void
622 623
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
624 625 626
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
627

Sergei Morozov's avatar
Sergei Morozov committed
628
        $driverMock = $this->createMock(Driver::class);
629

630
        $driverMock->expects(self::any())
631
            ->method('connect')
632
            ->will(self::returnValue(
633 634
                $this->createMock(DriverConnection::class)
            ));
635

636
        $driverStatementMock = $this->createMock(Statement::class);
637

638
        $driverStatementMock->expects(self::once())
639
            ->method('fetchColumn')
640
            ->will(self::returnValue($result));
641

Sergei Morozov's avatar
Sergei Morozov committed
642
        $conn = $this->getMockBuilder(Connection::class)
643
            ->onlyMethods(['executeQuery'])
644
            ->setConstructorArgs([[], $driverMock])
645 646
            ->getMock();

647
        $conn->expects(self::once())
648 649
            ->method('executeQuery')
            ->with($statement, $params, $types)
650
            ->will(self::returnValue($driverStatementMock));
651

652
        self::assertSame($result, $conn->fetchColumn($statement, $params, $types));
653
    }
malukenho's avatar
malukenho committed
654

655
    public function testFetchAll() : void
656 657
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
658 659 660
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
661

Sergei Morozov's avatar
Sergei Morozov committed
662
        $driverMock = $this->createMock(Driver::class);
663

664
        $driverMock->expects(self::any())
665
            ->method('connect')
666
            ->will(self::returnValue(
667 668
                $this->createMock(DriverConnection::class)
            ));
669

670
        $driverStatementMock = $this->createMock(Statement::class);
671

672
        $driverStatementMock->expects(self::once())
673
            ->method('fetchAll')
674
            ->will(self::returnValue($result));
675

Sergei Morozov's avatar
Sergei Morozov committed
676
        $conn = $this->getMockBuilder(Connection::class)
677
            ->onlyMethods(['executeQuery'])
678
            ->setConstructorArgs([[], $driverMock])
679 680
            ->getMock();

681
        $conn->expects(self::once())
682 683
            ->method('executeQuery')
            ->with($statement, $params, $types)
684
            ->will(self::returnValue($driverStatementMock));
685

686
        self::assertSame($result, $conn->fetchAll($statement, $params, $types));
687
    }
688

689
    public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException() : void
690
    {
691 692
        $driver = $this->createMock(Driver::class);
        $conn   = new Connection([], $driver);
693

Luís Cobucci's avatar
Luís Cobucci committed
694
        $this->expectException(InvalidArgumentException::class);
Sergei Morozov's avatar
Sergei Morozov committed
695
        $conn->delete('kittens', []);
696
    }
697

698
    public function testCallConnectOnce() : void
699
    {
700
        $driver = $this->createMock(Driver::class);
701
        $driver->expects(self::once())
702
            ->method('connect');
703

704
        $platform = $this->createMock(AbstractPlatform::class);
705

706 707 708
        $conn = new Connection(['platform' => $platform], $driver);
        $conn->connect();
        $conn->connect();
709
    }
710 711 712 713

    /**
     * @group DBAL-1127
     */
714
    public function testPlatformDetectionIsTriggerOnlyOnceOnRetrievingPlatform() : void
715
    {
716
        $driverMock = $this->createMock(VersionAwarePlatformDriver::class);
717

718
        $driverConnectionMock = $this->createMock(ServerInfoAwareConnection::class);
719

Sergei Morozov's avatar
Sergei Morozov committed
720
        $platformMock = $this->getMockForAbstractClass(AbstractPlatform::class);
721

Sergei Morozov's avatar
Sergei Morozov committed
722
        $connection = new Connection([], $driverMock);
723

724
        $driverMock->expects(self::once())
725
            ->method('connect')
726
            ->will(self::returnValue($driverConnectionMock));
727

728
        $driverConnectionMock->expects(self::once())
729
            ->method('requiresQueryForServerVersion')
730
            ->will(self::returnValue(false));
731

732
        $driverConnectionMock->expects(self::once())
733
            ->method('getServerVersion')
734
            ->will(self::returnValue('6.6.6'));
735

736
        $driverMock->expects(self::once())
737 738
            ->method('createDatabasePlatformForVersion')
            ->with('6.6.6')
739
            ->will(self::returnValue($platformMock));
740

741
        self::assertSame($platformMock, $connection->getDatabasePlatform());
742
    }
743

744
    public function testConnectionParamsArePassedToTheQueryCacheProfileInExecuteCacheQuery() : void
745
    {
746
        $resultCacheDriverMock = $this->createMock(Cache::class);
747

748
        $resultCacheDriverMock
749
            ->expects(self::atLeastOnce())
750
            ->method('fetch')
Antonio Vilar's avatar
Antonio Vilar committed
751
            ->with('cacheKey')
752
            ->will(self::returnValue(['realKey' => []]));
753

754 755
        $query  = 'SELECT * FROM foo WHERE bar = ?';
        $params = [666];
756
        $types  = [ParameterType::INTEGER];
757

758
        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
759

760
        $queryCacheProfileMock
761
            ->expects(self::any())
762
            ->method('getResultCacheDriver')
763
            ->will(self::returnValue($resultCacheDriverMock));
764 765

        // This is our main expectation
766
        $queryCacheProfileMock
767
            ->expects(self::once())
768 769
            ->method('generateCacheKeys')
            ->with($query, $params, $types, $this->params)
770
            ->will(self::returnValue(['cacheKey', 'realKey']));
771

772 773
        $driver = $this->createMock(Driver::class);

774
        self::assertInstanceOf(
775 776
            ArrayStatement::class,
            (new Connection($this->params, $driver))->executeCacheQuery($query, $params, $types, $queryCacheProfileMock)
777 778 779
        );
    }

780
    /**
781
     * @group #2821
782
     */
Sergei Morozov's avatar
Sergei Morozov committed
783
    public function testShouldNotPassPlatformInParamsToTheQueryCacheProfileInExecuteCacheQuery() : void
784 785 786 787
    {
        $resultCacheDriverMock = $this->createMock(Cache::class);

        $resultCacheDriverMock
788
            ->expects(self::atLeastOnce())
789 790
            ->method('fetch')
            ->with('cacheKey')
791
            ->will(self::returnValue(['realKey' => []]));
792 793 794 795

        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);

        $queryCacheProfileMock
796
            ->expects(self::any())
797
            ->method('getResultCacheDriver')
798
            ->will(self::returnValue($resultCacheDriverMock));
799

Sergei Morozov's avatar
Sergei Morozov committed
800
        $query = 'SELECT 1';
801

802
        $connectionParams = $this->params;
803 804

        $queryCacheProfileMock
805
            ->expects(self::once())
806
            ->method('generateCacheKeys')
807
            ->with($query, [], [], $connectionParams)
808
            ->will(self::returnValue(['cacheKey', 'realKey']));
809

810 811
        $connectionParams['platform'] = $this->createMock(AbstractPlatform::class);

812 813
        $driver = $this->createMock(Driver::class);

814
        (new Connection($connectionParams, $driver))->executeCacheQuery($query, [], [], $queryCacheProfileMock);
815
    }
816

817
    /**
818
     * @group #2821
819
     */
Sergei Morozov's avatar
Sergei Morozov committed
820
    public function testThrowsExceptionWhenInValidPlatformSpecified() : void
821
    {
Sergei Morozov's avatar
Sergei Morozov committed
822 823
        $connectionParams             = $this->params;
        $connectionParams['platform'] = new stdClass();
824 825

        $driver = $this->createMock(Driver::class);
826 827

        $this->expectException(DBALException::class);
828 829 830 831

        new Connection($connectionParams, $driver);
    }

832 833 834
    /**
     * @group DBAL-990
     */
835
    public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnectingToNonExistentDatabase() : void
836
    {
837
        $driverMock = $this->createMock(VersionAwarePlatformDriver::class);
838

Sergei Morozov's avatar
Sergei Morozov committed
839 840 841
        $connection        = new Connection(['dbname' => 'foo'], $driverMock);
        $originalException = new Exception('Original exception');
        $fallbackException = new Exception('Fallback exception');
842

843
        $driverMock->expects(self::at(0))
844 845 846
            ->method('connect')
            ->willThrowException($originalException);

847
        $driverMock->expects(self::at(1))
848 849 850 851 852 853 854
            ->method('connect')
            ->willThrowException($fallbackException);

        $this->expectExceptionMessage($originalException->getMessage());

        $connection->getDatabasePlatform();
    }
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869

    /**
     * @group #3194
     */
    public function testExecuteCacheQueryStripsPlatformFromConnectionParamsBeforeGeneratingCacheKeys() : void
    {
        $driver = $this->createMock(Driver::class);

        $platform = $this->createMock(AbstractPlatform::class);

        $queryCacheProfile = $this->createMock(QueryCacheProfile::class);

        $resultCacheDriver = $this->createMock(Cache::class);

        $queryCacheProfile
870
            ->expects(self::any())
871
            ->method('getResultCacheDriver')
872
            ->will(self::returnValue($resultCacheDriver));
873 874

        $resultCacheDriver
875
            ->expects(self::atLeastOnce())
876 877
            ->method('fetch')
            ->with('cacheKey')
878
            ->will(self::returnValue(['realKey' => []]));
879 880 881 882 883 884 885 886 887 888 889 890

        $query = 'SELECT 1';

        $params = [
            'dbname' => 'foo',
            'platform' => $platform,
        ];

        $paramsWithoutPlatform = $params;
        unset($paramsWithoutPlatform['platform']);

        $queryCacheProfile
891
            ->expects(self::once())
892 893
            ->method('generateCacheKeys')
            ->with($query, [], [], $paramsWithoutPlatform)
894
            ->will(self::returnValue(['cacheKey', 'realKey']));
895 896 897 898 899 900 901

        $connection = new Connection($params, $driver);

        self::assertSame($params, $connection->getParams());

        $connection->executeCacheQuery($query, [], [], $queryCacheProfile);
    }
902
}