Passed
Push — main ( f122aa...df8d6f )
by Anatoly
04:27 queued 15s
created

php$120 ➔ testHydrateRamseyUuidPropertyWithInvalidValue()   A

Complexity

Conditions 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 11
rs 9.9
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Sunrise\Hydrator\Tests;
6
7
use DateTimeImmutable;
8
use DateTimeZone;
9
use Generator;
10
use PHPUnit\Framework\TestCase;
11
use ReflectionClass;
12
use ReflectionFunction;
13
use ReflectionIntersectionType;
0 ignored issues
show
Bug introduced by
The type ReflectionIntersectionType 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...
14
use ReflectionMethod;
15
use ReflectionNamedType;
16
use ReflectionParameter;
17
use ReflectionUnionType;
18
use Sunrise\Hydrator\Annotation\Alias;
19
use Sunrise\Hydrator\Annotation\Ignore;
20
use Sunrise\Hydrator\Annotation\Subtype;
21
use Sunrise\Hydrator\Dictionary\BuiltinType;
22
use Sunrise\Hydrator\Dictionary\ContextKey;
23
use Sunrise\Hydrator\Dictionary\ErrorCode;
24
use Sunrise\Hydrator\Exception\InvalidDataException;
25
use Sunrise\Hydrator\Exception\InvalidObjectException;
26
use Sunrise\Hydrator\Hydrator;
27
use Sunrise\Hydrator\HydratorInterface;
28
use Sunrise\Hydrator\Type;
29
use Sunrise\Hydrator\TypeConverter\TimestampTypeConverter;
30
use Sunrise\Hydrator\TypeConverterInterface;
31
32
use function date;
33
use function get_class;
34
use function join;
35
use function sprintf;
36
use function version_compare;
37
38
use const PHP_VERSION;
39
use const PHP_VERSION_ID;
40
41
class HydratorTest extends TestCase
42
{
43
    private ?int $invalidValueExceptionCount = null;
44
    private array $invalidValueExceptionMessage = [];
45
    private array $invalidValueExceptionPropertyPath = [];
46
    private array $invalidValueExceptionErrorCode = [];
47
48
    public function testIssue25(): void
49
    {
50
        $this->assertInvalidValueExceptionCount(1);
51
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
52
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
53
        $this->assertInvalidValueExceptionPropertyPath(0, 'foo');
54
        $this->createHydrator()->hydrate(Fixture\Issue25::class, []);
55
    }
56
57
    public function testStdClassWithArrayProperty(): void
58
    {
59
        $object = new class {
60
            public array $value;
61
        };
62
63
        $this->assertInvalidValueExceptionCount(0);
64
        $this->createHydrator()->hydrate($object, ['value' => (object) ['foo' => 'bar']]);
65
        $this->assertSame(['foo' => 'bar'], $object->value);
66
    }
67
68
    public function testStdClassWithArrayAccessProperty(): void
69
    {
70
        $object = new class {
71
            public Fixture\Collection $value;
72
        };
73
74
        $this->assertInvalidValueExceptionCount(0);
75
        $this->createHydrator()->hydrate($object, ['value' => (object) ['foo' => 'bar']]);
76
        $this->assertSame(['foo' => 'bar'], $object->value->elements);
77
    }
78
79
    public function testStdClassWithAssociationProperty(): void
80
    {
81
        $object = new class {
82
            public Fixture\StringAssociation $value;
83
        };
84
85
        $this->assertInvalidValueExceptionCount(0);
86
        $this->createHydrator()->hydrate($object, ['value' => (object) ['value' => 'foo']]);
87
        $this->assertSame('foo', $object->value->value);
88
    }
89
90
    public function testCreateHydratorWithTypeConverters(): void
91
    {
92
        $object = new class {
93
            public \Closure $foo;
94
        };
95
96
        $typeConverter = $this->createMock(TypeConverterInterface::class);
97
98
        $typeConverter->method('castValue')->willReturnCallback(
99
            static function ($value, Type $type, array $path, array $context): Generator {
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

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

99
            static function ($value, Type $type, array $path, /** @scrutinizer ignore-unused */ array $context): Generator {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $path is not used and could be removed. ( Ignorable by Annotation )

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

99
            static function ($value, Type $type, /** @scrutinizer ignore-unused */ array $path, array $context): Generator {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
100
                if ($type->getName() === \Closure::class) {
101
                    yield static function () use ($value) {
102
                        return $value;
103
                    };
104
                }
105
            }
106
        );
107
108
        $this->createHydrator([], [$typeConverter])->hydrate($object, [
109
            'foo' => 'bar',
110
        ]);
111
112
        $this->assertSame('bar', ($object->foo)());
113
    }
114
115
    /**
116
     * @group boolean
117
     * @dataProvider strictBooleanDataProvider
118
     * @dataProvider nonStrictBooleanDataProvider
119
     */
120
    public function testHydrateBooleanProperty(array $data, bool $expected): void
121
    {
122
        $object = new class {
123
            public bool $value;
124
        };
125
126
        $this->assertInvalidValueExceptionCount(0);
127
        $this->createHydrator()->hydrate($object, $data);
128
        $this->assertSame($expected, $object->value);
129
    }
130
131
    /**
132
     * @group boolean
133
     * @dataProvider strictBooleanDataProvider
134
     * @dataProvider nonStrictBooleanDataProvider
135
     * @dataProvider strictNullDataProvider
136
     * @dataProvider nonStrictNullDataProvider
137
     */
138
    public function testHydrateNullableBooleanProperty(array $data, ?bool $expected): void
139
    {
140
        $object = new class {
141
            public ?bool $value;
142
        };
143
144
        $this->assertInvalidValueExceptionCount(0);
145
        $this->createHydrator()->hydrate($object, $data);
146
        $this->assertSame($expected, $object->value);
147
    }
148
149
    /**
150
     * @group boolean
151
     * @dataProvider strictBooleanDataProvider
152
     * @dataProvider nonStrictBooleanDataProvider
153
     * @dataProvider emptyArrayProvider
154
     */
155
    public function testHydrateOptionalBooleanProperty(array $data, bool $expected = true): void
156
    {
157
        $object = new class {
158
            public bool $value = true;
159
        };
160
161
        $this->assertInvalidValueExceptionCount(0);
162
        $this->createHydrator()->hydrate($object, $data);
163
        $this->assertSame($expected, $object->value);
164
    }
165
166
    /**
167
     * @group boolean
168
     * @dataProvider strictNullDataProvider
169
     * @dataProvider nonStrictNullDataProvider
170
     */
171
    public function testHydrateBooleanPropertyWithEmptyValue(array $data): void
172
    {
173
        $object = new class {
174
            public bool $value;
175
        };
176
177
        $this->assertInvalidValueExceptionCount(1);
178
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
179
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
180
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
181
        $this->createHydrator()->hydrate($object, $data);
182
    }
183
184
    /**
185
     * @group boolean
186
     * @dataProvider notBooleanDataProvider
187
     */
188
    public function testHydrateBooleanPropertyWithInvalidValue(array $data): void
189
    {
190
        $object = new class {
191
            public bool $value;
192
        };
193
194
        $this->assertInvalidValueExceptionCount(1);
195
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
196
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
197
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
198
        $this->createHydrator()->hydrate($object, $data);
199
    }
200
201
    /**
202
     * @group boolean
203
     */
204
    public function testHydrateBooleanPropertyWithoutValue(): void
205
    {
206
        $object = new class {
207
            public bool $value;
208
        };
209
210
        $this->assertInvalidValueExceptionCount(1);
211
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
212
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
213
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
214
        $this->createHydrator()->hydrate($object, []);
215
    }
216
217
    /**
218
     * @group integer
219
     * @dataProvider strictIntegerDataProvider
220
     * @dataProvider nonStrictIntegerDataProvider
221
     */
222
    public function testHydrateIntegerProperty(array $data, int $expected): void
223
    {
224
        $object = new class {
225
            public int $value;
226
        };
227
228
        $this->assertInvalidValueExceptionCount(0);
229
        $this->createHydrator()->hydrate($object, $data);
230
        $this->assertSame($expected, $object->value);
231
    }
232
233
    /**
234
     * @group integer
235
     * @dataProvider strictIntegerDataProvider
236
     * @dataProvider nonStrictIntegerDataProvider
237
     * @dataProvider strictNullDataProvider
238
     * @dataProvider nonStrictNullDataProvider
239
     */
240
    public function testHydrateNullableIntegerProperty(array $data, ?int $expected): void
241
    {
242
        $object = new class {
243
            public ?int $value;
244
        };
245
246
        $this->assertInvalidValueExceptionCount(0);
247
        $this->createHydrator()->hydrate($object, $data);
248
        $this->assertSame($expected, $object->value);
249
    }
250
251
    /**
252
     * @group integer
253
     * @dataProvider strictIntegerDataProvider
254
     * @dataProvider nonStrictIntegerDataProvider
255
     * @dataProvider emptyArrayProvider
256
     */
257
    public function testHydrateOptionalIntegerProperty(array $data, int $expected = 42): void
258
    {
259
        $object = new class {
260
            public int $value = 42;
261
        };
262
263
        $this->assertInvalidValueExceptionCount(0);
264
        $this->createHydrator()->hydrate($object, $data);
265
        $this->assertSame($expected, $object->value);
266
    }
267
268
    /**
269
     * @group integer
270
     * @dataProvider strictNullDataProvider
271
     * @dataProvider nonStrictNullDataProvider
272
     */
273
    public function testHydrateIntegerPropertyWithEmptyValue(array $data): void
274
    {
275
        $object = new class {
276
            public int $value;
277
        };
278
279
        $this->assertInvalidValueExceptionCount(1);
280
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
281
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
282
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
283
        $this->createHydrator()->hydrate($object, $data);
284
    }
285
286
    /**
287
     * @group integer
288
     * @dataProvider notIntegerDataProvider
289
     */
290
    public function testHydrateIntegerPropertyWithInvalidValue(array $data): void
291
    {
292
        $object = new class {
293
            public int $value;
294
        };
295
296
        $this->assertInvalidValueExceptionCount(1);
297
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type integer.');
298
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_INTEGER);
299
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
300
        $this->createHydrator()->hydrate($object, $data);
301
    }
302
303
    /**
304
     * @group integer
305
     */
306
    public function testHydrateIntegerPropertyWithoutValue(): void
307
    {
308
        $object = new class {
309
            public int $value;
310
        };
311
312
        $this->assertInvalidValueExceptionCount(1);
313
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
314
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
315
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
316
        $this->createHydrator()->hydrate($object, []);
317
    }
318
319
    /**
320
     * @group number
321
     * @dataProvider strictNumberDataProvider
322
     * @dataProvider nonStrictNumberDataProvider
323
     */
324
    public function testHydrateNumericProperty(array $data, float $expected): void
325
    {
326
        $object = new class {
327
            public float $value;
328
        };
329
330
        $this->assertInvalidValueExceptionCount(0);
331
        $this->createHydrator()->hydrate($object, $data);
332
        $this->assertSame($expected, $object->value);
333
    }
334
335
    /**
336
     * @group number
337
     * @dataProvider strictNumberDataProvider
338
     * @dataProvider nonStrictNumberDataProvider
339
     * @dataProvider strictNullDataProvider
340
     * @dataProvider nonStrictNullDataProvider
341
     */
342
    public function testHydrateNullableNumericProperty(array $data, ?float $expected): void
343
    {
344
        $object = new class {
345
            public ?float $value;
346
        };
347
348
        $this->assertInvalidValueExceptionCount(0);
349
        $this->createHydrator()->hydrate($object, $data);
350
        $this->assertSame($expected, $object->value);
351
    }
352
353
    /**
354
     * @group number
355
     * @dataProvider strictNumberDataProvider
356
     * @dataProvider nonStrictNumberDataProvider
357
     * @dataProvider emptyArrayProvider
358
     */
359
    public function testHydrateOptionalNumericProperty(array $data, float $expected = 3.14159): void
360
    {
361
        $object = new class {
362
            public float $value = 3.14159;
363
        };
364
365
        $this->assertInvalidValueExceptionCount(0);
366
        $this->createHydrator()->hydrate($object, $data);
367
        $this->assertSame($expected, $object->value);
368
    }
369
370
    /**
371
     * @group number
372
     * @dataProvider strictNullDataProvider
373
     * @dataProvider nonStrictNullDataProvider
374
     */
375
    public function testHydrateNumericPropertyWithEmptyValue(array $data): void
376
    {
377
        $object = new class {
378
            public float $value;
379
        };
380
381
        $this->assertInvalidValueExceptionCount(1);
382
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
383
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
384
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
385
        $this->createHydrator()->hydrate($object, $data);
386
    }
387
388
    /**
389
     * @group number
390
     * @dataProvider notNumberDataProvider
391
     */
392
    public function testHydrateNumericPropertyWithInvalidValue(array $data): void
393
    {
394
        $object = new class {
395
            public float $value;
396
        };
397
398
        $this->assertInvalidValueExceptionCount(1);
399
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type number.');
400
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_NUMBER);
401
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
402
        $this->createHydrator()->hydrate($object, $data);
403
    }
404
405
    /**
406
     * @group number
407
     */
408
    public function testHydrateNumericPropertyWithoutValue(): void
409
    {
410
        $object = new class {
411
            public float $value;
412
        };
413
414
        $this->assertInvalidValueExceptionCount(1);
415
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
416
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
417
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
418
        $this->createHydrator()->hydrate($object, []);
419
    }
420
421
    /**
422
     * @group string
423
     * @dataProvider strictStringDataProvider
424
     * @dataProvider nonStrictStringDataProvider
425
     */
426
    public function testHydrateStringProperty(array $data, string $expected): void
427
    {
428
        $object = new class {
429
            public string $value;
430
        };
431
432
        $this->assertInvalidValueExceptionCount(0);
433
        $this->createHydrator()->hydrate($object, $data);
434
        $this->assertSame($expected, $object->value);
435
    }
436
437
    /**
438
     * @group string
439
     * @dataProvider strictStringDataProvider
440
     * @dataProvider nonStrictStringDataProvider
441
     * @dataProvider strictNullDataProvider
442
     */
443
    public function testHydrateNullableStringProperty(array $data, ?string $expected): void
444
    {
445
        $object = new class {
446
            public ?string $value;
447
        };
448
449
        $this->assertInvalidValueExceptionCount(0);
450
        $this->createHydrator()->hydrate($object, $data);
451
        $this->assertSame($expected, $object->value);
452
    }
453
454
    /**
455
     * @group string
456
     * @dataProvider strictStringDataProvider
457
     * @dataProvider nonStrictStringDataProvider
458
     * @dataProvider emptyArrayProvider
459
     */
460
    public function testHydrateOptionalStringProperty(array $data, string $expected = 'default'): void
461
    {
462
        $object = new class {
463
            public string $value = 'default';
464
        };
465
466
        $this->assertInvalidValueExceptionCount(0);
467
        $this->createHydrator()->hydrate($object, $data);
468
        $this->assertSame($expected, $object->value);
469
    }
470
471
    /**
472
     * @group string
473
     * @dataProvider strictNullDataProvider
474
     */
475
    public function testHydrateStringPropertyWithNull(array $data): void
476
    {
477
        $object = new class {
478
            public string $value;
479
        };
480
481
        $this->assertInvalidValueExceptionCount(1);
482
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
483
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
484
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
485
        $this->createHydrator()->hydrate($object, $data);
486
    }
487
488
    /**
489
     * @group string
490
     * @dataProvider nonStrictNotStringDataProvider
491
     */
492
    public function testHydrateStringPropertyWithInvalidValue(array $data): void
493
    {
494
        $object = new class {
495
            public string $value;
496
        };
497
498
        $this->assertInvalidValueExceptionCount(1);
499
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type string.');
500
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_STRING);
501
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
502
        $this->createHydrator()->hydrate($object, $data);
503
    }
