Passed
Pull Request — master (#412)
by Wilmer
32:12 queued 29:30
created

CommonSchemaTest::normalizeConstraints()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 7
nc 5
nop 2
dl 0
loc 12
rs 8.4444
c 1
b 0
f 0
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\Exception\Exception;
14
use Yiisoft\Db\Exception\InvalidConfigException;
15
use Yiisoft\Db\Exception\NotSupportedException;
16
use Yiisoft\Db\QueryBuilder\QueryBuilder;
17
use Yiisoft\Db\Schema\Schema;
18
use Yiisoft\Db\Schema\TableSchemaInterface;
19
use Yiisoft\Db\Tests\AbstractSchemaTest;
20
use Yiisoft\Db\Tests\Support\AnyCaseValue;
21
use Yiisoft\Db\Tests\Support\AnyValue;
22
use Yiisoft\Db\Tests\Support\DbHelper;
23
24
use function array_keys;
25
use function count;
26
use function gettype;
27
use function is_array;
28
use function is_object;
29
use function json_encode;
30
use function ksort;
31
use function mb_chr;
32
use function sort;
33
use function strtolower;
34
35
abstract class CommonSchemaTest extends AbstractSchemaTest
36
{
37
    public function testColumnComment(): void
38
    {
39
        $db = $this->getConnection();
40
41
        $command = $db->createCommand();
42
        $schema = $db->getSchema();
43
44
        if ($schema->getTableSchema('testCommentTable') !== null) {
45
            $command->dropTable('testCommentTable')->execute();
46
        }
47
48
        $command->createTable('testCommentTable', ['bar' => Schema::TYPE_INTEGER,])->execute();
49
        $command->addCommentOnColumn('testCommentTable', 'bar', 'Test comment for column.')->execute();
50
51
        $this->assertSame(
52
            'Test comment for column.',
53
            $schema->getTableSchema('testCommentTable')->getColumn('bar')->getComment(),
54
        );
55
    }
56
57
    /**
58
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::columns()
59
     */
60
    public function testColumnSchema(array $columns): void
61
    {
62
        $this->columnSchema($columns);
63
    }
64
65
    public function testCompositeFk(): void
66
    {
67
        $db = $this->getConnection(true);
68
69
        $schema = $db->getSchema();
70
        $table = $schema->getTableSchema('composite_fk');
71
72
        $this->assertNotNull($table);
73
74
        $fk = $table->getForeignKeys();
75
76
        $this->assertCount(1, $fk);
77
        $this->assertTrue(isset($fk['fk_composite_fk_order_item']));
78
        $this->assertEquals('order_item', $fk['fk_composite_fk_order_item'][0]);
79
        $this->assertEquals('order_id', $fk['fk_composite_fk_order_item']['order_id']);
80
        $this->assertEquals('item_id', $fk['fk_composite_fk_order_item']['item_id']);
81
    }
82
83
    public function testContraintTablesExistance(): void
84
    {
85
        $db = $this->getConnection(true);
86
87
        $tableNames = ['T_constraints_1', 'T_constraints_2', 'T_constraints_3', 'T_constraints_4'];
88
        $schema = $db->getSchema();
89
90
        foreach ($tableNames as $tableName) {
91
            $tableSchema = $schema->getTableSchema($tableName);
92
            $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema, $tableName);
93
        }
94
    }
95
96
    public function testFindUniquesIndexes(): void
97
    {
98
        $db = $this->getConnection();
99
100
        $command = $db->createCommand();
101
        $schema = $db->getSchema();
102
103
        try {
104
            $command->dropTable('uniqueIndex')->execute();
105
        } catch (Exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
106
        }
107
108
        $command->createTable('uniqueIndex', ['somecol' => 'string', 'someCol2' => 'string'])->execute();
109
        $tableSchema = $schema->getTableSchema('uniqueIndex', true);
110
111
        $this->assertNotNull($tableSchema);
112
113
        $uniqueIndexes = $schema->findUniqueIndexes($tableSchema);
0 ignored issues
show
Bug introduced by
It seems like $tableSchema can also be of type null; however, parameter $table of Yiisoft\Db\Schema\Schema...ce::findUniqueIndexes() does only seem to accept Yiisoft\Db\Schema\TableSchemaInterface, 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

113
        $uniqueIndexes = $schema->findUniqueIndexes(/** @scrutinizer ignore-type */ $tableSchema);
Loading history...
114
115
        $this->assertSame([], $uniqueIndexes);
116
117
        $command->createIndex('somecolUnique', 'uniqueIndex', 'somecol', QueryBuilder::INDEX_UNIQUE)->execute();
118
        $tableSchema = $schema->getTableSchema('uniqueIndex', true);
119
120
        $this->assertNotNull($tableSchema);
121
122
        $uniqueIndexes = $schema->findUniqueIndexes($tableSchema);
123
124
        $this->assertSame(['somecolUnique' => ['somecol']], $uniqueIndexes);
125
126
        /* Create another column with upper case letter that fails postgres @link https://github.com/yiisoft/yii2/issues/10613 */
127
        $command->createIndex('someCol2Unique', 'uniqueIndex', 'someCol2', QueryBuilder::INDEX_UNIQUE)->execute();
128
        $tableSchema = $schema->getTableSchema('uniqueIndex', true);
129
130
        $this->assertNotNull($tableSchema);
131
132
        $uniqueIndexes = $schema->findUniqueIndexes($tableSchema);
133
134
        $this->assertSame(['someCol2Unique' => ['someCol2'], 'somecolUnique' => ['somecol']], $uniqueIndexes);
135
136
        /* see https://github.com/yiisoft/yii2/issues/13814 */
137
        $command->createIndex('another unique index', 'uniqueIndex', 'someCol2', QueryBuilder::INDEX_UNIQUE)->execute();
138
        $tableSchema = $schema->getTableSchema('uniqueIndex', true);
139
140
        $this->assertNotNull($tableSchema);
141
142
        $uniqueIndexes = $schema->findUniqueIndexes($tableSchema);
143
144
        $this->assertSame(
145
            ['another unique index' => ['someCol2'], 'someCol2Unique' => ['someCol2'], 'somecolUnique' => ['somecol']],
146
            $uniqueIndexes,
147
        );
148
    }
149
150
    public function testGetColumnNoExist(): void
151
    {
152
        $db = $this->getConnection(true);
153
154
        $schema = $db->getSchema();
155
        $table = $schema->getTableSchema('negative_default_values');
156
157
        $this->assertNotNull($table);
158
        $this->assertNull($table->getColumn('no_exist'));
159
    }
160
161
    public function testGetDefaultSchema(): void
162
    {
163
        $db = $this->getConnection();
164
165
        $schema = $db->getSchema();
166
167
        $this->assertNull($schema->getDefaultSchema());
168
    }
169
170
    public function testGetNonExistingTableSchema(): void
171
    {
172
        $db = $this->getConnection();
173
174
        $schema = $db->getSchema();
175
176
        $this->assertNull($schema->getTableSchema('nonexisting_table'));
177
    }
178
179
    public function testGetPrimaryKey(): void
180
    {
181
        $db = $this->getConnection();
182
183
        $command = $db->createCommand();
184
185
        if ($db->getSchema()->getTableSchema('testPKTable') !== null) {
186
            $command->dropTable('testPKTable')->execute();
187
        }
188
189
        $command->createTable('testPKTable', ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER])->execute();
190
        $insertResult = $command->insertEx('testPKTable', ['bar' => 1]);
191
        $selectResult = $command->setSql(
192
            DbHelper::replaceQuotes(
193
                <<<SQL
194
                SELECT [[id]] FROM [[testPKTable]] WHERE [[bar]] = 1
195
                SQL,
196
                $db->getName(),
197
            )
198
        )->queryOne();
199
200
        $this->assertIsArray($insertResult);
201
        $this->assertIsArray($selectResult);
202
        $this->assertEquals($selectResult['id'], $insertResult['id']);
203
    }
