Passed
Branch main (e71635)
by Anatoly
05:44
created

HydratorTest.php$130 ➔ testUnstantiableObject()   A

Complexity

Conditions 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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

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

2947
        /** @scrutinizer ignore-call */ 
2948
        $foo = Stub\MyclabsEnum::FOO();
Loading history...
2948
        $bar = Stub\MyclabsEnum::BAR();
0 ignored issues
show
Bug introduced by
The method BAR() does not exist on Sunrise\Hydrator\Tests\Stub\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

2948
        /** @scrutinizer ignore-call */ 
2949
        $bar = Stub\MyclabsEnum::BAR();
Loading history...
2949
        $baz = Stub\MyclabsEnum::BAZ();
0 ignored issues
show
Bug introduced by
The method BAZ() does not exist on Sunrise\Hydrator\Tests\Stub\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

2949
        /** @scrutinizer ignore-call */ 
2950
        $baz = Stub\MyclabsEnum::BAZ();
Loading history...
2950
2951
        yield [['value' => $foo->getValue()], $foo];
2952
        yield [['value' => $bar->getValue()], $bar];
2953
        yield [['value' => $baz->getValue()], $baz];
2954
    }
2955
2956
    public function arrayDataProvider(): Generator
2957
    {
2958
        yield [['value' => []], []];
2959
        yield [['value' => ['foo']], ['foo']];
2960
    }
2961
2962
    public function notArrayDataProvider(): Generator
2963
    {
2964
        foreach ($this->notArrayProvider() as [$actual]) {
2965
            yield [['value' => $actual]];
2966
        }
2967
    }
2968
2969
    public function timestampDataProvider(): Generator
2970
    {
2971
        // default formatted timestamp
2972
        $timestamp = date(TimestampTypeConverter::DEFAULT_FORMAT);
2973
2974
        yield [['value' => $timestamp], $timestamp];
2975
        yield [['value' => $timestamp], $timestamp, TimestampTypeConverter::DEFAULT_FORMAT];
2976
2977
        yield [['value' => '700101'], '700101', 'ymd'];
2978
        yield [['value' => '000000'], '000000', 'His'];
2979
2980
        yield [['value' => '-1'], '-1', 'U'];
2981
        yield [['value' => '0'], '0', 'U'];
2982
        yield [['value' => '1'], '1', 'U'];
2983
        yield [['value' => '+1'], '1', 'U'];
2984
2985
        // Must be converted to a string...
2986
        yield [['value' => -1], '-1', 'U'];
2987
        yield [['value' => 0], '0', 'U'];
2988
        yield [['value' => 1], '1', 'U'];
2989
2990
        // The timezone must be applied...
2991
        yield [['value' => '00:00:00'], '00:00:00', 'H:i:s', 'Europe/Kiev'];
2992
    }
2993
2994
    public function invalidTimestampDataProvider(): Generator
2995
    {
2996
        yield [
2997
            ['value' => '01/01/1970 00:00:00'],
2998
            'Y-m-d H:i:s',
2999
            ErrorCode::INVALID_TIMESTAMP,
3000
            'This value is not a valid timestamp.',
3001
        ];
3002
3003
        yield [
3004
            ['value' => '1970-01-01 00:00:00'],
3005
            'U',
3006
            ErrorCode::MUST_BE_INTEGER,
3007
            'This value must be of type integer.',
3008
        ];
3009
3010
        yield [
3011
            ['value' => 0],
3012
            'Y',
3013
            ErrorCode::MUST_BE_STRING,
3014
            'This value must be of type string.',
3015
        ];
3016
    }
3017
3018
    public function timezoneDataProvider(): Generator
3019
    {
3020
        foreach (DateTimeZone::listIdentifiers() as $timezone) {
3021
            yield [['value' => $timezone], $timezone];
3022
        }
3023
    }
3024
3025
    public function uuidDataProvider(): Generator
3026
    {
3027
        yield [['value' => '207ddb61-c300-4368-9f26-33d0a99eac00'], '207ddb61-c300-4368-9f26-33d0a99eac00'];
3028
    }
3029
3030
    private function createHydrator(array $context = []): HydratorInterface