504
505
    /**
506
     * @group string
507
     */
508
    public function testHydrateStringPropertyWithoutValue(): void
509
    {
510
        $object = new class {
511
            public string $value;
512
        };
513
514
        $this->assertInvalidValueExceptionCount(1);
515
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
516
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
517
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
518
        $this->createHydrator()->hydrate($object, []);
519
    }
520
521
    /**
522
     * @group enum
523
     * @group integer-enum
524
     * @dataProvider integerEnumDataProvider
525
     */
526
    public function testHydrateIntegerEnumProperty(array $data, $expected): void
527
    {
528
        $this->phpRequired('8.1');
529
530
        $object = new class {
531
            public Fixture\IntegerEnum $value;
532
        };
533
534
        $this->assertInvalidValueExceptionCount(0);
535
        $this->createHydrator()->hydrate($object, $data);
536
        $this->assertSame($expected, $object->value);
537
    }
538
539
    /**
540
     * @group enum
541
     * @group integer-enum
542
     * @dataProvider integerEnumDataProvider
543
     * @dataProvider strictNullDataProvider
544
     * @dataProvider nonStrictNullDataProvider
545
     */
546
    public function testHydrateNullableIntegerEnumProperty(array $data, $expected): void
547
    {
548
        $this->phpRequired('8.1');
549
550
        $object = new class {
551
            public ?Fixture\IntegerEnum $value;
552
        };
553
554
        $this->assertInvalidValueExceptionCount(0);
555
        $this->createHydrator()->hydrate($object, $data);
556
        $this->assertSame($expected, $object->value);
557
    }
558
559
    /**
560
     * @group enum
561
     * @group integer-enum
562
     * @dataProvider integerEnumDataProvider
563
     * @dataProvider emptyArrayProvider
564
     */
565
    public function testHydrateOptionalIntegerEnumProperty(array $data, $expected = null): void
566
    {
567
        $this->phpRequired('8.1');
568
569
        $object = new class {
570
            public ?Fixture\IntegerEnum $value = null;
571
        };
572
573
        $this->assertInvalidValueExceptionCount(0);
574
        $this->createHydrator()->hydrate($object, $data);
575
        $this->assertSame($expected, $object->value);
576
    }
577
578
    /**
579
     * @group enum
580
     * @group integer-enum
581
     * @dataProvider strictNullDataProvider
582
     * @dataProvider nonStrictNullDataProvider
583
     */
584
    public function testHydrateIntegerEnumPropertyWithEmptyValue(array $data): void
585
    {
586
        $this->phpRequired('8.1');
587
588
        $object = new class {
589
            public Fixture\IntegerEnum $value;
590
        };
591
592
        $this->assertInvalidValueExceptionCount(1);
593
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
594
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
595
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
596
        $this->createHydrator()->hydrate($object, $data);
597
    }
598
599
    /**
600
     * @group enum
601
     * @group integer-enum
602
     * @dataProvider notIntegerDataProvider
603
     */
604
    public function testHydrateIntegerEnumPropertyWithInvalidValue(array $data): void
605
    {
606
        $this->phpRequired('8.1');
607
608
        $object = new class {
609
            public Fixture\IntegerEnum $value;
610
        };
611
612
        $this->assertInvalidValueExceptionCount(1);
613
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type integer.');
614
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_INTEGER);
615
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
616
        $this->createHydrator()->hydrate($object, $data);
617
    }
618
619
    /**
620
     * @group enum
621
     * @group integer-enum
622
     */
623
    public function testHydrateIntegerEnumPropertyWithUnknownValue(): void
624
    {
625
        $this->phpRequired('8.1');
626
627
        $object = new class {
628
            public Fixture\IntegerEnum $value;
629
        };
630
631
        $this->assertInvalidValueExceptionCount(1);
632
        // phpcs:ignore Generic.Files.LineLength
633
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid choice; expected values: ' . join(', ', Fixture\IntegerEnum::values()) . '.');
634
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_CHOICE);
635
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
636
        $this->createHydrator()->hydrate($object, ['value' => 42]);
637
    }
638
639
    /**
640
     * @group enum
641
     * @group integer-enum
642
     */
643
    public function testHydrateIntegerEnumPropertyWithoutValue(): void
644
    {
645
        $this->phpRequired('8.1');
646
647
        $object = new class {
648
            public Fixture\IntegerEnum $value;
649
        };
650
651
        $this->assertInvalidValueExceptionCount(1);
652
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
653
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
654
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
655
        $this->createHydrator()->hydrate($object, []);
656
    }
657
658
    /**
659
     * @group enum
660
     * @group string-enum
661
     * @dataProvider stringEnumDataProvider
662
     */
663
    public function testHydrateStringEnumProperty(array $data, $expected): void
664
    {
665
        $this->phpRequired('8.1');
666
667
        $object = new class {
668
            public Fixture\StringEnum $value;
669
        };
670
671
        $this->assertInvalidValueExceptionCount(0);
672
        $this->createHydrator()->hydrate($object, $data);
673
        $this->assertSame($expected, $object->value);
674
    }
675
676
    /**
677
     * @group enum
678
     * @group string-enum
679
     * @dataProvider stringEnumDataProvider
680
     * @dataProvider strictNullDataProvider
681
     * @dataProvider nonStrictNullDataProvider
682
     */
683
    public function testHydrateNullableStringEnumProperty(array $data, $expected): void
684
    {
685
        $this->phpRequired('8.1');
686
687
        $object = new class {
688
            public ?Fixture\StringEnum $value;
689
        };
690
691
        $this->assertInvalidValueExceptionCount(0);
692
        $this->createHydrator()->hydrate($object, $data);
693
        $this->assertSame($expected, $object->value);
694
    }
695
696
    /**
697
     * @group enum
698
     * @group string-enum
699
     * @dataProvider stringEnumDataProvider
700
     * @dataProvider emptyArrayProvider
701
     */
702
    public function testHydrateOptionalStringEnumProperty(array $data, $expected = null): void
703
    {
704
        $this->phpRequired('8.1');
705
706
        $object = new class {
707
            public ?Fixture\StringEnum $value = null;
708
        };
709
710
        $this->assertInvalidValueExceptionCount(0);
711
        $this->createHydrator()->hydrate($object, $data);
712
        $this->assertSame($expected, $object->value);
713
    }
714
715
    /**
716
     * @group enum
717
     * @group string-enum
718
     * @dataProvider strictNullDataProvider
719
     * @dataProvider nonStrictNullDataProvider
720
     */
721
    public function testHydrateStringEnumPropertyWithEmptyValue(array $data): void
722
    {
723
        $this->phpRequired('8.1');
724
725
        $object = new class {
726
            public Fixture\StringEnum $value;
727
        };
728
729
        $this->assertInvalidValueExceptionCount(1);
730
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
731
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
732
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
733
        $this->createHydrator()->hydrate($object, $data);
734
    }
735
736
    /**
737
     * @group enum
738
     * @group string-enum
739
     * @dataProvider strictNotStringDataProvider
740
     */
741
    public function testHydrateStringEnumPropertyWithInvalidValue(array $data): void
742
    {
743
        $this->phpRequired('8.1');
744
745
        $object = new class {
746
            public Fixture\StringEnum $value;
747
        };
748
749
        $this->assertInvalidValueExceptionCount(1);
750
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type string.');
751
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_STRING);
752
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
753
        $this->createHydrator()->hydrate($object, $data);
754
    }
755
756
    /**
757
     * @group enum
758
     * @group string-enum
759
     */
760
    public function testHydrateStringEnumPropertyWithUnknownValue(): void
761
    {
762
        $this->phpRequired('8.1');
763
764
        $object = new class {
765
            public Fixture\StringEnum $value;
766
        };
767
768
        $this->assertInvalidValueExceptionCount(1);
769
        // phpcs:ignore Generic.Files.LineLength
770
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid choice; expected values: ' . join(', ', Fixture\StringEnum::values()) . '.');
771
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_CHOICE);
772
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
773
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
774
    }
775
776
    /**
777
     * @group enum
778
     * @group string-enum
779
     */
780
    public function testHydrateStringEnumPropertyWithoutValue(): void
781
    {
782
        $this->phpRequired('8.1');
783
784
        $object = new class {
785
            public Fixture\StringEnum $value;
786
        };
787
788
        $this->assertInvalidValueExceptionCount(1);
789
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
790
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
791
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
792
        $this->createHydrator()->hydrate($object, []);
793
    }
794
795
    /**
796
     * @group array
797
     * @dataProvider arrayDataProvider
798
     */
799
    public function testHydrateArrayProperty(array $data, array $expected): void
800
    {
801
        $object = new class {
802
            public array $value;
803
        };
804
805
        $this->assertInvalidValueExceptionCount(0);
806
        $this->createHydrator()->hydrate($object, $data);
807
        $this->assertSame($expected, $object->value);
808
    }
809
810
    /**
811
     * @group array
812
     * @dataProvider arrayDataProvider
813
     * @dataProvider strictNullDataProvider
814
     */
815
    public function testHydrateNullableArrayProperty(array $data, ?array $expected): void
816
    {
817
        $object = new class {
818
            public ?array $value;
819
        };
820
821
        $this->assertInvalidValueExceptionCount(0);
822
        $this->createHydrator()->hydrate($object, $data);
823
        $this->assertSame($expected, $object->value);
824
    }
825
826
    /**
827
     * @group array
828
     * @dataProvider arrayDataProvider
829
     * @dataProvider emptyArrayProvider
830
     */
831
    public function testHydrateOptionalArrayProperty(array $data, array $expected = []): void
832
    {
833
        $object = new class {
834
            public array $value = [];
835
        };
836
837
        $this->assertInvalidValueExceptionCount(0);
838
        $this->createHydrator()->hydrate($object, $data);
839
        $this->assertSame($expected, $object->value);
840
    }
841
842
    /**
843
     * @group array
844
     * @dataProvider strictNullDataProvider
845
     */
846
    public function testHydrateArrayPropertyWithNull(array $data): void
847
    {
848
        $object = new class {
849
            public array $value;
850
        };
851
852
        $this->assertInvalidValueExceptionCount(1);
853
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
854
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
855
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
856
        $this->createHydrator()->hydrate($object, $data);
857
    }
858
859
    /**
860
     * @group array
861
     * @dataProvider notArrayDataProvider
862
     */
863
    public function testHydrateArrayPropertyWithInvalidValue(array $data): void
864
    {
865
        $object = new class {
866
            public array $value;
867
        };
868
869
        $this->assertInvalidValueExceptionCount(1);
870
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
871
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
872
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
873
        $this->createHydrator()->hydrate($object, $data);
874
    }
875
876
    /**
877
     * @group array
878
     */
879
    public function testHydrateArrayPropertyWithoutValue(): void
880
    {
881
        $object = new class {
882
            public array $value;
883
        };
884
885
        $this->assertInvalidValueExceptionCount(1);
886
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
887
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
888
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
889
        $this->createHydrator()->hydrate($object, []);
890
    }
891
892
    /**
893
     * @group array
894
     * @group boolean-array
895
     * @dataProvider strictBooleanProvider
896
     * @dataProvider nonStrictBooleanProvider
897
     */
898
    public function testHydrateBooleanArrayProperty($element, bool $expected): void
899
    {
900
        $object = new class {
901
            /** @Subtype(BuiltinType::BOOL) */
902
            #[Subtype(BuiltinType::BOOL)]
903
            public array $value;
904
        };
905
906
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
907
        $this->assertSame([$expected], $object->value);
908
    }