204
205
    public function testGetSchemaChecks(): void
206
    {
207
        $db = $this->getConnection();
208
209
        $schema = $db->getSchema();
210
        $tableChecks = $schema->getSchemaChecks();
211
212
        $this->assertIsArray($tableChecks);
213
214
        foreach ($tableChecks as $checks) {
215
            $this->assertIsArray($checks);
216
            $this->assertContainsOnlyInstancesOf(CheckConstraint::class, $checks);
217
        }
218
    }
219
220
    public function testGetSchemaDefaultValues(): void
221
    {
222
        $db = $this->getConnection();
223
224
        $schema = $db->getSchema();
225
        $tableDefaultValues = $schema->getSchemaDefaultValues();
226
227
        $this->assertIsArray($tableDefaultValues);
228
229
        foreach ($tableDefaultValues as $defaultValues) {
230
            $this->assertIsArray($defaultValues);
231
            $this->assertContainsOnlyInstancesOf(DefaultValueConstraint::class, $defaultValues);
232
        }
233
    }
234
235
    public function testGetSchemaForeignKeys(): void
236
    {
237
        $db = $this->getConnection();
238
239
        $schema = $db->getSchema();
240
        $tableForeignKeys = $schema->getSchemaForeignKeys();
241
242
        $this->assertIsArray($tableForeignKeys);
243
244
        foreach ($tableForeignKeys as $foreignKeys) {
245
            $this->assertIsArray($foreignKeys);
246
            $this->assertContainsOnlyInstancesOf(ForeignKeyConstraint::class, $foreignKeys);
247
        }
248
    }
