Passed
Push — master ( 927318...d1f7a6 )
by Wilmer
02:28
created

TestConnectionTrait::testGetName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

23
        /** @scrutinizer ignore-call */ 
24
        $db = $this->getConnection();
Loading history...
24
        $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

24
        $this->/** @scrutinizer ignore-call */ 
25
               assertEquals([$this->dsn, $this->username], $db->getCacheKey());
Loading history...
25
    }
26
27
    public function testGetName(): void
28
    {
29
        $db = $this->getConnection();
30
        $this->assertEquals($this->drivername, $db->getName());
31
    }
32
33
    public function testOpenClose(): void
34
    {
35
        $db = $this->getConnection();
36
        $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

36
        $this->/** @scrutinizer ignore-call */ 
37
               assertFalse($db->isActive());
Loading history...
37
        $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

37
        $this->/** @scrutinizer ignore-call */ 
38
               assertNull($db->getPDO());
Loading history...
38
39
        $db->open();
40
        $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

40
        $this->/** @scrutinizer ignore-call */ 
41
               assertTrue($db->isActive());
Loading history...
41
        $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

41
        $this->/** @scrutinizer ignore-call */ 
42
               assertInstanceOf(PDO::class, $db->getPDO());
Loading history...
42
43
        $db->close();
44
        $this->assertFalse($db->isActive());
45
        $this->assertNull($db->getPDO());
46
47
        $db = $this->getConnection(false, 'unknown::memory:');
48
        $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

48
        $this->/** @scrutinizer ignore-call */ 
49
               expectException(Exception::class);
Loading history...
49
        $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

49
        $this->/** @scrutinizer ignore-call */ 
50
               expectExceptionMessage('could not find driver');
Loading history...
50
        $db->open();
51
    }
52
53
    public function testSerialize(): void
54
    {
55
        $db = $this->getConnection();
56
57
        $db->open();
58
59
        $serialized = serialize($db);
60
61
        $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

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

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

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

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