909
910
    /**
911
     * @group array
912
     * @group boolean-array
913
     * @dataProvider strictBooleanProvider
914
     * @dataProvider nonStrictBooleanProvider
915
     * @dataProvider strictNullProvider
916
     * @dataProvider nonStrictNullProvider
917
     */
918
    public function testHydrateNullableBooleanArrayProperty($element, ?bool $expected): void
919
    {
920
        $object = new class {
921
            /** @Subtype(BuiltinType::BOOL, allowsNull=true) */
922
            #[Subtype(BuiltinType::BOOL, allowsNull: true)]
923
            public array $value;
924
        };
925
926
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
927
        $this->assertSame([$expected], $object->value);
928
    }
929
930
    /**
931
     * @group array
932
     * @group boolean-array
933
     * @dataProvider strictNullProvider
934
     * @dataProvider nonStrictNullProvider
935
     */
936
    public function testHydrateBooleanArrayPropertyWithEmptyElement($element): void
937
    {
938
        $object = new class {
939
            /** @Subtype(BuiltinType::BOOL) */
940
            #[Subtype(BuiltinType::BOOL)]
941
            public array $value;
942
        };
943
944
        $this->assertInvalidValueExceptionCount(1);
945
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
946
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
947
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
948
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
949
    }
950
951
    /**
952
     * @group array
953
     * @group boolean-array
954
     * @dataProvider notBooleanProvider
955
     */
956
    public function testHydrateBooleanArrayPropertyWithInvalidElement($element): void
957
    {
958
        $object = new class {
959
            /** @Subtype(BuiltinType::BOOL) */
960
            #[Subtype(BuiltinType::BOOL)]
961
            public array $value;
962
        };
963
964
        $this->assertInvalidValueExceptionCount(1);
965
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
966
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
967
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
968
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
969
    }
970
971
    /**
972
     * @group array
973
     * @group boolean-array
974
     * @dataProvider strictBooleanProvider
975
     * @dataProvider nonStrictBooleanProvider
976
     */
977
    public function testHydrateLimitedBooleanArrayProperty($element): void
978
    {
979
        $object = new class {
980
            /** @Subtype(BuiltinType::BOOL, limit=1) */
981
            #[Subtype(BuiltinType::BOOL, limit: 1)]
982
            public array $value;
983
        };
984
985
        $this->assertInvalidValueExceptionCount(1);
986
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
987
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
988
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
989
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
990
    }
991
992
    /**
993
     * @group array
994
     * @group boolean-association-array
995
     * @dataProvider strictBooleanDataProvider
996
     * @dataProvider nonStrictBooleanDataProvider
997
     */
998
    public function testHydrateBooleanAssociationArrayProperty(array $data, bool $expected): void
999
    {
1000
        $object = new class {
1001
            /**
1002
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1003
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1004
             */
1005
            #[Subtype(Fixture\BooleanAssociation::class)]
1006
            public array $value;
1007
        };
1008
1009
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1010
        $this->assertSame($expected, $object->value[0]->value);
1011
    }
1012
1013
    /**
1014
     * @group array
1015
     * @group boolean-association-array
1016
     * @dataProvider strictNullDataProvider
1017
     */
1018
    public function testHydrateBooleanAssociationArrayPropertyWithNull(array $data): void
1019
    {
1020
        $object = new class {
1021
            /**
1022
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1023
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1024
             */
1025
            #[Subtype(Fixture\BooleanAssociation::class)]
1026
            public array $value;
1027
        };
1028
1029
        $this->assertInvalidValueExceptionCount(1);
1030
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1031
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1032
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1033
        $this->createHydrator()->hydrate($object, $data);
1034
    }
1035
1036
    /**
1037
     * @group array
1038
     * @group boolean-association-array
1039
     * @dataProvider notArrayDataProvider
1040
     */
1041
    public function testHydrateBooleanAssociationArrayPropertyWithInvalidValue(array $data): void
1042
    {
1043
        $object = new class {
1044
            /**
1045
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1046
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1047
             */
1048
            #[Subtype(Fixture\BooleanAssociation::class)]
1049
            public array $value;
1050
        };
1051
1052
        $this->assertInvalidValueExceptionCount(1);
1053
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1054
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1055
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1056
        $this->createHydrator()->hydrate($object, $data);
1057
    }
1058
1059
    /**
1060
     * @group array
1061
     * @group boolean-association-array
1062
     */
1063
    public function testHydrateBooleanAssociationArrayPropertyWithoutValue(): void
1064
    {
1065
        $object = new class {
1066
            /**
1067
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1068
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1069
             */
1070
            #[Subtype(Fixture\BooleanAssociation::class)]
1071
            public array $value;
1072
        };
1073
1074
        $this->assertInvalidValueExceptionCount(1);
1075
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1076
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1077
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1078
        $this->createHydrator()->hydrate($object, []);
1079
    }
1080
1081
    /**
1082
     * @group array
1083
     * @group boolean-association-array
1084
     * @dataProvider notArrayProvider
1085
     */
1086
    public function testHydrateBooleanAssociationArrayPropertyWithInvalidAssociation($element): void
1087
    {
1088
        $object = new class {
1089
            /**
1090
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1091
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1092
             */
1093
            #[Subtype(Fixture\BooleanAssociation::class)]
1094
            public array $value;
1095
        };
1096
1097
        $this->assertInvalidValueExceptionCount(1);
1098
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1099
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1100
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1101
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1102
    }
1103
1104
    /**
1105
     * @group array
1106
     * @group boolean-association-array
1107
     * @dataProvider strictNullDataProvider
1108
     * @dataProvider nonStrictNullDataProvider
1109
     */
1110
    public function testHydrateBooleanAssociationArrayPropertyWithEmptyAssociationValue(array $data): void
1111
    {
1112
        $object = new class {
1113
            /**
1114
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1115
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1116
             */
1117
            #[Subtype(Fixture\BooleanAssociation::class)]
1118
            public array $value;
1119
        };
1120
1121
        $this->assertInvalidValueExceptionCount(1);
1122
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1123
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1124
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value');
1125
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1126
    }
1127
1128
    /**
1129
     * @group array
1130
     * @group boolean-association-array
1131
     * @dataProvider notBooleanDataProvider
1132
     */
1133
    public function testHydrateBooleanAssociationArrayPropertyWithInvalidAssociationValue(array $data): void
1134
    {
1135
        $object = new class {
1136
            /**
1137
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1138
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1139
             */
1140
            #[Subtype(Fixture\BooleanAssociation::class)]
1141
            public array $value;
1142
        };
1143
1144
        $this->assertInvalidValueExceptionCount(1);
1145
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1146
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1147
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value');
1148
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1149
    }
1150
1151
    /**
1152
     * @group array-access
1153
     * @dataProvider arrayDataProvider
1154
     */
1155
    public function testHydrateArrayAccessProperty(array $data, array $expected): void
1156
    {
1157
        $object = new class {
1158
            public Fixture\Collection $value;
1159
        };
1160
1161
        $this->assertInvalidValueExceptionCount(0);
1162
        $this->createHydrator()->hydrate($object, $data);
1163
        $this->assertSame($expected, $object->value->elements);
1164
    }
1165
1166
    /**
1167
     * @group array-access
1168
     * @dataProvider arrayDataProvider
1169
     * @dataProvider strictNullDataProvider
1170
     */
1171
    public function testHydrateNullableArrayAccessProperty(array $data, ?array $expected): void
1172
    {
1173
        $object = new class {
1174
            public ?Fixture\Collection $value;
1175
        };
1176
1177
        $this->assertInvalidValueExceptionCount(0);
1178
        $this->createHydrator()->hydrate($object, $data);
1179
        $this->assertSame($expected, $object->value->elements ?? null);
1180
    }
1181
1182
    /**
1183
     * @group array-access
1184
     * @dataProvider arrayDataProvider
1185
     * @dataProvider emptyArrayProvider
1186
     */
1187
    public function testHydrateOptionalArrayAccessProperty(array $data, array $expected = []): void
1188
    {
1189
        $object = new class {
1190
            public ?Fixture\Collection $value = null;
1191
        };
1192
1193
        $this->assertInvalidValueExceptionCount(0);
1194
        $this->createHydrator()->hydrate($object, $data);
1195
        $this->assertSame($expected, $object->value->elements ?? []);
1196
    }
1197
1198
    /**
1199
     * @group array-access
1200
     * @dataProvider strictNullDataProvider
1201
     */
1202
    public function testHydrateArrayAccessPropertyWithNull(array $data): void
1203
    {
1204
        $object = new class {
1205
            public Fixture\Collection $value;
1206
        };
1207
1208
        $this->assertInvalidValueExceptionCount(1);
1209
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1210
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1211
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1212
        $this->createHydrator()->hydrate($object, $data);
1213
    }
1214
1215
    /**
1216
     * @group array-access
1217
     * @dataProvider notArrayDataProvider
1218
     */
1219
    public function testHydrateArrayAccessPropertyWithInvalidValue(array $data): void
1220
    {
1221
        $object = new class {
1222
            public Fixture\Collection $value;
1223
        };
1224
1225
        $this->assertInvalidValueExceptionCount(1);
1226
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1227
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1228
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1229
        $this->createHydrator()->hydrate($object, $data);
1230
    }
1231
1232
    /**
1233
     * @group array-access
1234
     */
1235
    public function testHydrateArrayAccessPropertyWithoutValue(): void
1236
    {
1237
        $object = new class {
1238
            public Fixture\Collection $value;
1239
        };
1240
1241
        $this->assertInvalidValueExceptionCount(1);
1242
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1243
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1244
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1245
        $this->createHydrator()->hydrate($object, []);
1246
    }
1247
1248
    /**
1249
     * @group array-access
1250
     */
1251
    public function testHydrateOverflowedArrayAccessProperty(): void
1252
    {
1253
        $object = new class {
1254
            public Fixture\LimitedCollection $value;
1255
        };
1256
1257
        $this->assertInvalidValueExceptionCount(1);
1258
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1259
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1260
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1261
        $this->createHydrator()->hydrate($object, ['value' => ['foo', 'bar']]);
1262
    }
1263
1264
    /**
1265
     * @group array-access
1266
     */
1267
    public function testHydrateUnstantiableArrayAccessProperty(): void
1268
    {
1269
        $object = new class {
1270
            public Fixture\UnstantiableCollection $value;
1271
        };
1272
1273
        $this->expectException(InvalidObjectException::class);
1274
        $this->createHydrator()->hydrate($object, ['value' => []]);
1275
    }
1276
1277
    /**
1278
     * @group array-access
1279
     * @group annotated-boolean-array-access
1280
     * @dataProvider strictBooleanProvider
1281
     * @dataProvider nonStrictBooleanProvider
1282
     */
1283
    public function testHydrateAnnotatedBooleanArrayAccessProperty($element, bool $expected): void
1284
    {
1285
        $object = new class {
1286
            /** @Subtype(BuiltinType::BOOL) */
1287
            #[Subtype(BuiltinType::BOOL)]
1288
            public Fixture\Collection $value;
1289
        };
1290
1291
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1292
        $this->assertSame([$expected], $object->value->elements);
1293
    }
1294
1295
    /**
1296
     * @group array-access
1297
     * @group annotated-boolean-array-access
1298
     * @dataProvider strictBooleanProvider
1299
     * @dataProvider nonStrictBooleanProvider
1300
     * @dataProvider strictNullProvider
1301
     * @dataProvider nonStrictNullProvider
1302
     */
1303
    public function testHydrateAnnotatedNullableBooleanArrayAccessProperty($element, ?bool $expected): void
1304
    {
1305
        $object = new class {
1306
            /** @Subtype(BuiltinType::BOOL, allowsNull=true) */
1307
            #[Subtype(BuiltinType::BOOL, allowsNull: true)]
1308
            public Fixture\Collection $value;
1309
        };
1310
1311
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1312
        $this->assertSame([$expected], $object->value->elements);
1313
    }
1314
1315
    /**
1316
     * @group array-access
1317
     * @group annotated-boolean-array-access
1318
     * @dataProvider strictNullProvider
1319
     * @dataProvider nonStrictNullProvider
1320
     */
1321
    public function testHydrateAnnotatedBooleanArrayAccessPropertyWithEmptyElement($element): void
1322
    {
1323
        $object = new class {
1324
            /** @Subtype(BuiltinType::BOOL) */
1325
            #[Subtype(BuiltinType::BOOL)]
1326
            public Fixture\Collection $value;
1327
        };
1328
1329
        $this->assertInvalidValueExceptionCount(1);
1330
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1331
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1332
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1333
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1334
    }
1335
1336
    /**
1337
     * @group array-access
1338
     * @group annotated-boolean-array-access
1339
     * @dataProvider notBooleanProvider
1340
     */
1341
    public function testHydrateAnnotatedBooleanArrayAccessPropertyWithInvalidElement($element): void
1342
    {
1343
        $object = new class {
1344
            /** @Subtype(BuiltinType::BOOL) */
1345
            #[Subtype(BuiltinType::BOOL)]
1346
            public Fixture\Collection $value;
1347
        };
1348
1349
        $this->assertInvalidValueExceptionCount(1);
1350
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1351
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1352
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1353
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1354
    }
1355
1356
    /**
1357
     * @group array-access
1358
     * @group annotated-boolean-array-access
1359
     * @dataProvider strictBooleanProvider
1360
     * @dataProvider nonStrictBooleanProvider
1361
     */
1362
    public function testHydrateAnnotatedLimitedBooleanArrayAccessProperty($element): void