249
250
    public function testGetSchemaIndexes(): void
251
    {
252
        $db = $this->getConnection();
253
254
        $schema = $db->getSchema();
255
        $tableIndexes = $schema->getSchemaIndexes();
256
257
        $this->assertIsArray($tableIndexes);
258
259
        foreach ($tableIndexes as $indexes) {
260
            $this->assertIsArray($indexes);
261
            $this->assertContainsOnlyInstancesOf(IndexConstraint::class, $indexes);
262
        }
263
    }
264
265
    public function testGetSchemaPrimaryKeys(): void
266
    {
267
        $db = $this->getConnection();
268
269
        $schema = $db->getSchema();
270
        $tablePks = $schema->getSchemaPrimaryKeys();
271
272
        $this->assertIsArray($tablePks);
273
        $this->assertContainsOnlyInstancesOf(Constraint::class, $tablePks);
274
    }
275
276
    public function testGetSchemaUniques(): void
277
    {
278
        $db = $this->getConnection();
279
280
        $schema = $db->getSchema();
281
        $tableUniques = $schema->getSchemaUniques();
282
283
        $this->assertIsArray($tableUniques);
284
285
        foreach ($tableUniques as $uniques) {
286
            $this->assertIsArray($uniques);
287
            $this->assertContainsOnlyInstancesOf(Constraint::class, $uniques);
288
        }
289
    }
290
291
    /**
292
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::columnsTypeChar()
293
     */
294
    public function testGetStringFieldsSize(
295
        string $columnName,
296
        string $columnType,
297
        int|null $columnSize,
298
        string $columnDbType
299
    ): void {
300
        $db = $this->getConnection(true);
301
302
        $schema = $db->getSchema();
303
        $tableSchema = $schema->getTableSchema('type');
304
305
        $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema);
306
307
        $columns = $tableSchema->getColumns();
308
309
        foreach ($columns as $name => $column) {
310
            $type = $column->getType();
311
            $size = $column->getSize();
312
            $dbType = $column->getDbType();
313
314
            if ($name === $columnName) {
315
                $this->assertSame($columnType, $type);
316
                $this->assertSame($columnSize, $size);
317
                $this->assertSame($columnDbType, $dbType);
318
            }
319
        }
320
    }
321
322
    public function testGetTableChecks(): void
323
    {
324
        $db = $this->getConnection(true);
325
326
        $schema = $db->getSchema();
327
        $tableChecks = $schema->getTableChecks('T_constraints_1');
328
329
        $this->assertIsArray($tableChecks);
330
331
        $this->assertContainsOnlyInstancesOf(CheckConstraint::class, $tableChecks);
332
    }
333
334
    /**
335
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::pdoAttributes()
336
     */
337
    public function testGetTableNames(array $pdoAttributes): void
