Failed Conditions
Pull Request — 2.10 (#3803)
by Sergei
62:38
created

ConnectionTest   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 356
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 45
eloc 180
dl 0
loc 356
rs 8.8
c 4
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A tearDown() 0 8 2
A testCreateSavepointsNotSupportedThrowsException() 0 10 2
A testTransactionalWithException() 0 11 2
A testTransactionBehaviour() 0 12 2
A testRollbackSavepointsNotSupportedThrowsException() 0 10 2
A setUp() 0 4 1
A testTransactional() 0 8 1
A testReleaseSavepointsNotSupportedThrowsException() 0 10 2
A testTransactionNestingLevelIsResetOnReconnect() 0 27 2
A testTransactionalWithThrowable() 0 11 2
B testTransactionNestingBehaviorWithSavepoints() 0 36 5
A testTransactionalReturnValue() 0 7 1
A testTransactionNestingBehaviorCantBeChangedInActiveTransaction() 0 9 2
A testTransactionNestingBehavior() 0 30 3
A testSetNestedTransactionsThroughSavepointsNotSupportedThrowsException() 0 10 2
A testCommitWithRollbackOnlyThrowsException() 0 7 1
A testGetWrappedConnection() 0 3 1
A testTransactionBehaviorWithRollback() 0 13 2
A testConnectWithoutExplicitDatabaseName() 0 18 2
A testPersistentConnection() 0 22 4
A testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase() 0 21 2
A testQuote() 0 5 1
A testPingDoesTriggersConnect() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like ConnectionTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ConnectionTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\Tests\DBAL\Functional;
4
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\ConnectionException;
7
use Doctrine\DBAL\Driver\Connection as DriverConnection;
8
use Doctrine\DBAL\Driver\PDOConnection;
9
use Doctrine\DBAL\DriverManager;
10
use Doctrine\DBAL\ParameterType;
11
use Doctrine\DBAL\Platforms\AbstractPlatform;
12
use Doctrine\DBAL\Platforms\SqlitePlatform;
13
use Doctrine\DBAL\Platforms\SQLServerPlatform;
14
use Doctrine\DBAL\Types\Types;
15
use Doctrine\Tests\DbalFunctionalTestCase;
16
use Doctrine\Tests\TestUtil;
17
use Error;
18
use Exception;
19
use PDO;
20
use RuntimeException;
21
use Throwable;
22
use function file_exists;
23
use function in_array;
24
use function unlink;
25
26
class ConnectionTest extends DbalFunctionalTestCase
27
{
28
    protected function setUp() : void
29
    {
30
        $this->resetSharedConn();
31
        parent::setUp();
32
    }
33
34
    protected function tearDown() : void
35
    {
36
        if (file_exists('/tmp/test_nesting.sqlite')) {
37
            unlink('/tmp/test_nesting.sqlite');
38
        }
39
40
        parent::tearDown();
41
        $this->resetSharedConn();
42
    }
43
44
    public function testGetWrappedConnection() : void
45
    {
46
        self::assertInstanceOf(DriverConnection::class, $this->connection->getWrappedConnection());
47
    }
48
49
    public function testCommitWithRollbackOnlyThrowsException() : void
50
    {
51
        $this->connection->beginTransaction();
52
        $this->connection->setRollbackOnly();
53
54
        $this->expectException(ConnectionException::class);
55
        $this->connection->commit();
56
    }
57
58
    public function testTransactionNestingBehavior() : void
59
    {
60
        try {
61
            $this->connection->beginTransaction();
62
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
63
64
            try {
65
                $this->connection->beginTransaction();
66
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
67
                throw new Exception();
68
                $this->connection->commit(); // never reached
0 ignored issues
show
Unused Code introduced by
$this->connection->commit() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
69
            } catch (Throwable $e) {
70
                $this->connection->rollBack();
71
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
72
                //no rethrow
73
            }
74
            self::assertTrue($this->connection->isRollbackOnly());
75
76
            $this->connection->commit(); // should throw exception
77
            $this->fail('Transaction commit after failed nested transaction should fail.');
78
        } catch (ConnectionException $e) {
79
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
80
            $this->connection->rollBack();
81
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
82
        }
83
84
        $this->connection->beginTransaction();
85
        $this->connection->close();
86
        $this->connection->beginTransaction();
87
        self::assertEquals(1, $this->connection->getTransactionNestingLevel());
88
    }
89
90
    public function testTransactionNestingLevelIsResetOnReconnect() : void
91
    {
92
        if ($this->connection->getDatabasePlatform()->getName() === 'sqlite') {
93
            $params           = $this->connection->getParams();
94
            $params['memory'] = false;
95
            $params['path']   = '/tmp/test_nesting.sqlite';
96
97
            $connection = DriverManager::getConnection(
98
                $params,
99
                $this->connection->getConfiguration(),
100
                $this->connection->getEventManager()
101
            );
102
        } else {
103
            $connection = $this->connection;
104
        }
105
106
        $connection->executeQuery('CREATE TABLE test_nesting(test int not null)');
107
108
        $this->connection->beginTransaction();
109
        $this->connection->beginTransaction();
110
        $connection->close(); // connection closed in runtime (for example if lost or another application logic)
111
112
        $connection->beginTransaction();
113
        $connection->executeQuery('insert into test_nesting values (33)');
114
        $connection->rollback();
115
116
        self::assertEquals(0, $connection->fetchColumn('select count(*) from test_nesting'));
117
    }
118
119
    public function testTransactionNestingBehaviorWithSavepoints() : void
120
    {
121
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
122
            $this->markTestSkipped('This test requires the platform to support savepoints.');
123
        }
124
125
        $this->connection->setNestTransactionsWithSavepoints(true);
126
        try {
127
            $this->connection->beginTransaction();
128
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
129
130
            try {
131
                $this->connection->beginTransaction();
132
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
133
                $this->connection->beginTransaction();
134
                self::assertEquals(3, $this->connection->getTransactionNestingLevel());
135
                self::assertTrue($this->connection->commit());
136
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
137
                throw new Exception();
138
                $this->connection->commit(); // never reached
0 ignored issues
show
Unused Code introduced by
$this->connection->commit() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
139
            } catch (Throwable $e) {
140
                $this->connection->rollBack();
141
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
142
                //no rethrow
143
            }
144
            self::assertFalse($this->connection->isRollbackOnly());
145
            try {
146
                $this->connection->setNestTransactionsWithSavepoints(false);
147
                $this->fail('Should not be able to disable savepoints in usage for nested transactions inside an open transaction.');
148
            } catch (ConnectionException $e) {
149
                self::assertTrue($this->connection->getNestTransactionsWithSavepoints());
150
            }
151
            $this->connection->commit(); // should not throw exception
152
        } catch (ConnectionException $e) {
153
            $this->fail('Transaction commit after failed nested transaction should not fail when using savepoints.');
154
            $this->connection->rollBack();
155
        }
156
    }
157
158
    public function testTransactionNestingBehaviorCantBeChangedInActiveTransaction() : void
159
    {
160
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
161
            $this->markTestSkipped('This test requires the platform to support savepoints.');
162
        }
163
164
        $this->connection->beginTransaction();
165
        $this->expectException(ConnectionException::class);
166
        $this->connection->setNestTransactionsWithSavepoints(true);
167
    }