1363
    {
1364
        $object = new class {
1365
            /** @Subtype(BuiltinType::BOOL, limit=1) */
1366
            #[Subtype(BuiltinType::BOOL, limit: 1)]
1367
            public Fixture\Collection $value;
1368
        };
1369
1370
        $this->assertInvalidValueExceptionCount(1);
1371
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1372
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1373
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1374
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
1375
    }
1376
1377
    /**
1378
     * @group array-access
1379
     * @group annotated-boolean-array-access
1380
     * @dataProvider strictBooleanProvider
1381
     * @dataProvider nonStrictBooleanProvider
1382
     */
1383
    public function testHydrateAnnotatedOverflowedBooleanArrayAccessProperty($element): void
1384
    {
1385
        $object = new class {
1386
            /** @Subtype(BuiltinType::BOOL) */
1387
            #[Subtype(BuiltinType::BOOL)]
1388
            public Fixture\LimitedCollection $value;
1389
        };
1390
1391
        $this->assertInvalidValueExceptionCount(1);
1392
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1393
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1394
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1395
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
1396
    }
1397
1398
    /**
1399
     * @group array-access
1400
     * @group typed-boolean-array-access
1401
     * @dataProvider strictBooleanProvider
1402
     * @dataProvider nonStrictBooleanProvider
1403
     */
1404
    public function testHydrateTypedBooleanArrayAccessProperty($element, bool $expected): void
1405
    {
1406
        $object = new class {
1407
            public Fixture\BooleanCollection $value;
1408
        };
1409
1410
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1411
        $this->assertSame([$expected], $object->value->elements);
1412
    }
1413
1414
    /**
1415
     * @group array-access
1416
     * @group typed-boolean-array-access
1417
     * @dataProvider strictBooleanProvider
1418
     * @dataProvider nonStrictBooleanProvider
1419
     * @dataProvider strictNullProvider
1420
     * @dataProvider nonStrictNullProvider
1421
     */
1422
    public function testHydrateTypedNullableBooleanArrayAccessProperty($element, ?bool $expected): void
1423
    {
1424
        $object = new class {
1425
            public Fixture\NullableBooleanCollection $value;
1426
        };
1427
1428
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1429
        $this->assertSame([$expected], $object->value->elements);
1430
    }
1431
1432
    /**
1433
     * @group array-access
1434
     * @group typed-boolean-array-access
1435
     * @dataProvider strictNullProvider
1436
     * @dataProvider nonStrictNullProvider
1437
     */
1438
    public function testHydrateTypedBooleanArrayAccessPropertyWithEmptyElement($element): void
1439
    {
1440
        $object = new class {
1441
            public Fixture\BooleanCollection $value;
1442
        };
1443
1444
        $this->assertInvalidValueExceptionCount(1);
1445
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1446
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1447
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1448
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1449
    }
1450
1451
    /**
1452
     * @group array-access
1453
     * @group typed-boolean-array-access
1454
     * @dataProvider notBooleanProvider
1455
     */
1456
    public function testHydrateTypedBooleanArrayAccessPropertyWithInvalidElement($element): void
1457
    {
1458
        $object = new class {
1459
            public Fixture\BooleanCollection $value;
1460
        };
1461
1462
        $this->assertInvalidValueExceptionCount(1);
1463
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1464
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1465
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1466
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1467
    }
1468
1469
    /**
1470
     * @group array-access
1471
     * @group typed-boolean-array-access
1472
     * @dataProvider strictBooleanProvider
1473
     * @dataProvider nonStrictBooleanProvider
1474
     */
1475
    public function testHydrateTypedLimitedBooleanArrayAccessProperty($element): void
1476
    {
1477
        $object = new class {
1478
            public Fixture\LimitedCollection $value;
1479
        };
1480
1481
        $this->assertInvalidValueExceptionCount(1);
1482
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1483
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1484
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1485
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
1486
    }
1487
1488
    /**
1489
     * @group array-access
1490
     * @group typed-boolean-array-access
1491
     * @dataProvider strictBooleanProvider
1492
     * @dataProvider nonStrictBooleanProvider
1493
     */
1494
    public function testHydrateTypedOverflowedBooleanArrayAccessProperty($element): void
1495
    {
1496
        $object = new class {
1497
            public Fixture\LimitedBooleanCollection $value;
1498
        };
1499
1500
        $this->assertInvalidValueExceptionCount(1);
1501
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1502
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1503
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1504
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
1505
    }
1506
1507
    /**
1508
     * @group array-access
1509
     * @group boolean-association-array-access
1510
     * @dataProvider strictBooleanDataProvider
1511
     * @dataProvider nonStrictBooleanDataProvider
1512
     */
1513
    public function testHydrateBooleanAssociationArrayAccessProperty(array $data, bool $expected): void
1514
    {
1515
        $object = new class {
1516
            public Fixture\BooleanAssociationCollection $value;
1517
        };
1518
1519
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1520
        $this->assertSame($expected, $object->value[0]->value);
1521
    }
1522
1523
    /**
1524
     * @group array-access
1525
     * @group boolean-association-array-access
1526
     * @dataProvider strictNullDataProvider
1527
     */
1528
    public function testHydrateBooleanAssociationArrayAccessPropertyWithNull(array $data): void
1529
    {
1530
        $object = new class {
1531
            public Fixture\BooleanAssociationCollection $value;
1532
        };
1533
1534
        $this->assertInvalidValueExceptionCount(1);
1535
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1536
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1537
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1538
        $this->createHydrator()->hydrate($object, $data);
1539
    }
1540
1541
    /**
1542
     * @group array-access
1543
     * @group boolean-association-array-access
1544
     * @dataProvider notArrayDataProvider
1545
     */
1546
    public function testHydrateBooleanAssociationArrayAccessPropertyWithInvalidValue(array $data): void
1547
    {
1548
        $object = new class {
1549
            public Fixture\BooleanAssociationCollection $value;
1550
        };
1551
1552
        $this->assertInvalidValueExceptionCount(1);
1553
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1554
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1555
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1556
        $this->createHydrator()->hydrate($object, $data);
1557
    }
1558
1559
    /**
1560
     * @group array-access
1561
     * @group boolean-association-array-access
1562
     */
1563
    public function testHydrateBooleanAssociationArrayAccessPropertyWithoutValue(): void
1564
    {
1565
        $object = new class {
1566
            public Fixture\BooleanAssociationCollection $value;
1567
        };
1568
1569
        $this->assertInvalidValueExceptionCount(1);
1570
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1571
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1572
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1573
        $this->createHydrator()->hydrate($object, []);
1574
    }
1575
1576
    /**
1577
     * @group array-access
1578
     * @group boolean-association-array-access
1579
     * @dataProvider notArrayProvider
1580
     */
1581
    public function testHydrateBooleanAssociationArrayAccessPropertyWithInvalidAssociation($actual): void
1582
    {
1583
        $object = new class {
1584
            public Fixture\BooleanAssociationCollection $value;
1585
        };
1586
1587
        $this->assertInvalidValueExceptionCount(1);
1588
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1589
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1590
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1591
        $this->createHydrator()->hydrate($object, ['value' => [$actual]]);
1592
    }
1593
1594
    /**
1595
     * @group array-access
1596
     * @group boolean-association-array-access
1597
     * @dataProvider strictNullDataProvider
1598
     * @dataProvider nonStrictNullDataProvider
1599
     */
1600
    public function testHydrateBooleanAssociationArrayAccessPropertyWithEmptyAssociationValue(array $data): void
1601
    {
1602
        $object = new class {
1603
            public Fixture\BooleanAssociationCollection $value;
1604
        };
1605
1606
        $this->assertInvalidValueExceptionCount(1);
1607
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1608
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1609
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value');
1610
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1611
    }
1612
1613
    /**
1614
     * @group array-access
1615
     * @group boolean-association-array-access
1616
     * @dataProvider notBooleanDataProvider
1617
     */
1618
    public function testHydrateBooleanAssociationArrayAccessPropertyWithInvalidAssociationValue(array $data): void
1619
    {
1620
        $object = new class {
1621
            public Fixture\BooleanAssociationCollection $value;
1622
        };
1623
1624
        $this->assertInvalidValueExceptionCount(1);
1625
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1626
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1627
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value');
1628
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1629
    }
1630
1631
    /**
1632
     * @group association
1633
     */
1634
    public function testHydrateUnstantiableAssociationProperty(): void
1635
    {
1636
        $object = new class {
1637
            public Fixture\UnstantiableObject $value;
1638
        };
1639
1640
        $this->expectException(InvalidObjectException::class);
1641
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
1642
    }
1643
1644
    /**
1645
     * @group association
1646
     * @group boolean-association
1647
     * @dataProvider strictBooleanDataProvider
1648
     * @dataProvider nonStrictBooleanDataProvider
1649
     */
1650
    public function testHydrateBooleanAssociationProperty(array $data, bool $expected): void
1651
    {
1652
        $object = new class {
1653
            public Fixture\BooleanAssociation $value;
1654
        };
1655
1656
        $this->assertInvalidValueExceptionCount(0);
1657
        $this->createHydrator()->hydrate($object, ['value' => $data]);
1658
        $this->assertSame($expected, $object->value->value);
1659
    }
1660
1661
    /**
1662
     * @group association
1663
     * @group boolean-association
1664
     */
1665
    public function testHydrateNullableBooleanAssociationProperty(): void
1666
    {
1667
        $object = new class {
1668
            public ?Fixture\BooleanAssociation $value;
1669
        };
1670
1671
        $this->assertInvalidValueExceptionCount(0);
1672
        $this->createHydrator()->hydrate($object, ['value' => null]);
1673
        $this->assertNull($object->value);
1674
    }
1675
1676
    /**
1677
     * @group association
1678
     * @group boolean-association
1679
     */
1680
    public function testHydrateOptionalBooleanAssociationProperty(): void
1681
    {
1682
        $object = new class {
1683
            public ?Fixture\BooleanAssociation $value = null;
1684
        };
1685
1686
        $this->assertInvalidValueExceptionCount(0);
1687
        $this->createHydrator()->hydrate($object, []);
1688
        $this->assertNull($object->value);
1689
    }
1690
1691
    /**
1692
     * @group association
1693
     * @group boolean-association
1694
     */
1695
    public function testHydrateBooleanAssociationPropertyWithNull(): void
1696
    {
1697
        $object = new class {
1698
            public Fixture\BooleanAssociation $value;
1699
        };
1700
1701
        $this->assertInvalidValueExceptionCount(1);
1702
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1703
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1704
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1705
        $this->createHydrator()->hydrate($object, ['value' => null]);
1706
    }
1707
1708
    /**
1709
     * @group association
1710
     * @group boolean-association
1711
     * @dataProvider notArrayDataProvider
1712
     */
1713
    public function testHydrateBooleanAssociationPropertyWithInvalidValue(array $data): void
1714
    {
1715
        $object = new class {
1716
            public Fixture\BooleanAssociation $value;
1717
        };
1718
1719
        $this->assertInvalidValueExceptionCount(1);
1720
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1721
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1722
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1723
        $this->createHydrator()->hydrate($object, $data);
1724
    }
1725
1726
    /**
1727
     * @group association
1728
     * @group boolean-association
1729
     */
1730
    public function testHydrateBooleanAssociationPropertyWithoutValue(): void
1731
    {
1732
        $object = new class {
1733
            public Fixture\BooleanAssociation $value;
1734
        };
1735
1736
        $this->assertInvalidValueExceptionCount(1);
1737
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1738
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1739
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1740
        $this->createHydrator()->hydrate($object, []);
1741
    }
1742
1743
    /**
1744
     * @group association
1745
     * @group boolean-association
1746
     */
1747
    public function testHydrateBooleanAssociationPropertyWithEmptyAssociation(): void
1748
    {
1749
        $object = new class {
1750
            public Fixture\BooleanAssociation $value;
1751
        };
1752
1753
        $this->assertInvalidValueExceptionCount(1);
1754
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1755
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1756
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.value');
1757
        $this->createHydrator()->hydrate($object, ['value' => []]);
1758
    }
1759
1760
    /**
1761
     * @group association
1762
     * @group boolean-association
1763
     * @dataProvider notBooleanDataProvider
1764
     */
1765
    public function testHydrateBooleanAssociationPropertyWithInvalidAssociationValue(array $data): void
1766
    {
1767
        $object = new class {
1768
            public Fixture\BooleanAssociation $value;
1769
        };
1770
1771
        $this->assertInvalidValueExceptionCount(1);
1772
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1773
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1774
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.value');
1775
        $this->createHydrator()->hydrate($object, ['value' => $data]);
1776
    }
1777
1778
    /**
1779
     * @group timestamp
1780
     * @dataProvider timestampDataProvider
1781
     */
1782
    // phpcs:ignore Generic.Files.LineLength
1783
    public function testHydrateTimestampProperty(array $data, string $expected, ?string $format = null, ?string $timezone = null): void
1784
    {
1785
        $object = new class {
1786
            public DateTimeImmutable $value;
1787
        };
1788
1789
        $this->assertInvalidValueExceptionCount(0);
1790
        // phpcs:ignore Generic.Files.LineLength
1791
        $this->createHydrator([ContextKey::TIMESTAMP_FORMAT => $format, ContextKey::TIMEZONE => $timezone])->hydrate($object, $data);
1792
        $this->assertSame($expected, $object->value->format($format ?? TimestampTypeConverter::DEFAULT_FORMAT));
1793
    }
1794
1795
    /**
1796
     * @group timestamp
1797
     * @dataProvider timestampDataProvider
1798
     * @dataProvider strictNullDataProvider
1799
     * @dataProvider nonStrictNullDataProvider
1800
     */
1801
    // phpcs:ignore Generic.Files.LineLength