338
    {
339
        $db = $this->getConnection(true);
340
341
        foreach ($pdoAttributes as $name => $value) {
342
            if ($name === PDO::ATTR_EMULATE_PREPARES) {
343
                continue;
344
            }
345
346
            $db->getPDO()?->setAttribute($name, $value);
347
        }
348
349
        $schema = $db->getSchema();
350
        $tablesNames = $schema->getTableNames();
351
        $tablesNames = array_map(static fn ($item) => trim($item, '[]'), $tablesNames);
352
353
        $this->assertContains('customer', $tablesNames);
354
        $this->assertContains('category', $tablesNames);
355
        $this->assertContains('item', $tablesNames);
356
        $this->assertContains('order', $tablesNames);
357
        $this->assertContains('order_item', $tablesNames);
358
        $this->assertContains('type', $tablesNames);
359
        $this->assertContains('animal', $tablesNames);
360
        $this->assertContains('animal_view', $tablesNames);
361
    }
362
363
    /**
364
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::tableSchema()
365
     */
366
    public function testGetTableSchema(string $name, string $expectedName): void
367
    {
368
        $db = $this->getConnection(true);
369
370
        $tableSchema = $db->getSchema()->getTableSchema($name);
371
372
        $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema);
373
        $this->assertEquals($expectedName, $tableSchema->getName());
374
    }
375
376
    /**
377
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::pdoAttributes()
378
     */
379
    public function testGetTableSchemas(array $pdoAttributes): void
380
    {
381
        $db = $this->getConnection(true);
382
383
        foreach ($pdoAttributes as $name => $value) {
384
            if ($name === PDO::ATTR_EMULATE_PREPARES) {
385
                continue;
386
            }
387
388
            $db->getPDO()?->setAttribute($name, $value);
389
        }
390
391
        $schema = $db->getSchema();
392
        $tables = $schema->getTableSchemas();
393
394
        $this->assertCount(count($schema->getTableNames()), $tables);
395
396
        foreach ($tables as $table) {
397
            $this->assertInstanceOf(TableSchemaInterface::class, $table);
398
        }
399
    }
400
401
    public function testGetTableSchemaWithAttrCase(): void
402
    {
403
        $db = $this->getConnection(true);
404
405
        $schema = $db->getSchema();
406
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
407
408
        $this->assertCount(count($schema->getTableNames()), $schema->getTableSchemas());
409
410
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
411
412
        $this->assertCount(count($schema->getTableNames()), $schema->getTableSchemas());
413
    }
414
415
    public function testNegativeDefaultValues(): void
416
    {
417
        $schema = $this->getConnection(true);
418
419
        $schema = $schema->getSchema();
420
        $table = $schema->getTableSchema('negative_default_values');
421
422
        $this->assertNotNull($table);
423
        $this->assertSame(-123, $table->getColumn('tinyint_col')?->getDefaultValue());
424
        $this->assertSame(-123, $table->getColumn('smallint_col')?->getDefaultValue());
425
        $this->assertSame(-123, $table->getColumn('int_col')?->getDefaultValue());
426
        $this->assertSame(-123, $table->getColumn('bigint_col')?->getDefaultValue());
427
        $this->assertSame(-12345.6789, $table->getColumn('float_col')?->getDefaultValue());
428
        $this->assertEquals(-33.22, $table->getColumn('numeric_col')?->getDefaultValue());
429
    }
430
431
    public function testGetViewNames(): void
432
    {
433
        $db = $this->getConnection(true);
434
435
        $schema = $db->getSchema();
436
        $views = $schema->getViewNames();
437
438
        $this->assertSame(['animal_view'], $views);
439
    }
440
441
    public function testQuoterEscapingValue()
442
    {
443
        $db = $this->getConnection(true);
444
445
        $quoter = $db->getQuoter();
446
        $db->createCommand(
447
            <<<SQL
448
            DELETE FROM {{quoter}}
449
            SQL
450
        )->execute();
451
        $data = $this->generateQuoterEscapingValues();
452
453
        foreach ($data as $index => $value) {
454
            $quotedName = $quoter->quoteValue('testValue_' . $index);
455
            $quoteValue = $quoter->quoteValue($value);
456
            $db->createCommand(
457
                <<<SQL
458
                INSERT INTO {{quoter}} (name, description) VALUES ($quotedName, $quoteValue)
459
                SQL
460
            )->execute();
461
            $result = $db->createCommand(
462
                <<<SQL
463
                SELECT * FROM {{quoter}} WHERE name=$quotedName
464
                SQL
465
            )->queryOne();
466
467
            $this->assertSame($value, $result['description']);
468
        }
469
    }
