Passed
Pull Request — master (#368)
by Sergei
02:42
created

ActiveRecordTest::createConnection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\ActiveRecord\Tests\Driver\Pgsql;
6
7
use ArrayAccess;
8
use Traversable;
9
use Yiisoft\ActiveRecord\ActiveQuery;
10
use Yiisoft\ActiveRecord\Tests\Driver\Pgsql\Stubs\Type;
11
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\ArrayAndJsonTypes;
12
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Beta;
13
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\BoolAR;
14
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer;
15
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerClosureField;
16
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\DefaultPk;
17
use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\UserAR;
18
use Yiisoft\ActiveRecord\Tests\Support\PgsqlHelper;
19
use Yiisoft\Db\Connection\ConnectionInterface;
20
use Yiisoft\Db\Expression\ArrayExpression;
21
use Yiisoft\Db\Expression\Expression;
22
use Yiisoft\Db\Expression\JsonExpression;
23
use Yiisoft\Db\Pgsql\Schema as SchemaPgsql;
24
25
final class ActiveRecordTest extends \Yiisoft\ActiveRecord\Tests\ActiveRecordTest
0 ignored issues
show
Bug introduced by
The type Yiisoft\ActiveRecord\Tests\ActiveRecordTest was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
{
27
    protected function createConnection(): ConnectionInterface
28
    {
29
        return (new PgsqlHelper())->createConnection();
30
    }
31
32
    public function testDefaultValues(): void
33
    {
34
        $this->checkFixture($this->db(), 'type');
35
36
        $arClass = new Type($this->db());
37
38
        $arClass->loadDefaultValues();
39
40
        $this->assertEquals(1, $arClass->int_col2);
41
        $this->assertEquals('something', $arClass->char_col2);
42
        $this->assertEquals(1.23, $arClass->float_col2);
43
        $this->assertEquals(33.22, $arClass->numeric_col);
44
        $this->assertEquals(true, $arClass->bool_col2);
45
        $this->assertEquals('2002-01-01 00:00:00', $arClass->time);
46
47
        $arClass = new Type($this->db());
48
        $arClass->char_col2 = 'not something';
49
50
        $arClass->loadDefaultValues();
51
        $this->assertEquals('not something', $arClass->char_col2);
52
53
        $arClass = new Type($this->db());
54
        $arClass->char_col2 = 'not something';
55
56
        $arClass->loadDefaultValues(false);
57
        $this->assertEquals('something', $arClass->char_col2);
58
    }
59
60
    public function testCastValues(): void
61
    {
62
        $this->checkFixture($this->db(), 'type');
63
64
        $arClass = new Type($this->db());
65
66
        $arClass->int_col = 123;
67
        $arClass->int_col2 = 456;
68
        $arClass->smallint_col = 42;
69
        $arClass->char_col = '1337';
70
        $arClass->char_col2 = 'test';
71
        $arClass->char_col3 = 'test123';
72
        $arClass->float_col = 3.742;
73
        $arClass->float_col2 = 42.1337;
74
        $arClass->bool_col = true;
75
        $arClass->bool_col2 = false;
76
77
        $arClass->save();
78
79
        /** @var $model Type */
80
        $aqClass = new ActiveQuery(Type::class);
81
        $query = $aqClass->onePopulate();
82
83
        $this->assertSame(123, $query->int_col);
0 ignored issues
show
Bug introduced by
Accessing int_col on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
84
        $this->assertSame(456, $query->int_col2);
0 ignored issues
show
Bug introduced by
Accessing int_col2 on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
85
        $this->assertSame(42, $query->smallint_col);
0 ignored issues
show
Bug introduced by
Accessing smallint_col on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
86
        $this->assertSame('1337', trim($query->char_col));
0 ignored issues
show
Bug introduced by
Accessing char_col on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
87
        $this->assertSame('test', $query->char_col2);
0 ignored issues
show
Bug introduced by
Accessing char_col2 on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
88
        $this->assertSame('test123', $query->char_col3);
0 ignored issues
show
Bug introduced by
Accessing char_col3 on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
89
    }
90
91
    public function testExplicitPkOnAutoIncrement(): void
92
    {
93
        $this->checkFixture($this->db(), 'customer');
94
95
        $customer = new Customer($this->db());
0 ignored issues
show
Unused Code introduced by
The call to Yiisoft\ActiveRecord\Tes...Customer::__construct() has too many arguments starting with $this->db(). ( Ignorable by Annotation )

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

95
        $customer = /** @scrutinizer ignore-call */ new Customer($this->db());

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
96
        $customer->setId(1337);
97
        $customer->setEmail('[email protected]');
98
        $customer->setName('user1337');
99
        $customer->setAddress('address1337');
100
        $this->assertTrue($customer->getIsNewRecord());
101
102
        $customer->save();
103
        $this->assertEquals(1337, $customer->getId());
104
        $this->assertFalse($customer->getIsNewRecord());
105
    }
106
107
    /**
108
     * {@see https://github.com/yiisoft/yii2/issues/15482}
109
     */
110
    public function testEagerLoadingUsingStringIdentifiers(): void
111
    {
112
        $this->checkFixture($this->db(), 'beta');
113
114
        $betaQuery = new ActiveQuery(Beta::class);
115
        $betas = $betaQuery->with('alpha')->all();
116
        $this->assertNotEmpty($betas);
117
118
        $alphaIdentifiers = [];
119
120
        /** @var Beta[] $betas */
121
        foreach ($betas as $beta) {
122
            $this->assertNotNull($beta->getAlpha());
123
            $this->assertEquals($beta->getAlphaStringIdentifier(), $beta->getAlpha()->getStringIdentifier());
124
            $alphaIdentifiers[] = $beta->getAlpha()->getStringIdentifier();
125
        }
126
127
        $this->assertEquals(['1', '01', '001', '001', '2', '2b', '2b', '02'], $alphaIdentifiers);
128
    }
129
130
    public function testBooleanAttribute(): void
131
    {
132
        $this->checkFixture($this->db(), 'customer', true);
133
134
        $customer = new Customer($this->db());
0 ignored issues
show
Unused Code introduced by
The call to Yiisoft\ActiveRecord\Tes...Customer::__construct() has too many arguments starting with $this->db(). ( Ignorable by Annotation )

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

134
        $customer = /** @scrutinizer ignore-call */ new Customer($this->db());

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
135
        $customer->setName('boolean customer');
136
        $customer->setEmail('[email protected]');
137
        $customer->setBoolStatus(false);
138
        $customer->save();
139
140
        $customer->refresh();
141
        $this->assertFalse($customer->getBoolStatus());
142
143
        $customer->setBoolStatus(true);
144
145
        $customer->save();
146
        $customer->refresh();
147
        $this->assertTrue($customer->getBoolStatus());
148
149
        $customerQuery = new ActiveQuery(Customer::class);
150
        $customers = $customerQuery->where(['bool_status' => true])->all();
151
        $this->assertCount(3, $customers);
152
153
        $customers = $customerQuery->where(['bool_status' => false])->all();
154
        $this->assertCount(1, $customers);
155
    }
156
157
    public function testBooleanValues(): void
158
    {
159
        $this->checkFixture($this->db(), 'bool_values');
160
161
        $command = $this->db()->createCommand();
162
        $command->insertBatch('bool_values', [[true], [false]], ['bool_col'])->execute();
163
        $boolARQuery = new ActiveQuery(BoolAR::class);
164
165
        $this->assertTrue($boolARQuery->where(['bool_col' => true])->onePopulate()->bool_col);
0 ignored issues
show
Bug introduced by
Accessing bool_col on the interface Yiisoft\ActiveRecord\ActiveRecordInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
166
        $this->assertFalse($boolARQuery->where(['bool_col' => false])->onePopulate()->bool_col);
167
168
        $this->assertEquals(1, $boolARQuery->where('bool_col = TRUE')->count('*'));
169
        $this->assertEquals(1, $boolARQuery->where('bool_col = FALSE')->count('*'));
170
        $this->assertEquals(2, $boolARQuery->where('bool_col IN (TRUE, FALSE)')->count('*'));
171
172
        $this->assertEquals(1, $boolARQuery->where(['bool_col' => true])->count('*'));
173
        $this->assertEquals(1, $boolARQuery->where(['bool_col' => false])->count('*'));
174
        $this->assertEquals(2, $boolARQuery->where(['bool_col' => [true, false]])->count('*'));
175
176
        $this->assertEquals(1, $boolARQuery->where('bool_col = :bool_col', ['bool_col' => true])->count('*'));
177
        $this->assertEquals(1, $boolARQuery->where('bool_col = :bool_col', ['bool_col' => false])->count('*'));
178
    }
179
180
    /**
181
     * {@see https://github.com/yiisoft/yii2/issues/4672}
182
     */
183
    public function testBooleanValues2(): void
184
    {
185
        $this->checkFixture($this->db(), 'bool_user');
186
187
        //$this->db()->setCharset('utf8');
188
        $this->db()->createCommand('DROP TABLE IF EXISTS bool_user;')->execute();
189
        $this->db()->createCommand()->createTable('bool_user', [
190
            'id' => SchemaPgsql::TYPE_PK,
191
            'username' => SchemaPgsql::TYPE_STRING . ' NOT NULL',
192
            'auth_key' => SchemaPgsql::TYPE_STRING . '(32) NOT NULL',
193
            'password_hash' => SchemaPgsql::TYPE_STRING . ' NOT NULL',
194
            'password_reset_token' => SchemaPgsql::TYPE_STRING,
195
            'email' => SchemaPgsql::TYPE_STRING . ' NOT NULL',
196
            'role' => SchemaPgsql::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',
197
            'status' => SchemaPgsql::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',
198
            'created_at' => SchemaPgsql::TYPE_INTEGER . ' NOT NULL',
199
            'updated_at' => SchemaPgsql::TYPE_INTEGER . ' NOT NULL',
200
        ])->execute();
201
        $this->db()->createCommand()->addColumn(
202
            'bool_user',
203
            'is_deleted',
204
            SchemaPgsql::TYPE_BOOLEAN . ' NOT NULL DEFAULT FALSE'
205
        )->execute();
206
207
        $user = new UserAR();
208
        $user->username = 'test';
209
        $user->auth_key = 'test';
210
        $user->password_hash = 'test';
211
        $user->email = '[email protected]';
212
        $user->created_at = time();
213
        $user->updated_at = time();
214
        $user->save();
215
216
        $userQuery = new ActiveQuery(UserAR::class);
217
        $this->assertCount(1, $userQuery->where(['is_deleted' => false])->all());
218
        $this->assertCount(0, $userQuery->where(['is_deleted' => true])->all());
219
        $this->assertCount(1, $userQuery->where(['is_deleted' => [true, false]])->all());
220
    }
221
222
    public function testBooleanDefaultValues(): void
223
    {
224
        $this->checkFixture($this->db(), 'bool_values');
225
226
        $arClass = new BoolAR();
227
228
        $this->assertNull($arClass->bool_col);
229
        $this->assertTrue($arClass->default_true);
230
        $this->assertFalse($arClass->default_false);
231
232
        $arClass->loadDefaultValues();
233
234
        $this->assertNull($arClass->bool_col);
235
        $this->assertTrue($arClass->default_true);
236
        $this->assertFalse($arClass->default_false);
237
        $this->assertTrue($arClass->save());
238
    }
239
240
    public function testPrimaryKeyAfterSave(): void
241
    {
242
        $this->checkFixture($this->db(), 'default_pk');
243
244
        $record = new DefaultPk();
245
246
        $record->type = 'type';
247
248
        $record->save();
249
250
        $this->assertEquals(5, $record->getPrimaryKey());
251
    }
252
253
    public static function arrayValuesProvider(): array
254
    {
255
        return [
256
            'simple arrays values' => [[
257
                'intarray_col' => [
258
                    new ArrayExpression([1,-2,null,'42'], 'int4', 1),
259
                    new ArrayExpression([1,-2,null,42], 'int4', 1),
260
                ],
261
                'textarray2_col' => [
262
                    new ArrayExpression([['text'], [null], [1]], 'text', 2),
263
                    new ArrayExpression([['text'], [null], ['1']], 'text', 2),
264
                ],
265
                'json_col' => [['a' => 1, 'b' => null, 'c' => [1,3,5]]],
266
                'jsonb_col' => [[null, 'a', 'b', '\"', '{"af"}']],
267
                'jsonarray_col' => [new ArrayExpression([[',', 'null', true, 'false', 'f']], 'json')],
268
            ]],
269
            'null arrays values' => [[
270
                'intarray_col' => [
271
                    null,
272
                ],
273
                'textarray2_col' => [
274
                    [null, null],
275
                    new ArrayExpression([null, null], 'text', 2),
276
                ],
277
                'json_col' => [
278
                    null,
279
                ],
280
                'jsonarray_col' => [
281
                    null,
282
                ],
283
            ]],
284
            'empty arrays values' => [[
285
                'textarray2_col' => [
286
                    [[], []],
287
                    new ArrayExpression([], 'text', 2),
288
                ],
289
            ]],
290
            'nested objects' => [[
291
                'intarray_col' => [
292
                    new ArrayExpression(new ArrayExpression([1,2,3]), 'int', 1),
293
                    new ArrayExpression([1,2,3], 'int4', 1),
294
                ],
295
                'textarray2_col' => [
296
                    new ArrayExpression([new ArrayExpression(['text']), [null], [1]], 'text', 2),
297
                    new ArrayExpression([['text'], [null], ['1']], 'text', 2),
298
                ],
299
                'json_col' => [
300
                    new JsonExpression(new JsonExpression(new JsonExpression(['a' => 1, 'b' => null, 'c' => new JsonExpression([1,3,5])]))),
301
                    ['a' => 1, 'b' => null, 'c' => [1,3,5]],
302
                ],
303
                'jsonb_col' => [
304
                    new JsonExpression(new ArrayExpression([1,2,3])),
305
                    [1,2,3],
306
                ],
307
                'jsonarray_col' => [
308
                    new ArrayExpression([new JsonExpression(['1', 2]), [3,4,5]], 'json'),
309
                    new ArrayExpression([['1', 2], [3,4,5]], 'json'),
310
                ],
311
            ]],
312
            'arrays packed in classes' => [[
313
                'intarray_col' => [
314
                    new ArrayExpression([1,-2,null,'42'], 'int', 1),
315
                    new ArrayExpression([1,-2,null,42], 'int4', 1),
316
                ],
317
                'textarray2_col' => [
318
                    new ArrayExpression([['text'], [null], [1]], 'text', 2),
319
                    new ArrayExpression([['text'], [null], ['1']], 'text', 2),
320
                ],
321
                'json_col' => [
322
                    new JsonExpression(['a' => 1, 'b' => null, 'c' => [1,3,5]]),
323
                    ['a' => 1, 'b' => null, 'c' => [1,3,5]],
324
                ],
325
                'jsonb_col' => [
326
                    new JsonExpression([null, 'a', 'b', '\"', '{"af"}']),
327
                    [null, 'a', 'b', '\"', '{"af"}'],
328
                ],
329
                'jsonarray_col' => [
330
                    new Expression("array['[\",\",\"null\",true,\"false\",\"f\"]'::json]::json[]"),
331
                    new ArrayExpression([[',', 'null', true, 'false', 'f']], 'json'),
332
                ],
333
            ]],
334
            'scalars' => [[
335
                'json_col' => [
336
                    '5.8',
337
                ],
338
                'jsonb_col' => [
339
                    M_PI,
340
                ],
341
            ]],
342
        ];
343
    }
344
345
    /**
346
     * @dataProvider arrayValuesProvider
347
     */
348
    public function testArrayValues($attributes): void
349
    {
350
        $this->checkFixture($this->db(), 'array_and_json_types', true);
351
352
        $type = new ArrayAndJsonTypes();
353
354
        foreach ($attributes as $attribute => $expected) {
355
            $type->setAttribute($attribute, $expected[0]);
356
        }
357
358
        $type->save();
359
360
        $typeQuery = new ActiveQuery($type::class);
361
362
        $type = $typeQuery->onePopulate();
363
364
        foreach ($attributes as $attribute => $expected) {
365
            $expected = $expected[1] ?? $expected[0];
366
            $value = $type->getAttribute($attribute);
367
368
            if ($expected instanceof ArrayExpression) {
369
                $expected = $expected->getValue();
370
            }
371
372
            $this->assertEquals($expected, $value, 'In column ' . $attribute);
373
374
            if ($value instanceof ArrayExpression) {
375
                $this->assertInstanceOf(ArrayAccess::class, $value);
376
                $this->assertInstanceOf(Traversable::class, $value);
377
                /** testing arrayaccess */
378
                foreach ($type->getAttribute($attribute) as $key => $v) {
379
                    $this->assertSame($expected[$key], $value[$key]);
380
                }
381
            }
382
        }
383
384
        /** Testing update */
385
        foreach ($attributes as $attribute => $expected) {
386
            $type->markAttributeDirty($attribute);
0 ignored issues
show
Bug introduced by
The method markAttributeDirty() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\ActiveRecord\ActiveRecordInterface. ( Ignorable by Annotation )

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

386
            $type->/** @scrutinizer ignore-call */ 
387
                   markAttributeDirty($attribute);
Loading history...
387
        }
388
389
        $this->assertSame(1, $type->update(), 'The record got updated');
390
    }
391
392
    public function testToArray(): void
393
    {
394
        $this->checkFixture($this->db(), 'customer', true);
395
396
        $customerQuery = new ActiveQuery(Customer::class);
397
        $customer = $customerQuery->findOne(1);
398
399
        $this->assertSame(
400
            [
401
                'id' => 1,
402
                'email' => '[email protected]',
403
                'name' => 'user1',
404
                'address' => 'address1',
405
                'status' => 1,
406
                'bool_status' => true,
407
                'profile_id' => 1,
408
            ],
409
            $customer->toArray(),
0 ignored issues
show
Bug introduced by
The method toArray() does not exist on Yiisoft\ActiveRecord\ActiveRecordInterface. It seems like you code against a sub-type of Yiisoft\ActiveRecord\ActiveRecordInterface such as Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord or Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord. ( Ignorable by Annotation )

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

409
            $customer->/** @scrutinizer ignore-call */ 
410
                       toArray(),
Loading history...
410
        );
411
    }
412
413
    public function testToArrayWithClosure(): void
414
    {
415
        $this->checkFixture($this->db(), 'customer', true);
416
417
        $customerQuery = new ActiveQuery(CustomerClosureField::class);
418
        $customer = $customerQuery->findOne(1);
419
420
        $this->assertSame(
421
            [
422
                'id' => 1,
423
                'email' => '[email protected]',
424
                'name' => 'user1',
425
                'address' => 'address1',
426
                'status' => 'active',
427
                'bool_status' => true,
428
                'profile_id' => 1,
429
            ],
430
            $customer->toArray(),
431
        );
432
    }
433
}
434