Passed
Pull Request — master (#412)
by Wilmer
36:35 queued 23:53
created

CommonSchemaTest::normalizeArrayKeys()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 20
nc 10
nop 2
dl 0
loc 31
rs 8.6666
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
        $expectedKey = match ($db->getName()) {
77
            'mysql' => $fk['FK_composite_fk_order_item'],
78
            default => $fk['fk_composite_fk_order_item'],
79
        };
80
81
        $this->assertCount(1, $fk);
82
        $this->assertTrue(isset($expectedKey));
83
        $this->assertSame('order_item', $expectedKey[0]);
84
        $this->assertSame('order_id', $expectedKey['order_id']);
85
        $this->assertSame('item_id', $expectedKey['item_id']);
86
    }
87
88
    public function testContraintTablesExistance(): void
89
    {
90
        $db = $this->getConnection(true);
91
92
        $tableNames = ['T_constraints_1', 'T_constraints_2', 'T_constraints_3', 'T_constraints_4'];
93
        $schema = $db->getSchema();
94
95
        foreach ($tableNames as $tableName) {
96
            $tableSchema = $schema->getTableSchema($tableName);
97
            $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema, $tableName);
98
        }
99
    }
100
101
    public function testFindUniquesIndexes(): void
102
    {
103
        $db = $this->getConnection();
104
105
        $command = $db->createCommand();
106
        $schema = $db->getSchema();
107
108
        try {
109
            $command->dropTable('uniqueIndex')->execute();
110
        } catch (Exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
111
        }
112
113
        $command->createTable(
114
            'uniqueIndex',
115
            ['somecol' => 'string', 'someCol2' => 'string', 'someCol3' => 'string'],
116
        )->execute();
117
        $tableSchema = $schema->getTableSchema('uniqueIndex', true);
118
119
        $this->assertNotNull($tableSchema);
120
121
        $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

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

820
                $actualConstraint->name(new AnyCaseValue(/** @scrutinizer ignore-type */ $actualConstraintName));
Loading history...
821
            }
822
        }
823
    }
824
}
825