1802
    public function testHydrateNullableTimestampProperty(array $data, ?string $expected, ?string $format = null, ?string $timezone = null): void
1803
    {
1804
        $object = new class {
1805
            public ?DateTimeImmutable $value;
1806
        };
1807
1808
        $this->assertInvalidValueExceptionCount(0);
1809
        // phpcs:ignore Generic.Files.LineLength
1810
        $this->createHydrator([ContextKey::TIMESTAMP_FORMAT => $format, ContextKey::TIMEZONE => $timezone])->hydrate($object, $data);
1811
        // phpcs:ignore Generic.Files.LineLength
1812
        $this->assertSame($expected, isset($object->value) ? $object->value->format($format ?? TimestampTypeConverter::DEFAULT_FORMAT) : null);
0 ignored issues
show
Bug introduced by
The method format() does not exist on null. ( Ignorable by Annotation )

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

1812
        $this->assertSame($expected, isset($object->value) ? $object->value->/** @scrutinizer ignore-call */ format($format ?? TimestampTypeConverter::DEFAULT_FORMAT) : null);

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...
1813
    }
1814
1815
    /**
1816
     * @group timestamp
1817
     * @dataProvider timestampDataProvider
1818
     * @dataProvider emptyArrayProvider
1819
     */
1820
    // phpcs:ignore Generic.Files.LineLength
1821
    public function testHydrateOptionalTimestampProperty(array $data, ?string $expected = null, ?string $format = null, ?string $timezone = null): void
1822
    {
1823
        $object = new class {
1824
            public ?DateTimeImmutable $value = null;
1825
        };
1826
1827
        $this->assertInvalidValueExceptionCount(0);
1828
        // phpcs:ignore Generic.Files.LineLength
1829
        $this->createHydrator([ContextKey::TIMESTAMP_FORMAT => $format, ContextKey::TIMEZONE => $timezone])->hydrate($object, $data);
1830
        // phpcs:ignore Generic.Files.LineLength
1831
        $this->assertSame($expected, isset($object->value) ? $object->value->format($format ?? TimestampTypeConverter::DEFAULT_FORMAT) : null);
1832
    }
1833
1834
    /**
1835
     * @group timestamp
1836
     * @dataProvider strictNullDataProvider
1837
     * @dataProvider nonStrictNullDataProvider
1838
     */
1839
    public function testHydrateTimestampPropertyWithEmptyValue(array $data): void
1840
    {
1841
        $object = new class {
1842
            public DateTimeImmutable $value;
1843
        };
1844
1845
        $this->assertInvalidValueExceptionCount(1);
1846
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1847
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1848
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1849
        $this->createHydrator()->hydrate($object, $data);
1850
    }
1851
1852
    /**
1853
     * @group timestamp
1854
     * @dataProvider invalidTimestampDataProvider
1855
     */
1856
    // phpcs:ignore Generic.Files.LineLength
1857
    public function testHydrateTimestampPropertyWithInvalidValue(array $data, ?string $format, string $errorCode, string $errorMessage): void
1858
    {
1859
        $object = new class {
1860
            public DateTimeImmutable $value;
1861
        };
1862
1863
        $this->assertInvalidValueExceptionCount(1);
1864
        $this->assertInvalidValueExceptionMessage(0, $errorMessage);
1865
        $this->assertInvalidValueExceptionErrorCode(0, $errorCode);
1866
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1867
        $this->createHydrator([ContextKey::TIMESTAMP_FORMAT => $format])->hydrate($object, $data);
1868
    }
1869
1870
    /**
1871
     * @group timestamp
1872
     */
1873
    public function testHydrateTimestampPropertyWithoutValue(): void
1874
    {
1875
        $object = new class {
1876
            public DateTimeImmutable $value;
1877
        };
1878
1879
        $this->assertInvalidValueExceptionCount(1);
1880
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1881
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1882
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1883
        $this->createHydrator()->hydrate($object, []);
1884
    }
1885
1886
    /**
1887
     * @group timestamp
1888
     * @dataProvider timestampDataProvider
1889
     */
1890
    // phpcs:ignore Generic.Files.LineLength
1891
    public function testHydrateOverriddenDateTimeImmutable(array $data, string $expected, ?string $format = null, ?string $timezone = null): void
1892
    {
1893
        $this->phpRequired('8.0');
1894
1895
        $object = new class {
1896
            public Fixture\OverriddenDateTimeImmutable $value;
1897
        };
1898
1899
        $this->assertInvalidValueExceptionCount(0);
1900
        // phpcs:ignore Generic.Files.LineLength
1901
        $this->createHydrator([ContextKey::TIMESTAMP_FORMAT => $format, ContextKey::TIMEZONE => $timezone])->hydrate($object, $data);
1902
        $this->assertSame($expected, $object->value->format($format ?? TimestampTypeConverter::DEFAULT_FORMAT));
1903
    }
1904
1905
    /**
1906
     * @group timezone
1907
     * @dataProvider timezoneDataProvider
1908
     */
1909
    public function testHydrateTimezoneProperty(array $data, string $expected): void
1910
    {
1911
        $object = new class {
1912
            public DateTimeZone $value;
1913
        };
1914
1915
        $this->assertInvalidValueExceptionCount(0);
1916
        $this->createHydrator()->hydrate($object, $data);
1917
        $this->assertSame($expected, $object->value->getName());
1918
    }
1919
1920
    /**
1921
     * @group timezone
1922
     * @dataProvider timezoneDataProvider
1923
     * @dataProvider strictNullDataProvider
1924
     * @dataProvider nonStrictNullDataProvider
1925
     */
1926
    public function testHydrateNullableTimezoneProperty(array $data, ?string $expected): void
1927
    {
1928
        $object = new class {
1929
            public ?DateTimeZone $value;
1930
        };
1931
1932
        $this->assertInvalidValueExceptionCount(0);
1933
        $this->createHydrator()->hydrate($object, $data);
1934
        $this->assertSame($expected, isset($object->value) ? $object->value->getName() : null);
1935
    }
1936
1937
    /**
1938
     * @group timezone
1939
     * @dataProvider timezoneDataProvider
1940
     * @dataProvider emptyArrayProvider
1941
     */
1942
    public function testHydrateOptionalTimezoneProperty(array $data, ?string $expected = null): void
1943
    {
1944
        $object = new class {
1945
            public ?DateTimeZone $value = null;
1946
        };
1947
1948
        $this->assertInvalidValueExceptionCount(0);
1949
        $this->createHydrator()->hydrate($object, $data);
1950
        $this->assertSame($expected, isset($object->value) ? $object->value->getName() : null);
1951
    }
1952
1953
    /**
1954
     * @group timezone
1955
     * @dataProvider strictNullDataProvider
1956
     * @dataProvider nonStrictNullDataProvider
1957
     */
1958
    public function testHydrateTimezonePropertyWithEmptyValue(array $data): void
1959
    {
1960
        $object = new class {
1961
            public DateTimeZone $value;
1962
        };
1963
1964
        $this->assertInvalidValueExceptionCount(1);
1965
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1966
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1967
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1968
        $this->createHydrator()->hydrate($object, $data);
1969
    }
1970
1971
    /**
1972
     * @group timezone
1973
     * @dataProvider strictNotStringDataProvider
1974
     */
1975
    public function testHydrateTimezonePropertyWithInvalidValue(array $data): void
1976
    {
1977
        $object = new class {
1978
            public DateTimeZone $value;
1979
        };
1980
1981
        $this->assertInvalidValueExceptionCount(1);
1982
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type string.');
1983
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_STRING);
1984
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1985
        $this->createHydrator()->hydrate($object, $data);
1986
    }
1987
1988
    /**
1989
     * @group timezone
1990
     */
1991
    public function testHydrateTimezonePropertyWithUnknownValue(): void
1992
    {
1993
        $object = new class {
1994
            public DateTimeZone $value;
1995
        };
1996
1997
        $this->assertInvalidValueExceptionCount(1);
1998
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid timezone.');
1999
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_TIMEZONE);
2000
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2001
        $this->createHydrator()->hydrate($object, ['value' => 'Jupiter/Europa']);
2002
    }
2003
2004
    /**
2005
     * @group timezone
2006
     */
2007
    public function testHydrateTimezonePropertyWithoutValue(): void
2008
    {
2009
        $object = new class {
2010
            public DateTimeZone $value;
2011
        };
2012
2013
        $this->assertInvalidValueExceptionCount(1);
2014
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
2015
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
2016
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2017
        $this->createHydrator()->hydrate($object, []);
2018
    }
2019
2020
    /**
2021
     * @group myclabs-enum
2022
     * @dataProvider myclabsEnumDataProvider
2023
     */
2024
    public function testHydrateMyclabsEnumProperty(array $data, $expected): void
2025
    {
2026
        $object = new class {
2027
            public Fixture\MyclabsEnum $value;
2028
        };
2029
2030
        $this->assertInvalidValueExceptionCount(0);
2031
        $this->createHydrator()->hydrate($object, $data);
2032
        $this->assertEquals($expected, $object->value);
2033
    }
2034
2035
    /**
2036
     * @group myclabs-enum
2037
     * @dataProvider myclabsEnumDataProvider
2038
     * @dataProvider strictNullDataProvider
2039
     * @dataProvider nonStrictNullDataProvider
2040
     */
2041
    public function testHydrateNullableMyclabsEnumProperty(array $data, $expected): void
2042
    {
2043
        $object = new class {
2044
            public ?Fixture\MyclabsEnum $value;
2045
        };
2046
2047
        $this->assertInvalidValueExceptionCount(0);
2048
        $this->createHydrator()->hydrate($object, $data);
2049
        $this->assertEquals($expected, $object->value);
2050
    }
2051
2052
    /**
2053
     * @group myclabs-enum
2054
     * @dataProvider myclabsEnumDataProvider
2055
     * @dataProvider emptyArrayProvider
2056
     */
2057
    public function testHydrateOptionalMyclabsEnumProperty(array $data, $expected = null): void
2058
    {
2059
        $object = new class {
2060
            public ?Fixture\MyclabsEnum $value = null;
2061
        };
2062
2063
        $this->assertInvalidValueExceptionCount(0);
2064
        $this->createHydrator()->hydrate($object, $data);
2065
        $this->assertEquals($expected, $object->value);
2066
    }
2067
2068
    /**
2069
     * @group myclabs-enum
2070
     * @dataProvider strictNullDataProvider
2071
     * @dataProvider nonStrictNullDataProvider
2072
     */
2073
    public function testHydrateMyclabsEnumPropertyWithEmptyValue(array $data): void
2074
    {
2075
        $object = new class {
2076
            public Fixture\MyclabsEnum $value;
2077
        };
2078
2079
        $this->assertInvalidValueExceptionCount(1);
2080
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
2081
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
2082
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2083
        $this->createHydrator()->hydrate($object, $data);
2084
    }
2085
2086
    /**
2087
     * @group myclabs-enum
2088
     */
2089
    public function testHydrateMyclabsEnumPropertyWithUnknownValue(): void
2090
    {
2091
        $object = new class {
2092
            public Fixture\MyclabsEnum $value;
2093
        };
2094
2095
        $this->assertInvalidValueExceptionCount(1);
2096
        // phpcs:ignore Generic.Files.LineLength
2097
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid choice; expected values: ' . join(', ', Fixture\MyclabsEnum::toArray()) . '.');
2098
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_CHOICE);
2099
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2100
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
2101
    }
2102
2103
    /**
2104
     * @group myclabs-enum
2105
     */
2106
    public function testHydrateMyclabsEnumPropertyWithoutValue(): void
2107
    {
2108
        $object = new class {
2109
            public Fixture\MyclabsEnum $value;
2110
        };
2111
2112
        $this->assertInvalidValueExceptionCount(1);
2113
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
2114
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
2115
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2116
        $this->createHydrator()->hydrate($object, []);
2117
    }
2118
2119
    /**
2120
     * @group ramsey-uuid
2121
     * @dataProvider uuidDataProvider
2122
     */
2123
    public function testHydrateRamseyUuidProperty(array $data, string $expected): void
2124
    {
2125
        $object = new class {
2126
            public \Ramsey\Uuid\UuidInterface $value;
2127
        };
2128
2129
        $this->assertInvalidValueExceptionCount(0);
2130
        $this->createHydrator()->hydrate($object, $data);
2131
        $this->assertSame($expected, $object->value->toString());
2132
    }
2133
2134
    /**
2135
     * @group ramsey-uuid
2136
     * @dataProvider uuidDataProvider
2137
     * @dataProvider strictNullDataProvider
2138
     * @dataProvider nonStrictNullDataProvider
2139
     */
2140
    public function testHydrateNullableRamseyUuidProperty(array $data, ?string $expected): void
2141
    {
2142
        $object = new class {
2143
            public ?\Ramsey\Uuid\UuidInterface $value;
2144
        };
2145
2146
        $this->assertInvalidValueExceptionCount(0);
2147
        $this->createHydrator()->hydrate($object, $data);
2148
        $this->assertSame($expected, isset($object->value) ? $object->value->toString() : null);
2149
    }
2150
2151
    /**
2152
     * @group ramsey-uuid
2153
     * @dataProvider uuidDataProvider
2154
     * @dataProvider emptyArrayProvider
2155
     */
2156
    public function testHydrateOptionalRamseyUuidProperty(array $data, ?string $expected = null): void
2157
    {
2158
        $object = new class {
2159
            public ?\Ramsey\Uuid\UuidInterface $value = null;
2160
        };
2161
2162
        $this->assertInvalidValueExceptionCount(0);
2163
        $this->createHydrator()->hydrate($object, $data);
2164
        $this->assertSame($expected, isset($object->value) ? $object->value->toString() : null);
2165
    }
