ConnectionTest.php 27.6 KB
Newer Older
jwage's avatar
jwage committed
1 2 3 4
<?php

namespace Doctrine\Tests\DBAL;

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;
Sergei Morozov's avatar
Sergei Morozov committed
15 16
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\DriverManager;
17
use Doctrine\DBAL\Events;
Luís Cobucci's avatar
Luís Cobucci committed
18
use Doctrine\DBAL\Exception\InvalidArgumentException;
19
use Doctrine\DBAL\FetchMode;
Sergei Morozov's avatar
Sergei Morozov committed
20 21
use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\DBAL\Logging\EchoSQLLogger;
22
use Doctrine\DBAL\ParameterType;
23
use Doctrine\DBAL\Platforms\AbstractPlatform;
Sergei Morozov's avatar
Sergei Morozov committed
24
use Doctrine\Tests\DbalTestCase;
jeroendedauw's avatar
jeroendedauw committed
25
use Doctrine\Tests\Mocks\DriverMock;
Sergei Morozov's avatar
Sergei Morozov committed
26
use Doctrine\Tests\Mocks\DriverStatementMock;
Sergei Morozov's avatar
Sergei Morozov committed
27
use Doctrine\Tests\Mocks\ServerInfoAwareConnectionMock;
28
use Doctrine\Tests\Mocks\VersionAwarePlatformDriverMock;
Sergei Morozov's avatar
Sergei Morozov committed
29 30 31 32
use Exception;
use PHPUnit_Framework_MockObject_MockObject;
use ReflectionObject;
use stdClass;
33
use function call_user_func_array;
34

35 36 37
/**
 * @requires extension pdo_mysql
 */
Sergei Morozov's avatar
Sergei Morozov committed
38
class ConnectionTest extends DbalTestCase
jwage's avatar
jwage committed
39
{
Sergei Morozov's avatar
Sergei Morozov committed
40
    /** @var Connection */
Sergei Morozov's avatar
Sergei Morozov committed
41
    private $connection;
42

Sergei Morozov's avatar
Sergei Morozov committed
43 44
    /** @var string[] */
    protected $params = [
45 46 47 48
        'driver' => 'pdo_mysql',
        'host' => 'localhost',
        'user' => 'root',
        'password' => 'password',
Sergei Morozov's avatar
Sergei Morozov committed
49 50
        'port' => '1234',
    ];
51

52
    protected function setUp()
jwage's avatar
jwage committed
53
    {
Sergei Morozov's avatar
Sergei Morozov committed
54
        $this->connection = DriverManager::getConnection($this->params);
jwage's avatar
jwage committed
55 56
    }

57 58
    public function getExecuteUpdateMockConnection()
    {
Sergei Morozov's avatar
Sergei Morozov committed
59
        $driverMock = $this->createMock(Driver::class);
60 61 62

        $driverMock->expects($this->any())
            ->method('connect')
63 64 65
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
66

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

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

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

Sergei Morozov's avatar
Sergei Morozov committed
83
    public function testCommitWithNoActiveTransactionThrowsException()
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
    }

Sergei Morozov's avatar
Sergei Morozov committed
89
    public function testRollbackWithNoActiveTransactionThrowsException()
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
    }

Sergei Morozov's avatar
Sergei Morozov committed
95
    public function testSetRollbackOnlyNoActiveTransactionThrowsException()
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
    }

Sergei Morozov's avatar
Sergei Morozov committed
101
    public function testIsRollbackOnlyNoActiveTransactionThrowsException()
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 108
    }

    public function testGetConfiguration()
    {
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
    }

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

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

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

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

    public function testGetDriver()
    {
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 140
    }

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

    public function testConnectDispatchEvent()
    {
146
        $listenerMock = $this->getMockBuilder('ConnectDispatchEventListener')
Sergei Morozov's avatar
Sergei Morozov committed
147
            ->setMethods(['postConnect'])
148
            ->getMock();
149 150 151
        $listenerMock->expects($this->once())->method('postConnect');

        $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);
Sergei Morozov's avatar
Sergei Morozov committed
155
        $driverMock->expects($this->at(0))
156 157 158
                   ->method('connect');
        $platform = new Mocks\MockPlatform();

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