470
471
    /**
472
     * @depends testSchemaCache
473
     */
474
    public function testRefreshTableSchema(): void
475
    {
476
        $db = $this->getConnection(true);
477
478
        $schema = $db->getSchema();
479
        $schema->schemaCacheEnable(true);
480
        $noCacheTable = $schema->getTableSchema('type', true);
481
        $schema->refreshTableSchema('type');
482
        $refreshedTable = $schema->getTableSchema('type');
483
484
        $this->assertNotSame($noCacheTable, $refreshedTable);
485
    }
486
487
    public function testSchemaCache(): void
488
    {
489
        $db = $this->getConnection(true);
490
491
        $schema = $db->getSchema();
492
        $schema->schemaCacheEnable(true);
493
        $noCacheTable = $schema->getTableSchema('type', true);
494
        $cachedTable = $schema->getTableSchema('type');
495
496
        $this->assertSame($noCacheTable, $cachedTable);
497
498
        $db->createCommand()->renameTable('type', 'type_test');
499
        $noCacheTable = $schema->getTableSchema('type', true);
500
501
        $this->assertNotSame($noCacheTable, $cachedTable);
502
503
        $db->createCommand()->renameTable('type_test', 'type');
504
    }
505
506
    /**
507
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::tableSchemaCachePrefixes()
508
     */
509
    public function testTableSchemaCacheWithTablePrefixes(
510
        string $tablePrefix,
511
        string $tableName,
512
        string $testTablePrefix,
513
        string $testTableName
514
    ): void {
515
        $db = $this->getConnection(true);
516
517
        $schema = $db->getSchema();
518
        $schema->schemaCacheEnable(true);
519
        $db->setTablePrefix($tablePrefix);
520
        $noCacheTable = $schema->getTableSchema($tableName, true);
521
522
        $this->assertInstanceOf(TableSchemaInterface::class, $noCacheTable);
523
524
        /* Compare */
525
        $db->setTablePrefix($testTablePrefix);
526
        $testNoCacheTable = $schema->getTableSchema($testTableName);
527
528
        $this->assertSame($noCacheTable, $testNoCacheTable);
529
530
        $db->setTablePrefix($tablePrefix);
531
        $schema->refreshTableSchema($tableName);
532
        $refreshedTable = $schema->getTableSchema($tableName);
533
534
        $this->assertInstanceOf(TableSchemaInterface::class, $refreshedTable);
535
        $this->assertNotSame($noCacheTable, $refreshedTable);
536
537
        /* Compare */
538
        $db->setTablePrefix($testTablePrefix);
539
        $schema->refreshTableSchema($testTablePrefix);
540
        $testRefreshedTable = $schema->getTableSchema($testTableName);
541
542
        $this->assertInstanceOf(TableSchemaInterface::class, $testRefreshedTable);
543
        $this->assertEquals($refreshedTable, $testRefreshedTable);
544
        $this->assertNotSame($testNoCacheTable, $testRefreshedTable);
545
    }
546
547
    /**
548
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::constraints()
549
     *
550
     * @throws Exception
551
     */
552
    public function testTableSchemaConstraints(string $tableName, string $type, mixed $expected): void
553
    {
554
        if ($expected === false) {
555
            $this->expectException(NotSupportedException::class);
556
        }
557
558
        $db = $this->getConnection(true);
559
        $schema = $db->getSchema();
560
        $constraints = $schema->{'getTable' . ucfirst($type)}($tableName);
561
562
        $this->assertMetadataEquals($expected, $constraints);
563
564
        $db->close();
565
    }
566
567
    /**
568
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::constraints()
569
     *
570
     * @throws Exception
571
     * @throws InvalidConfigException
572
     */
573
    public function testTableSchemaConstraintsWithPdoLowercase(string $tableName, string $type, mixed $expected): void