2166
2167
    /**
2168
     * @group ramsey-uuid
2169
     * @dataProvider strictNullDataProvider
2170
     * @dataProvider nonStrictNullDataProvider
2171
     */
2172
    public function testHydrateRamseyUuidPropertyWithEmptyValue(array $data): void
2173
    {
2174
        $object = new class {
2175
            public \Ramsey\Uuid\UuidInterface $value;
2176
        };
2177
2178
        $this->assertInvalidValueExceptionCount(1);
2179
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
2180
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
2181
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2182
        $this->createHydrator()->hydrate($object, $data);
2183
    }
2184
2185
    /**
2186
     * @group ramsey-uuid
2187
     * @dataProvider strictNotStringDataProvider
2188
     */
2189
    public function testHydrateRamseyUuidPropertyWithInvalidValue(array $data): void
2190
    {
2191
        $object = new class {
2192
            public \Ramsey\Uuid\UuidInterface $value;
2193
        };
2194
2195
        $this->assertInvalidValueExceptionCount(1);
2196
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type string.');
2197
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_STRING);
2198
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2199
        $this->createHydrator()->hydrate($object, $data);
2200
    }
2201
2202
    /**
2203
     * @group ramsey-uuid
2204
     */
2205
    public function testHydrateRamseyUuidPropertyWithInvalidUuid(): void
2206
    {
2207
        $object = new class {
2208
            public \Ramsey\Uuid\UuidInterface $value;
2209
        };
2210
2211
        $this->assertInvalidValueExceptionCount(1);
2212
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid UID.');
2213
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_UID);
2214
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2215
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
2216
    }
2217
2218
    /**
2219
     * @group ramsey-uuid
2220
     */
2221
    public function testHydrateRamseyUuidPropertyWithoutValue(): void
2222
    {
2223
        $object = new class {
2224
            public \Ramsey\Uuid\UuidInterface $value;
2225
        };
2226
2227
        $this->assertInvalidValueExceptionCount(1);
2228
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
2229
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
2230
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2231
        $this->createHydrator()->hydrate($object, []);
2232
    }
2233
2234
    /**
2235
     * @group symfony-uuid
2236
     * @dataProvider uuidDataProvider
2237
     */
2238
    public function testHydrateSymfonyUuidProperty(array $data, string $expected): void
2239
    {
2240
        $object = new class {
2241
            public \Symfony\Component\Uid\UuidV4 $value;
2242
        };
2243
2244
        $this->assertInvalidValueExceptionCount(0);
2245
        $this->createHydrator()->hydrate($object, $data);
2246
        $this->assertSame($expected, $object->value->toRfc4122());
2247
    }
2248
2249
    /**
2250
     * @group symfony-uuid
2251
     * @dataProvider uuidDataProvider
2252
     * @dataProvider strictNullDataProvider
2253
     * @dataProvider nonStrictNullDataProvider
2254
     */
2255
    public function testHydrateNullableSymfonyUuidProperty(array $data, ?string $expected): void
2256
    {
2257
        $object = new class {
2258
            public ?\Symfony\Component\Uid\UuidV4 $value;
2259
        };
2260
2261
        $this->assertInvalidValueExceptionCount(0);
2262
        $this->createHydrator()->hydrate($object, $data);
2263
        $this->assertSame($expected, isset($object->value) ? $object->value->toRfc4122() : null);
2264
    }
2265
2266
    /**
2267
     * @group symfony-uuid
2268
     * @dataProvider uuidDataProvider
2269
     * @dataProvider emptyArrayProvider
2270
     */
2271
    public function testHydrateOptionalSymfonyUuidProperty(array $data, ?string $expected = null): void
2272
    {
2273
        $object = new class {
2274
            public ?\Symfony\Component\Uid\UuidV4 $value = null;
2275
        };
2276
2277
        $this->assertInvalidValueExceptionCount(0);
2278
        $this->createHydrator()->hydrate($object, $data);
2279
        $this->assertSame($expected, isset($object->value) ? $object->value->toRfc4122() : null);
2280
    }
2281
2282
    /**
2283
     * @group symfony-uuid
2284
     * @dataProvider strictNullDataProvider
2285
     * @dataProvider nonStrictNullDataProvider
2286
     */
2287
    public function testHydrateSymfonyUuidPropertyWithEmptyValue(array $data): void
2288
    {
2289
        $object = new class {
2290
            public \Symfony\Component\Uid\UuidV4 $value;
2291
        };
2292
2293
        $this->assertInvalidValueExceptionCount(1);
2294
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
2295
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
2296
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2297
        $this->createHydrator()->hydrate($object, $data);
2298
    }
2299
2300
    /**
2301
     * @group symfony-uuid
2302
     * @dataProvider strictNotStringDataProvider
2303
     */
2304
    public function testHydrateSymfonyUuidPropertyWithInvalidValue(array $data): void
2305
    {
2306
        $object = new class {
2307
            public \Symfony\Component\Uid\UuidV4 $value;
2308
        };
2309
2310
        $this->assertInvalidValueExceptionCount(1);
2311
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type string.');
2312
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_STRING);
2313
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2314
        $this->createHydrator()->hydrate($object, $data);
2315
    }
2316
2317
    /**
2318
     * @group symfony-uuid
2319
     */
2320
    public function testHydrateSymfonyUuidPropertyWithInvalidUuid(): void
2321
    {
2322
        $object = new class {
2323
            public \Symfony\Component\Uid\UuidV4 $value;
2324
        };
2325
2326
        $this->assertInvalidValueExceptionCount(1);
2327
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid UID.');
2328
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_UID);
2329
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2330
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
2331
    }
2332
2333
    /**
2334
     * @group symfony-uuid
2335
     */
2336
    public function testHydrateSymfonyUuidPropertyWithoutValue(): void
2337
    {
2338
        $object = new class {
2339
            public \Symfony\Component\Uid\UuidV4 $value;
2340
        };
2341
2342
        $this->assertInvalidValueExceptionCount(1);
2343
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
2344
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
2345
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2346
        $this->createHydrator()->hydrate($object, []);
2347
    }
2348
2349
    /**
2350
     * @group json
2351
     */
2352
    public function testHydrateObjectWithJson(): void
2353
    {
2354
        $object = new class {
2355
            public string $value;
2356
        };
2357
2358
        $this->assertInvalidValueExceptionCount(0);
2359
        $this->createHydrator()->hydrateWithJson($object, '{"value": "foo"}');
2360
        $this->assertSame('foo', $object->value);
2361
    }
2362
2363
    /**
2364
     * @group json
2365
     */
2366
    public function testHydrateObjectWithInvalidJson(): void
2367
    {
2368
        $object = new class {
2369
            public string $value;
2370
        };
2371
2372
        $this->expectException(InvalidDataException::class);
2373
        $this->expectExceptionMessageMatches('/^The JSON is invalid and couldn‘t be decoded due to: .+$/');
2374
        $this->createHydrator()->hydrateWithJson($object, '[[]]', 0, 1);
2375
    }
2376
2377
    /**
2378
     * @group json
2379
     */
2380
    public function testHydrateObjectWithNonObjectableJson(): void
2381
    {
2382
        $object = new class {
2383
            public string $value;
2384
        };
2385
2386
        $this->expectException(InvalidDataException::class);
2387
        $this->expectExceptionMessage('The JSON must be in the form of an array or an object.');
2388
        $this->createHydrator()->hydrateWithJson($object, 'null');
2389
    }
2390
2391
    public function testInvalidObjectExceptionUnsupportedMethodParameterType(): void
2392
    {
2393
        $class = $this->createMock(ReflectionClass::class);
2394
        $class->method('getName')->willReturn('foo');
2395
2396
        $method = $this->createMock(ReflectionMethod::class);
2397
        $method->method('getDeclaringClass')->willReturn($class);
2398
        $method->method('getName')->willReturn('bar');
2399
2400
        $parameter = $this->createMock(ReflectionParameter::class);
2401
        $parameter->method('getDeclaringClass')->willReturn($class);
2402
        $parameter->method('getDeclaringFunction')->willReturn($method);
2403
        $parameter->method('getName')->willReturn('baz');
2404
2405
        $type = new Type($parameter, 'mixed', false);
2406
2407
        $e = InvalidObjectException::unsupportedParameterType($type, $parameter);
2408
        // phpcs:ignore Generic.Files.LineLength
2409
        $this->assertSame('The parameter {foo::bar($baz[0])} is associated with an unsupported type {mixed}.', $e->getMessage());
2410
    }
2411
2412
    public function testInvalidObjectExceptionUnsupportedFunctionParameterType(): void
2413
    {
2414
        $function = $this->createMock(ReflectionFunction::class);
2415
        $function->method('getName')->willReturn('foo');
2416
2417
        $parameter = $this->createMock(ReflectionParameter::class);
2418
        $parameter->method('getDeclaringFunction')->willReturn($function);
2419
        $parameter->method('getName')->willReturn('bar');
2420
2421
        $type = new Type($parameter, 'mixed', false);
2422
2423
        $e = InvalidObjectException::unsupportedParameterType($type, $parameter);
2424
        // phpcs:ignore Generic.Files.LineLength
2425
        $this->assertSame('The parameter {foo($bar[0])} is associated with an unsupported type {mixed}.', $e->getMessage());
2426
    }
2427
2428
    public function testTypeFromParameter(): void
2429
    {
2430
        $parameter = $this->createMock(ReflectionParameter::class);
2431
        $parameter->method('getType')->willReturn(null);
2432
        $type = Type::fromParameter($parameter);
2433
        $this->assertSame(BuiltinType::MIXED, $type->getName());
2434
        $this->assertTrue($type->allowsNull());
2435
2436
        $namedType = $this->createMock(ReflectionNamedType::class);
2437
        $namedType->method('getName')->willReturn('foo');
2438
        $namedType->method('allowsNull')->willReturn(false);
2439
        $parameter = $this->createMock(ReflectionParameter::class);
2440
        $parameter->method('getType')->willReturn($namedType);
2441
        $type = Type::fromParameter($parameter);
2442
        $this->assertSame('foo', $type->getName());
2443
        $this->assertFalse($type->allowsNull());
2444
2445
        $namedType = $this->createMock(ReflectionNamedType::class);
2446
        $namedType->method('getName')->willReturn('foo');
2447
        $namedType->method('allowsNull')->willReturn(true);
2448
        $parameter = $this->createMock(ReflectionParameter::class);
2449
        $parameter->method('getType')->willReturn($namedType);
2450
        $type = Type::fromParameter($parameter);
2451
        $this->assertSame('foo', $type->getName());
2452
        $this->assertTrue($type->allowsNull());
2453
2454
        if (PHP_VERSION_ID < 80000) {
2455
            return;
2456
        }
2457
2458
        $namedTypes = [];
2459
        $namedTypes[0] = $this->createMock(ReflectionNamedType::class);
2460
        $namedTypes[0]->method('getName')->willReturn('foo');
2461
        $namedTypes[1] = $this->createMock(ReflectionNamedType::class);
2462
        $namedTypes[1]->method('getName')->willReturn('bar');
2463
        $unionType = $this->createMock(ReflectionUnionType::class);
2464
        $unionType->method('getTypes')->willReturn($namedTypes);
2465
        $unionType->method('allowsNull')->willReturn(false);
2466
        $unionType->method('__toString')->willReturn('foo|bar');
2467
        $parameter = $this->createMock(ReflectionParameter::class);
2468
        $parameter->method('getType')->willReturn($unionType);
2469
        $type = Type::fromParameter($parameter);
2470
        $this->assertSame('foo|bar', $type->getName());
2471
        $this->assertFalse($type->allowsNull());
2472
2473
        $namedTypes = [];
2474
        $namedTypes[0] = $this->createMock(ReflectionNamedType::class);
2475
        $namedTypes[0]->method('getName')->willReturn('foo');
2476
        $namedTypes[1] = $this->createMock(ReflectionNamedType::class);
2477
        $namedTypes[1]->method('getName')->willReturn('bar');
2478
        $unionType = $this->createMock(ReflectionUnionType::class);
2479
        $unionType->method('getTypes')->willReturn($namedTypes);
2480
        $unionType->method('allowsNull')->willReturn(true);
2481
        $unionType->method('__toString')->willReturn('foo|bar|null');
2482
        $parameter = $this->createMock(ReflectionParameter::class);
2483
        $parameter->method('getType')->willReturn($unionType);
2484
        $type = Type::fromParameter($parameter);
2485
        $this->assertSame('foo|bar|null', $type->getName());
2486
        $this->assertTrue($type->allowsNull());
2487
2488
        if (PHP_VERSION_ID < 80100) {
2489
            return;
2490
        }
2491
2492
        $namedTypes = [];
2493
        $namedTypes[0] = $this->createMock(ReflectionNamedType::class);
2494
        $namedTypes[0]->method('getName')->willReturn('foo');
2495
        $namedTypes[1] = $this->createMock(ReflectionNamedType::class);
2496
        $namedTypes[1]->method('getName')->willReturn('bar');
2497
        $intersectionType = $this->createMock(ReflectionIntersectionType::class);
2498
        $intersectionType->method('getTypes')->willReturn($namedTypes);
2499
        $intersectionType->method('allowsNull')->willReturn(false);
2500
        $intersectionType->method('__toString')->willReturn('foo&bar');
2501
        $parameter = $this->createMock(ReflectionParameter::class);
2502
        $parameter->method('getType')->willReturn($intersectionType);
2503
        $type = Type::fromParameter($parameter);
2504
        $this->assertSame('foo&bar', $type->getName());
2505
        $this->assertFalse($type->allowsNull());
2506
2507
        $namedTypes = [];
2508
        $namedTypes[0] = $this->createMock(ReflectionNamedType::class);
2509
        $namedTypes[0]->method('getName')->willReturn('foo');
2510
        $namedTypes[1] = $this->createMock(ReflectionNamedType::class);
2511
        $namedTypes[1]->method('getName')->willReturn('bar');
2512
        $intersectionType = $this->createMock(ReflectionIntersectionType::class);
2513
        $intersectionType->method('getTypes')->willReturn($namedTypes);
2514
        $intersectionType->method('allowsNull')->willReturn(true);
2515
        $intersectionType->method('__toString')->willReturn('(foo&bar)|null');
2516
        $parameter = $this->createMock(ReflectionParameter::class);
2517
        $parameter->method('getType')->willReturn($intersectionType);
2518
        $type = Type::fromParameter($parameter);
2519
        $this->assertSame('(foo&bar)|null', $type->getName());
2520
        $this->assertTrue($type->allowsNull());
2521
    }