163 164
    public function testEventManagerPassedToPlatform()
    {
165 166
        $driverMock = new DriverMock();
        $connection = new Connection($this->params, $driverMock);
Sergei Morozov's avatar
Sergei Morozov committed
167
        self::assertInstanceOf(EventManager::class, $connection->getDatabasePlatform()->getEventManager());
168
        self::assertSame($connection->getEventManager(), $connection->getDatabasePlatform()->getEventManager());
169 170
    }

171
    /**
172
     * @requires extension pdo_sqlite
jeroendedauw's avatar
jeroendedauw committed
173
     * @expectedException \Doctrine\DBAL\DBALException
174 175 176 177
     * @dataProvider getQueryMethods
     */
    public function testDriverExceptionIsWrapped($method)
    {
Luís Cobucci's avatar
Luís Cobucci committed
178 179
        $this->expectException(DBALException::class);
        $this->expectExceptionMessage("An exception occurred while executing 'MUUHAAAAHAAAA':\n\nSQLSTATE[HY000]: General error: 1 near \"MUUHAAAAHAAAA\"");
180

Sergei Morozov's avatar
Sergei Morozov committed
181
        $connection = DriverManager::getConnection([
182 183
            'driver' => 'pdo_sqlite',
            'memory' => true,
Sergei Morozov's avatar
Sergei Morozov committed
184
        ]);
185

Sergei Morozov's avatar
Sergei Morozov committed
186
        $connection->$method('MUUHAAAAHAAAA');
187 188 189 190
    }

    public function getQueryMethods()
    {
Sergei Morozov's avatar
Sergei Morozov committed
191 192 193 194 195 196 197
        return [
            ['exec'],
            ['query'],
            ['executeQuery'],
            ['executeUpdate'],
            ['prepare'],
        ];
198 199
    }

200 201 202 203 204 205 206
    /**
     * Pretty dumb test, however we want to check that the EchoSQLLogger correctly implements the interface.
     *
     * @group DBAL-11
     */
    public function testEchoSQLLogger()
    {
Sergei Morozov's avatar
Sergei Morozov committed
207
        $logger = new EchoSQLLogger();
Sergei Morozov's avatar
Sergei Morozov committed
208 209
        $this->connection->getConfiguration()->setSQLLogger($logger);
        self::assertSame($logger, $this->connection->getConfiguration()->getSQLLogger());
210 211 212 213 214 215 216 217 218
    }

    /**
     * Pretty dumb test, however we want to check that the DebugStack correctly implements the interface.
     *
     * @group DBAL-11
     */
    public function testDebugSQLStack()
    {
Sergei Morozov's avatar
Sergei Morozov committed
219
        $logger = new DebugStack();
Sergei Morozov's avatar
Sergei Morozov committed
220 221
        $this->connection->getConfiguration()->setSQLLogger($logger);
        self::assertSame($logger, $this->connection->getConfiguration()->getSQLLogger());
222
    }
223 224 225 226

    /**
     * @group DBAL-81
     */
227
    public function testIsAutoCommit()
