Passed
Pull Request — master (#372)
by Wilmer
03:26
created

AbstractSchemaTest::normalizeArrayKeys()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 20
c 1
b 0
f 0
nc 10
nop 2
dl 0
loc 33
rs 8.6666
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Tests;
6
7
use PDO;
8
use PHPUnit\Framework\TestCase;
9
use Yiisoft\Db\Constraint\Constraint;
10
use Yiisoft\Db\Schema\TableSchemaInterface;
11
use Yiisoft\Db\Tests\Support\AnyCaseValue;
12
use Yiisoft\Db\Tests\Support\AnyValue;
13
14
use function fclose;
15
use function fopen;
16
use function print_r;
17
18
abstract class AbstractSchemaTest extends TestCase
19
{
20
    public function testCompositeFk(): void
21
    {
22
        $db = $this->getConnection();
0 ignored issues
show
Bug introduced by
The method getConnection() does not exist on Yiisoft\Db\Tests\AbstractSchemaTest. ( Ignorable by Annotation )

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

22
        /** @scrutinizer ignore-call */ 
23
        $db = $this->getConnection();

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...
23
24
        $schema = $db->getSchema();
25
        $table = $schema->getTableSchema('composite_fk');
26
27
        $this->assertNotNull($table);
28
29
        $fk = $table->getForeignKeys();
30
31
        $this->assertCount(1, $fk);
32
        $this->assertTrue(isset($fk['FK_composite_fk_order_item']));
33
        $this->assertEquals('order_item', $fk['FK_composite_fk_order_item'][0]);
34
        $this->assertEquals('order_id', $fk['FK_composite_fk_order_item']['order_id']);
35
        $this->assertEquals('item_id', $fk['FK_composite_fk_order_item']['item_id']);
36
    }
37
38
    public function testContraintTablesExistance(): void
39
    {
40
        $tableNames = [
41
            'T_constraints_1',
42
            'T_constraints_2',
43
            'T_constraints_3',
44
            'T_constraints_4',
45
        ];
46
47
        $db = $this->getConnection();
48
49
        $schema = $db->getSchema();
50
51
        foreach ($tableNames as $tableName) {
52
            $tableSchema = $schema->getTableSchema($tableName);
53
            $this->assertInstanceOf(TableSchemaInterface::class, $tableSchema, $tableName);
54
        }
55
    }
56
57
    public function testGetColumnNoExist(): void
58
    {
59
        $db = $this->getConnection();
60
61
        $schema = $db->getSchema();
62
        $table = $schema->getTableSchema('negative_default_values');
63
64
        $this->assertNotNull($table);
65
        $this->assertNull($table->getColumn('no_exist'));
66
    }
67
68
    public function testGetNonExistingTableSchema(): void
69
    {
70
        $this->assertNull($this->getConnection()->getSchema()->getTableSchema('nonexisting_table'));
71
    }
72
73
    public function testGetPDOType(): void
74
    {
75
        $values = [
76
            [null, PDO::PARAM_NULL],
77
            ['', PDO::PARAM_STR],
78
            ['hello', PDO::PARAM_STR],
79
            [0, PDO::PARAM_INT],
80
            [1, PDO::PARAM_INT],
81
            [1337, PDO::PARAM_INT],
82
            [true, PDO::PARAM_BOOL],
83
            [false, PDO::PARAM_BOOL],
84
            [$fp = fopen(__FILE__, 'rb'), PDO::PARAM_LOB],
85
        ];
86
87
        $db = $this->getConnection();
88
        $schema = $db->getSchema();
89
90
        foreach ($values as $value) {
91
            $this->assertSame(
92
                $value[1],
93
                $schema->getPdoType($value[0]),
94
                'type for value ' . print_r($value[0], true) . ' does not match.'
0 ignored issues
show
Bug introduced by
Are you sure print_r($value[0], true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

94
                'type for value ' . /** @scrutinizer ignore-type */ print_r($value[0], true) . ' does not match.'
Loading history...
95
            );
96
        }
97
98
        fclose($fp);
99
    }
100
101
    public function testNegativeDefaultValues(): void
102
    {
103
        $db = $this->getConnection();
104
105
        $schema = $db->getSchema();
106
        $table = $schema->getTableSchema('negative_default_values');
107
108
        $this->assertNotNull($table);
109
        $this->assertEquals(-123, $table->getColumn('tinyint_col')?->getDefaultValue());
110
        $this->assertEquals(-123, $table->getColumn('smallint_col')?->getDefaultValue());
111
        $this->assertEquals(-123, $table->getColumn('int_col')?->getDefaultValue());
112
        $this->assertEquals(-123, $table->getColumn('bigint_col')?->getDefaultValue());
113
        $this->assertEquals(-12345.6789, $table->getColumn('float_col')?->getDefaultValue());
114
        $this->assertEquals(-33.22, $table->getColumn('numeric_col')?->getDefaultValue());
115
    }
116
117
    public function testGetTableSchemasWithAttrCase(): void
118
    {
119
        $db = $this->getConnection();
120
121
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
122
123
        $this->assertCount(is_countable($db->getSchema()->getTableNames()) ? count($db->getSchema()->getTableNames()) : 0, $db->getSchema()->getTableSchemas());
124
125
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
126
127
        $this->assertCount(is_countable($db->getSchema()->getTableNames()) ? count($db->getSchema()->getTableNames()) : 0, $db->getSchema()->getTableSchemas());
128
    }
129
130
    protected function assertMetadataEquals($expected, $actual): void
131
    {
132
        switch (strtolower(gettype($expected))) {
133
            case 'object':
134
                $this->assertIsObject($actual);
135
                break;
136
            case 'array':
137
                $this->assertIsArray($actual);
138
                break;
139
            case 'null':
140
                $this->assertNull($actual);
141
                break;
142
        }
143
144
        if (is_array($expected)) {
145
            $this->normalizeArrayKeys($expected, false);
146
            $this->normalizeArrayKeys($actual, false);
147
        }
148
149
        $this->normalizeConstraints($expected, $actual);
150
151
        if (is_array($expected)) {
152
            $this->normalizeArrayKeys($expected, true);
153
            $this->normalizeArrayKeys($actual, true);
154
        }
155
156
        $this->assertEquals($expected, $actual);
157
    }
158
159
    private function normalizeArrayKeys(array &$array, bool $caseSensitive): void
160
    {
161
        $newArray = [];
162
163
        foreach ($array as $value) {
164
            if ($value instanceof Constraint) {
165
                $key = (array) $value;
166
                unset(
167
                    $key["\000Yiisoft\Db\Constraint\Constraint\000name"],
168
                    $key["\u0000Yiisoft\\Db\\Constraint\\ForeignKeyConstraint\u0000foreignSchemaName"]
169
                );
170
171
                foreach ($key as $keyName => $keyValue) {
172
                    if ($keyValue instanceof AnyCaseValue) {
173
                        $key[$keyName] = $keyValue->value;
174
                    } elseif ($keyValue instanceof AnyValue) {
175
                        $key[$keyName] = '[AnyValue]';
176
                    }
177
                }
178
179
                ksort($key, SORT_STRING);
180
181
                $newArray[$caseSensitive
182
                    ? json_encode($key, JSON_THROW_ON_ERROR)
183
                    : strtolower(json_encode($key, JSON_THROW_ON_ERROR))] = $value;
184
            } else {
185
                $newArray[] = $value;
186
            }
187
        }
188
189
        ksort($newArray, SORT_STRING);
190
191
        $array = $newArray;
192
    }
193
194
    protected function normalizeConstraints(&$expected, &$actual): void
195
    {
196
        if (is_array($expected)) {
197
            foreach ($expected as $key => $value) {
198
                if (!$value instanceof Constraint || !isset($actual[$key]) || !$actual[$key] instanceof Constraint) {
199
                    continue;
200
                }
201
202
                $this->normalizeConstraintPair($value, $actual[$key]);
203
            }
204
        } elseif ($expected instanceof Constraint && $actual instanceof Constraint) {
205
            $this->normalizeConstraintPair($expected, $actual);
206
        }
207
    }
208
209
    private function normalizeConstraintPair(Constraint $expectedConstraint, Constraint $actualConstraint): void
210
    {
211
        if ($expectedConstraint::class !== $actualConstraint::class) {
212
            return;
213
        }
214
215
        foreach (array_keys((array) $expectedConstraint) as $name) {
216
            if ($expectedConstraint->getName() instanceof AnyValue) {
217
                $actualConstraint->name($expectedConstraint->getName());
218
            } elseif ($expectedConstraint->getName() instanceof AnyCaseValue) {
219
                $actualConstraintName = $actualConstraint->getName();
220
                $this->assertIsString($actualConstraintName);
221
                $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

221
                $actualConstraint->name(new AnyCaseValue(/** @scrutinizer ignore-type */ $actualConstraintName));
Loading history...
222
            }
223
        }
224
    }
225
}
226