Completed
Pull Request — develop (#3348)
by Sergei
65:02
created

ConnectionTest   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 309
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 41
eloc 154
dl 0
loc 309
rs 9.1199
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A tearDown() 0 4 1
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 testTransactionalWithThrowable() 0 11 2
B testTransactionNestingBehaviorWithSavepoints() 0 36 5
A testTransactionalReturnValue() 0 7 1
A testTransactionNestingBehaviorCantBeChangedInActiveTransaction() 0 9 2
A testTransactionNestingBehavior() 0 24 3
A testSetNestedTransactionsThroughSavepointsNotSupportedThrowsException() 0 10 2
A testCommitWithRollbackOnlyThrowsException() 0 7 1
A testGetWrappedConnection() 0 3 1
A testTransactionBehaviorWithRollback() 0 13 2
A testConnectWithoutExplicitDatabaseName() 0 20 2
A testPersistentConnection() 0 22 4
A testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase() 0 21 2
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
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
79
    public function testTransactionNestingBehaviorWithSavepoints() : void
80
    {
81
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
82
            $this->markTestSkipped('This test requires the platform to support savepoints.');
83
        }
84
85
        $this->connection->setNestTransactionsWithSavepoints(true);
86
        try {
87
            $this->connection->beginTransaction();
88
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
89
90
            try {
91
                $this->connection->beginTransaction();
92
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
93
                $this->connection->beginTransaction();
94
                self::assertEquals(3, $this->connection->getTransactionNestingLevel());
95
                $this->connection->commit();
96
                self::assertEquals(2, $this->connection->getTransactionNestingLevel());
97
                throw new Exception();
98
                $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...
99
            } catch (Throwable $e) {
100
                $this->connection->rollBack();
101
                self::assertEquals(1, $this->connection->getTransactionNestingLevel());
102
                //no rethrow
103
            }
104
            self::assertFalse($this->connection->isRollbackOnly());
105
            try {
106
                $this->connection->setNestTransactionsWithSavepoints(false);
107
                $this->fail('Should not be able to disable savepoints in usage for nested transactions inside an open transaction.');
108
            } catch (ConnectionException $e) {
109
                self::assertTrue($this->connection->getNestTransactionsWithSavepoints());
110
            }
111
            $this->connection->commit(); // should not throw exception
112
        } catch (ConnectionException $e) {
113
            $this->fail('Transaction commit after failed nested transaction should not fail when using savepoints.');
114
            $this->connection->rollBack();
115
        }
116
    }
117
118
    public function testTransactionNestingBehaviorCantBeChangedInActiveTransaction() : void
119
    {
120
        if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
121
            $this->markTestSkipped('This test requires the platform to support savepoints.');
122
        }
123
124
        $this->connection->beginTransaction();
125
        $this->expectException(ConnectionException::class);
126
        $this->connection->setNestTransactionsWithSavepoints(true);
127
    }
128
129
    public function testSetNestedTransactionsThroughSavepointsNotSupportedThrowsException() : void
130
    {
131
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
132
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
133
        }
134
135
        $this->expectException(ConnectionException::class);
136
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
137
138
        $this->connection->setNestTransactionsWithSavepoints(true);
139
    }
140
141
    public function testCreateSavepointsNotSupportedThrowsException() : void
142
    {
143
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
144
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
145
        }
146
147
        $this->expectException(ConnectionException::class);
148
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
149
150
        $this->connection->createSavepoint('foo');
151
    }
152
153
    public function testReleaseSavepointsNotSupportedThrowsException() : void
154
    {
155
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
156
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
157
        }
158
159
        $this->expectException(ConnectionException::class);
160
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
161
162
        $this->connection->releaseSavepoint('foo');
163
    }
164
165
    public function testRollbackSavepointsNotSupportedThrowsException() : void
166
    {
167
        if ($this->connection->getDatabasePlatform()->supportsSavepoints()) {
168
            $this->markTestSkipped('This test requires the platform not to support savepoints.');
169
        }
170
171
        $this->expectException(ConnectionException::class);
172
        $this->expectExceptionMessage('Savepoints are not supported by this driver.');
173
174
        $this->connection->rollbackSavepoint('foo');
175
    }
176
177
    public function testTransactionBehaviorWithRollback() : void
178
    {
179
        try {
180
            $this->connection->beginTransaction();
181
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
182
183
            throw new Exception();
184
185
            $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...
186
        } catch (Throwable $e) {
187
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
188
            $this->connection->rollBack();
189
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
190
        }
191
    }