574
    {
575
        if ($expected === false) {
576
            $this->expectException(NotSupportedException::class);
577
        }
578
579
        $db = $this->getConnection(true);
580
581
        $schema = $db->getSchema();
582
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
583
        $constraints = $schema->{'getTable' . ucfirst($type)}($tableName, true);
584
585
        $this->assertMetadataEquals($expected, $constraints);
586
587
        $db->close();
588
    }
589
590
    /**
591
     * @dataProvider \Yiisoft\Db\Tests\Provider\SchemaProvider::constraints()
592
     *
593
     * @throws Exception
594
     * @throws InvalidConfigException
595
     */
596
    public function testTableSchemaConstraintsWithPdoUppercase(string $tableName, string $type, mixed $expected): void
597
    {
598
        if ($expected === false) {
599
            $this->expectException(NotSupportedException::class);
600
        }
601
602
        $db = $this->getConnection(true);
603
604
        $schema = $db->getSchema();
605
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
606
        $constraints = $schema->{'getTable' . ucfirst($type)}($tableName, true);
607
608
        $this->assertMetadataEquals($expected, $constraints);
609
610
        $db->close();
611
    }
612
613
    private function generateQuoterEscapingValues(): array
614
    {
615
        $result = [];
616
        $stringLength = 16;
617
618
        for ($i = 32; $i < 128 - $stringLength; $i += $stringLength) {
619
            $str = '';
620
621
            for ($symbol = $i; $symbol < $i + $stringLength; $symbol++) {
622
                $str .= mb_chr($symbol, 'UTF-8');
623
            }
624
625
            $result[] = $str;
626
            $str = '';
627
628
            for ($symbol = $i; $symbol < $i + $stringLength; $symbol++) {
629
                $str .= mb_chr($symbol, 'UTF-8') . mb_chr($symbol, 'UTF-8');
630
            }
631
632
            $result[] = $str;
633
        }
634
635
        return $result;
636
    }
637
638
    protected function assertMetadataEquals($expected, $actual): void
639
    {
640
        switch (strtolower(gettype($expected))) {
641
            case 'object':
642
                $this->assertIsObject($actual);
643
                break;
644
            case 'array':
645
                $this->assertIsArray($actual);
646
                break;
647
            case 'null':
648
                $this->assertNull($actual);
649
                break;
650
        }
651
652
        if (is_array($expected)) {
653
            $this->normalizeArrayKeys($expected, false);
654
            $this->normalizeArrayKeys($actual, false);
655
        }
656
657
        $this->normalizeConstraints($expected, $actual);
658
659
        if (is_array($expected)) {
660
            $this->normalizeArrayKeys($expected, true);
661
            $this->normalizeArrayKeys($actual, true);
662
        }
663
664
        $this->assertEquals($expected, $actual);
665
    }
666
667
    protected function columnSchema(array $columns, string $table = 'type'): void