228
    {
Sergei Morozov's avatar
Sergei Morozov committed
229
        self::assertTrue($this->connection->isAutoCommit());
230 231 232 233 234 235 236
    }

    /**
     * @group DBAL-81
     */
    public function testSetAutoCommit()
    {
Sergei Morozov's avatar
Sergei Morozov committed
237 238 239 240
        $this->connection->setAutoCommit(false);
        self::assertFalse($this->connection->isAutoCommit());
        $this->connection->setAutoCommit(0);
        self::assertFalse($this->connection->isAutoCommit());
241 242 243 244 245 246 247
    }

    /**
     * @group DBAL-81
     */
    public function testConnectStartsTransactionInNoAutoCommitMode()
    {
Sergei Morozov's avatar
Sergei Morozov committed
248
        $driverMock = $this->createMock(Driver::class);
249 250
        $driverMock->expects($this->any())
            ->method('connect')
251 252 253
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
Sergei Morozov's avatar
Sergei Morozov committed
254
        $conn = new Connection(['platform' => new Mocks\MockPlatform()], $driverMock);
255 256 257

        $conn->setAutoCommit(false);

258
        self::assertFalse($conn->isTransactionActive());
259 260 261

        $conn->connect();

262
        self::assertTrue($conn->isTransactionActive());
263 264 265 266 267 268 269
    }

    /**
     * @group DBAL-81
     */
    public function testCommitStartsTransactionInNoAutoCommitMode()
    {
Sergei Morozov's avatar
Sergei Morozov committed
270
        $driverMock = $this->createMock(Driver::class);
271 272
        $driverMock->expects($this->any())
            ->method('connect')
273 274 275
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
Sergei Morozov's avatar
Sergei Morozov committed
276
        $conn = new Connection(['platform' => new Mocks\MockPlatform()], $driverMock);
277 278 279 280 281

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

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

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

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

302
        self::assertTrue($conn->isTransactionActive());
303 304 305 306 307 308 309
    }

    /**
     * @group DBAL-81
     */
    public function testSwitchingAutoCommitModeCommitsAllCurrentTransactions()
    {
Sergei Morozov's avatar
Sergei Morozov committed
310
        $driverMock = $this->createMock(Driver::class);
311 312
        $driverMock->expects($this->any())
            ->method('connect')
313 314 315
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
Sergei Morozov's avatar
Sergei Morozov committed
316
        $conn = new Connection(['platform' => new Mocks\MockPlatform()], $driverMock);
317 318 319 320 321 322

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

323
        self::assertSame(1, $conn->getTransactionNestingLevel());
324 325 326 327 328

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

329
        self::assertFalse($conn->isTransactionActive());
330
    }
331 332 333

    public function testEmptyInsert()
    {
334
        $conn = $this->getExecuteUpdateMockConnection();
335 336 337 338 339

        $conn->expects($this->once())
            ->method('executeUpdate')
            ->with('INSERT INTO footable () VALUES ()');

Sergei Morozov's avatar
Sergei Morozov committed
340
        $conn->insert('footable', []);
341
    }
342

343 344 345 346 347
    /**
     * @group DBAL-2511
     */
    public function testUpdateWithDifferentColumnsInDataAndIdentifiers()
    {
348
        $conn = $this->getExecuteUpdateMockConnection();
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391

        $conn->expects($this->once())
            ->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
     */
    public function testUpdateWithSameColumnInDataAndIdentifiers()
    {
392
        $conn = $this->getExecuteUpdateMockConnection();
393 394 395 396 397 398 399 400 401 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

        $conn->expects($this->once())
            ->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',
            ]
        );
    }

