Failed Conditions
Push — master ( 01c22b...e42c1f )
by Marco
79:13 queued 10s
created

ConnectionTest   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 346
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 43
eloc 178
c 3
b 0
f 0
dl 0
loc 346
rs 8.96

22 Methods

Rating   Name   Duplication   Size   Complexity  
A tearDown() 0 4 1
A setUp() 0 4 1
A testCommitWithRollbackOnlyThrowsException() 0 7 1
A testGetWrappedConnection() 0 3 1
A testConnectWithoutExplicitDatabaseName() 0 20 2
A testCreateSavepointsNotSupportedThrowsException() 0 10 2
A testTransactionalWithException() 0 11 2
A testTransactionBehaviour() 0 12 2
A testRollbackSavepointsNotSupportedThrowsException() 0 10 2
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 testPersistentConnection() 0 22 4
A testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase() 0 21 2
A testTransactionBehaviorWithRollback() 0 13 2
A testPingDoesTriggersConnect() 0 7 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
declare(strict_types=1);
4
5
namespace Doctrine\Tests\DBAL\Functional;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\DBAL\ConnectionException;
9
use Doctrine\DBAL\Driver\Connection as DriverConnection;
10
use Doctrine\DBAL\Driver\PDOConnection;
11
use Doctrine\DBAL\DriverManager;
12
use Doctrine\DBAL\Platforms\AbstractPlatform;
13
use Doctrine\DBAL\Platforms\SqlitePlatform;
14
use Doctrine\DBAL\Platforms\SQLServerPlatform;
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 in_array;
23
24
class ConnectionTest extends DbalFunctionalTestCase
25
{
26
    protected function setUp() : void
27
    {
28
        $this->resetSharedConn();
29
        parent::setUp();
30
    }
31
32
    protected function tearDown() : void
33
    {
34
        parent::tearDown();
35
        $this->resetSharedConn();
36
    }
37
38
    public function testGetWrappedConnection() : void
39
    {
40
        self::assertInstanceOf(DriverConnection::class, $this->connection->getWrappedConnection());
41
    }
42
43
    public function testCommitWithRollbackOnlyThrowsException() : void
44
    {
45
        $this->connection->beginTransaction();
46
        $this->connection->setRollbackOnly();
47
48
        $this->expectException(ConnectionException::class);
49
        $this->connection->commit();
50
    }
51
52
    public function testTransactionNestingBehavior() : void
53
    {
54
        try {
55
            $this->connection->beginTransaction();
56
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
57
58
            try {
59
                $this->connection->beginTransaction();
60
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
61
                throw new Exception();
62
                $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...
63
            } catch (Throwable $e) {
64
                $this->connection->rollBack();
65
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
66
                //no rethrow
67
            }
68
            self::assertTrue($this->connection->isRollbackOnly());
69
70
            $this->connection->commit(); // should throw exception
71
            $this->fail('Transaction commit after failed nested transaction should fail.');
72
        } catch (ConnectionException $e) {
73
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
74
            $this->connection->rollBack();
75
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
76
        }
77
78
        $this->connection->beginTransaction();
79
        $this->connection->close();
80
        $this->connection->beginTransaction();
81
        self::assertEquals(1, $this->connection->getTransactionNestingLevel());
82
    }
83
84
    public function testTransactionNestingLevelIsResetOnReconnect() : void
85
    {
86
        if ($this->connection->getDatabasePlatform()->getName() === 'sqlite') {
87
            $params           = $this->connection->getParams();
88
            $params['memory'] = false;
89
            $params['path']   = '/tmp/test_nesting.sqlite';
90
91
            $connection = DriverManager::getConnection(
92
                $params,
93
                $this->connection->getConfiguration(),
94
                $this->connection->getEventManager()
95
            );
96
        } else {
97
            $connection = $this->connection;
98
        }
99
100
        $connection->executeQuery('CREATE TABLE test_nesting(test int not null)');
101
102
        $this->connection->beginTransaction();
103
        $this->connection->beginTransaction();
104
        $connection->close(); // connection closed in runtime (for example if lost or another application logic)
105
106
        $connection->beginTransaction();
107
        $connection->executeQuery('insert into test_nesting values (33)');
108
        $connection->rollback();
109
110
        self::assertEquals(0, $connection->fetchColumn('select count(*) from test_nesting'));
111
    }
112
113
    public function testTransactionNestingBehaviorWithSavepoints() : void
114
    {
115
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
116
            $this->markTestSkipped('This test requires the platform to support savepoints.');
117
        }
118
119
        $this->connection->setNestTransactionsWithSavepoints(true);
120
        try {
121
            $this->connection->beginTransaction();
122
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
123
124
            try {
125
                $this->connection->beginTransaction();
126
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
127
                $this->connection->beginTransaction();
128
                self::assertEquals(3, $this->connection->getTransactionNestingLevel());
129
                $this->connection->commit();
130
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
131
                throw new Exception();
132
                $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...
133
            } catch (Throwable $e) {
134
                $this->connection->rollBack();
135
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
136
                //no rethrow
137
            }
138
            self::assertFalse($this->connection->isRollbackOnly());
139
            try {
140
                $this->connection->setNestTransactionsWithSavepoints(false);
141
                $this->fail('Should not be able to disable savepoints in usage for nested transactions inside an open transaction.');
142
            } catch (ConnectionException $e) {
143
                self::assertTrue($this->connection->getNestTransactionsWithSavepoints());
144
            }
145
            $this->connection->commit(); // should not throw exception
146
        } catch (ConnectionException $e) {
147
            $this->fail('Transaction commit after failed nested transaction should not fail when using savepoints.');
148
            $this->connection->rollBack();
149
        }
150
    }
151
152
    public function testTransactionNestingBehaviorCantBeChangedInActiveTransaction() : void
