Passed
Push — master ( d7b91e...70f900 )
by Def
15:34 queued 12:37
created

TestConnectionTrait::testSerialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 0
dl 0
loc 15
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\TestSupport;
6
7
use PDO;
8
use Yiisoft\Db\Connection\ConnectionInterface;
9
use Yiisoft\Db\Driver\DriverInterface;
10
use Yiisoft\Db\Driver\PDO\ConnectionPDOInterface;
11
use Yiisoft\Db\Exception\Exception;
12
use Yiisoft\Db\Exception\NotSupportedException;
13
14
use function PHPUnit\Framework\assertEquals;
15
use function serialize;
16
use function unserialize;
17
18
trait TestConnectionTrait
19
{
20
    public function testCacheKey(): void
21
    {
22
        $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

22
        /** @scrutinizer ignore-call */ 
23
        $db = $this->getConnection();
Loading history...
23
        $this->assertEquals([$this->dsn, $this->username], $db->getCacheKey());
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

23
        $this->/** @scrutinizer ignore-call */ 
24
               assertEquals([$this->dsn, $this->username], $db->getCacheKey());
Loading history...
24
    }
25
26
    public function testOpenClose(): void
27
    {
28
        $db = $this->getConnection();
29
        $this->assertFalse($db->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

29
        $this->/** @scrutinizer ignore-call */ 
30
               assertFalse($db->isActive());
Loading history...
30
        $this->assertNull($db->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($db->getPDO());
Loading history...
31
32
        $db->open();
33
        $this->assertTrue($db->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

33
        $this->/** @scrutinizer ignore-call */ 
34
               assertTrue($db->isActive());
Loading history...
34
        $this->assertInstanceOf(PDO::class, $db->getPDO());
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

34
        $this->/** @scrutinizer ignore-call */ 
35
               assertInstanceOf(PDO::class, $db->getPDO());
Loading history...
35
36
        $db->close();
37
        $this->assertFalse($db->isActive());
38
        $this->assertNull($db->getPDO());
39
40
        $db = $this->getConnection(false, 'unknown::memory:');
41
        $this->expectException(Exception::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

41
        $this->/** @scrutinizer ignore-call */ 
42
               expectException(Exception::class);
Loading history...
42
        $this->expectExceptionMessage('could not find driver');
0 ignored issues
show
Bug introduced by
It seems like expectExceptionMessage() 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

42
        $this->/** @scrutinizer ignore-call */ 
43
               expectExceptionMessage('could not find driver');
Loading history...
43
        $db->open();
44
    }
45
46
    public function testSerialize(): void
47
    {
48
        $db = $this->getConnection();
49
50
        $db->open();
51
52
        $serialized = serialize($db);
53
54
        $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

54
        $this->/** @scrutinizer ignore-call */ 
55
               assertNotNull($db->getPDO());
Loading history...
55
56
        $unserialized = unserialize($serialized);
57
58
        $this->assertInstanceOf(ConnectionPDOInterface::class, $unserialized);
59
        $this->assertNull($unserialized->getPDO());
60
        $this->assertEquals(123, $unserialized->createCommand('SELECT 123')->queryScalar());
61
    }
62
63
    public function testTransaction(): void
64
    {
65
        $db = $this->getConnection(true);
66
67
        $this->assertNull($db->getTransaction());
68
69
        $transaction = $db->beginTransaction();
70
71
        $this->assertNotNull($db->getTransaction());
72
        $this->assertTrue($transaction->isActive());
73
74
        $db->createCommand()->insert('profile', ['description' => 'test transaction'])->execute();
75
76
        $transaction->rollBack();
77
78
        $this->assertFalse($transaction->isActive());
79
        $this->assertNull($db->getTransaction());
80
        $this->assertEquals(0, $db->createCommand(
81
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction'"
82
        )->queryScalar());
83
84
        $transaction = $db->beginTransaction();
85
86
        $db->createCommand()->insert('profile', ['description' => 'test transaction'])->execute();
87
88
        $transaction->commit();
89
90
        $this->assertFalse($transaction->isActive());
91
        $this->assertNull($db->getTransaction());
92
        $this->assertEquals(1, $db->createCommand(
93
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction'"
94
        )->queryScalar());
95
    }
96
97
    public function testRollbackTransactionsWithSavePoints(): void
98
    {
99
        $db = $this->getConnection(true);
100
        $db->open();
101
102
        $transaction = $db->beginTransaction();
103
        assertEquals(1, $transaction->getLevel());
104
105
        $db->createCommand()->insert('profile', ['description' => 'test transaction'])->execute();
106
107
        $transaction->begin();
108
        assertEquals(2, $transaction->getLevel());
109
110
        $db->createCommand()->insert('profile', ['description' => 'test transaction'])->execute();
111
112
        $transaction->rollBack();
113
        assertEquals(1, $transaction->getLevel());
114
        $this->assertTrue($transaction->isActive());
115
116
        $db->createCommand()->insert('profile', ['description' => 'test transaction'])->execute();
117
118
        $transaction->rollBack();
119
        assertEquals(0, $transaction->getLevel());
120
121
        $this->assertFalse($transaction->isActive());
122
        $this->assertNull($db->getTransaction());
123
        $this->assertEquals(0, $db->createCommand(
124
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction'"
125
        )->queryScalar());
126
    }
127
128
    public function testPartialRollbackTransactionsWithSavePoints(): void
129
    {
130
        $db = $this->getConnection(true);
131
        $db->open();
132
133
        $transaction = $db->beginTransaction();
134
        assertEquals(1, $transaction->getLevel());
135
136
        $db->createCommand()->insert('profile', ['description' => 'test transaction1'])->execute();
137
138
        $transaction->begin();
139
        assertEquals(2, $transaction->getLevel());
140
141
        $db->createCommand()->insert('profile', ['description' => 'test transaction2'])->execute();
142
143
        $transaction->rollBack();
144
        assertEquals(1, $transaction->getLevel());
145
        $this->assertTrue($transaction->isActive());
146
147
        $db->createCommand()->insert('profile', ['description' => 'test transaction3'])->execute();
148
149
        $transaction->commit();
150
        assertEquals(0, $transaction->getLevel());
151
152
        $this->assertFalse($transaction->isActive());
153
        $this->assertNull($db->getTransaction());
154
        $this->assertEquals(1, $db->createCommand(
155
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction1'"
156
        )->queryScalar());
157
        $this->assertEquals(0, $db->createCommand(
158
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction2'"
159
        )->queryScalar());
160
        $this->assertEquals(1, $db->createCommand(
161
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction3'"
162
        )->queryScalar());
163
    }
164
165
    public function testCommitTransactionsWithSavepoints(): void
166
    {
167
        $db = $this->getConnection(true);
168
169
        $transaction = $db->beginTransaction();
170
        assertEquals(1, $transaction->getLevel());
171
172
        $db->createCommand()->insert('profile', ['description' => 'test transaction1'])->execute();
173
174
        $transaction->begin();
175
        assertEquals(2, $transaction->getLevel());
176
177
        $db->createCommand()->insert('profile', ['description' => 'test transaction2'])->execute();
178
179
        $transaction->commit();
180
        assertEquals(1, $transaction->getLevel());
181
182
        $db->createCommand()->insert('profile', ['description' => 'test transaction3'])->execute();
183
184
        $transaction->commit();
185
        assertEquals(0, $transaction->getLevel());
186
187
        $this->assertFalse($transaction->isActive());
188
        $this->assertNull($db->getTransaction());
189
        $this->assertEquals(1, $db->createCommand(
190
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction1'"
191
        )->queryScalar());
192
        $this->assertEquals(1, $db->createCommand(
193
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction2'"
194
        )->queryScalar());
195
        $this->assertEquals(1, $db->createCommand(
196
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction3'"
197
        )->queryScalar());
198
    }
199
200
    public function testTransactionShortcutException(): void
201
    {
202
        $db = $this->getConnection(true);
203
204
        $this->expectException(Exception::class);
205
206
        $db->transaction(function () use ($db) {
207
            $db->createCommand()->insert('profile', ['description' => 'test transaction shortcut'])->execute();
208
            throw new Exception('Exception in transaction shortcut');
209
        });
210
        $profilesCount = $db->createCommand(
211
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction shortcut'"
212
        )->queryScalar();
213
        $this->assertEquals(0, $profilesCount, 'profile should not be inserted in transaction shortcut');
214
    }
215
216
    public function testTransactionShortcutCorrect(): void
217
    {
218
        $db = $this->getConnection(true);
219
220
        $result = $db->transaction(static function () use ($db) {
221
            $db->createCommand()->insert('profile', ['description' => 'test transaction shortcut'])->execute();
222
            return true;
223
        });
224
225
        $this->assertTrue($result, 'transaction shortcut valid value should be returned from callback');
226
227
        $profilesCount = $db->createCommand(
228
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction shortcut'"
229
        )->queryScalar();
230
231
        $this->assertEquals(1, $profilesCount, 'profile should be inserted in transaction shortcut');
232
    }
233
234
    /**
235
     * Tests nested transactions with partial rollback.
236
     *
237
     * {@see https://github.com/yiisoft/yii2/issues/9851}
238
     */
239
    public function testNestedTransaction(): void
240
    {
241
        $db = $this->getConnection();
242
243
        $db->transaction(function (ConnectionInterface $db) {
244
            $this->assertNotNull($db->getTransaction());
245
246
            $db->transaction(function (ConnectionInterface $db) {
247
                $transaction = $db->getTransaction();
248
                $this->assertNotNull($transaction);
249
                $transaction->rollBack();
250
            });
251
252
            $this->assertNotNull($db->getTransaction());
253
        });
254
    }
255
256
    public function testNestedTransactionNotSupported(): void
257
    {
258
        $db = $this->getConnection();
259
260
        $db->setEnableSavepoint(false);
261
262
        $db->transaction(function (ConnectionInterface $db) {
263
            $this->assertNotNull($db->getTransaction());
264
            $this->expectException(NotSupportedException::class);
265
            $db->beginTransaction();
266
        });
267
    }
268
269
    public function testEnableQueryLog(): void
270
    {
271
        $db = $this->getConnection();
272
273
        foreach (['qlog1', 'qlog2', 'qlog3', 'qlog4'] as $table) {
274
            if ($db->getTableSchema($table, true) !== null) {
275
                $db->createCommand()->dropTable($table)->execute();
276
            }
277
        }
278
279
        /* profiling and logging */
280
        $db->setLogger($this->logger);
281
        $db->setProfiler($this->profiler);
282
283
        $this->assertNotNull($this->logger);
284
        $this->assertNotNull($this->profiler);
285
286
        $this->logger->flush();
287
        $this->profiler->flush();
288
289
        $db->createCommand()->createTable('qlog1', ['id' => 'pk'])->execute();
290
291
        $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

291
        $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

291
        $this->/** @scrutinizer ignore-call */ 
292
               assertCount(1, $this->getInaccessibleProperty($this->logger, 'messages'));
Loading history...
292
        $this->assertCount(1, $this->getInaccessibleProperty($this->profiler, 'messages'));
293
        $this->assertNotNull($db->getTableSchema('qlog1', true));
294
295
        $this->logger->flush();
296
        $this->profiler->flush();
297
298
        $db->createCommand('SELECT * FROM {{qlog1}}')->queryAll();
299
300
        $this->assertCount(1, $this->getInaccessibleProperty($this->logger, 'messages'));
301
        $this->assertCount(1, $this->getInaccessibleProperty($this->profiler, 'messages'));
302
303
        /* profiling only */
304
        $db->setLogger(null);
305
        $db->setProfiler($this->profiler);
306
307
        $this->logger->flush();
308
        $this->profiler->flush();
309
310
        $db->createCommand()->createTable('qlog2', ['id' => 'pk'])->execute();
311
312
        $this->assertCount(0, $this->getInaccessibleProperty($this->logger, 'messages'));
313
        $this->assertCount(1, $this->getInaccessibleProperty($this->profiler, 'messages'));
314
        $this->assertNotNull($db->getTableSchema('qlog2', true));
315
316
        $this->logger->flush();
317
        $this->profiler->flush();
318
319
        $db->createCommand('SELECT * FROM {{qlog2}}')->queryAll();
320
321
        $this->assertCount(0, $this->getInaccessibleProperty($this->logger, 'messages'));
322
        $this->assertCount(1, $this->getInaccessibleProperty($this->profiler, 'messages'));
323
324
        /* logging only */
325
        $db->setLogger($this->logger);
326
        $db->setProfiler(null);
327
328
        $this->logger->flush();
329
        $this->profiler->flush();
330
331
        $db->createCommand()->createTable('qlog3', ['id' => 'pk'])->execute();
332
333
        $this->assertCount(1, $this->getInaccessibleProperty($this->logger, 'messages'));
334
        $this->assertCount(0, $this->getInaccessibleProperty($this->profiler, 'messages'));
335
        $this->assertNotNull($db->getTableSchema('qlog3', true));
336
337
        $this->logger->flush();
338
        $this->profiler->flush();
339
340
        $db->createCommand('SELECT * FROM {{qlog3}}')->queryAll();
341
342
        $this->assertCount(1, $this->getInaccessibleProperty($this->logger, 'messages'));
343
        $this->assertCount(0, $this->getInaccessibleProperty($this->profiler, 'messages'));
344
345
        /* disabled */
346
        $db->setLogger(null);
347
        $db->setProfiler(null);
348
349
        $this->logger->flush();
350
        $this->profiler->flush();
351
352
        $db->createCommand()->createTable('qlog4', ['id' => 'pk'])->execute();
353
354
        $this->assertNotNull($db->getTableSchema('qlog4', true));
355
        $this->assertCount(0, $this->getInaccessibleProperty($this->logger, 'messages'));
356
        $this->assertCount(0, $this->getInaccessibleProperty($this->profiler, 'messages'));
357
358
        $db->createCommand('SELECT * FROM {{qlog4}}')->queryAll();
359
360
        $this->assertCount(0, $this->getInaccessibleProperty($this->logger, 'messages'));
361
        $this->assertCount(0, $this->getInaccessibleProperty($this->profiler, 'messages'));
362
    }
363
364
    public function testExceptionContainsRawQuery(): void
365
    {
366
        $db = $this->getConnection();
367
368
        if ($db->getTableSchema('qlog1', true) === null) {
369
            $db->createCommand()->createTable('qlog1', ['id' => 'pk'])->execute();
370
        }
371
372
        $db->setEmulatePrepare(true);
373
374
        /* profiling and logging */
375
        $db->setLogger($this->logger);
376
        $db->setProfiler($this->profiler);
377
378
        $this->runExceptionTest($db);
379
380
        /* profiling only */
381
        $db->setLogger(null);
382
        $db->setProfiler($this->profiler);
383
384
        $this->runExceptionTest($db);
385
386
        /* logging only */
387
        $db->setLogger($this->logger);
388
        $db->setProfiler(null);
389
390
        $this->runExceptionTest($db);
391
392
        /* disabled */
393
        $db->setLogger(null);
394
        $db->setProfiler(null);
395
396
        $this->runExceptionTest($db);
397
    }
398
399
    /**
400
     * @param ConnectionInterface $db
401
     */
402
    private function runExceptionTest(ConnectionInterface $db): void
403
    {
404
        $thrown = false;
405
406
        try {
407
            $db->createCommand('INSERT INTO qlog1(a) VALUES(:a);', [':a' => 1])->execute();
408
        } catch (Exception $e) {
409
            $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

409
            $this->/** @scrutinizer ignore-call */ 
410
                   assertStringContainsString(
Loading history...
410
                'INSERT INTO qlog1(a) VALUES(1);',
411
                $e->getMessage(),
412
                'Exceptions message should contain raw SQL query: ' . (string) $e
413
            );
414
415
            $thrown = true;
416
        }
417
418
        $this->assertTrue($thrown, 'An exception should have been thrown by the command.');
419
420
        $thrown = false;
421
422
        try {
423
            $db->createCommand(
424
                'SELECT * FROM qlog1 WHERE id=:a ORDER BY nonexistingcolumn;',
425
                [':a' => 1]
426
            )->queryAll();
427
        } catch (Exception $e) {
428
            $this->assertStringContainsString(
429
                'SELECT * FROM qlog1 WHERE id=1 ORDER BY nonexistingcolumn;',
430
                $e->getMessage(),
431
                'Exceptions message should contain raw SQL query: ' . (string) $e
432
            );
433
434
            $thrown = true;
435
        }
436
437
        $this->assertTrue($thrown, 'An exception should have been thrown by the command.');
438
    }
439
440
    /**
441
     * Ensure database connection is reset on when a connection is cloned.
442
     *
443
     * Make sure each connection element has its own PDO instance i.e. own connection to the DB.
444
     * Also transaction elements should not be shared between two connections.
445
     */
446
    public function testClone(): void
447
    {
448
        $db = $this->getConnection();
449
450
        $this->assertNull($db->getTransaction());
451
        $this->assertNull($db->getPDO());
452
453
        $db->open();
454
455
        $this->assertNull($db->getTransaction());
456
        $this->assertNotNull($db->getPDO());
457
458
        $conn2 = clone $db;
459
460
        $this->assertNull($db->getTransaction());
461
        $this->assertNotNull($db->getPDO());
462
463
        $this->assertNull($conn2->getTransaction());
464
        $this->assertNull($conn2->getPDO());
465
466
        $db->beginTransaction();
467
468
        $this->assertNotNull($db->getTransaction());
469
        $this->assertNotNull($db->getPDO());
470
471
        $this->assertNull($conn2->getTransaction());
472
        $this->assertNull($conn2->getPDO());
473
474
        $conn3 = clone $db;
475
476
        $this->assertNotNull($db->getTransaction());
477
        $this->assertNotNull($db->getPDO());
478
        $this->assertNull($conn3->getTransaction());
479
        $this->assertNull($conn3->getPDO());
480
    }
481
482
    public function testGetDriver(): void
483
    {
484
        $db = $this->getConnection();
485
        $this->assertInstanceOf(DriverInterface::class, $db->getDriver());
486
    }
487
}
488