430 431 432 433 434
    /**
     * @group DBAL-2688
     */
    public function testUpdateWithIsNull()
    {
435
        $conn = $this->getExecuteUpdateMockConnection();
436 437 438 439 440 441 442 443 444 445 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

        $conn->expects($this->once())
            ->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
     */
    public function testDeleteWithIsNull()
    {
477
        $conn = $this->getExecuteUpdateMockConnection();
478 479 480 481 482

        $conn->expects($this->once())
            ->method('executeUpdate')
            ->with(
                'DELETE FROM TestTable WHERE id IS NULL AND name = ?',
Sergei Morozov's avatar
Sergei Morozov committed
483 484
                ['foo'],
                ['string']
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
            );

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

500 501 502
    public function testFetchAssoc()
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
503 504 505
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
506

Sergei Morozov's avatar
Sergei Morozov committed
507
        $driverMock = $this->createMock(Driver::class);
508 509 510

        $driverMock->expects($this->any())
            ->method('connect')
511 512 513
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
514

Sergei Morozov's avatar
Sergei Morozov committed
515
        $driverStatementMock = $this->createMock(DriverStatementMock::class);
516 517 518

        $driverStatementMock->expects($this->once())
            ->method('fetch')
519
            ->with(FetchMode::ASSOCIATIVE)
520 521
            ->will($this->returnValue($result));

Sergei Morozov's avatar
Sergei Morozov committed
522
        /** @var PHPUnit_Framework_MockObject_MockObject|Connection $conn */
Sergei Morozov's avatar
Sergei Morozov committed
523
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
524 525
            ->setMethods(['executeQuery'])
            ->setConstructorArgs([['platform' => new Mocks\MockPlatform()], $driverMock])
526 527 528 529 530 531 532
            ->getMock();

        $conn->expects($this->once())
            ->method('executeQuery')
            ->with($statement, $params, $types)
            ->will($this->returnValue($driverStatementMock));

533
        self::assertSame($result, $conn->fetchAssoc($statement, $params, $types));
534 535 536 537 538
    }

    public function testFetchArray()
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
539 540 541
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
542

Sergei Morozov's avatar
Sergei Morozov committed
543
        $driverMock = $this->createMock(Driver::class);
544 545 546

        $driverMock->expects($this->any())
            ->method('connect')
547 548 549
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
550

Sergei Morozov's avatar
Sergei Morozov committed
551
        $driverStatementMock = $this->createMock(DriverStatementMock::class);
552 553 554

        $driverStatementMock->expects($this->once())
            ->method('fetch')
555
            ->with(FetchMode::NUMERIC)
556 557
            ->will($this->returnValue($result));

Sergei Morozov's avatar
Sergei Morozov committed
558
        /** @var PHPUnit_Framework_MockObject_MockObject|Connection $conn */
Sergei Morozov's avatar
Sergei Morozov committed
559
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
560 561
            ->setMethods(['executeQuery'])
            ->setConstructorArgs([['platform' => new Mocks\MockPlatform()], $driverMock])
562 563 564 565 566 567 568
            ->getMock();

        $conn->expects($this->once())
            ->method('executeQuery')
            ->with($statement, $params, $types)
            ->will($this->returnValue($driverStatementMock));

569
        self::assertSame($result, $conn->fetchArray($statement, $params, $types));
570 571 572 573 574
    }

    public function testFetchColumn()
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
575 576
        $params    = [666];
        $types     = [ParameterType::INTEGER];
577
        $column    = 0;
Sergei Morozov's avatar
Sergei Morozov committed
578
        $result    = [];
579

Sergei Morozov's avatar
Sergei Morozov committed
580
        $driverMock = $this->createMock(Driver::class);
581 582 583

        $driverMock->expects($this->any())
            ->method('connect')
584 585 586
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
587

Sergei Morozov's avatar
Sergei Morozov committed
588
        $driverStatementMock = $this->createMock(DriverStatementMock::class);
589 590 591 592 593 594

        $driverStatementMock->expects($this->once())
            ->method('fetchColumn')
            ->with($column)
            ->will($this->returnValue($result));

Sergei Morozov's avatar
Sergei Morozov committed
595
        /** @var PHPUnit_Framework_MockObject_MockObject|Connection $conn */
Sergei Morozov's avatar
Sergei Morozov committed
596
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
597 598
            ->setMethods(['executeQuery'])
            ->setConstructorArgs([['platform' => new Mocks\MockPlatform()], $driverMock])
599 600 601 602 603 604 605
            ->getMock();

        $conn->expects($this->once())
            ->method('executeQuery')
            ->with($statement, $params, $types)
            ->will($this->returnValue($driverStatementMock));

606
        self::assertSame($result, $conn->fetchColumn($statement, $params, $column, $types));
607
    }
malukenho's avatar
malukenho committed
608

609
    public function testConnectionIsClosedButNotUnset()
malukenho's avatar
malukenho committed
610
    {
611
        // mock Connection, and make connect() purposefully do nothing
Sergei Morozov's avatar
Sergei Morozov committed
612
        $connection = $this->getMockBuilder(Connection::class)
613
            ->disableOriginalConstructor()
Sergei Morozov's avatar
Sergei Morozov committed
614
            ->setMethods(['connect'])
615 616 617
            ->getMock();

        // artificially set the wrapped connection to non-null
Sergei Morozov's avatar
Sergei Morozov committed
618
        $reflection   = new ReflectionObject($connection);
619 620
        $connProperty = $reflection->getProperty('_conn');
        $connProperty->setAccessible(true);
Sergei Morozov's avatar
Sergei Morozov committed
621
        $connProperty->setValue($connection, new stdClass());
622

623 624
        // close the connection (should nullify the wrapped connection)
        $connection->close();
625

626 627 628
        // the wrapped connection should be null
        // (and since connect() does nothing, this will not reconnect)
        // this will also fail if this _conn property was unset instead of set to null
629
        self::assertNull($connection->getWrappedConnection());
malukenho's avatar
malukenho committed
630
    }
