Passed
Pull Request — master (#163)
by Wilmer
12:24
created

TestConnectionTrait::testClone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 21
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 34
rs 9.584
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\TestUtility;
6
7
use Yiisoft\Db\Connection\ConnectionInterface;
8
use Yiisoft\Db\Exception\Exception;
9
use Yiisoft\Db\Exception\NotSupportedException;
10
use Yiisoft\Db\Transaction\Transaction;
11
12
use function serialize;
13
use function unserialize;
14
15
trait TestConnectionTrait
16
{
17
    public function testSerialize(): void
18
    {
19
        $db = $this->getConnection();
0 ignored issues
show
Bug introduced by
It seems like getConnection() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

19
        /** @scrutinizer ignore-call */ 
20
        $db = $this->getConnection();
Loading history...
20
21
        $db->open();
22
23
        $serialized = serialize($db);
24
25
        $this->assertNotNull($db->getPDO());
0 ignored issues
show
Bug introduced by
It seems like assertNotNull() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

25
        $this->/** @scrutinizer ignore-call */ 
26
               assertNotNull($db->getPDO());
Loading history...
26
27
        $unserialized = unserialize($serialized);
28
29
        $this->assertInstanceOf(ConnectionInterface::class, $unserialized);
0 ignored issues
show
Bug introduced by
It seems like assertInstanceOf() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

29
        $this->/** @scrutinizer ignore-call */ 
30
               assertInstanceOf(ConnectionInterface::class, $unserialized);
Loading history...
30
        $this->assertNull($unserialized->getPDO());
0 ignored issues
show
Bug introduced by
It seems like assertNull() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

30
        $this->/** @scrutinizer ignore-call */ 
31
               assertNull($unserialized->getPDO());
Loading history...
31
        $this->assertEquals(123, $unserialized->createCommand('SELECT 123')->queryScalar());
0 ignored issues
show
Bug introduced by
It seems like assertEquals() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

31
        $this->/** @scrutinizer ignore-call */ 
32
               assertEquals(123, $unserialized->createCommand('SELECT 123')->queryScalar());
Loading history...
32
    }
33
34
    public function testTransaction(): void
35
    {
36
        $db = $this->getConnection();
37
38
        $this->assertNull($db->getTransaction());
39
40
        $transaction = $db->beginTransaction();
41
42
        $this->assertNotNull($db->getTransaction());
43
        $this->assertTrue($transaction->isActive());
0 ignored issues
show
Bug introduced by
It seems like assertTrue() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

43
        $this->/** @scrutinizer ignore-call */ 
44
               assertTrue($transaction->isActive());
Loading history...
44
45
        $db->createCommand()->insert('profile', ['description' => 'test transaction'])->execute();
46
47
        $transaction->rollBack();
48
49
        $this->assertFalse($transaction->isActive());
0 ignored issues
show
Bug introduced by
It seems like assertFalse() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

49
        $this->/** @scrutinizer ignore-call */ 
50
               assertFalse($transaction->isActive());
Loading history...
50
        $this->assertNull($db->getTransaction());
51
        $this->assertEquals(0, $db->createCommand(
52
            "SELECT COUNT(*) FROM profile WHERE description = 'test transaction';"
53
        )->queryScalar());
54
55
        $transaction = $db->beginTransaction();
56
57
        $db->createCommand()->insert('profile', ['description' => 'test transaction'])->execute();
58
59
        $transaction->commit();
60
61
        $this->assertFalse($transaction->isActive());
62
        $this->assertNull($db->getTransaction());
63
        $this->assertEquals(1, $db->createCommand(
64
            "SELECT COUNT(*) FROM profile WHERE description = 'test transaction';"
65
        )->queryScalar());
66
    }
67
68
    public function testTransactionIsolation(): void
69
    {
70
        $db = $this->getConnection(true);
71
72
        $transaction = $db->beginTransaction(Transaction::READ_UNCOMMITTED);
73
74
        $transaction->commit();
75
76
        $transaction = $db->beginTransaction(Transaction::READ_COMMITTED);
77
78
        $transaction->commit();
79
80
        $transaction = $db->beginTransaction(Transaction::REPEATABLE_READ);
81
82
        $transaction->commit();
83
84
        $transaction = $db->beginTransaction(Transaction::SERIALIZABLE);
85
86
        $transaction->commit();
87
88
        /* should not be any exception so far */
89
        $this->assertTrue(true);
90
    }
91
92
    public function testTransactionShortcutException(): void
93
    {
94
        $db = $this->getConnection();
95
96
        $result = $db->transaction(static function (ConnectionInterface $db) {
97
            $db->createCommand()->insert('profile', ['description' => 'test transaction shortcut'])->execute();
98
            return true;
99
        }, Transaction::READ_UNCOMMITTED);
100
101
        $this->assertTrue($result, 'transaction shortcut valid value should be returned from callback');
102
103
        $profilesCount = $db->createCommand(
104
            "SELECT COUNT(*) FROM profile WHERE description = 'test transaction shortcut';"
105
        )->queryScalar();
106
107
        $this->assertEquals(1, $profilesCount, 'profile should be inserted in transaction shortcut');
108
    }
109
110
    public function testTransactionShortcutCorrect(): void
111
    {
112
        $db = $this->getConnection(true);
113
114
        $result = $db->transaction(static function () use ($db) {
115
            $db->createCommand()->insert('profile', ['description' => 'test transaction shortcut'])->execute();
116
            return true;
117
        });
118
119
        $this->assertTrue($result, 'transaction shortcut valid value should be returned from callback');
120
121
        $profilesCount = $db->createCommand(
122
            "SELECT COUNT(*) FROM profile WHERE description = 'test transaction shortcut';"
123
        )->queryScalar();
124
125
        $this->assertEquals(1, $profilesCount, 'profile should be inserted in transaction shortcut');
126
    }
127
128
    public function testTransactionShortcutCustom(): void
129
    {
130
        $db = $this->getConnection(true);
131
132
        $result = $db->transaction(static function (ConnectionInterface $db) {
133
            $db->createCommand()->insert('profile', ['description' => 'test transaction shortcut'])->execute();
134
            return true;
135
        }, Transaction::READ_UNCOMMITTED);
136
137
        $this->assertTrue($result, 'transaction shortcut valid value should be returned from callback');
138
139
        $profilesCount = $db->createCommand(
140
            "SELECT COUNT(*) FROM profile WHERE description = 'test transaction shortcut';"
141
        )->queryScalar();
142
143
        $this->assertEquals(1, $profilesCount, 'profile should be inserted in transaction shortcut');
144
    }
145
146
    /**
147
     * Tests nested transactions with partial rollback.
148
     *
149
     * {@see https://github.com/yiisoft/yii2/issues/9851}
150
     */
151
    public function testNestedTransaction(): void
152
    {
153
        $db = $this->getConnection();
154
155
        $db->transaction(function (ConnectionInterface $db) {
156
            $this->assertNotNull($db->getTransaction());
0 ignored issues
show
Bug introduced by
The method getTransaction() does not exist on Yiisoft\Db\Connection\ConnectionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\Db\Connection\ConnectionInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

156
            $this->assertNotNull($db->/** @scrutinizer ignore-call */ getTransaction());
Loading history...
157
158
            $db->transaction(function (ConnectionInterface $db) {
0 ignored issues
show
Bug introduced by
The method transaction() does not exist on Yiisoft\Db\Connection\ConnectionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\Db\Connection\ConnectionInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

158
            $db->/** @scrutinizer ignore-call */ 
159
                 transaction(function (ConnectionInterface $db) {
Loading history...
159
                $this->assertNotNull($db->getTransaction());
160
                $db->getTransaction()->rollBack();
161
            });
162
163
            $this->assertNotNull($db->getTransaction());
164
        });
165
    }
166
167
    public function testNestedTransactionNotSupported(): void
168
    {
169
        $db = $this->getConnection();
170
171
        $db->setEnableSavepoint(false);
172
173
        $db->transaction(function (ConnectionInterface $db) {
174
            $this->assertNotNull($db->getTransaction());
175
            $this->expectException(NotSupportedException::class);
0 ignored issues
show
Bug introduced by
It seems like expectException() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

175
            $this->/** @scrutinizer ignore-call */ 
176
                   expectException(NotSupportedException::class);
Loading history...
176
            $db->beginTransaction();
0 ignored issues
show
Bug introduced by
The method beginTransaction() does not exist on Yiisoft\Db\Connection\ConnectionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\Db\Connection\ConnectionInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

176
            $db->/** @scrutinizer ignore-call */ 
177
                 beginTransaction();
Loading history...
177
        });
178
    }
179
180
    public function testEnableQueryLog(): void
181
    {
182
        $db = $this->getConnection();
183
184
        foreach (['qlog1', 'qlog2', 'qlog3', 'qlog4'] as $table) {
185
            if ($db->getTableSchema($table, true) !== null) {
186
                $db->createCommand()->dropTable($table)->execute();
187
            }
188
        }
189
190
        /* profiling and logging */
191
        $db->setEnableLogging(true);
192
        $db->setEnableProfiling(true);
193
194
        $this->logger->flush();
195
        $this->profiler->flush();
196
197
        $db->createCommand()->createTable('qlog1', ['id' => 'pk'])->execute();
198
199
        $this->assertCount(1, $this->getInaccessibleProperty($this->logger, 'messages'));
0 ignored issues
show
Bug introduced by
It seems like getInaccessibleProperty() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

199
        $this->assertCount(1, $this->/** @scrutinizer ignore-call */ getInaccessibleProperty($this->logger, 'messages'));
Loading history...
Bug introduced by
It seems like assertCount() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

199
        $this->/** @scrutinizer ignore-call */ 
200
               assertCount(1, $this->getInaccessibleProperty($this->logger, 'messages'));
Loading history...
200
        $this->assertCount(1, $this->getInaccessibleProperty($this->profiler, 'messages'));
201
        $this->assertNotNull($db->getTableSchema('qlog1', true));
202
203
        $this->logger->flush();
204
        $this->profiler->flush();
205
206
        $db->createCommand('SELECT * FROM qlog1')->queryAll();
207
208
        $this->assertCount(1, $this->getInaccessibleProperty($this->logger, 'messages'));
209
        $this->assertCount(1, $this->getInaccessibleProperty($this->profiler, 'messages'));
210
211
        /* profiling only */
212
        $db->setEnableLogging(false);
213
        $db->setEnableProfiling(true);
214
215
        $this->logger->flush();
216
        $this->profiler->flush();
217
218
        $db->createCommand()->createTable('qlog2', ['id' => 'pk'])->execute();
219
220
        $this->assertCount(0, $this->getInaccessibleProperty($this->logger, 'messages'));
221
        $this->assertCount(1, $this->getInaccessibleProperty($this->profiler, 'messages'));
222
        $this->assertNotNull($db->getTableSchema('qlog2', true));
223
224
        $this->logger->flush();
225
        $this->profiler->flush();
226
227
        $db->createCommand('SELECT * FROM qlog2')->queryAll();
228
229
        $this->assertCount(0, $this->getInaccessibleProperty($this->logger, 'messages'));
230
        $this->assertCount(1, $this->getInaccessibleProperty($this->profiler, 'messages'));
231
232
        /* logging only */
233
        $db->setEnableLogging(true);
234
        $db->setEnableProfiling(false);
235
236
        $this->logger->flush();
237
        $this->profiler->flush();
238
239
        $db->createCommand()->createTable('qlog3', ['id' => 'pk'])->execute();
240
241
        $this->assertCount(1, $this->getInaccessibleProperty($this->logger, 'messages'));
242
        $this->assertCount(0, $this->getInaccessibleProperty($this->profiler, 'messages'));
243
        $this->assertNotNull($db->getTableSchema('qlog3', true));
244
245
        $this->logger->flush();
246
        $this->profiler->flush();
247
248
        $db->createCommand('SELECT * FROM qlog3')->queryAll();
249
250
        $this->assertCount(1, $this->getInaccessibleProperty($this->logger, 'messages'));
251
        $this->assertCount(0, $this->getInaccessibleProperty($this->profiler, 'messages'));
252
253
        /* disabled */
254
        $db->setEnableLogging(false);
255
        $db->setEnableProfiling(false);
256
257
        $this->logger->flush();
258
        $this->profiler->flush();
259
260
        $db->createCommand()->createTable('qlog4', ['id' => 'pk'])->execute();
261
262
        $this->assertNotNull($db->getTableSchema('qlog4', true));
263
        $this->assertCount(0, $this->getInaccessibleProperty($this->logger, 'messages'));
264
        $this->assertCount(0, $this->getInaccessibleProperty($this->profiler, 'messages'));
265
266
        $db->createCommand('SELECT * FROM qlog4')->queryAll();
267
268
        $this->assertCount(0, $this->getInaccessibleProperty($this->logger, 'messages'));
269
        $this->assertCount(0, $this->getInaccessibleProperty($this->profiler, 'messages'));
270
    }
271
272
    public function testExceptionContainsRawQuery(): void
273
    {
274
        $db = $this->getConnection();
275
276
        if ($db->getTableSchema('qlog1', true) === null) {
277
            $db->createCommand()->createTable('qlog1', ['id' => 'pk'])->execute();
278
        }
279
280
        $db->setEmulatePrepare(true);
281
282
        /* profiling and logging */
283
        $db->setEnableLogging(true);
284
        $db->setEnableProfiling(true);
285
286
        $this->runExceptionTest($db);
287
288
        /* profiling only */
289
        $db->setEnableLogging(false);
290
        $db->setEnableProfiling(true);
291
292
        $this->runExceptionTest($db);
293
294
        /* logging only */
295
        $db->setEnableLogging(true);
296
        $db->setEnableProfiling(false);
297
298
        $this->runExceptionTest($db);
299
300
        /* disabled */
301
        $db->setEnableLogging(false);
302
        $db->setEnableProfiling(false);
303
304
        $this->runExceptionTest($db);
305
    }
306
307
    /**
308
     * @param ConnectionInterface $db
309
     */
310
    private function runExceptionTest(ConnectionInterface $db): void
311
    {
312
        $thrown = false;
313
314
        try {
315
            $db->createCommand('INSERT INTO qlog1(a) VALUES(:a);', [':a' => 1])->execute();
316
        } catch (Exception $e) {
317
            $this->assertStringContainsString(
0 ignored issues
show
Bug introduced by
It seems like assertStringContainsString() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

317
            $this->/** @scrutinizer ignore-call */ 
318
                   assertStringContainsString(
Loading history...
318
                'INSERT INTO qlog1(a) VALUES(1);',
319
                $e->getMessage(),
320
                'Exceptions message should contain raw SQL query: ' . (string) $e
321
            );
322
323
            $thrown = true;
324
        }
325
326
        $this->assertTrue($thrown, 'An exception should have been thrown by the command.');
327
328
        $thrown = false;
329
330
        try {
331
            $db->createCommand(
332
                'SELECT * FROM qlog1 WHERE id=:a ORDER BY nonexistingcolumn;',
333
                [':a' => 1]
334
            )->queryAll();
335
        } catch (Exception $e) {
336
            $this->assertStringContainsString(
337
                'SELECT * FROM qlog1 WHERE id=1 ORDER BY nonexistingcolumn;',
338
                $e->getMessage(),
339
                'Exceptions message should contain raw SQL query: ' . (string) $e
340
            );
341
342
            $thrown = true;
343
        }
344
345
        $this->assertTrue($thrown, 'An exception should have been thrown by the command.');
346
    }
347
348
    /**
349
     * Ensure database connection is reset on when a connection is cloned.
350
     *
351
     * Make sure each connection element has its own PDO instance i.e. own connection to the DB.
352
     * Also transaction elements should not be shared between two connections.
353
     */
354
    public function testClone(): void
355
    {
356
        $db = $this->getConnection();
357
358
        $this->assertNull($db->getTransaction());
359
        $this->assertNull($db->getPDO());
360
361
        $db->open();
362
363
        $this->assertNull($db->getTransaction());
364
        $this->assertNotNull($db->getPDO());
365
366
        $conn2 = clone $db;
367
368
        $this->assertNull($db->getTransaction());
369
        $this->assertNotNull($db->getPDO());
370
371
        $this->assertNull($conn2->getTransaction());
372
        $this->assertNull($conn2->getPDO());
373
374
        $db->beginTransaction();
375
376
        $this->assertNotNull($db->getTransaction());
377
        $this->assertNotNull($db->getPDO());
378
379
        $this->assertNull($conn2->getTransaction());
380
        $this->assertNull($conn2->getPDO());
381
382
        $conn3 = clone $db;
383
384
        $this->assertNotNull($db->getTransaction());
385
        $this->assertNotNull($db->getPDO());
386
        $this->assertNull($conn3->getTransaction());
387
        $this->assertNull($conn3->getPDO());
388
    }
389
}
390