Passed
Push — master ( f4eeb8...0d22ee )
by Sergei
11:47 queued 01:39
created

testDisableAutoSlaveForReads()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 1
rs 10
c 1
b 0
f 0
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 4
    public function testSerialize(): void
18
    {
19 4
        $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 4
        $db->open();
22
23 4
        $serialized = serialize($db);
24
25 4
        $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 4
        $unserialized = unserialize($serialized);
28
29 4
        $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 4
        $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 4
        $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 5
    public function testTransaction(): void
35
    {
36 5
        $db = $this->getConnection();
37
38 5
        $this->assertNull($db->getTransaction());
39
40 5
        $transaction = $db->beginTransaction();
41
42 5
        $this->assertNotNull($db->getTransaction());
43 5
        $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 5
        $db->createCommand()->insert('profile', ['description' => 'test transaction'])->execute();
46
47 5
        $transaction->rollBack();
48
49 5
        $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 5
        $this->assertNull($db->getTransaction());
51 5
        $this->assertEquals(0, $db->createCommand(
52
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction'"
53 5
        )->queryScalar());
54
55 5
        $transaction = $db->beginTransaction();
56
57 5
        $db->createCommand()->insert('profile', ['description' => 'test transaction'])->execute();
58
59 5
        $transaction->commit();
60
61 5
        $this->assertFalse($transaction->isActive());
62 5
        $this->assertNull($db->getTransaction());
63 5
        $this->assertEquals(1, $db->createCommand(
64
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction'"
65 5
        )->queryScalar());
66
    }
67
68 2
    public function testTransactionIsolation(): void
69
    {
70 2
        $db = $this->getConnection(true);
71
72 2
        $transaction = $db->beginTransaction(Transaction::READ_UNCOMMITTED);
73
74 2
        $transaction->commit();
75
76 2
        $transaction = $db->beginTransaction(Transaction::READ_COMMITTED);
77
78 2
        $transaction->commit();
79
80 2
        $transaction = $db->beginTransaction(Transaction::REPEATABLE_READ);
81
82 2
        $transaction->commit();
83
84 2
        $transaction = $db->beginTransaction(Transaction::SERIALIZABLE);
85
86 2
        $transaction->commit();
87
88
        /* should not be any exception so far */
89 2
        $this->assertTrue(true);
90
    }
91
92 5
    public function testTransactionShortcutException(): void
93
    {
94 5
        $db = $this->getConnection(true);
95
96 5
        $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

96
        $this->/** @scrutinizer ignore-call */ 
97
               expectException(Exception::class);
Loading history...
97
98 5
        $db->transaction(function () use ($db) {
99 5
            $db->createCommand()->insert('profile', ['description' => 'test transaction shortcut'])->execute();
100 5
            throw new Exception('Exception in transaction shortcut');
101
        });
102
        $profilesCount = $db->createCommand(
103
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction shortcut'"
104
        )->queryScalar();
105
        $this->assertEquals(0, $profilesCount, 'profile should not be inserted in transaction shortcut');
106
    }
107
108 5
    public function testTransactionShortcutCorrect(): void
109
    {
110 5
        $db = $this->getConnection(true);
111
112 5
        $result = $db->transaction(static function () use ($db) {
113 5
            $db->createCommand()->insert('profile', ['description' => 'test transaction shortcut'])->execute();
114 5
            return true;
115
        });
116
117 5
        $this->assertTrue($result, 'transaction shortcut valid value should be returned from callback');
118
119 5
        $profilesCount = $db->createCommand(
120
            "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'test transaction shortcut'"
121 5
        )->queryScalar();
122
123 5
        $this->assertEquals(1, $profilesCount, 'profile should be inserted in transaction shortcut');
124
    }
125
126 4
    public function testTransactionShortcutCustom(): void
127
    {
128 4
        $db = $this->getConnection(true);
129
130 4
        $result = $db->transaction(static function (ConnectionInterface $db) {
131 4
            $db->createCommand()->insert('profile', ['description' => 'test transaction shortcut'])->execute();
132 4
            return true;
133
        }, Transaction::READ_UNCOMMITTED);
134
135 4
        $this->assertTrue($result, 'transaction shortcut valid value should be returned from callback');
136
137 4
        $profilesCount = $db->createCommand(
138
            "SELECT COUNT(*) FROM profile WHERE description = 'test transaction shortcut';"
139 4
        )->queryScalar();
140
141 4
        $this->assertEquals(1, $profilesCount, 'profile should be inserted in transaction shortcut');
142
    }
143
144
    /**
145
     * Tests nested transactions with partial rollback.
146
     *
147
     * {@see https://github.com/yiisoft/yii2/issues/9851}
148
     */
149 5
    public function testNestedTransaction(): void
150
    {
151 5
        $db = $this->getConnection();
152
153 5
        $db->transaction(function (ConnectionInterface $db) {
154 5
            $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

154
            $this->assertNotNull($db->/** @scrutinizer ignore-call */ getTransaction());
Loading history...
155
156 5
            $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

156
            $db->/** @scrutinizer ignore-call */ 
157
                 transaction(function (ConnectionInterface $db) {
Loading history...
157 5
                $this->assertNotNull($db->getTransaction());
158 5
                $db->getTransaction()->rollBack();
159
            });
160
161 5
            $this->assertNotNull($db->getTransaction());
162
        });
163
    }
164
165 5
    public function testNestedTransactionNotSupported(): void
166
    {
167 5
        $db = $this->getConnection();
168
169 5
        $db->setEnableSavepoint(false);
170
171 5
        $db->transaction(function (ConnectionInterface $db) {
172 5
            $this->assertNotNull($db->getTransaction());
173 5
            $this->expectException(NotSupportedException::class);
174 5
            $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

174
            $db->/** @scrutinizer ignore-call */ 
175
                 beginTransaction();
Loading history...
175
        });
176
    }
177
178 5
    public function testEnableQueryLog(): void
179
    {
180 5
        $db = $this->getConnection();
181
182 5
        foreach (['qlog1', 'qlog2', 'qlog3', 'qlog4'] as $table) {
183 5
            if ($db->getTableSchema($table, true) !== null) {
184 1
                $db->createCommand()->dropTable($table)->execute();
185
            }
186
        }
187
188
        /* profiling and logging */
189 5
        $db->setLogger($this->logger);
190 5
        $db->setProfiler($this->profiler);
191
192 5
        $this->logger->flush();
193 5
        $this->profiler->flush();
194
195 5
        $db->createCommand()->createTable('qlog1', ['id' => 'pk'])->execute();
196
197 5
        $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

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

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

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