631 632 633 634

    public function testFetchAll()
    {
        $statement = 'SELECT * FROM foo WHERE bar = ?';
Sergei Morozov's avatar
Sergei Morozov committed
635 636 637
        $params    = [666];
        $types     = [ParameterType::INTEGER];
        $result    = [];
638

Sergei Morozov's avatar
Sergei Morozov committed
639
        $driverMock = $this->createMock(Driver::class);
640 641 642

        $driverMock->expects($this->any())
            ->method('connect')
643 644 645
            ->will($this->returnValue(
                $this->createMock(DriverConnection::class)
            ));
646

Sergei Morozov's avatar
Sergei Morozov committed
647
        $driverStatementMock = $this->createMock(DriverStatementMock::class);
648 649 650 651 652

        $driverStatementMock->expects($this->once())
            ->method('fetchAll')
            ->will($this->returnValue($result));

Sergei Morozov's avatar
Sergei Morozov committed
653
        /** @var PHPUnit_Framework_MockObject_MockObject|Connection $conn */
Sergei Morozov's avatar
Sergei Morozov committed
654
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
655 656
            ->setMethods(['executeQuery'])
            ->setConstructorArgs([['platform' => new Mocks\MockPlatform()], $driverMock])
657 658 659 660 661 662 663
            ->getMock();

        $conn->expects($this->once())
            ->method('executeQuery')
            ->with($statement, $params, $types)
            ->will($this->returnValue($driverStatementMock));

664
        self::assertSame($result, $conn->fetchAll($statement, $params, $types));
665
    }
666 667 668

    public function testConnectionDoesNotMaintainTwoReferencesToExternalPDO()
    {
Sergei Morozov's avatar
Sergei Morozov committed
669
        $params['pdo'] = new stdClass();
670

Sergei Morozov's avatar
Sergei Morozov committed
671
        $driverMock = $this->createMock(Driver::class);
672 673 674

        $conn = new Connection($params, $driverMock);

Sergei Morozov's avatar
Sergei Morozov committed
675
        self::assertArrayNotHasKey('pdo', $conn->getParams(), 'Connection is maintaining additional reference to the PDO connection');
676 677 678 679
    }

    public function testPassingExternalPDOMeansConnectionIsConnected()
    {
Sergei Morozov's avatar
Sergei Morozov committed
680
        $params['pdo'] = new stdClass();
681

Sergei Morozov's avatar
Sergei Morozov committed
682
        $driverMock = $this->createMock(Driver::class);
683 684 685

        $conn = new Connection($params, $driverMock);

Sergei Morozov's avatar
Sergei Morozov committed
686
        self::assertTrue($conn->isConnected(), 'Connection is not connected after passing external PDO');
687
    }
688

689
    public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException()
690
    {
Sergei Morozov's avatar
Sergei Morozov committed
691
        /** @var Driver $driver */
Sergei Morozov's avatar
Sergei Morozov committed
692 693
        $driver  = $this->createMock(Driver::class);
        $pdoMock = $this->createMock(\Doctrine\DBAL\Driver\Connection::class);
694

695 696 697
        // should never execute queries with invalid arguments
        $pdoMock->expects($this->never())->method('exec');
        $pdoMock->expects($this->never())->method('prepare');
698

Sergei Morozov's avatar
Sergei Morozov committed
699
        $conn = new Connection(['pdo' => $pdoMock], $driver);
700

Luís Cobucci's avatar
Luís Cobucci committed
701
        $this->expectException(InvalidArgumentException::class);
Sergei Morozov's avatar
Sergei Morozov committed
702
        $conn->delete('kittens', []);
703
    }