153
    {
154
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
155
            $this->markTestSkipped('This test requires the platform to support savepoints.');
156
        }
157
158
        $this->connection->beginTransaction();
159
        $this->expectException(ConnectionException::class);
160
        $this->connection->setNestTransactionsWithSavepoints(true);
161
    }
162
163
    public function testSetNestedTransactionsThroughSavepointsNotSupportedThrowsException() : void
164
    {
165
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
166
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
167
        }
168
169
        $this->expectException(ConnectionException::class);
170
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
171
172
        $this->connection->setNestTransactionsWithSavepoints(true);
173
    }
174
175
    public function testCreateSavepointsNotSupportedThrowsException() : void
176
    {
177
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
178
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
179
        }
180
181
        $this->expectException(ConnectionException::class);
182
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
183
184
        $this->connection->createSavepoint('foo');
185
    }
186
187
    public function testReleaseSavepointsNotSupportedThrowsException() : void
188
    {
189
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
190
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
191
        }
192
193
        $this->expectException(ConnectionException::class);
194
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
195
196
        $this->connection->releaseSavepoint('foo');
197
    }
198
199
    public function testRollbackSavepointsNotSupportedThrowsException() : void
200
    {
201
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
202
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
203
        }
204
205
        $this->expectException(ConnectionException::class);
206
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
207
208
        $this->connection->rollbackSavepoint('foo');
209
    }
210
211
    public function testTransactionBehaviorWithRollback() : void
212
    {
213
        try {
214
            $this->connection->beginTransaction();
215
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
216
217
            throw new Exception();
218
219
            $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...
220
        } catch (Throwable $e) {
221
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
222
            $this->connection->rollBack();
223
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
224
        }
225
    }
226
227
    public function testTransactionBehaviour() : void
228
    {
229
        try {
230
            $this->connection->beginTransaction();
231
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
232
            $this->connection->commit();
233
        } catch (Throwable $e) {
234
            $this->connection->rollBack();
235
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
236
        }
237
238
        self::assertEquals(0, $this->connection->getTransactionNestingLevel());
239
    }
240
241
    public function testTransactionalWithException() : void
242
    {
243
        try {
244
            $this->connection->transactional(static function ($conn) : void {
245
                /** @var Connection $conn */
246
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
247
                throw new RuntimeException('Ooops!');
248
            });
249
            $this->fail('Expected exception');
250
        } catch (RuntimeException $expected) {
251
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
252
        }
253
    }
254
255
    public function testTransactionalWithThrowable() : void
256
    {
257
        try {
258
            $this->connection->transactional(static function ($conn) : void {
259
                /** @var Connection $conn */
260
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
261
                throw new Error('Ooops!');
262
            });
263
            $this->fail('Expected exception');
264
        } catch (Error $expected) {
265
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
266
        }
267
    }
268
269
    public function testTransactional() : void
270
    {
271
        $res = $this->connection->transactional(static function ($conn) : void {
272
            /** @var Connection $conn */
273
            $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
274
        });
275
276
        self::assertNull($res);
277
    }
278
279
    public function testTransactionalReturnValue() : void
280
    {
281
        $res = $this->connection->transactional(static function () {
282
            return 42;
283
        });
284
285
        self::assertEquals(42, $res);
286
    }
287
288
    public function testPingDoesTriggersConnect() : void
289
    {
290
        $this->connection->close();
291
        self::assertFalse($this->connection->isConnected());
292
293
        $this->connection->ping();
294
        self::assertTrue($this->connection->isConnected());
295
    }
296
297
    /**
298
     * @group DBAL-1025
299
     */
300
    public function testConnectWithoutExplicitDatabaseName() : void
301
    {
302
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
303
            $this->markTestSkipped('Platform does not support connecting without database name.');
304
        }
305
306
        $params = $this->connection->getParams();
307
        unset($params['dbname']);
308
309
        $connection = DriverManager::getConnection(
310
            $params,
311
            $this->connection->getConfiguration(),
312
            $this->connection->getEventManager()
313
        );
314
315
        $connection->connect();
316
317
        self::assertTrue($connection->isConnected());
318
319
        $connection->close();
320
    }
321
322
    /**
323
     * @group DBAL-990
324
     */
325
    public function testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase() : void
326
    {
327
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
328
            $this->markTestSkipped('Platform does not support connecting without database name.');
329
        }
330
331
        $params = $this->connection->getParams();
332
333
        $params['dbname'] = 'foo_bar';
334
335
        $connection = DriverManager::getConnection(
336
            $params,
337
            $this->connection->getConfiguration(),
338
            $this->connection->getEventManager()
339
        );
340
341
        self::assertInstanceOf(AbstractPlatform::class, $connection->getDatabasePlatform());
342
        self::assertFalse($connection->isConnected());
343
        self::assertSame($params, $connection->getParams());
344
345
        $connection->close();
346
    }
347
348
    public function testPersistentConnection() : void
349
    {
350
        $platform = $this->connection->getDatabasePlatform();
351
352
        if ($platform instanceof SqlitePlatform
353
            || $platform instanceof SQLServerPlatform) {
354
            self::markTestSkipped('The platform does not support persistent connections');
355
        }
356
357
        $params               = TestUtil::getConnectionParams();
358
        $params['persistent'] = true;
359
360
        $connection       = DriverManager::getConnection($params);
361
        $driverConnection = $connection->getWrappedConnection();
362
363
        if (! $driverConnection instanceof PDOConnection) {
364
            self::markTestSkipped('Unable to test if the connection is persistent');
365
        }
366
367
        $pdo = $driverConnection->getWrappedConnection();
368
369
        self::assertTrue($pdo->getAttribute(PDO::ATTR_PERSISTENT));
370
    }
371
}
372