Passed
Push — main ( df8d6f...066d74 )
by Anatoly
04:33 queued 15s
created

HydratorTest.php$141 ➔ strictNotStringProvider()   A

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 7
rs 10
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\DefaultValue;
20
use Sunrise\Hydrator\Annotation\Ignore;
21
use Sunrise\Hydrator\Annotation\Subtype;
22
use Sunrise\Hydrator\Dictionary\BuiltinType;
23
use Sunrise\Hydrator\Dictionary\ContextKey;
24
use Sunrise\Hydrator\Dictionary\ErrorCode;
25
use Sunrise\Hydrator\Exception\InvalidDataException;
26
use Sunrise\Hydrator\Exception\InvalidObjectException;
27
use Sunrise\Hydrator\Hydrator;
28
use Sunrise\Hydrator\HydratorInterface;
29
use Sunrise\Hydrator\Tests\Fixture\BooleanArrayCollection;
30
use Sunrise\Hydrator\Type;
31
use Sunrise\Hydrator\TypeConverter\TimestampTypeConverter;
32
use Sunrise\Hydrator\TypeConverterInterface;
33
34
use function date;
35
use function get_class;
36
use function join;
37
use function sprintf;
38
use function version_compare;
39
40
use const PHP_VERSION;
41
use const PHP_VERSION_ID;
42
43
class HydratorTest extends TestCase
44
{
45
    private ?int $invalidValueExceptionCount = null;
46
    private array $invalidValueExceptionMessage = [];
47
    private array $invalidValueExceptionPropertyPath = [];
48
    private array $invalidValueExceptionErrorCode = [];
49
50
    public function testIssue25(): void
51
    {
52
        $this->assertInvalidValueExceptionCount(1);
53
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
54
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
55
        $this->assertInvalidValueExceptionPropertyPath(0, 'foo');
56
        $this->createHydrator()->hydrate(Fixture\Issue25::class, []);
57
    }
58
59
    public function testStdClassWithArrayProperty(): void
60
    {
61
        $object = new class {
62
            public array $value;
63
        };
64
65
        $this->assertInvalidValueExceptionCount(0);
66
        $this->createHydrator()->hydrate($object, ['value' => (object) ['foo' => 'bar']]);
67
        $this->assertSame(['foo' => 'bar'], $object->value);
68
    }
69
70
    public function testStdClassWithArrayAccessProperty(): void
71
    {
72
        $object = new class {
73
            public Fixture\Collection $value;
74
        };
75
76
        $this->assertInvalidValueExceptionCount(0);
77
        $this->createHydrator()->hydrate($object, ['value' => (object) ['foo' => 'bar']]);
78
        $this->assertSame(['foo' => 'bar'], $object->value->elements);
79
    }
80
81
    public function testStdClassWithAssociationProperty(): void
82
    {
83
        $object = new class {
84
            public Fixture\StringAssociation $value;
85
        };
86
87
        $this->assertInvalidValueExceptionCount(0);
88
        $this->createHydrator()->hydrate($object, ['value' => (object) ['value' => 'foo']]);
89
        $this->assertSame('foo', $object->value->value);
90
    }
91
92
    public function testCreateHydratorWithTypeConverters(): void
93
    {
94
        $object = new class {
95
            public \Closure $foo;
96
        };
97
98
        $typeConverter = $this->createMock(TypeConverterInterface::class);
99
100
        $typeConverter->method('castValue')->willReturnCallback(
101
            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

101
            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

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

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

3055
        /** @scrutinizer ignore-call */ 
3056
        $foo = Fixture\MyclabsEnum::FOO();
Loading history...
3056
        $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

3056
        /** @scrutinizer ignore-call */ 
3057
        $bar = Fixture\MyclabsEnum::BAR();
Loading history...
3057
        $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

3057
        /** @scrutinizer ignore-call */ 
3058
        $baz = Fixture\MyclabsEnum::BAZ();
Loading history...
3058
3059
        yield [['value' => $foo->getValue()], $foo];
3060
        yield [['value' => $bar->getValue()], $bar];
3061
        yield [['value' => $baz->getValue()], $baz];
3062
    }
3063
3064
    public function arrayDataProvider(): Generator
3065
    {
3066
        yield [['value' => []], []];
3067
        yield [['value' => ['foo']], ['foo']];
3068
    }
3069
3070
    public function notArrayDataProvider(): Generator
3071
    {
3072
        foreach ($this->notArrayProvider() as [$actual]) {
3073
            yield [['value' => $actual]];
3074
        }
3075
    }
3076
3077
    public function timestampDataProvider(): Generator
3078
    {
3079
        // default formatted timestamp
3080
        $timestamp = date(TimestampTypeConverter::DEFAULT_FORMAT);
3081
3082
        yield [['value' => $timestamp], $timestamp];
3083
        yield [['value' => $timestamp], $timestamp, TimestampTypeConverter::DEFAULT_FORMAT];
3084
3085
        yield [['value' => '700101'], '700101', 'ymd'];
3086
        yield [['value' => '000000'], '000000', 'His'];
3087
3088
        yield [['value' => '-1'], '-1', 'U'];
3089
        yield [['value' => '0'], '0', 'U'];
3090
        yield [['value' => '1'], '1', 'U'];
3091
        yield [['value' => '+1'], '1', 'U'];
3092
3093
        // Must be converted to a string...
3094
        yield [['value' => -1], '-1', 'U'];
3095
        yield [['value' => 0], '0', 'U'];
3096
        yield [['value' => 1], '1', 'U'];
3097
3098
        // The timezone must be applied...
3099
        yield [['value' => '00:00:00'], '00:00:00', 'H:i:s', 'Europe/Kiev'];
3100
3101
        // ISO 8601
3102
        yield [['value' => '00:00:00.123456'], '00:00:00.123456', 'H:i:s.u'];
3103
        yield [['value' => '00:00:00.123456+00:00'], '00:00:00.123456+00:00', 'H:i:s.uP'];
3104
        yield [['value' => 'Monday 00:00:00.123456'], 'Monday 00:00:00.123456', 'l H:i:s.u'];
3105
        yield [['value' => '00:00:00.1234567890'], '00:00:00.123456', 'H:i:s.u'];
3106
        yield [['value' => '00:00:00.1234567890+00:00'], '00:00:00.123456+00:00', 'H:i:s.uP'];
3107
        yield [['value' => 'Monday 00:00:00.1234567890'], 'Monday 00:00:00.123456', 'l H:i:s.u'];
3108
    }
3109
3110
    public function invalidTimestampDataProvider(): Generator
3111
    {
3112
        yield [
3113
            ['value' => '01/01/1970 00:00:00'],
3114
            'Y-m-d H:i:s',
3115
            ErrorCode::INVALID_TIMESTAMP,
3116
            'This value is not a valid timestamp; expected format: Y-m-d H:i:s.',
3117
        ];
3118
3119
        yield [
3120
            ['value' => '1970-01-01 00:00:00'],
3121
            'U',
3122
            ErrorCode::MUST_BE_INTEGER,
3123
            'This value must be of type integer.',
3124
        ];
3125
3126
        yield [
3127
            ['value' => 0],
3128
            'Y',
3129
            ErrorCode::MUST_BE_STRING,
3130
            'This value must be of type string.',
3131
        ];
3132
    }
3133
3134
    public function timezoneDataProvider(): Generator
3135
    {
3136
        foreach (DateTimeZone::listIdentifiers() as $timezone) {
3137
            yield [['value' => $timezone], $timezone];
3138
        }
3139
    }
3140
3141
    public function uuidDataProvider(): Generator
3142
    {
3143
        yield [['value' => '207ddb61-c300-4368-9f26-33d0a99eac00'], '207ddb61-c300-4368-9f26-33d0a99eac00'];
3144
    }
3145
3146
    private function createHydrator(array $context = [], array $typeConverters = []): HydratorInterface
3147
    {
3148
        $hydrator = new Hydrator($context, $typeConverters);
3149
        if (PHP_VERSION_ID < 80000) {
3150
            $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

3150
            /** @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...
3151
        }
3152
3153
        return $hydrator;
3154
    }
3155
3156
    private function phpRequired(string $version): void
3157
    {
3158
        if (version_compare(PHP_VERSION, $version, '<')) {
3159
            $this->markTestSkipped(sprintf('PHP %s is required.', $version));
3160
        }
3161
    }
3162
3163
    private function assertInvalidValueExceptionCount(int $expectedCount): void
3164
    {
3165
        $this->invalidValueExceptionCount = $expectedCount;
3166
    }
3167
3168
    private function assertInvalidValueExceptionMessage(int $exceptionIndex, string $expectedMessage): void
3169
    {
3170
        $this->invalidValueExceptionMessage[] = [$exceptionIndex, $expectedMessage];
3171
    }
3172
3173
    private function assertInvalidValueExceptionPropertyPath(int $exceptionIndex, string $expectedPropertyPath): void
3174
    {
3175
        $this->invalidValueExceptionPropertyPath[] = [$exceptionIndex, $expectedPropertyPath];
3176
    }
3177
3178
    private function assertInvalidValueExceptionErrorCode(int $exceptionIndex, string $expectedErrorCode): void
3179
    {
3180
        $this->invalidValueExceptionErrorCode[] = [$exceptionIndex, $expectedErrorCode];
3181
    }
3182
3183
    protected function runTest(): void
3184
    {
3185
        $invalidDataExceptionHandled = false;
3186
3187
        try {
3188
            parent::runTest();
3189
        } catch (InvalidDataException $invalidDataException) {
3190
            $invalidDataExceptionMessages = [];
3191
            foreach ($invalidDataException->getExceptions() as $invalidValueException) {
3192
                $invalidDataExceptionMessages[] = sprintf(
3193
                    '[%s] %s',
3194
                    $invalidValueException->getPropertyPath(),
3195
                    $invalidValueException->getMessage(),
3196
                );
3197
            }
3198
3199
            if (isset($this->invalidValueExceptionCount)) {
3200
                $invalidDataExceptionHandled = true;
3201
                $this->assertCount(
3202
                    $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

3202
                    /** @scrutinizer ignore-type */ $this->invalidValueExceptionCount,
Loading history...
3203
                    $invalidDataException->getExceptions(),
3204
                    \join(\PHP_EOL, $invalidDataExceptionMessages),
3205
                );
3206
            }
3207
3208
            foreach ($this->invalidValueExceptionMessage as [$index, $invalidValueExceptionMessage]) {
3209
                $invalidDataExceptionHandled = true;
3210
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3211
                $this->assertSame(
3212
                    $invalidValueExceptionMessage,
3213
                    $invalidDataException->getExceptions()[$index]->getMessage(),
3214
                );
3215
            }
3216
3217
            foreach ($this->invalidValueExceptionPropertyPath as [$index, $invalidValueExceptionPropertyPath]) {
3218
                $invalidDataExceptionHandled = true;
3219
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3220
                $this->assertSame(
3221
                    $invalidValueExceptionPropertyPath,
3222
                    $invalidDataException->getExceptions()[$index]->getPropertyPath(),
3223
                );
3224
            }
3225
3226
            foreach ($this->invalidValueExceptionErrorCode as [$index, $invalidValueExceptionErrorCode]) {
3227
                $invalidDataExceptionHandled = true;
3228
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3229
                $this->assertSame(
3230
                    $invalidValueExceptionErrorCode,
3231
                    $invalidDataException->getExceptions()[$index]->getErrorCode(),
3232
                );
3233
            }
3234
3235
            if (!$invalidDataExceptionHandled) {
3236
                throw $invalidDataException;
3237
            }
3238
        } finally {
3239
            $this->invalidValueExceptionCount = null;
3240
            $this->invalidValueExceptionMessage = [];
3241
            $this->invalidValueExceptionPropertyPath = [];
3242
            $this->invalidValueExceptionErrorCode = [];
3243
3244
            if ($invalidDataExceptionHandled) {
3245
                $this->assertTrue(true);
3246
            }
3247
        }
3248
    }
3249
}
3250