668
    {
669
        $db = $this->getConnection(true);
670
671
        $table = $db->getTableSchema($table, true);
672
673
        $this->assertNotNull($table);
674
675
        $expectedColNames = array_keys($columns);
676
        sort($expectedColNames);
677
        $colNames = $table->getColumnNames();
678
        sort($colNames);
679
680
        $this->assertSame($expectedColNames, $colNames);
681
682
        foreach ($table->getColumns() as $name => $column) {
683
            $expected = $columns[$name];
684
685
            $this->assertSame(
686
                $expected['dbType'],
687
                $column->getDbType(),
688
                "dbType of column $name does not match. type is {$column->getType()}, dbType is {$column->getDbType()}."
689
            );
690
            $this->assertSame(
691
                $expected['phpType'],
692
                $column->getPhpType(),
693
                "phpType of column $name does not match. type is {$column->getType()}, dbType is {$column->getDbType()}."
694
            );
695
            $this->assertSame($expected['type'], $column->getType(), "type of column $name does not match.");
696
            $this->assertSame(
697
                $expected['allowNull'],
698
                $column->isAllowNull(),
699
                "allowNull of column $name does not match."
700
            );
701
            $this->assertSame(
702
                $expected['autoIncrement'],
703
                $column->isAutoIncrement(),
704
                "autoIncrement of column $name does not match."
705
            );
706
            $this->assertSame(
707
                $expected['enumValues'],
708
                $column->getEnumValues(),
709
                "enumValues of column $name does not match."
710
            );
711
            $this->assertSame($expected['size'], $column->getSize(), "size of column $name does not match.");
712
            $this->assertSame(
713
                $expected['precision'],
714
                $column->getPrecision(),
715
                "precision of column $name does not match."
716
            );
717
718
            $this->assertSame($expected['scale'], $column->getScale(), "scale of column $name does not match.");
719
720
            if (is_object($expected['defaultValue'])) {
721
                $this->assertIsObject(
722
                    $column->getDefaultValue(),
723
                    "defaultValue of column $name is expected to be an object but it is not."
724
                );
725
                $this->assertSame(
726
                    (string) $expected['defaultValue'],
727
                    (string) $column->getDefaultValue(),
728
                    "defaultValue of column $name does not match."
729
                );
730
            } else {
731
                $this->assertSame(
732
                    $expected['defaultValue'],
733
                    $column->getDefaultValue(),
734
                    "defaultValue of column $name does not match."
735
                );
736
            }
737
738
            /* Pgsql only */
739
            if (isset($expected['dimension'])) {
740
                /** @psalm-suppress UndefinedMethod */
741
                $this->assertSame(
742
                    $expected['dimension'],
743
                    $column->getDimension(),
744
                    "dimension of column $name does not match"
745
                );
746
            }
747
        }
748
749
        $db->close();
750
    }
751
752
    private function normalizeArrayKeys(array &$array, bool $caseSensitive): void
753
    {
754
        $newArray = [];
755
756
        foreach ($array as $value) {
757
            if ($value instanceof Constraint) {
758
                $key = (array) $value;
759
                unset(
760
                    $key["\000Yiisoft\Db\Constraint\Constraint\000name"],
761
                    $key["\u0000Yiisoft\\Db\\Constraint\\ForeignKeyConstraint\u0000foreignSchemaName"]
762
                );
763
764
                foreach ($key as $keyName => $keyValue) {
765
                    if ($keyValue instanceof AnyCaseValue) {
766
                        $key[$keyName] = $keyValue->value;
767
                    } elseif ($keyValue instanceof AnyValue) {
768
                        $key[$keyName] = '[AnyValue]';
769
                    }
770
                }
771
772
                ksort($key, SORT_STRING);
773
                $newArray[$caseSensitive
774
                    ? json_encode($key, JSON_THROW_ON_ERROR)
775
                    : strtolower(json_encode($key, JSON_THROW_ON_ERROR))] = $value;
776
            } else {
777
                $newArray[] = $value;
778
            }
779
        }
780
781
        ksort($newArray, SORT_STRING);
782
        $array = $newArray;
783
    }
784
785
    private function normalizeConstraints($expected, $actual): void
786
    {
787
        if (is_array($expected)) {
788
            foreach ($expected as $key => $value) {
789
                if (!$value instanceof Constraint || !isset($actual[$key]) || !$actual[$key] instanceof Constraint) {
790
                    continue;
791
                }
792
793
                $this->normalizeConstraintPair($value, $actual[$key]);
794
            }
795
        } elseif ($expected instanceof Constraint && $actual instanceof Constraint) {
796
            $this->normalizeConstraintPair($expected, $actual);
797
        }
798
    }
799
800
    private function normalizeConstraintPair(Constraint $expectedConstraint, Constraint $actualConstraint): void
801
    {
802
        if ($expectedConstraint::class !== $actualConstraint::class) {
803
            return;
804
        }
805
806
        foreach (array_keys((array) $expectedConstraint) as $name) {
807
            if ($expectedConstraint->getName() instanceof AnyValue) {
808
                $actualConstraint->name($expectedConstraint->getName());
809
            } elseif ($expectedConstraint->getName() instanceof AnyCaseValue) {
810
                $actualConstraintName = $actualConstraint->getName();
811
812
                $this->assertIsString($actualConstraintName);
813
814
                $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

814
                $actualConstraint->name(new AnyCaseValue(/** @scrutinizer ignore-type */ $actualConstraintName));
Loading history...
815
            }
816
        }
817
    }
818
}
819