Passed
Pull Request — master (#20)
by Wilmer
13:35
created

ConnectionTest   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 276
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 133
c 2
b 0
f 0
dl 0
loc 276
rs 10
wmc 24
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Sqlite\Tests;
6
7
use Yiisoft\Db\Connection\Connection;
8
use Yiisoft\Db\Exception\InvalidConfigException;
9
use Yiisoft\Db\Transaction\Transaction;
10
use Yiisoft\Db\Tests\ConnectionTest as AbstractConnectionTest;
11
12
class ConnectionTest extends AbstractConnectionTest
13
{
14
    protected ?string $driverName = 'sqlite';
15
16
    public function testConstruct(): void
17
    {
18
        $connection = $this->getConnection(false);
19
20
        $this->assertEquals($this->databases['dsn'], $connection->getDsn());
21
    }
22
23
    /**
24
     * Test whether slave connection is recovered when call getSlavePdo() after close().
25
     *
26
     * @see https://github.com/yiisoft/yii2/issues/14165
27
     */
28
    public function testGetPdoAfterClose(): void
29
    {
30
        $connection = $this->getConnection();
31
32
        $connection->setSlaves('1', $this->databases['dsn']);
33
34
        $this->assertNotNull($connection->getSlavePdo(false));
35
36
        $connection->close();
37
38
        $masterPdo = $connection->getMasterPdo();
39
        $this->assertNotFalse($masterPdo);
40
        $this->assertNotNull($masterPdo);
41
42
        $slavePdo = $connection->getSlavePdo(false);
43
        $this->assertNotFalse($slavePdo);
44
        $this->assertNotNull($slavePdo);
45
        $this->assertNotSame($masterPdo, $slavePdo);
46
    }
47
48
    public function testServerStatusCacheWorks(): void
49
    {
50
        $connection = $this->getConnection(true, false);
51
52
        $connection->setMasters('1', $this->databases['dsn']);
53
54
        $connection->setShuffleMasters(false);
55
56
        $cacheKey = ['Yiisoft\Db\Connection\Connection::openFromPoolSequentially', $connection->getDsn()];
57
58
        $this->assertFalse($this->cache->has($cacheKey));
59
60
        $connection->open();
61
62
        $this->assertFalse(
63
            $this->cache->has($cacheKey),
64
            'Connection was successful – cache must not contain information about this DSN'
65
        );
66
67
        $connection->close();
68
69
        $connection = $this->getConnection(true, false);
70
71
        $cacheKey = ['Yiisoft\Db\Connection\Connection::openFromPoolSequentially', 'host:invalid'];
72
73
        $connection->setMasters('1', 'host:invalid');
74
75
        $connection->setShuffleMasters(true);
76
77
        try {
78
            $connection->open();
79
        } catch (InvalidConfigException $e) {
80
        }
81
82
        $this->assertTrue(
83
            $this->cache->has($cacheKey),
84
            'Connection was not successful – cache must contain information about this DSN'
85
        );
86
87
        $connection->close();
88
    }
89
90
    public function testServerStatusCacheCanBeDisabled(): void
91
    {
92
        $this->cache->clear();
93
94
        $connection = $this->getConnection(true, false);
95
96
        $connection->setMasters('1', $this->databases['dsn']);
97
98
        $connection->setSchemaCache(null);
99
100
        $connection->setShuffleMasters(false);
101
102
        $cacheKey = ['Yiisoft\Db\Connection\Connection::openFromPoolSequentially', $connection->getDsn()];
103
104
        $this->assertFalse($this->cache->has($cacheKey));
105
106
        $connection->open();
107
108
        $this->assertFalse($this->cache->has($cacheKey), 'Caching is disabled');
109
110
        $connection->close();
111
112
        $cacheKey = ['Yiisoft\Db\Connection\Connection::openFromPoolSequentially', 'host:invalid'];
113
114
        $connection->setMasters('1', 'host:invalid');
115
116
        try {
117
            $connection->open();
118
        } catch (InvalidConfigException $e) {
119
        }
120
121
        $this->assertFalse($this->cache->has($cacheKey), 'Caching is disabled');
122
123
        $connection->close();
124
    }
125
126
    public function testQuoteValue(): void
127
    {
128
        $connection = $this->getConnection(false);
129
130
        $this->assertEquals(123, $connection->quoteValue(123));
131
        $this->assertEquals("'string'", $connection->quoteValue('string'));
132
        $this->assertEquals("'It''s interesting'", $connection->quoteValue("It's interesting"));
133
    }
134
135
    public function testTransactionIsolation(): void
136
    {
137
        $connection = $this->getConnection(true);
138
139
        $transaction = $connection->beginTransaction(Transaction::READ_UNCOMMITTED);
140
        $transaction->rollBack();
141
142
        $transaction = $connection->beginTransaction(Transaction::SERIALIZABLE);
143
        $transaction->rollBack();
144
145
        $this->assertTrue(true); // No exceptions means test is passed.
146
    }
147
148
    public function testMasterSlave(): void
149
    {
150
        $counts = [[0, 2], [1, 2], [2, 2]];
151
152
        foreach ($counts as $count) {
153
            [$masterCount, $slaveCount] = $count;
154
155
            $db = $this->prepareMasterSlave($masterCount, $slaveCount);
156
157
            $this->assertInstanceOf(Connection::class, $db->getSlave());
158
            $this->assertTrue($db->getSlave()->isActive());
159
            $this->assertFalse($db->isActive());
160
161
            // test SELECT uses slave
162
            $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM profile')->queryScalar());
163
            $this->assertFalse($db->isActive());
164
165
            // test UPDATE uses master
166
            $db->createCommand("UPDATE profile SET description='test' WHERE id=1")->execute();
167
            $this->assertTrue($db->isActive());
168
169
            if ($masterCount > 0) {
170
                $this->assertInstanceOf(Connection::class, $db->getMaster());
171
                $this->assertTrue($db->getMaster()->isActive());
172
            } else {
173
                $this->assertNull($db->getMaster());
174
            }
175
176
            $this->assertNotEquals(
177
                'test',
178
                $db->createCommand('SELECT description FROM profile WHERE id=1')->queryScalar()
179
            );
180
181
            $result = $db->useMaster(static function (Connection $db) {
182
                return $db->createCommand('SELECT description FROM profile WHERE id=1')->queryScalar();
183
            });
184
185
            $this->assertEquals('test', $result);
186
        }
187
    }
188
189
    public function testMastersShuffled(): void
190
    {
191
        $mastersCount = 2;
192
        $slavesCount = 2;
193
        $retryPerNode = 10;
194
195
        $nodesCount = $mastersCount + $slavesCount;
196
197
        $hit_slaves = $hit_masters = [];
198
199
        for ($i = $nodesCount * $retryPerNode; $i-- > 0;) {
200
            $db = $this->prepareMasterSlave($mastersCount, $slavesCount);
201
            $db->setShuffleMasters(true);
202
203
            $hit_slaves[$db->getSlave()->getDsn()] = true;
204
            $hit_masters[$db->getMaster()->getDsn()] = true;
205
206
            if (\count($hit_slaves) === $slavesCount && \count($hit_masters) === $mastersCount) {
207
                break;
208
            }
209
        }
210
211
        $this->assertCount($mastersCount, $hit_masters, 'all masters hit');
212
        $this->assertCount($slavesCount, $hit_slaves, 'all slaves hit');
213
    }
214
215
    public function testMastersSequential(): void
216
    {
217
        $mastersCount = 2;
218
        $slavesCount = 2;
219
        $retryPerNode = 10;
220
221
        $nodesCount = $mastersCount + $slavesCount;
222
223
        $hit_slaves = $hit_masters = [];
224
225
        for ($i = $nodesCount * $retryPerNode; $i-- > 0;) {
226
            $db = $this->prepareMasterSlave($mastersCount, $slavesCount);
227
            $db->setShuffleMasters(false);
228
229
            $hit_slaves[$db->getSlave()->getDsn()] = true;
230
            $hit_masters[$db->getMaster()->getDsn()] = true;
231
232
            if (\count($hit_slaves) === $slavesCount) {
233
                break;
234
            }
235
        }
236
237
        $this->assertCount(1, $hit_masters, 'same master hit');
238
239
        // slaves are always random
240
        $this->assertCount($slavesCount, $hit_slaves, 'all slaves hit');
241
    }
242
243
    public function testRestoreMasterAfterException(): void
244
    {
245
        $db = $this->prepareMasterSlave(1, 1);
246
        $this->assertTrue($db->areSlavesEnabled());
247
248
        try {
249
            $db->useMaster(static function (Connection $db) {
250
                throw new \Exception('fail');
251
            });
252
            $this->fail('Exceptions was caught somewhere');
253
        } catch (\Exception $e) {
254
            // ok
255
        }
256
257
        $this->assertTrue($db->areSlavesEnabled());
258
    }
259
260
    public function testExceptionContainsRawQuery(): void
261
    {
262
        $this->markTestSkipped('This test does not work on sqlite because preparing the failing query fails');
263
    }
264
265
    protected function prepareMasterSlave($masterCount, $slaveCount): Connection
266
    {
267
        $db = $this->getConnection(true, true, true);
268
269
        for ($i = 0; $i < $masterCount; ++$i) {
270
            $this->prepareDatabase(true, true, [
271
                'dsn' => 'sqlite:' . __DIR__ . "/data/yii_test_master{$i}.sq3",
272
            ]);
273
274
            $db->setMasters("$i", 'sqlite:' . __DIR__ . "/data/yii_test_master{$i}.sq3");
275
        }
276
277
        for ($i = 0; $i < $slaveCount; ++$i) {
278
            $this->prepareDatabase(true, true, [
279
                'dsn' =>  'sqlite:' . __DIR__ . "/data/yii_test_slave{$i}.sq3",
280
            ]);
281
282
            $db->setSlaves("$i", 'sqlite:' . __DIR__ . "/data/yii_test_slave{$i}.sq3");
283
        }
284
285
        $db->close();
286
287
        return $db;
288
    }
289
}
290