192
193
    public function testTransactionBehaviour() : void
194
    {
195
        try {
196
            $this->connection->beginTransaction();
197
            self::assertEquals(1, $this->connection->getTransactionNestingLevel());
198
            $this->connection->commit();
199
        } catch (Throwable $e) {
200
            $this->connection->rollBack();
201
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
202
        }
203
204
        self::assertEquals(0, $this->connection->getTransactionNestingLevel());
205
    }
206
207
    public function testTransactionalWithException() : void
208
    {
209
        try {
210
            $this->connection->transactional(static function ($conn) : void {
211
                /** @var Connection $conn */
212
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
213
                throw new RuntimeException('Ooops!');
214
            });
215
            $this->fail('Expected exception');
216
        } catch (RuntimeException $expected) {
217
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
218
        }
219
    }
220
221
    public function testTransactionalWithThrowable() : void
222
    {
223
        try {
224
            $this->connection->transactional(static function ($conn) : void {
225
                /** @var Connection $conn */
226
                $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
227
                throw new Error('Ooops!');
228
            });
229
            $this->fail('Expected exception');
230
        } catch (Error $expected) {
231
            self::assertEquals(0, $this->connection->getTransactionNestingLevel());
232
        }
233
    }
234
235
    public function testTransactional() : void
236
    {
237
        $res = $this->connection->transactional(static function ($conn) : void {
238
            /** @var Connection $conn */
239
            $conn->executeQuery($conn->getDatabasePlatform()->getDummySelectSQL());
240
        });
241
242
        self::assertNull($res);
243
    }
244
245
    public function testTransactionalReturnValue() : void
246
    {
247
        $res = $this->connection->transactional(static function () {
248
            return 42;
249
        });
250
251
        self::assertEquals(42, $res);
252
    }
253
254
    public function testPingDoesTriggersConnect() : void
255
    {
256
        self::assertTrue($this->connection->ping());
257
        self::assertTrue($this->connection->isConnected());
258
    }
259
260
    /**
261
     * @group DBAL-1025
262
     */
263
    public function testConnectWithoutExplicitDatabaseName() : void
264
    {
265
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
266
            $this->markTestSkipped('Platform does not support connecting without database name.');
267
        }
268
269
        $params = $this->connection->getParams();
270
        unset($params['dbname']);
271
272
        $connection = DriverManager::getConnection(
273
            $params,
274
            $this->connection->getConfiguration(),
275
            $this->connection->getEventManager()
276
        );
277
278
        $connection->connect();
279
280
        self::assertTrue($connection->isConnected());
281
282
        $connection->close();
283
    }
284
285
    /**
286
     * @group DBAL-990
287
     */
288
    public function testDeterminesDatabasePlatformWhenConnectingToNonExistentDatabase() : void
289
    {
290
        if (in_array($this->connection->getDatabasePlatform()->getName(), ['oracle', 'db2'], true)) {
291
            $this->markTestSkipped('Platform does not support connecting without database name.');
292
        }
293
294
        $params = $this->connection->getParams();
295
296
        $params['dbname'] = 'foo_bar';
297
298
        $connection = DriverManager::getConnection(
299
            $params,
300
            $this->connection->getConfiguration(),
301
            $this->connection->getEventManager()
302
        );
303
304
        self::assertInstanceOf(AbstractPlatform::class, $connection->getDatabasePlatform());
305
        self::assertFalse($connection->isConnected());
306
        self::assertSame($params, $connection->getParams());
307
308
        $connection->close();
309
    }
310
311
    public function testPersistentConnection() : void
312
    {
313
        $platform = $this->connection->getDatabasePlatform();
314
315
        if ($platform instanceof SqlitePlatform
316
            || $platform instanceof SQLServerPlatform) {
317
            self::markTestSkipped('The platform does not support persistent connections');
318
        }
319
320
        $params               = TestUtil::getConnectionParams();
321
        $params['persistent'] = true;
322
323
        $connection       = DriverManager::getConnection($params);
324
        $driverConnection = $connection->getWrappedConnection();
325
326
        if (! $driverConnection instanceof PDOConnection) {
327
            self::markTestSkipped('Unable to test if the connection is persistent');
328
        }
329
330
        $pdo = $driverConnection->getWrappedConnection();
331
332
        self::assertTrue($pdo->getAttribute(PDO::ATTR_PERSISTENT));
333
    }
334
}
335