3031
    {
3032
        $hydrator = new Hydrator($context);
3033
        if (PHP_VERSION_ID < 80000) {
3034
            $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

3034
            /** @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...
3035
        }
3036
3037
        return $hydrator;
3038
    }
3039
3040
    private function phpRequired(string $version): void
3041
    {
3042
        if (version_compare(PHP_VERSION, $version, '<')) {
3043
            $this->markTestSkipped(sprintf('PHP %s is required.', $version));
3044
        }
3045
    }
3046
3047
    private function assertInvalidValueExceptionCount(int $expectedCount): void
3048
    {
3049
        $this->invalidValueExceptionCount = $expectedCount;
3050
    }
3051
3052
    private function assertInvalidValueExceptionMessage(int $exceptionIndex, string $expectedMessage): void
3053
    {
3054
        $this->invalidValueExceptionMessage[] = [$exceptionIndex, $expectedMessage];
3055
    }
3056
3057
    private function assertInvalidValueExceptionPropertyPath(int $exceptionIndex, string $expectedPropertyPath): void
3058
    {
3059
        $this->invalidValueExceptionPropertyPath[] = [$exceptionIndex, $expectedPropertyPath];
3060
    }
3061
3062
    private function assertInvalidValueExceptionErrorCode(int $exceptionIndex, string $expectedErrorCode): void
3063
    {
3064
        $this->invalidValueExceptionErrorCode[] = [$exceptionIndex, $expectedErrorCode];
3065
    }
3066
3067
    protected function runTest(): void
3068
    {
3069
        $invalidDataExceptionHandled = false;
3070
3071
        try {
3072
            parent::runTest();
3073
        } catch (InvalidDataException $invalidDataException) {
3074
            $invalidDataExceptionMessages = [];
3075
            foreach ($invalidDataException->getExceptions() as $invalidValueException) {
3076
                $invalidDataExceptionMessages[] = sprintf(
3077
                    '[%s] %s',
3078
                    $invalidValueException->getPropertyPath(),
3079
                    $invalidValueException->getMessage(),
3080
                );
3081
            }
3082
3083
            if (isset($this->invalidValueExceptionCount)) {
3084
                $invalidDataExceptionHandled = true;
3085
                $this->assertCount(
3086
                    $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

3086
                    /** @scrutinizer ignore-type */ $this->invalidValueExceptionCount,
Loading history...
3087
                    $invalidDataException->getExceptions(),
3088
                    \join(\PHP_EOL, $invalidDataExceptionMessages),
3089
                );
3090
            }
3091
3092
            foreach ($this->invalidValueExceptionMessage as [$index, $invalidValueExceptionMessage]) {
3093
                $invalidDataExceptionHandled = true;
3094
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3095
                $this->assertSame(
3096
                    $invalidValueExceptionMessage,
3097
                    $invalidDataException->getExceptions()[$index]->getMessage(),
3098
                );
3099
            }
3100
3101
            foreach ($this->invalidValueExceptionPropertyPath as [$index, $invalidValueExceptionPropertyPath]) {
3102
                $invalidDataExceptionHandled = true;
3103
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3104
                $this->assertSame(
3105
                    $invalidValueExceptionPropertyPath,
3106
                    $invalidDataException->getExceptions()[$index]->getPropertyPath(),
3107
                );
3108
            }
3109
3110
            foreach ($this->invalidValueExceptionErrorCode as [$index, $invalidValueExceptionErrorCode]) {
3111
                $invalidDataExceptionHandled = true;
3112
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3113
                $this->assertSame(
3114
                    $invalidValueExceptionErrorCode,
3115
                    $invalidDataException->getExceptions()[$index]->getErrorCode(),
3116
                );
3117
            }
3118
3119
            if (!$invalidDataExceptionHandled) {
3120
                throw $invalidDataException;
3121
            }
3122
        } finally {
3123
            $this->invalidValueExceptionCount = null;
3124
            $this->invalidValueExceptionMessage = [];
3125
            $this->invalidValueExceptionPropertyPath = [];
3126
            $this->invalidValueExceptionErrorCode = [];
3127
3128
            if ($invalidDataExceptionHandled) {
3129
                $this->assertTrue(true);
3130
            }
3131
        }
3132
    }
3133
}
3134