168
169
    public function testSetNestedTransactionsThroughSavepointsNotSupportedThrowsException() : void
170
    {
171
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
172
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
173
        }
174
175
        $this->expectException(ConnectionException::class);
176
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
177
178
        $this->connection->setNestTransactionsWithSavepoints(true);
179
    }
180
181
    public function testCreateSavepointsNotSupportedThrowsException() : void
182
    {
183
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
184
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
185
        }
186
187
        $this->expectException(ConnectionException::class);
188
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
189
190
        $this->connection->createSavepoint('foo');
191
    }
192
193
    public function testReleaseSavepointsNotSupportedThrowsException() : void
194
    {
195
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
196
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
197
        }
198
199
        $this->expectException(ConnectionException::class);
200
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
201
202
        $this->connection->releaseSavepoint('foo');
203
    }
204
205
    public function testRollbackSavepointsNotSupportedThrowsException() : void
206
    {
207
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
208
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
209
        }
210
211
        $this->expectException(ConnectionException::class);
212
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
213
214
        $this->connection->rollbackSavepoint('foo');
215
    }
216
217
    public function testTransactionBehaviorWithRollback() : void
218
    {
219
        try {
220
            $this->connection->beginTransaction();
221
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
222
223
            throw new Exception();
224
225
            $this->connection->commit(); // never reached
0 ignored issues
show
Unused Code introduced by
$this->connection->commit() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
226
        } catch (Throwable $e) {
227
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
228
            $this->connection->rollBack();
229
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
230
        }
231
    }