2522
2523
    public function testHydrateStore(): void
2524
    {
2525
        $this->phpRequired('8.1');
2526
2527
        $sold = Fixture\Store\Status::SOLD;
2528
2529
        $data = [
2530
            'name' => 'Pear',
2531
            'category' => [
2532
                'name' => 'Vegetables',
2533
            ],
2534
            'tags' => [
2535
                [
2536
                    'name' => 'foo',
2537
                ],
2538
                [
2539
                    'name' => 'bar',
2540
                ],
2541
            ],
2542
            'status' => $sold->value,
2543
        ];
2544
2545
        $this->assertInvalidValueExceptionCount(0);
2546
        $product = $this->createHydrator()->hydrate(Fixture\Store\Product::class, $data);
2547
        $this->assertSame('Pear', $product->name);
2548
        $this->assertSame('Vegetables', $product->category->name);
2549
        $this->assertCount(2, $product->tags);
2550
        $this->assertArrayHasKey(0, $product->tags);
2551
        $this->assertSame('foo', $product->tags[0]->name);
2552
        $this->assertArrayHasKey(1, $product->tags);
2553
        $this->assertSame('bar', $product->tags[1]->name);
2554
        $this->assertSame($sold, $product->status);
2555
        $this->assertSame('2020-01-01 12:00:00', $product->createdAt->format('Y-m-d H:i:s'));
2556
    }
2557
2558
    public function testUnknownObject(): void
2559
    {
2560
        $this->expectException(InvalidObjectException::class);
2561
        $this->createHydrator()->hydrate(\Unknown::class, []);
0 ignored issues
show
Bug introduced by
The type Unknown 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...
2562
    }
2563
2564
    public function testUnstantiableObject(): void
2565
    {
2566
        $this->expectException(InvalidObjectException::class);
2567
        $this->createHydrator()->hydrate(Fixture\UnstantiableObject::class, []);
2568
    }
2569
2570
    public function testStaticalProperty(): void
2571
    {
2572
        $object = new class {
2573
            public static string $value = 'foo';
2574
        };
2575
2576
        $this->assertInvalidValueExceptionCount(0);
2577
        $this->createHydrator()->hydrate($object, ['value' => 'bar']);
2578
        $this->assertSame('foo', $object::$value);
2579
    }
2580
2581
    public function testIgnoredProperty(): void
2582
    {
2583
        $object = new class {
2584
            /** @Ignore() */
2585
            #[Ignore]
2586
            public string $value = 'foo';
2587
        };
2588
2589
        $this->assertInvalidValueExceptionCount(0);
2590
        $this->createHydrator()->hydrate($object, ['value' => 'bar']);
2591
        $this->assertSame('foo', $object->value);
2592
    }
2593
2594
    public function testAliasedProperty(): void
2595
    {
2596
        $proto = new class {
2597
            /** @Alias("alias") */
2598
            #[Alias('alias')]
2599
            public string $value;
2600
        };
2601
2602
        $this->assertInvalidValueExceptionCount(0);
2603
        $object = $this->createHydrator()->hydrate(get_class($proto), ['alias' => 'foo']);
2604
        $this->assertSame('foo', $object->value);
2605
2606
        $this->assertInvalidValueExceptionCount(1);
2607
        $this->createHydrator()->hydrate(get_class($proto), ['value' => 'foo']);
2608
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
2609
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
2610
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
2611
    }
2612
2613
    public function testUntypedProperty(): void
2614
    {
2615
        $object = new class {
2616
            public $value;
2617
        };
2618
2619
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
2620
        $this->assertSame('foo', $object->value);
2621
    }
2622
2623
    public function testUnsupportedPropertyType(): void
2624
    {
2625
        $object = new class {
2626
            public balalaika $value;
0 ignored issues
show
Bug introduced by
The type Sunrise\Hydrator\Tests\balalaika 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...
2627
        };
2628
2629
        $this->expectException(InvalidObjectException::class);
2630
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
2631
    }
2632
2633
    public function testSymfonyViolations(): void
2634
    {
2635
        $violations = null;
2636
2637
        try {
2638
            $this->createHydrator()->hydrate(new class {
2639
                public string $value;
2640
            }, []);
2641
        } catch (InvalidDataException $e) {
2642
            $violations = $e->getViolations();
2643
        }
2644
2645
        $this->assertNotNull($violations);
2646
        $this->assertCount(1, $violations);
2647
        $this->assertTrue($violations->has(0));
2648
        $this->assertSame(ErrorCode::MUST_BE_PROVIDED, $violations->get(0)->getCode());
2649
        $this->assertSame('This value must be provided.', $violations->get(0)->getMessage());
2650
        $this->assertSame('value', $violations->get(0)->getPropertyPath());
2651
    }
2652
2653
    /**
2654
     * @dataProvider strictBooleanProvider
2655
     * @dataProvider nonStrictBooleanProvider
2656
     */
2657
    public function testSourcelessType($element, bool $expected): void
2658
    {
2659
        $type = Type::fromName(BuiltinType::BOOL);
2660
2661
        $this->assertSame($expected, $this->createHydrator()->castValue($element, $type));
2662
    }
2663
2664
    public function strictNullProvider(): Generator
2665
    {
2666
        yield [null, null];
2667
    }
2668
2669
    public function nonStrictNullProvider(): Generator
2670
    {
2671
        yield ['', null];
2672
        yield [' ', null];
2673
    }
2674
2675
    public function strictBooleanProvider(): Generator
2676
    {
2677
        yield [true, true];
2678
        yield [false, false];
2679
    }
2680
2681
    public function nonStrictBooleanProvider(): Generator
2682
    {
2683
        yield ['1', true];
2684
        yield ['0', false];
2685
        yield ['true', true];
2686
        yield ['false', false];
2687
        yield ['yes', true];
2688
        yield ['no', false];
2689
        yield ['on', true];
2690
        yield ['off', false];
2691
    }
2692
2693
    public function notBooleanProvider(): Generator
2694
    {
2695
        yield [0];
2696
        yield [1];
2697
        yield [42];
2698
        yield [3.14159];
2699
        yield ['foo'];
2700
        yield [[]];
2701
    }
2702
2703
    public function strictIntegerProvider(): Generator
2704
    {
2705
        yield [-1, -1];
2706
        yield [0, 0];
2707
        yield [1, 1];
2708
    }
2709
2710
    public function nonStrictIntegerProvider(): Generator
2711
    {
2712
        yield ['-1', -1];
2713
        yield ['0', 0];
2714
        yield ['1', 1];
2715
        yield ['+1', 1];
2716
    }
2717
2718
    public function notIntegerProvider(): Generator
2719
    {
2720
        yield [true];
2721
        yield [false];
2722
        yield [3.14159];
2723
        yield ['foo'];
2724
        yield [[]];
2725
    }
2726
2727
    public function strictNumberProvider(): Generator
2728
    {
2729
        yield [-1, -1.];
2730
        yield [0, 0.];
2731
        yield [1, 1.];
2732
        yield [-1., -1.];
2733
        yield [0., 0.];
2734
        yield [1., 1.];
2735
        yield [-.1, -.1];
2736
        yield [.0, .0];
2737
        yield [.1, .1];
2738
    }
2739
2740
    public function nonStrictNumberProvider(): Generator
2741
    {
2742
        yield ['-1', -1.];
2743
        yield ['0', 0.];
2744
        yield ['1', 1.];
2745
        yield ['+1', 1.];
2746
        yield ['-1.', -1.];
2747
        yield ['0.', 0.];
2748
        yield ['1.', 1.];
2749
        yield ['+1.', 1.];
2750
        yield ['-.1', -.1];
2751
        yield ['.0', .0];
2752
        yield ['.1', .1];
2753
        yield ['+.1', .1];
2754
        yield ['-1.0', -1.];
2755
        yield ['0.0', 0.];
2756
        yield ['1.0', 1.];
2757
        yield ['+1.0', 1.];
2758
        yield ['1e-1', .1];
2759
        yield ['1e1', 10.];
2760
        yield ['1e+1', 10.];
2761
        yield ['1.e-1', .1];
2762
        yield ['1.e1', 10.];
2763
        yield ['1.e+1', 10.];
2764
        yield ['.1e-1', .01];
2765
        yield ['.1e1', 1.];
2766
        yield ['.1e+1', 1.];
2767
        yield ['1.0e-1', .1];
2768
        yield ['1.0e1', 10.];
2769
        yield ['1.0e+1', 10.];
2770
    }
2771
2772
    public function notNumberProvider(): Generator
2773
    {
2774
        yield [true];
2775
        yield [false];
2776
        yield ['foo'];
2777
        yield [[]];
2778
    }
2779
2780
    public function strictStringProvider(): Generator
2781
    {
2782
        yield ['foo', 'foo'];
2783
2784
        // Must not be cast to a null
2785
        yield ['', ''];
2786
        yield [' ', ' '];
2787
2788
        // Must not be cast to a boolean
2789
        yield ['true', 'true'];
2790
        yield ['false', 'false'];
2791
        yield ['yes', 'yes'];
2792
        yield ['no', 'no'];
2793
        yield ['on', 'on'];
2794
        yield ['off', 'off'];
2795
2796
        // Must not be cast to a number
2797
        yield ['-1', '-1'];
2798
        yield ['0', '0'];
2799
        yield ['1', '1'];
2800
        yield ['+1', '+1'];
2801
        yield ['-1.', '-1.'];
2802
        yield ['0.', '0.'];
2803
        yield ['1.', '1.'];
2804
        yield ['+1.', '+1.'];
2805
        yield ['-.1', '-.1'];
2806
        yield ['.0', '.0'];
2807
        yield ['.1', '.1'];
2808
        yield ['+.1', '+.1'];
2809
        yield ['-1.0', '-1.0'];
2810
        yield ['0.0', '0.0'];
2811
        yield ['1.0', '1.0'];
2812
        yield ['+1.0', '+1.0'];
2813
        yield ['1e-1', '1e-1'];
2814
        yield ['1e1', '1e1'];
2815
        yield ['1e+1', '1e+1'];
2816
        yield ['1.e-1', '1.e-1'];
2817
        yield ['1.e1', '1.e1'];
2818
        yield ['1.e+1', '1.e+1'];
2819
        yield ['.1e-1', '.1e-1'];
2820
        yield ['.1e1', '.1e1'];
2821
        yield ['.1e+1', '.1e+1'];
2822
        yield ['1.0e-1', '1.0e-1'];
2823
        yield ['1.0e1', '1.0e1'];
2824
        yield ['1.0e+1', '1.0e+1'];
2825
    }
2826
2827
    public function nonStrictStringProvider(): Generator
2828
    {
2829
        yield [-1, '-1'];
2830
        yield [0, '0'];
2831
        yield [1, '1'];
2832
    }
2833
2834
    public function strictNotStringProvider(): Generator
2835
    {
2836
        yield [true];
2837
        yield [false];
2838
        yield [42];
2839
        yield [3.14159];
2840
        yield [[]];
2841
    }
2842
2843
    public function nonStrictNotStringProvider(): Generator
2844
    {
2845
        yield [true];
2846
        yield [false];
2847
        yield [3.14159];
2848
        yield [[]];
2849
    }
2850
2851
    public function emptyArrayProvider(): Generator
2852
    {
2853
        yield [[]];
2854
    }
2855
2856
    public function notArrayProvider(): Generator
2857
    {
2858
        yield [true];
2859
        yield [false];
2860
        yield [42];
2861
        yield [3.14159];
2862
        yield ['foo'];
2863
    }
2864
2865
    public function strictNullDataProvider(): Generator
2866
    {
2867
        foreach ($this->strictNullProvider() as [$actual, $expected]) {
2868
            yield [['value' => $actual], $expected];
2869
        }
2870
    }
2871
2872
    public function nonStrictNullDataProvider(): Generator
2873
    {
2874
        foreach ($this->nonStrictNullProvider() as [$actual, $expected]) {
2875
            yield [['value' => $actual], $expected];
2876
        }
2877
    }
2878
2879
    public function strictBooleanDataProvider(): Generator
2880
    {
2881
        foreach ($this->strictBooleanProvider() as [$actual, $expected]) {
2882
            yield [['value' => $actual], $expected];
2883
        }
2884
    }
2885
2886
    public function nonStrictBooleanDataProvider(): Generator
2887
    {
2888
        foreach ($this->nonStrictBooleanProvider() as [$actual, $expected]) {
2889
            yield [['value' => $actual], $expected];
2890
        }
2891
    }
2892
2893
    public function notBooleanDataProvider(): Generator
2894
    {
2895
        foreach ($this->notBooleanProvider() as [$actual]) {
2896
            yield [['value' => $actual]];
2897
        }
2898
    }