704 705 706

    public function dataCallConnectOnce()
    {
Sergei Morozov's avatar
Sergei Morozov committed
707 708 709 710 711 712 713
        return [
            ['delete', ['tbl', ['id' => 12345]]],
            ['insert', ['tbl', ['data' => 'foo']]],
            ['update', ['tbl', ['data' => 'bar'], ['id' => 12345]]],
            ['prepare', ['select * from dual']],
            ['executeUpdate', ['insert into tbl (id) values (?)'], [123]],
        ];
714 715 716 717 718 719 720
    }

    /**
     * @dataProvider dataCallConnectOnce
     */
    public function testCallConnectOnce($method, $params)
    {
Sergei Morozov's avatar
Sergei Morozov committed
721 722
        $driverMock   = $this->createMock(Driver::class);
        $pdoMock      = $this->createMock(\Doctrine\DBAL\Driver\Connection::class);
723
        $platformMock = new Mocks\MockPlatform();
Sergei Morozov's avatar
Sergei Morozov committed
724
        $stmtMock     = $this->createMock(Statement::class);
725 726 727 728 729

        $pdoMock->expects($this->any())
            ->method('prepare')
            ->will($this->returnValue($stmtMock));

Sergei Morozov's avatar
Sergei Morozov committed
730
        $conn = $this->getMockBuilder(Connection::class)
Sergei Morozov's avatar
Sergei Morozov committed
731 732
            ->setConstructorArgs([['pdo' => $pdoMock, 'platform' => $platformMock], $driverMock])
            ->setMethods(['connect'])
733 734 735 736
            ->getMock();

        $conn->expects($this->once())->method('connect');

Sergei Morozov's avatar
Sergei Morozov committed
737
        call_user_func_array([$conn, $method], $params);
738
    }
739 740 741 742 743 744

    /**
     * @group DBAL-1127
     */
    public function testPlatformDetectionIsTriggerOnlyOnceOnRetrievingPlatform()
    {
Sergei Morozov's avatar
Sergei Morozov committed
745
        /** @var VersionAwarePlatformDriverMock|PHPUnit_Framework_MockObject_MockObject $driverMock */
Sergei Morozov's avatar
Sergei Morozov committed
746
        $driverMock = $this->createMock(VersionAwarePlatformDriverMock::class);
747

Sergei Morozov's avatar
Sergei Morozov committed
748
        /** @var ServerInfoAwareConnectionMock|PHPUnit_Framework_MockObject_MockObject $driverConnectionMock */
Sergei Morozov's avatar
Sergei Morozov committed
749
        $driverConnectionMock = $this->createMock(ServerInfoAwareConnectionMock::class);
750

Sergei Morozov's avatar
Sergei Morozov committed
751
        /** @var AbstractPlatform|PHPUnit_Framework_MockObject_MockObject $platformMock */
Sergei Morozov's avatar
Sergei Morozov committed
752
        $platformMock = $this->getMockForAbstractClass(AbstractPlatform::class);
753

Sergei Morozov's avatar
Sergei Morozov committed
754
        $connection = new Connection([], $driverMock);
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772

        $driverMock->expects($this->once())
            ->method('connect')
            ->will($this->returnValue($driverConnectionMock));

        $driverConnectionMock->expects($this->once())
            ->method('requiresQueryForServerVersion')
            ->will($this->returnValue(false));

        $driverConnectionMock->expects($this->once())
            ->method('getServerVersion')
            ->will($this->returnValue('6.6.6'));

        $driverMock->expects($this->once())
            ->method('createDatabasePlatformForVersion')
            ->with('6.6.6')
            ->will($this->returnValue($platformMock));

773
        self::assertSame($platformMock, $connection->getDatabasePlatform());
774
    }