232
233
    public function testTransactionBehaviour() : void
234
    {
235
        try {
236
            $this->connection->beginTransaction();
237
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
238
            $this->connection->commit();
239
        } catch (Throwable $e) {
240
            $this->connection->rollBack();
241
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
242
        }
243
244
        self::assertEquals(0, $this->connection->getTransactionNestingLevel());
245
    }
246
247
    public function testTransactionalWithException() : void
248
    {
249
        try {
250
            $this->connection->transactional(static function ($conn) : void {
251
                /** @var Connection $conn */
252
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
253
                throw new RuntimeException('Ooops!');
254
            });
255
            $this->fail('Expected exception');
256
        } catch (RuntimeException $expected) {
257
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
258
        }
259
    }
260
261
    public function testTransactionalWithThrowable() : void
262
    {
263
        try {
264
            $this->connection->transactional(static function ($conn) : void {
265
                /** @var Connection $conn */
266
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
267
                throw new Error('Ooops!');
268
            });
269
            $this->fail('Expected exception');
270
        } catch (Error $expected) {
271
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
272
        }
273
    }
274
275
    public function testTransactional() : void
276
    {
277
        $res = $this->connection->transactional(static function ($conn) : void {
278
            /** @var Connection $conn */
279
            $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
280
        });
281
282
        self::assertNull($res);
283
    }
284
285
    public function testTransactionalReturnValue() : void
286
    {
287
        $res = $this->connection->transactional(static function () {
288
            return 42;
289
        });
290
291
        self::assertEquals(42, $res);
292
    }
293
294
    /**
295
     * Tests that the quote function accepts DBAL and PDO types.
296
     */
297
    public function testQuote() : void
298
    {
299
        self::assertEquals(
300
            $this->connection->quote('foo', Types::STRING),
301
            $this->connection->quote('foo', ParameterType::STRING)
302
        );
303
    }
304
305
    public function testPingDoesTriggersConnect() : void
306
    {
307
        self::assertTrue($this->connection->ping());
308
        self::assertTrue($this->connection->isConnected());
309
    }
310
311
    /**
312
     * @group DBAL-1025
313
     */
314
    public function testConnectWithoutExplicitDatabaseName() : void
315
    {
316
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
317
            $this->markTestSkipped('Platform does not support connecting without database name.');
318
        }
319
320
        $params = $this->connection->getParams();
321
        unset($params['dbname']);
322
323
        $connection = DriverManager::getConnection(
324
            $params,
325
            $this->connection->getConfiguration(),
326
            $this->connection->getEventManager()
327
        );
328
329
        self::assertTrue($connection->connect());
330
331
        $connection->close();
332
    }
333
334
    /**
335
     * @group DBAL-990
336
     */
337
    public function testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase() : void
338
    {
339
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
340
            $this->markTestSkipped('Platform does not support connecting without database name.');
341
        }
342
343
        $params = $this->connection->getParams();
344
345
        $params['dbname'] = 'foo_bar';
346
347
        $connection = DriverManager::getConnection(
348
            $params,
349
            $this->connection->getConfiguration(),
350
            $this->connection->getEventManager()
351
        );
352
353
        self::assertInstanceOf(AbstractPlatform::class, $connection->getDatabasePlatform());
354
        self::assertFalse($connection->isConnected());
355
        self::assertSame($params, $connection->getParams());
356
357
        $connection->close();
358
    }
359
360
    public function testPersistentConnection() : void
361
    {
362
        $platform = $this->connection->getDatabasePlatform();
363
364
        if ($platform instanceof SqlitePlatform
365
            || $platform instanceof SQLServerPlatform) {
366
            self::markTestSkipped('The platform does not support persistent connections');
367
        }
368
369
        $params               = TestUtil::getConnectionParams();
370
        $params['persistent'] = true;
371
372
        $connection       = DriverManager::getConnection($params);
373
        $driverConnection = $connection->getWrappedConnection();
374
375
        if (! $driverConnection instanceof PDOConnection) {
376
            self::markTestSkipped('Unable to test if the connection is persistent');
377
        }
378
379
        $pdo = $driverConnection->getWrappedConnection();
380
381
        self::assertTrue($pdo->getAttribute(PDO::ATTR_PERSISTENT));
382
    }
383
}
384