2899
2900
    public function strictIntegerDataProvider(): Generator
2901
    {
2902
        foreach ($this->strictIntegerProvider() as [$actual, $expected]) {
2903
            yield [['value' => $actual], $expected];
2904
        }
2905
    }
2906
2907
    public function nonStrictIntegerDataProvider(): Generator
2908
    {
2909
        foreach ($this->nonStrictIntegerProvider() as [$actual, $expected]) {
2910
            yield [['value' => $actual], $expected];
2911
        }
2912
    }
2913
2914
    public function notIntegerDataProvider(): Generator
2915
    {
2916
        foreach ($this->notIntegerProvider() as [$actual]) {
2917
            yield [['value' => $actual]];
2918
        }
2919
    }
2920
2921
    public function strictNumberDataProvider(): Generator
2922
    {
2923
        foreach ($this->strictNumberProvider() as [$actual, $expected]) {
2924
            yield [['value' => $actual], $expected];
2925
        }
2926
    }
2927
2928
    public function nonStrictNumberDataProvider(): Generator
2929
    {
2930
        foreach ($this->nonStrictNumberProvider() as [$actual, $expected]) {
2931
            yield [['value' => $actual], $expected];
2932
        }
2933
    }
2934
2935
    public function notNumberDataProvider(): Generator
2936
    {
2937
        foreach ($this->notNumberProvider() as [$actual]) {
2938
            yield [['value' => $actual]];
2939
        }
2940
    }
2941
2942
    public function strictStringDataProvider(): Generator
2943
    {
2944
        foreach ($this->strictStringProvider() as [$actual, $expected]) {
2945
            yield [['value' => $actual], $expected];
2946
        }
2947
    }
2948
2949
    public function nonStrictStringDataProvider(): Generator
2950
    {
2951
        foreach ($this->nonStrictStringProvider() as [$actual, $expected]) {
2952
            yield [['value' => $actual], $expected];
2953
        }
2954
    }
2955
2956
    public function strictNotStringDataProvider(): Generator
2957
    {
2958
        foreach ($this->strictNotStringProvider() as [$actual]) {
2959
            yield [['value' => $actual]];
2960
        }
2961
    }
2962
2963
    public function nonStrictNotStringDataProvider(): Generator
2964
    {
2965
        foreach ($this->nonStrictNotStringProvider() as [$actual]) {
2966
            yield [['value' => $actual]];
2967
        }
2968
    }
2969
2970
    public function integerEnumDataProvider(): Generator
2971
    {
2972
        if (PHP_VERSION_ID < 80100) {
2973
            return [[[], null]];
2974
        }
2975
2976
        $foo = Fixture\IntegerEnum::FOO;
2977
        $bar = Fixture\IntegerEnum::BAR;
2978
        $baz = Fixture\IntegerEnum::BAZ;
2979
2980
        yield [['value' => $foo->value], $foo];
2981
        yield [['value' => $bar->value], $bar];
2982
        yield [['value' => $baz->value], $baz];
2983
2984
        yield [['value' => (string) $foo->value], $foo];
2985
        yield [['value' => (string) $bar->value], $bar];
2986
        yield [['value' => (string) $baz->value], $baz];
2987
    }
2988
2989
    public function stringEnumDataProvider(): Generator
2990
    {
2991
        if (PHP_VERSION_ID < 80100) {
2992
            return [[[], null]];
2993
        }
2994
2995
        $foo = Fixture\StringEnum::FOO;
2996
        $bar = Fixture\StringEnum::BAR;
2997
        $baz = Fixture\StringEnum::BAZ;
2998
2999
        yield [['value' => $foo->value], $foo];
3000
        yield [['value' => $bar->value], $bar];
3001
        yield [['value' => $baz->value], $baz];
3002
    }
3003
3004
    public function myclabsEnumDataProvider(): Generator
3005
    {
3006
        $foo = Fixture\MyclabsEnum::FOO();
0 ignored issues
show
Bug introduced by
The method FOO() does not exist on Sunrise\Hydrator\Tests\Fixture\MyclabsEnum. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

3006
        /** @scrutinizer ignore-call */ 
3007
        $foo = Fixture\MyclabsEnum::FOO();
Loading history...
3007
        $bar = Fixture\MyclabsEnum::BAR();
0 ignored issues
show
Bug introduced by
The method BAR() does not exist on Sunrise\Hydrator\Tests\Fixture\MyclabsEnum. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

3007
        /** @scrutinizer ignore-call */ 
3008
        $bar = Fixture\MyclabsEnum::BAR();
Loading history...
3008
        $baz = Fixture\MyclabsEnum::BAZ();
0 ignored issues
show
Bug introduced by
The method BAZ() does not exist on Sunrise\Hydrator\Tests\Fixture\MyclabsEnum. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

3008
        /** @scrutinizer ignore-call */ 
3009
        $baz = Fixture\MyclabsEnum::BAZ();
Loading history...
3009
3010
        yield [['value' => $foo->getValue()], $foo];
3011
        yield [['value' => $bar->getValue()], $bar];
3012
        yield [['value' => $baz->getValue()], $baz];
3013
    }
3014
3015
    public function arrayDataProvider(): Generator
3016
    {
3017
        yield [['value' => []], []];
3018
        yield [['value' => ['foo']], ['foo']];
3019
    }
3020
3021
    public function notArrayDataProvider(): Generator
3022
    {
3023
        foreach ($this->notArrayProvider() as [$actual]) {
3024
            yield [['value' => $actual]];
3025
        }
3026
    }
3027
3028
    public function timestampDataProvider(): Generator
3029
    {
3030
        // default formatted timestamp
3031
        $timestamp = date(TimestampTypeConverter::DEFAULT_FORMAT);
3032
3033
        yield [['value' => $timestamp], $timestamp];
3034
        yield [['value' => $timestamp], $timestamp, TimestampTypeConverter::DEFAULT_FORMAT];
3035
3036
        yield [['value' => '700101'], '700101', 'ymd'];
3037
        yield [['value' => '000000'], '000000', 'His'];
3038
3039
        yield [['value' => '-1'], '-1', 'U'];
3040
        yield [['value' => '0'], '0', 'U'];
3041
        yield [['value' => '1'], '1', 'U'];
3042
        yield [['value' => '+1'], '1', 'U'];
3043
3044
        // Must be converted to a string...
3045
        yield [['value' => -1], '-1', 'U'];
3046
        yield [['value' => 0], '0', 'U'];
3047
        yield [['value' => 1], '1', 'U'];
3048
3049
        // The timezone must be applied...
3050
        yield [['value' => '00:00:00'], '00:00:00', 'H:i:s', 'Europe/Kiev'];
3051
3052
        // ISO 8601
3053
        yield [['value' => '00:00:00.123456'], '00:00:00.123456', 'H:i:s.u'];
3054
        yield [['value' => '00:00:00.123456+00:00'], '00:00:00.123456+00:00', 'H:i:s.uP'];
3055
        yield [['value' => 'Monday 00:00:00.123456'], 'Monday 00:00:00.123456', 'l H:i:s.u'];
3056
        yield [['value' => '00:00:00.1234567890'], '00:00:00.123456', 'H:i:s.u'];
3057
        yield [['value' => '00:00:00.1234567890+00:00'], '00:00:00.123456+00:00', 'H:i:s.uP'];
3058
        yield [['value' => 'Monday 00:00:00.1234567890'], 'Monday 00:00:00.123456', 'l H:i:s.u'];
3059
    }
3060
3061
    public function invalidTimestampDataProvider(): Generator
3062
    {
3063
        yield [
3064
            ['value' => '01/01/1970 00:00:00'],
3065
            'Y-m-d H:i:s',
3066
            ErrorCode::INVALID_TIMESTAMP,
3067
            'This value is not a valid timestamp; expected format: Y-m-d H:i:s.',
3068
        ];
3069
3070
        yield [
3071
            ['value' => '1970-01-01 00:00:00'],
3072
            'U',
3073
            ErrorCode::MUST_BE_INTEGER,
3074
            'This value must be of type integer.',
3075
        ];
3076
3077
        yield [
3078
            ['value' => 0],
3079
            'Y',
3080
            ErrorCode::MUST_BE_STRING,
3081
            'This value must be of type string.',
3082
        ];
3083
    }
3084
3085
    public function timezoneDataProvider(): Generator
3086
    {
3087
        foreach (DateTimeZone::listIdentifiers() as $timezone) {
3088
            yield [['value' => $timezone], $timezone];
3089
        }
3090
    }
3091
3092
    public function uuidDataProvider(): Generator
3093
    {
3094
        yield [['value' => '207ddb61-c300-4368-9f26-33d0a99eac00'], '207ddb61-c300-4368-9f26-33d0a99eac00'];
3095
    }
3096
3097
    private function createHydrator(array $context = [], array $typeConverters = []): HydratorInterface
3098
    {
3099
        $hydrator = new Hydrator($context, $typeConverters);
3100
        if (PHP_VERSION_ID < 80000) {
3101
            $hydrator->useDefaultAnnotationReader();
0 ignored issues
show
Deprecated Code introduced by
The function Sunrise\Hydrator\Hydrato...faultAnnotationReader() has been deprecated: 3.2.0 Use the {@see setAnnotationReader()} method with the {@see DoctrineAnnotationReader::default()} attribute. ( Ignorable by Annotation )

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

3101
            /** @scrutinizer ignore-deprecated */ $hydrator->useDefaultAnnotationReader();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
3102
        }
3103
3104
        return $hydrator;
3105
    }
3106
3107
    private function phpRequired(string $version): void
3108
    {
3109
        if (version_compare(PHP_VERSION, $version, '<')) {
3110
            $this->markTestSkipped(sprintf('PHP %s is required.', $version));
3111
        }
3112
    }
3113
3114
    private function assertInvalidValueExceptionCount(int $expectedCount): void
3115
    {
3116
        $this->invalidValueExceptionCount = $expectedCount;
3117
    }
3118
3119
    private function assertInvalidValueExceptionMessage(int $exceptionIndex, string $expectedMessage): void
3120
    {
3121
        $this->invalidValueExceptionMessage[] = [$exceptionIndex, $expectedMessage];
3122
    }
3123
3124
    private function assertInvalidValueExceptionPropertyPath(int $exceptionIndex, string $expectedPropertyPath): void
3125
    {
3126
        $this->invalidValueExceptionPropertyPath[] = [$exceptionIndex, $expectedPropertyPath];
3127
    }
3128
3129
    private function assertInvalidValueExceptionErrorCode(int $exceptionIndex, string $expectedErrorCode): void
3130
    {
3131
        $this->invalidValueExceptionErrorCode[] = [$exceptionIndex, $expectedErrorCode];
3132
    }
3133
3134
    protected function runTest(): void
3135
    {
3136
        $invalidDataExceptionHandled = false;
3137
3138
        try {
3139
            parent::runTest();
3140
        } catch (InvalidDataException $invalidDataException) {
3141
            $invalidDataExceptionMessages = [];
3142
            foreach ($invalidDataException->getExceptions() as $invalidValueException) {
3143
                $invalidDataExceptionMessages[] = sprintf(
3144
                    '[%s] %s',
3145
                    $invalidValueException->getPropertyPath(),
3146
                    $invalidValueException->getMessage(),
3147
                );
3148
            }
3149
3150
            if (isset($this->invalidValueExceptionCount)) {
3151
                $invalidDataExceptionHandled = true;
3152
                $this->assertCount(
3153
                    $this->invalidValueExceptionCount,
0 ignored issues
show
Bug introduced by
It seems like $this->invalidValueExceptionCount can also be of type null; however, parameter $expectedCount of PHPUnit\Framework\Assert::assertCount() does only seem to accept integer, 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

3153
                    /** @scrutinizer ignore-type */ $this->invalidValueExceptionCount,
Loading history...
3154
                    $invalidDataException->getExceptions(),
3155
                    \join(\PHP_EOL, $invalidDataExceptionMessages),
3156
                );
3157
            }
3158
3159
            foreach ($this->invalidValueExceptionMessage as [$index, $invalidValueExceptionMessage]) {
3160
                $invalidDataExceptionHandled = true;
3161
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3162
                $this->assertSame(
3163
                    $invalidValueExceptionMessage,
3164
                    $invalidDataException->getExceptions()[$index]->getMessage(),
3165
                );
3166
            }
3167
3168
            foreach ($this->invalidValueExceptionPropertyPath as [$index, $invalidValueExceptionPropertyPath]) {
3169
                $invalidDataExceptionHandled = true;
3170
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3171
                $this->assertSame(
3172
                    $invalidValueExceptionPropertyPath,
3173
                    $invalidDataException->getExceptions()[$index]->getPropertyPath(),
3174
                );
3175
            }
3176
3177
            foreach ($this->invalidValueExceptionErrorCode as [$index, $invalidValueExceptionErrorCode]) {
3178
                $invalidDataExceptionHandled = true;
3179
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3180
                $this->assertSame(
3181
                    $invalidValueExceptionErrorCode,
3182
                    $invalidDataException->getExceptions()[$index]->getErrorCode(),
3183
                );
3184
            }
3185
3186
            if (!$invalidDataExceptionHandled) {
3187
                throw $invalidDataException;
3188
            }
3189
        } finally {
3190
            $this->invalidValueExceptionCount = null;
3191
            $this->invalidValueExceptionMessage = [];
3192
            $this->invalidValueExceptionPropertyPath = [];
3193
            $this->invalidValueExceptionErrorCode = [];
3194
3195
            if ($invalidDataExceptionHandled) {
3196
                $this->assertTrue(true);
3197
            }
3198
        }
3199
    }
3200
}
3201