Passed
Pull Request — master (#380)
by Wilmer
04:47 queued 01:51
created

CommonSchemaTest::testColumnSchema()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 76
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 56
nc 5
nop 0
dl 0
loc 76
rs 8.9599
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Tests\Common;
6
7
use PDO;
8
use Yiisoft\Db\Constraint\CheckConstraint;
9
use Yiisoft\Db\Constraint\Constraint;
10
use Yiisoft\Db\Constraint\DefaultValueConstraint;
11
use Yiisoft\Db\Constraint\ForeignKeyConstraint;
12
use Yiisoft\Db\Constraint\IndexConstraint;
13
use Yiisoft\Db\Schema\TableSchemaInterface;
14
use Yiisoft\Db\Tests\AbstractSchemaTest;
15
use Yiisoft\Db\Tests\Support\AnyCaseValue;
16
use Yiisoft\Db\Tests\Support\AnyValue;
17
use Yiisoft\Db\Tests\Support\TestTrait;
18
19
use function array_keys;
20
use function count;
21
use function gettype;
22
use function is_array;
23
use function is_object;
24
use function json_encode;
25
use function ksort;
26
use function mb_chr;
27
use function sort;
28
use function strtolower;
29
30
/**
31
 * @group mssql
32
 * @group mysql
33
 * @group pgsql
34
 * @group oracle
35
 * @group sqlite
36
 */
37
abstract class CommonSchemaTest extends AbstractSchemaTest
38
{
39
    use TestTrait;
40
41
    public function testColumnSchema(): void
42
    {
43
        $db = $this->getConnectionWithData();
44
45
        $columns = $this->getExpectedColumns();
0 ignored issues
show
Bug introduced by
The method getExpectedColumns() does not exist on Yiisoft\Db\Tests\Common\CommonSchemaTest. Did you maybe mean getExpectedException()? ( Ignorable by Annotation )

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

45
        /** @scrutinizer ignore-call */ 
46
        $columns = $this->getExpectedColumns();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
46
        $table = $db->getTableSchema('type', true);
47
48
        $this->assertNotNull($table);
49
50
        $expectedColNames = array_keys($columns);
51
        sort($expectedColNames);
52
        $colNames = $table->getColumnNames();
53
        sort($colNames);
54
55
        $this->assertSame($expectedColNames, $colNames);
56
57
        foreach ($table->getColumns() as $name => $column) {
58
            $expected = $columns[$name];
59
60
            $this->assertSame(
61
                $expected['dbType'],
62
                $column->getDbType(),
63
                "dbType of column $name does not match. type is {$column->getType()}, dbType is {$column->getDbType()}."
64
            );
65
            $this->assertSame(
66
                $expected['phpType'],
67
                $column->getPhpType(),
68
                "phpType of column $name does not match. type is {$column->getType()}, dbType is {$column->getDbType()}."
69
            );
70
            $this->assertSame($expected['type'], $column->getType(), "type of column $name does not match.");
71
            $this->assertSame(
72
                $expected['allowNull'],
73
                $column->isAllowNull(),
74
                "allowNull of column $name does not match."
75
            );
76
            $this->assertSame(
77
                $expected['autoIncrement'],
78
                $column->isAutoIncrement(),
79
                "autoIncrement of column $name does not match."
80
            );
81
            $this->assertSame(
82
                $expected['enumValues'],
83
                $column->getEnumValues(),
84
                "enumValues of column $name does not match."
85
            );
86
            $this->assertSame($expected['size'], $column->getSize(), "size of column $name does not match.");
87
            $this->assertSame(
88
                $expected['precision'],
89
                $column->getPrecision(),
90
                "precision of column $name does not match."
91
            );
92
            $this->assertSame($expected['scale'], $column->getScale(), "scale of column $name does not match.");
93
            if (is_object($expected['defaultValue'])) {
94
                $this->assertIsObject(
95
                    $column->getDefaultValue(),
96
                    "defaultValue of column $name is expected to be an object but it is not."
97
                );
98
                $this->assertSame(
99
                    (string) $expected['defaultValue'],
100
                    (string) $column->getDefaultValue(),
101
                    "defaultValue of column $name does not match."
102
                );
103
            } else {
104
                $this->assertSame(
105
                    $expected['defaultValue'],
106
                    $column->getDefaultValue(),
107
                    "defaultValue of column $name does not match."
108
                );
109
            }
110
            /* Pgsql only */
111
            if (isset($expected['dimension'])) {
112
                /** @psalm-suppress UndefinedMethod */
113
                $this->assertSame(
114
                    $expected['dimension'],
115
                    $column->getDimension(),
116
                    "dimension of column $name does not match"
117
                );
118
            }
119
        }
120
    }
121
122
    public function testCompositeFk(): void
123
    {
124
        $db = $this->getConnection();
125
126
        $schema = $db->getSchema();
127
        $table = $schema->getTableSchema('composite_fk');
128
129
        $this->assertNotNull($table);
130
131
        $fk = $table->getForeignKeys();
132
133
        $this->assertCount(1, $fk);
134
        $this->assertTrue(isset($fk['FK_composite_fk_order_item']));
135
        $this->assertEquals('order_item', $fk['FK_composite_fk_order_item'][0]);
136
        $this->assertEquals('order_id', $fk['FK_composite_fk_order_item']['order_id']);
137
        $this->assertEquals('item_id', $fk['FK_composite_fk_order_item']['item_id']);
138
    }
139
140
    public function testContraintTablesExistance(): void
141
    {
142
        $db = $this->getConnectionWithData();
143
144
        $tableNames = ['T_constraints_1', 'T_constraints_2', 'T_constraints_3', 'T_constraints_4'];
145
        $schema = $db->getSchema();
146
147
        foreach ($tableNames as $tableName) {
148
            $tableSchema = $schema->getTableSchema($tableName);
149
            $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema, $tableName);
150
        }
151
    }
152
153
    public function testGetColumnNoExist(): void
154
    {
155
        $db = $this->getConnectionWithData();
156
157
        $schema = $db->getSchema();
158
        $table = $schema->getTableSchema('negative_default_values');
159
160
        $this->assertNotNull($table);
161
        $this->assertNull($table->getColumn('no_exist'));
162
    }
163
164
    public function testGetNonExistingTableSchema(): void
165
    {
166
        $db = $this->getConnection();
167
168
        $schema = $db->getSchema();
169
170
        $this->assertNull($schema->getTableSchema('nonexisting_table'));
171
    }
172
173
    public function testGetSchemaChecks(): void
174
    {
175
        $db = $this->getConnection();
176
177
        $schema = $db->getSchema();
178
        $tableChecks = $schema->getSchemaChecks();
179
180
        $this->assertIsArray($tableChecks);
181
182
        foreach ($tableChecks as $checks) {
183
            $this->assertIsArray($checks);
184
            $this->assertContainsOnlyInstancesOf(CheckConstraint::class, $checks);
185
        }
186
    }
187
188
    public function testGetSchemaDefaultValues(): void
189
    {
190
        $db = $this->getConnection();
191
192
        $schema = $db->getSchema();
193
        $tableDefaultValues = $schema->getSchemaDefaultValues();
194
195
        $this->assertIsArray($tableDefaultValues);
196
197
        foreach ($tableDefaultValues as $defaultValues) {
198
            $this->assertIsArray($defaultValues);
199
            $this->assertContainsOnlyInstancesOf(DefaultValueConstraint::class, $defaultValues);
200
        }
201
    }
202
203
    public function testGetSchemaForeignKeys(): void
204
    {
205
        $db = $this->getConnection();
206
207
        $schema = $db->getSchema();
208
        $tableForeignKeys = $schema->getSchemaForeignKeys();
209
210
        $this->assertIsArray($tableForeignKeys);
211
212
        foreach ($tableForeignKeys as $foreignKeys) {
213
            $this->assertIsArray($foreignKeys);
214
            $this->assertContainsOnlyInstancesOf(ForeignKeyConstraint::class, $foreignKeys);
215
        }
216
    }
217
218
    public function testGetSchemaIndexes(): void
219
    {
220
        $db = $this->getConnection();
221
222
        $schema = $db->getSchema();
223
        $tableIndexes = $schema->getSchemaIndexes();
224
225
        $this->assertIsArray($tableIndexes);
226
227
        foreach ($tableIndexes as $indexes) {
228
            $this->assertIsArray($indexes);
229
            $this->assertContainsOnlyInstancesOf(IndexConstraint::class, $indexes);
230
        }
231
    }
232
233
    public function testGetSchemaPrimaryKeys(): void
234
    {
235
        $db = $this->getConnection();
236
237
        $schema = $db->getSchema();
238
        $tablePks = $schema->getSchemaPrimaryKeys();
239
240
        $this->assertIsArray($tablePks);
241
        $this->assertContainsOnlyInstancesOf(Constraint::class, $tablePks);
242
    }
243
244
    public function testGetSchemaUniques(): void
245
    {
246
        $db = $this->getConnection();
247
248
        $schema = $db->getSchema();
249
        $tableUniques = $schema->getSchemaUniques();
250
251
        $this->assertIsArray($tableUniques);
252
253
        foreach ($tableUniques as $uniques) {
254
            $this->assertIsArray($uniques);
255
            $this->assertContainsOnlyInstancesOf(Constraint::class, $uniques);
256
        }
257
    }
258
259
    public function testGetTableChecks(): void
260
    {
261
        $db = $this->getConnectionWithData();
262
263
        $schema = $db->getSchema();
264
        $tableChecks = $schema->getTableChecks('T_constraints_1');
265
266
        $this->assertIsArray($tableChecks);
267
        $this->assertContainsOnlyInstancesOf(CheckConstraint::class, $tableChecks);
268
    }
269
270
    public function testGetTableSchemasWithAttrCase(): void
271
    {
272
        $db = $this->getConnectionWithData();
273
274
        $schema = $db->getSchema();
275
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
276
277
        $this->assertCount(count($schema->getTableNames()), $schema->getTableSchemas());
278
279
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
280
281
        $this->assertCount(count($schema->getTableNames()), $schema->getTableSchemas());
282
    }
283
284
    public function testNegativeDefaultValues(): void
285
    {
286
        $schema = $this->getConnectionWithData();
287
288
        $schema = $schema->getSchema();
289
        $table = $schema->getTableSchema('negative_default_values');
290
291
        $this->assertNotNull($table);
292
        $this->assertSame(-123, $table->getColumn('tinyint_col')?->getDefaultValue());
293
        $this->assertSame(-123, $table->getColumn('smallint_col')?->getDefaultValue());
294
        $this->assertSame(-123, $table->getColumn('int_col')?->getDefaultValue());
295
        $this->assertSame(-123, $table->getColumn('bigint_col')?->getDefaultValue());
296
        $this->assertSame(-12345.6789, $table->getColumn('float_col')?->getDefaultValue());
297
        $this->assertEquals(-33.22, $table->getColumn('numeric_col')?->getDefaultValue());
298
    }
299
300
    public function testQuoterEscapingValue()
301
    {
302
        $db = $this->getConnectionWithData();
303
304
        $quoter = $db->getQuoter();
305
        $db->createCommand(
306
            <<<SQL
307
            DELETE FROM {{quoter}}
308
            SQL
309
        )->execute();
310
        $data = $this->generateQuoterEscapingValues();
311
312
        foreach ($data as $index => $value) {
313
            $quotedName = $quoter->quoteValue('testValue_' . $index);
314
            $quoteValue = $quoter->quoteValue($value);
315
            $db->createCommand(
316
                <<<SQL
317
                INSERT INTO {{quoter}} (name, description) VALUES ($quotedName, $quoteValue)
318
                SQL
319
            )->execute();
320
            $result = $db->createCommand(
321
                <<<SQL
322
                SELECT * FROM {{quoter}} WHERE name=$quotedName
323
                SQL
324
            )->queryOne();
325
326
            $this->assertSame($value, $result['description']);
327
        }
328
    }
329
330
    /**
331
     * @depends testSchemaCache
332
     */
333
    public function testRefreshTableSchema(): void
334
    {
335
        $db = $this->getConnectionWithData();
336
337
        $schema = $db->getSchema();
338
        $schemaCache = $this->getSchemaCache();
339
340
        $this->assertNotNull($schemaCache);
341
342
        $schema->schemaCacheEnable(true);
343
        $noCacheTable = $schema->getTableSchema('type', true);
344
        $schema->refreshTableSchema('type');
345
        $refreshedTable = $schema->getTableSchema('type');
346
347
        $this->assertNotSame($noCacheTable, $refreshedTable);
348
    }
349
350
    public function testSchemaCache(): void
351
    {
352
        $db = $this->getConnectionWithData();
353
354
        $schema = $db->getSchema();
355
        $schemaCache = $this->getSchemaCache();
356
357
        $this->assertNotNull($schemaCache);
358
359
        $schema->schemaCacheEnable(true);
360
        $noCacheTable = $schema->getTableSchema('type', true);
361
        $cachedTable = $schema->getTableSchema('type');
362
363
        $this->assertSame($noCacheTable, $cachedTable);
364
365
        $db->createCommand()->renameTable('type', 'type_test');
366
        $noCacheTable = $schema->getTableSchema('type', true);
367
368
        $this->assertNotSame($noCacheTable, $cachedTable);
369
370
        $db->createCommand()->renameTable('type_test', 'type');
371
    }
372
373
    private function generateQuoterEscapingValues(): array
374
    {
375
        $result = [];
376
        $stringLength = 16;
377
378
        for ($i = 32; $i < 128 - $stringLength; $i += $stringLength) {
379
            $str = '';
380
381
            for ($symbol = $i; $symbol < $i + $stringLength; $symbol++) {
382
                $str .= mb_chr($symbol, 'UTF-8');
383
            }
384
385
            $result[] = $str;
386
            $str = '';
387
388
            for ($symbol = $i; $symbol < $i + $stringLength; $symbol++) {
389
                $str .= mb_chr($symbol, 'UTF-8') . mb_chr($symbol, 'UTF-8');
390
            }
391
392
            $result[] = $str;
393
        }
394
395
        return $result;
396
    }
397
398
    protected function assertMetadataEquals($expected, $actual): void
399
    {
400
        switch (strtolower(gettype($expected))) {
401
            case 'object':
402
                $this->assertIsObject($actual);
403
                break;
404
            case 'array':
405
                $this->assertIsArray($actual);
406
                break;
407
            case 'null':
408
                $this->assertNull($actual);
409
                break;
410
        }
411
412
        if (is_array($expected)) {
413
            $this->normalizeArrayKeys($expected, false);
414
            $this->normalizeArrayKeys($actual, false);
415
        }
416
417
        $this->normalizeConstraints($expected, $actual);
418
419
        if (is_array($expected)) {
420
            $this->normalizeArrayKeys($expected, true);
421
            $this->normalizeArrayKeys($actual, true);
422
        }
423
424
        $this->assertEquals($expected, $actual);
425
    }
426
427
    private function normalizeArrayKeys(array &$array, bool $caseSensitive): void
428
    {
429
        $newArray = [];
430
431
        foreach ($array as $value) {
432
            if ($value instanceof Constraint) {
433
                $key = (array) $value;
434
                unset(
435
                    $key["\000Yiisoft\Db\Constraint\Constraint\000name"],
436
                    $key["\u0000Yiisoft\\Db\\Constraint\\ForeignKeyConstraint\u0000foreignSchemaName"]
437
                );
438
439
                foreach ($key as $keyName => $keyValue) {
440
                    if ($keyValue instanceof AnyCaseValue) {
441
                        $key[$keyName] = $keyValue->value;
442
                    } elseif ($keyValue instanceof AnyValue) {
443
                        $key[$keyName] = '[AnyValue]';
444
                    }
445
                }
446
447
                ksort($key, SORT_STRING);
448
                $newArray[$caseSensitive
449
                    ? json_encode($key, JSON_THROW_ON_ERROR)
450
                    : strtolower(json_encode($key, JSON_THROW_ON_ERROR))] = $value;
451
            } else {
452
                $newArray[] = $value;
453
            }
454
        }
455
456
        ksort($newArray, SORT_STRING);
457
        $array = $newArray;
458
    }
459
460
    private function normalizeConstraints($expected, $actual): void
461
    {
462
        if (is_array($expected)) {
463
            foreach ($expected as $key => $value) {
464
                if (!$value instanceof Constraint || !isset($actual[$key]) || !$actual[$key] instanceof Constraint) {
465
                    continue;
466
                }
467
468
                $this->normalizeConstraintPair($value, $actual[$key]);
469
            }
470
        } elseif ($expected instanceof Constraint && $actual instanceof Constraint) {
471
            $this->normalizeConstraintPair($expected, $actual);
472
        }
473
    }
474
475
    private function normalizeConstraintPair(Constraint $expectedConstraint, Constraint $actualConstraint): void
476
    {
477
        if ($expectedConstraint::class !== $actualConstraint::class) {
478
            return;
479
        }
480
481
        foreach (array_keys((array) $expectedConstraint) as $name) {
482
            if ($expectedConstraint->getName() instanceof AnyValue) {
483
                $actualConstraint->name($expectedConstraint->getName());
484
            } elseif ($expectedConstraint->getName() instanceof AnyCaseValue) {
485
                $actualConstraintName = $actualConstraint->getName();
486
487
                $this->assertIsString($actualConstraintName);
488
489
                $actualConstraint->name(new AnyCaseValue($actualConstraintName));
0 ignored issues
show
Bug introduced by
It seems like $actualConstraintName can also be of type null and object; however, parameter $value of Yiisoft\Db\Tests\Support...aseValue::__construct() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

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

489
                $actualConstraint->name(new AnyCaseValue(/** @scrutinizer ignore-type */ $actualConstraintName));
Loading history...
490
            }
491
        }
492
    }
493
}
494