775 776 777

    public function testConnectionParamsArePassedToTheQueryCacheProfileInExecuteCacheQuery()
    {
778
        $resultCacheDriverMock = $this->createMock(Cache::class);
779

780 781
        $resultCacheDriverMock
            ->expects($this->atLeastOnce())
782
            ->method('fetch')
Antonio Vilar's avatar
Antonio Vilar committed
783
            ->with('cacheKey')
784
            ->will($this->returnValue(['realKey' => []]));
785

786 787
        $query  = 'SELECT * FROM foo WHERE bar = ?';
        $params = [666];
788
        $types  = [ParameterType::INTEGER];
789

Sergei Morozov's avatar
Sergei Morozov committed
790
        /** @var QueryCacheProfile|PHPUnit_Framework_MockObject_MockObject $queryCacheProfileMock */
791
        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
792

793 794
        $queryCacheProfileMock
            ->expects($this->any())
795 796 797 798
            ->method('getResultCacheDriver')
            ->will($this->returnValue($resultCacheDriverMock));

        // This is our main expectation
799 800
        $queryCacheProfileMock
            ->expects($this->once())
801 802
            ->method('generateCacheKeys')
            ->with($query, $params, $types, $this->params)
803
            ->will($this->returnValue(['cacheKey', 'realKey']));
804

Sergei Morozov's avatar
Sergei Morozov committed
805
        /** @var Driver $driver */
806 807
        $driver = $this->createMock(Driver::class);

808
        self::assertInstanceOf(
809 810
            ArrayStatement::class,
            (new Connection($this->params, $driver))->executeCacheQuery($query, $params, $types, $queryCacheProfileMock)
811 812 813
        );
    }

814
    /**
815
     * @group #2821
816
     */
Sergei Morozov's avatar
Sergei Morozov committed
817
    public function testShouldNotPassPlatformInParamsToTheQueryCacheProfileInExecuteCacheQuery() : void
818 819 820 821 822 823 824 825 826
    {
        $resultCacheDriverMock = $this->createMock(Cache::class);

        $resultCacheDriverMock
            ->expects($this->atLeastOnce())
            ->method('fetch')
            ->with('cacheKey')
            ->will($this->returnValue(['realKey' => []]));

Sergei Morozov's avatar
Sergei Morozov committed
827
        /** @var QueryCacheProfile|PHPUnit_Framework_MockObject_MockObject $queryCacheProfileMock */
828 829 830 831 832 833 834
        $queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);

        $queryCacheProfileMock
            ->expects($this->any())
            ->method('getResultCacheDriver')
            ->will($this->returnValue($resultCacheDriverMock));

Sergei Morozov's avatar
Sergei Morozov committed
835
        $query = 'SELECT 1';
836

837
        $connectionParams = $this->params;
838 839 840 841

        $queryCacheProfileMock
            ->expects($this->once())
            ->method('generateCacheKeys')
842
            ->with($query, [], [], $connectionParams)
843 844
            ->will($this->returnValue(['cacheKey', 'realKey']));

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

Sergei Morozov's avatar
Sergei Morozov committed
847
        /** @var Driver $driver */
848 849
        $driver = $this->createMock(Driver::class);

850
        (new Connection($connectionParams, $driver))->executeCacheQuery($query, [], [], $queryCacheProfileMock);
851
    }
852

853
    /**
854
     * @group #2821
855
     */
Sergei Morozov's avatar
Sergei Morozov committed
856
    public function testThrowsExceptionWhenInValidPlatformSpecified() : void
857
    {
Sergei Morozov's avatar
Sergei Morozov committed
858 859
        $connectionParams             = $this->params;
        $connectionParams['platform'] = new stdClass();
860

Sergei Morozov's avatar
Sergei Morozov committed
861
        /** @var Driver $driver */
862
        $driver = $this->createMock(Driver::class);
863 864

        $this->expectException(DBALException::class);
865 866 867 868

        new Connection($connectionParams, $driver);
    }

869 870 871 872 873
    /**
     * @group DBAL-990
     */
    public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnectingToNonExistentDatabase()
    {
Sergei Morozov's avatar
Sergei Morozov committed
874
        /** @var VersionAwarePlatformDriverMock|PHPUnit_Framework_MockObject_MockObject $driverMock */
875 876
        $driverMock = $this->createMock(VersionAwarePlatformDriverMock::class);

Sergei Morozov's avatar
Sergei Morozov committed
877 878 879
        $connection        = new Connection(['dbname' => 'foo'], $driverMock);
        $originalException = new Exception('Original exception');
        $fallbackException = new Exception('Fallback exception');
880 881 882 883 884 885 886 887 888 889 890 891 892

        $driverMock->expects($this->at(0))
            ->method('connect')
            ->willThrowException($originalException);

        $driverMock->expects($this->at(1))
            ->method('connect')
            ->willThrowException($fallbackException);

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

        $connection->getDatabasePlatform();
    }
893
}