Passed
Push — main ( 131673...f122aa )
by Anatoly
04:13 queued 13s
created

php$113 ➔ testHydrateMyclabsEnumPropertyWithUnknownValue()   A

Complexity

Conditions 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 12
rs 9.8666
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Sunrise\Hydrator\Tests;
6
7
use DateTimeImmutable;
8
use DateTimeZone;
9
use Generator;
10
use PHPUnit\Framework\TestCase;
11
use ReflectionClass;
12
use ReflectionFunction;
13
use ReflectionIntersectionType;
0 ignored issues
show
Bug introduced by
The type ReflectionIntersectionType was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
14
use ReflectionMethod;
15
use ReflectionNamedType;
16
use ReflectionParameter;
17
use ReflectionUnionType;
18
use Sunrise\Hydrator\Annotation\Alias;
19
use Sunrise\Hydrator\Annotation\Ignore;
20
use Sunrise\Hydrator\Annotation\Subtype;
21
use Sunrise\Hydrator\Dictionary\BuiltinType;
22
use Sunrise\Hydrator\Dictionary\ContextKey;
23
use Sunrise\Hydrator\Dictionary\ErrorCode;
24
use Sunrise\Hydrator\Exception\InvalidDataException;
25
use Sunrise\Hydrator\Exception\InvalidObjectException;
26
use Sunrise\Hydrator\Hydrator;
27
use Sunrise\Hydrator\HydratorInterface;
28
use Sunrise\Hydrator\Type;
29
use Sunrise\Hydrator\TypeConverter\TimestampTypeConverter;
30
31
use function date;
32
use function get_class;
33
use function join;
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(Fixture\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 Fixture\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 Fixture\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 Fixture\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 ?Fixture\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 ?Fixture\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 Fixture\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 Fixture\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 Fixture\IntegerEnum $value;
603
        };
604
605
        $this->assertInvalidValueExceptionCount(1);
606
        // phpcs:ignore Generic.Files.LineLength
607
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid choice; expected values: ' . join(', ', Fixture\IntegerEnum::values()) . '.');
608
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_CHOICE);
609
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
610
        $this->createHydrator()->hydrate($object, ['value' => 42]);
611
    }
612
613
    /**
614
     * @group enum
615
     * @group integer-enum
616
     */
617
    public function testHydrateIntegerEnumPropertyWithoutValue(): void
618
    {
619
        $this->phpRequired('8.1');
620
621
        $object = new class {
622
            public Fixture\IntegerEnum $value;
623
        };
624
625
        $this->assertInvalidValueExceptionCount(1);
626
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
627
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
628
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
629
        $this->createHydrator()->hydrate($object, []);
630
    }
631
632
    /**
633
     * @group enum
634
     * @group string-enum
635
     * @dataProvider stringEnumDataProvider
636
     */
637
    public function testHydrateStringEnumProperty(array $data, $expected): void
638
    {
639
        $this->phpRequired('8.1');
640
641
        $object = new class {
642
            public Fixture\StringEnum $value;
643
        };
644
645
        $this->assertInvalidValueExceptionCount(0);
646
        $this->createHydrator()->hydrate($object, $data);
647
        $this->assertSame($expected, $object->value);
648
    }
649
650
    /**
651
     * @group enum
652
     * @group string-enum
653
     * @dataProvider stringEnumDataProvider
654
     * @dataProvider strictNullDataProvider
655
     * @dataProvider nonStrictNullDataProvider
656
     */
657
    public function testHydrateNullableStringEnumProperty(array $data, $expected): void
658
    {
659
        $this->phpRequired('8.1');
660
661
        $object = new class {
662
            public ?Fixture\StringEnum $value;
663
        };
664
665
        $this->assertInvalidValueExceptionCount(0);
666
        $this->createHydrator()->hydrate($object, $data);
667
        $this->assertSame($expected, $object->value);
668
    }
669
670
    /**
671
     * @group enum
672
     * @group string-enum
673
     * @dataProvider stringEnumDataProvider
674
     * @dataProvider emptyArrayProvider
675
     */
676
    public function testHydrateOptionalStringEnumProperty(array $data, $expected = null): void
677
    {
678
        $this->phpRequired('8.1');
679
680
        $object = new class {
681
            public ?Fixture\StringEnum $value = null;
682
        };
683
684
        $this->assertInvalidValueExceptionCount(0);
685
        $this->createHydrator()->hydrate($object, $data);
686
        $this->assertSame($expected, $object->value);
687
    }
688
689
    /**
690
     * @group enum
691
     * @group string-enum
692
     * @dataProvider strictNullDataProvider
693
     * @dataProvider nonStrictNullDataProvider
694
     */
695
    public function testHydrateStringEnumPropertyWithEmptyValue(array $data): void
696
    {
697
        $this->phpRequired('8.1');
698
699
        $object = new class {
700
            public Fixture\StringEnum $value;
701
        };
702
703
        $this->assertInvalidValueExceptionCount(1);
704
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
705
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
706
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
707
        $this->createHydrator()->hydrate($object, $data);
708
    }
709
710
    /**
711
     * @group enum
712
     * @group string-enum
713
     * @dataProvider strictNotStringDataProvider
714
     */
715
    public function testHydrateStringEnumPropertyWithInvalidValue(array $data): void
716
    {
717
        $this->phpRequired('8.1');
718
719
        $object = new class {
720
            public Fixture\StringEnum $value;
721
        };
722
723
        $this->assertInvalidValueExceptionCount(1);
724
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type string.');
725
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_STRING);
726
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
727
        $this->createHydrator()->hydrate($object, $data);
728
    }
729
730
    /**
731
     * @group enum
732
     * @group string-enum
733
     */
734
    public function testHydrateStringEnumPropertyWithUnknownValue(): void
735
    {
736
        $this->phpRequired('8.1');
737
738
        $object = new class {
739
            public Fixture\StringEnum $value;
740
        };
741
742
        $this->assertInvalidValueExceptionCount(1);
743
        // phpcs:ignore Generic.Files.LineLength
744
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid choice; expected values: ' . join(', ', Fixture\StringEnum::values()) . '.');
745
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_CHOICE);
746
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
747
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
748
    }
749
750
    /**
751
     * @group enum
752
     * @group string-enum
753
     */
754
    public function testHydrateStringEnumPropertyWithoutValue(): void
755
    {
756
        $this->phpRequired('8.1');
757
758
        $object = new class {
759
            public Fixture\StringEnum $value;
760
        };
761
762
        $this->assertInvalidValueExceptionCount(1);
763
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
764
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
765
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
766
        $this->createHydrator()->hydrate($object, []);
767
    }
768
769
    /**
770
     * @group array
771
     * @dataProvider arrayDataProvider
772
     */
773
    public function testHydrateArrayProperty(array $data, array $expected): void
774
    {
775
        $object = new class {
776
            public array $value;
777
        };
778
779
        $this->assertInvalidValueExceptionCount(0);
780
        $this->createHydrator()->hydrate($object, $data);
781
        $this->assertSame($expected, $object->value);
782
    }
783
784
    /**
785
     * @group array
786
     * @dataProvider arrayDataProvider
787
     * @dataProvider strictNullDataProvider
788
     */
789
    public function testHydrateNullableArrayProperty(array $data, ?array $expected): void
790
    {
791
        $object = new class {
792
            public ?array $value;
793
        };
794
795
        $this->assertInvalidValueExceptionCount(0);
796
        $this->createHydrator()->hydrate($object, $data);
797
        $this->assertSame($expected, $object->value);
798
    }
799
800
    /**
801
     * @group array
802
     * @dataProvider arrayDataProvider
803
     * @dataProvider emptyArrayProvider
804
     */
805
    public function testHydrateOptionalArrayProperty(array $data, array $expected = []): void
806
    {
807
        $object = new class {
808
            public array $value = [];
809
        };
810
811
        $this->assertInvalidValueExceptionCount(0);
812
        $this->createHydrator()->hydrate($object, $data);
813
        $this->assertSame($expected, $object->value);
814
    }
815
816
    /**
817
     * @group array
818
     * @dataProvider strictNullDataProvider
819
     */
820
    public function testHydrateArrayPropertyWithNull(array $data): void
821
    {
822
        $object = new class {
823
            public array $value;
824
        };
825
826
        $this->assertInvalidValueExceptionCount(1);
827
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
828
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
829
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
830
        $this->createHydrator()->hydrate($object, $data);
831
    }
832
833
    /**
834
     * @group array
835
     * @dataProvider notArrayDataProvider
836
     */
837
    public function testHydrateArrayPropertyWithInvalidValue(array $data): void
838
    {
839
        $object = new class {
840
            public array $value;
841
        };
842
843
        $this->assertInvalidValueExceptionCount(1);
844
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
845
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
846
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
847
        $this->createHydrator()->hydrate($object, $data);
848
    }
849
850
    /**
851
     * @group array
852
     */
853
    public function testHydrateArrayPropertyWithoutValue(): void
854
    {
855
        $object = new class {
856
            public array $value;
857
        };
858
859
        $this->assertInvalidValueExceptionCount(1);
860
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
861
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
862
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
863
        $this->createHydrator()->hydrate($object, []);
864
    }
865
866
    /**
867
     * @group array
868
     * @group boolean-array
869
     * @dataProvider strictBooleanProvider
870
     * @dataProvider nonStrictBooleanProvider
871
     */
872
    public function testHydrateBooleanArrayProperty($element, bool $expected): void
873
    {
874
        $object = new class {
875
            /** @Subtype(BuiltinType::BOOL) */
876
            #[Subtype(BuiltinType::BOOL)]
877
            public array $value;
878
        };
879
880
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
881
        $this->assertSame([$expected], $object->value);
882
    }
883
884
    /**
885
     * @group array
886
     * @group boolean-array
887
     * @dataProvider strictBooleanProvider
888
     * @dataProvider nonStrictBooleanProvider
889
     * @dataProvider strictNullProvider
890
     * @dataProvider nonStrictNullProvider
891
     */
892
    public function testHydrateNullableBooleanArrayProperty($element, ?bool $expected): void
893
    {
894
        $object = new class {
895
            /** @Subtype(BuiltinType::BOOL, allowsNull=true) */
896
            #[Subtype(BuiltinType::BOOL, allowsNull: true)]
897
            public array $value;
898
        };
899
900
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
901
        $this->assertSame([$expected], $object->value);
902
    }
903
904
    /**
905
     * @group array
906
     * @group boolean-array
907
     * @dataProvider strictNullProvider
908
     * @dataProvider nonStrictNullProvider
909
     */
910
    public function testHydrateBooleanArrayPropertyWithEmptyElement($element): void
911
    {
912
        $object = new class {
913
            /** @Subtype(BuiltinType::BOOL) */
914
            #[Subtype(BuiltinType::BOOL)]
915
            public array $value;
916
        };
917
918
        $this->assertInvalidValueExceptionCount(1);
919
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
920
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
921
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
922
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
923
    }
924
925
    /**
926
     * @group array
927
     * @group boolean-array
928
     * @dataProvider notBooleanProvider
929
     */
930
    public function testHydrateBooleanArrayPropertyWithInvalidElement($element): void
931
    {
932
        $object = new class {
933
            /** @Subtype(BuiltinType::BOOL) */
934
            #[Subtype(BuiltinType::BOOL)]
935
            public array $value;
936
        };
937
938
        $this->assertInvalidValueExceptionCount(1);
939
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
940
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
941
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
942
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
943
    }
944
945
    /**
946
     * @group array
947
     * @group boolean-array
948
     * @dataProvider strictBooleanProvider
949
     * @dataProvider nonStrictBooleanProvider
950
     */
951
    public function testHydrateLimitedBooleanArrayProperty($element): void
952
    {
953
        $object = new class {
954
            /** @Subtype(BuiltinType::BOOL, limit=1) */
955
            #[Subtype(BuiltinType::BOOL, limit: 1)]
956
            public array $value;
957
        };
958
959
        $this->assertInvalidValueExceptionCount(1);
960
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
961
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
962
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
963
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
964
    }
965
966
    /**
967
     * @group array
968
     * @group boolean-association-array
969
     * @dataProvider strictBooleanDataProvider
970
     * @dataProvider nonStrictBooleanDataProvider
971
     */
972
    public function testHydrateBooleanAssociationArrayProperty(array $data, bool $expected): void
973
    {
974
        $object = new class {
975
            /**
976
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
977
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
978
             */
979
            #[Subtype(Fixture\BooleanAssociation::class)]
980
            public array $value;
981
        };
982
983
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
984
        $this->assertSame($expected, $object->value[0]->value);
985
    }
986
987
    /**
988
     * @group array
989
     * @group boolean-association-array
990
     * @dataProvider strictNullDataProvider
991
     */
992
    public function testHydrateBooleanAssociationArrayPropertyWithNull(array $data): void
993
    {
994
        $object = new class {
995
            /**
996
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
997
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
998
             */
999
            #[Subtype(Fixture\BooleanAssociation::class)]
1000
            public array $value;
1001
        };
1002
1003
        $this->assertInvalidValueExceptionCount(1);
1004
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1005
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1006
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1007
        $this->createHydrator()->hydrate($object, $data);
1008
    }
1009
1010
    /**
1011
     * @group array
1012
     * @group boolean-association-array
1013
     * @dataProvider notArrayDataProvider
1014
     */
1015
    public function testHydrateBooleanAssociationArrayPropertyWithInvalidValue(array $data): void
1016
    {
1017
        $object = new class {
1018
            /**
1019
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1020
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1021
             */
1022
            #[Subtype(Fixture\BooleanAssociation::class)]
1023
            public array $value;
1024
        };
1025
1026
        $this->assertInvalidValueExceptionCount(1);
1027
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1028
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1029
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1030
        $this->createHydrator()->hydrate($object, $data);
1031
    }
1032
1033
    /**
1034
     * @group array
1035
     * @group boolean-association-array
1036
     */
1037
    public function testHydrateBooleanAssociationArrayPropertyWithoutValue(): void
1038
    {
1039
        $object = new class {
1040
            /**
1041
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1042
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1043
             */
1044
            #[Subtype(Fixture\BooleanAssociation::class)]
1045
            public array $value;
1046
        };
1047
1048
        $this->assertInvalidValueExceptionCount(1);
1049
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1050
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1051
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1052
        $this->createHydrator()->hydrate($object, []);
1053
    }
1054
1055
    /**
1056
     * @group array
1057
     * @group boolean-association-array
1058
     * @dataProvider notArrayProvider
1059
     */
1060
    public function testHydrateBooleanAssociationArrayPropertyWithInvalidAssociation($element): void
1061
    {
1062
        $object = new class {
1063
            /**
1064
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1065
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1066
             */
1067
            #[Subtype(Fixture\BooleanAssociation::class)]
1068
            public array $value;
1069
        };
1070
1071
        $this->assertInvalidValueExceptionCount(1);
1072
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1073
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1074
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1075
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1076
    }
1077
1078
    /**
1079
     * @group array
1080
     * @group boolean-association-array
1081
     * @dataProvider strictNullDataProvider
1082
     * @dataProvider nonStrictNullDataProvider
1083
     */
1084
    public function testHydrateBooleanAssociationArrayPropertyWithEmptyAssociationValue(array $data): void
1085
    {
1086
        $object = new class {
1087
            /**
1088
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1089
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1090
             */
1091
            #[Subtype(Fixture\BooleanAssociation::class)]
1092
            public array $value;
1093
        };
1094
1095
        $this->assertInvalidValueExceptionCount(1);
1096
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1097
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1098
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value');
1099
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1100
    }
1101
1102
    /**
1103
     * @group array
1104
     * @group boolean-association-array
1105
     * @dataProvider notBooleanDataProvider
1106
     */
1107
    public function testHydrateBooleanAssociationArrayPropertyWithInvalidAssociationValue(array $data): void
1108
    {
1109
        $object = new class {
1110
            /**
1111
             * @Subtype(\Sunrise\Hydrator\Tests\Fixture\BooleanAssociation::class)
1112
             * @var non-empty-list<Fixture\BooleanAssociation>
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-list<Fixture\BooleanAssociation> at position 0 could not be parsed: Unknown type name 'non-empty-list' at position 0 in non-empty-list<Fixture\BooleanAssociation>.
Loading history...
1113
             */
1114
            #[Subtype(Fixture\BooleanAssociation::class)]
1115
            public array $value;
1116
        };
1117
1118
        $this->assertInvalidValueExceptionCount(1);
1119
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1120
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1121
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value');
1122
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1123
    }
1124
1125
    /**
1126
     * @group array-access
1127
     * @dataProvider arrayDataProvider
1128
     */
1129
    public function testHydrateArrayAccessProperty(array $data, array $expected): void
1130
    {
1131
        $object = new class {
1132
            public Fixture\Collection $value;
1133
        };
1134
1135
        $this->assertInvalidValueExceptionCount(0);
1136
        $this->createHydrator()->hydrate($object, $data);
1137
        $this->assertSame($expected, $object->value->elements);
1138
    }
1139
1140
    /**
1141
     * @group array-access
1142
     * @dataProvider arrayDataProvider
1143
     * @dataProvider strictNullDataProvider
1144
     */
1145
    public function testHydrateNullableArrayAccessProperty(array $data, ?array $expected): void
1146
    {
1147
        $object = new class {
1148
            public ?Fixture\Collection $value;
1149
        };
1150
1151
        $this->assertInvalidValueExceptionCount(0);
1152
        $this->createHydrator()->hydrate($object, $data);
1153
        $this->assertSame($expected, $object->value->elements ?? null);
1154
    }
1155
1156
    /**
1157
     * @group array-access
1158
     * @dataProvider arrayDataProvider
1159
     * @dataProvider emptyArrayProvider
1160
     */
1161
    public function testHydrateOptionalArrayAccessProperty(array $data, array $expected = []): void
1162
    {
1163
        $object = new class {
1164
            public ?Fixture\Collection $value = null;
1165
        };
1166
1167
        $this->assertInvalidValueExceptionCount(0);
1168
        $this->createHydrator()->hydrate($object, $data);
1169
        $this->assertSame($expected, $object->value->elements ?? []);
1170
    }
1171
1172
    /**
1173
     * @group array-access
1174
     * @dataProvider strictNullDataProvider
1175
     */
1176
    public function testHydrateArrayAccessPropertyWithNull(array $data): void
1177
    {
1178
        $object = new class {
1179
            public Fixture\Collection $value;
1180
        };
1181
1182
        $this->assertInvalidValueExceptionCount(1);
1183
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1184
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1185
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1186
        $this->createHydrator()->hydrate($object, $data);
1187
    }
1188
1189
    /**
1190
     * @group array-access
1191
     * @dataProvider notArrayDataProvider
1192
     */
1193
    public function testHydrateArrayAccessPropertyWithInvalidValue(array $data): void
1194
    {
1195
        $object = new class {
1196
            public Fixture\Collection $value;
1197
        };
1198
1199
        $this->assertInvalidValueExceptionCount(1);
1200
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1201
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1202
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1203
        $this->createHydrator()->hydrate($object, $data);
1204
    }
1205
1206
    /**
1207
     * @group array-access
1208
     */
1209
    public function testHydrateArrayAccessPropertyWithoutValue(): void
1210
    {
1211
        $object = new class {
1212
            public Fixture\Collection $value;
1213
        };
1214
1215
        $this->assertInvalidValueExceptionCount(1);
1216
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1217
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1218
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1219
        $this->createHydrator()->hydrate($object, []);
1220
    }
1221
1222
    /**
1223
     * @group array-access
1224
     */
1225
    public function testHydrateOverflowedArrayAccessProperty(): void
1226
    {
1227
        $object = new class {
1228
            public Fixture\LimitedCollection $value;
1229
        };
1230
1231
        $this->assertInvalidValueExceptionCount(1);
1232
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1233
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1234
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1235
        $this->createHydrator()->hydrate($object, ['value' => ['foo', 'bar']]);
1236
    }
1237
1238
    /**
1239
     * @group array-access
1240
     */
1241
    public function testHydrateUnstantiableArrayAccessProperty(): void
1242
    {
1243
        $object = new class {
1244
            public Fixture\UnstantiableCollection $value;
1245
        };
1246
1247
        $this->expectException(InvalidObjectException::class);
1248
        $this->createHydrator()->hydrate($object, ['value' => []]);
1249
    }
1250
1251
    /**
1252
     * @group array-access
1253
     * @group annotated-boolean-array-access
1254
     * @dataProvider strictBooleanProvider
1255
     * @dataProvider nonStrictBooleanProvider
1256
     */
1257
    public function testHydrateAnnotatedBooleanArrayAccessProperty($element, bool $expected): void
1258
    {
1259
        $object = new class {
1260
            /** @Subtype(BuiltinType::BOOL) */
1261
            #[Subtype(BuiltinType::BOOL)]
1262
            public Fixture\Collection $value;
1263
        };
1264
1265
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1266
        $this->assertSame([$expected], $object->value->elements);
1267
    }
1268
1269
    /**
1270
     * @group array-access
1271
     * @group annotated-boolean-array-access
1272
     * @dataProvider strictBooleanProvider
1273
     * @dataProvider nonStrictBooleanProvider
1274
     * @dataProvider strictNullProvider
1275
     * @dataProvider nonStrictNullProvider
1276
     */
1277
    public function testHydrateAnnotatedNullableBooleanArrayAccessProperty($element, ?bool $expected): void
1278
    {
1279
        $object = new class {
1280
            /** @Subtype(BuiltinType::BOOL, allowsNull=true) */
1281
            #[Subtype(BuiltinType::BOOL, allowsNull: true)]
1282
            public Fixture\Collection $value;
1283
        };
1284
1285
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1286
        $this->assertSame([$expected], $object->value->elements);
1287
    }
1288
1289
    /**
1290
     * @group array-access
1291
     * @group annotated-boolean-array-access
1292
     * @dataProvider strictNullProvider
1293
     * @dataProvider nonStrictNullProvider
1294
     */
1295
    public function testHydrateAnnotatedBooleanArrayAccessPropertyWithEmptyElement($element): void
1296
    {
1297
        $object = new class {
1298
            /** @Subtype(BuiltinType::BOOL) */
1299
            #[Subtype(BuiltinType::BOOL)]
1300
            public Fixture\Collection $value;
1301
        };
1302
1303
        $this->assertInvalidValueExceptionCount(1);
1304
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1305
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1306
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1307
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1308
    }
1309
1310
    /**
1311
     * @group array-access
1312
     * @group annotated-boolean-array-access
1313
     * @dataProvider notBooleanProvider
1314
     */
1315
    public function testHydrateAnnotatedBooleanArrayAccessPropertyWithInvalidElement($element): void
1316
    {
1317
        $object = new class {
1318
            /** @Subtype(BuiltinType::BOOL) */
1319
            #[Subtype(BuiltinType::BOOL)]
1320
            public Fixture\Collection $value;
1321
        };
1322
1323
        $this->assertInvalidValueExceptionCount(1);
1324
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1325
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1326
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1327
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1328
    }
1329
1330
    /**
1331
     * @group array-access
1332
     * @group annotated-boolean-array-access
1333
     * @dataProvider strictBooleanProvider
1334
     * @dataProvider nonStrictBooleanProvider
1335
     */
1336
    public function testHydrateAnnotatedLimitedBooleanArrayAccessProperty($element): void
1337
    {
1338
        $object = new class {
1339
            /** @Subtype(BuiltinType::BOOL, limit=1) */
1340
            #[Subtype(BuiltinType::BOOL, limit: 1)]
1341
            public Fixture\Collection $value;
1342
        };
1343
1344
        $this->assertInvalidValueExceptionCount(1);
1345
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1346
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1347
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1348
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
1349
    }
1350
1351
    /**
1352
     * @group array-access
1353
     * @group annotated-boolean-array-access
1354
     * @dataProvider strictBooleanProvider
1355
     * @dataProvider nonStrictBooleanProvider
1356
     */
1357
    public function testHydrateAnnotatedOverflowedBooleanArrayAccessProperty($element): void
1358
    {
1359
        $object = new class {
1360
            /** @Subtype(BuiltinType::BOOL) */
1361
            #[Subtype(BuiltinType::BOOL)]
1362
            public Fixture\LimitedCollection $value;
1363
        };
1364
1365
        $this->assertInvalidValueExceptionCount(1);
1366
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1367
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1368
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1369
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
1370
    }
1371
1372
    /**
1373
     * @group array-access
1374
     * @group typed-boolean-array-access
1375
     * @dataProvider strictBooleanProvider
1376
     * @dataProvider nonStrictBooleanProvider
1377
     */
1378
    public function testHydrateTypedBooleanArrayAccessProperty($element, bool $expected): void
1379
    {
1380
        $object = new class {
1381
            public Fixture\BooleanCollection $value;
1382
        };
1383
1384
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1385
        $this->assertSame([$expected], $object->value->elements);
1386
    }
1387
1388
    /**
1389
     * @group array-access
1390
     * @group typed-boolean-array-access
1391
     * @dataProvider strictBooleanProvider
1392
     * @dataProvider nonStrictBooleanProvider
1393
     * @dataProvider strictNullProvider
1394
     * @dataProvider nonStrictNullProvider
1395
     */
1396
    public function testHydrateTypedNullableBooleanArrayAccessProperty($element, ?bool $expected): void
1397
    {
1398
        $object = new class {
1399
            public Fixture\NullableBooleanCollection $value;
1400
        };
1401
1402
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1403
        $this->assertSame([$expected], $object->value->elements);
1404
    }
1405
1406
    /**
1407
     * @group array-access
1408
     * @group typed-boolean-array-access
1409
     * @dataProvider strictNullProvider
1410
     * @dataProvider nonStrictNullProvider
1411
     */
1412
    public function testHydrateTypedBooleanArrayAccessPropertyWithEmptyElement($element): void
1413
    {
1414
        $object = new class {
1415
            public Fixture\BooleanCollection $value;
1416
        };
1417
1418
        $this->assertInvalidValueExceptionCount(1);
1419
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1420
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1421
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1422
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1423
    }
1424
1425
    /**
1426
     * @group array-access
1427
     * @group typed-boolean-array-access
1428
     * @dataProvider notBooleanProvider
1429
     */
1430
    public function testHydrateTypedBooleanArrayAccessPropertyWithInvalidElement($element): void
1431
    {
1432
        $object = new class {
1433
            public Fixture\BooleanCollection $value;
1434
        };
1435
1436
        $this->assertInvalidValueExceptionCount(1);
1437
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1438
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1439
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1440
        $this->createHydrator()->hydrate($object, ['value' => [$element]]);
1441
    }
1442
1443
    /**
1444
     * @group array-access
1445
     * @group typed-boolean-array-access
1446
     * @dataProvider strictBooleanProvider
1447
     * @dataProvider nonStrictBooleanProvider
1448
     */
1449
    public function testHydrateTypedLimitedBooleanArrayAccessProperty($element): void
1450
    {
1451
        $object = new class {
1452
            public Fixture\LimitedCollection $value;
1453
        };
1454
1455
        $this->assertInvalidValueExceptionCount(1);
1456
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1457
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1458
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1459
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
1460
    }
1461
1462
    /**
1463
     * @group array-access
1464
     * @group typed-boolean-array-access
1465
     * @dataProvider strictBooleanProvider
1466
     * @dataProvider nonStrictBooleanProvider
1467
     */
1468
    public function testHydrateTypedOverflowedBooleanArrayAccessProperty($element): void
1469
    {
1470
        $object = new class {
1471
            public Fixture\LimitedBooleanCollection $value;
1472
        };
1473
1474
        $this->assertInvalidValueExceptionCount(1);
1475
        $this->assertInvalidValueExceptionMessage(0, 'This value is limited to 1 elements.');
1476
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::ARRAY_OVERFLOW);
1477
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1478
        $this->createHydrator()->hydrate($object, ['value' => [true, $element]]);
1479
    }
1480
1481
    /**
1482
     * @group array-access
1483
     * @group boolean-association-array-access
1484
     * @dataProvider strictBooleanDataProvider
1485
     * @dataProvider nonStrictBooleanDataProvider
1486
     */
1487
    public function testHydrateBooleanAssociationArrayAccessProperty(array $data, bool $expected): void
1488
    {
1489
        $object = new class {
1490
            public Fixture\BooleanAssociationCollection $value;
1491
        };
1492
1493
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1494
        $this->assertSame($expected, $object->value[0]->value);
1495
    }
1496
1497
    /**
1498
     * @group array-access
1499
     * @group boolean-association-array-access
1500
     * @dataProvider strictNullDataProvider
1501
     */
1502
    public function testHydrateBooleanAssociationArrayAccessPropertyWithNull(array $data): void
1503
    {
1504
        $object = new class {
1505
            public Fixture\BooleanAssociationCollection $value;
1506
        };
1507
1508
        $this->assertInvalidValueExceptionCount(1);
1509
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1510
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1511
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1512
        $this->createHydrator()->hydrate($object, $data);
1513
    }
1514
1515
    /**
1516
     * @group array-access
1517
     * @group boolean-association-array-access
1518
     * @dataProvider notArrayDataProvider
1519
     */
1520
    public function testHydrateBooleanAssociationArrayAccessPropertyWithInvalidValue(array $data): void
1521
    {
1522
        $object = new class {
1523
            public Fixture\BooleanAssociationCollection $value;
1524
        };
1525
1526
        $this->assertInvalidValueExceptionCount(1);
1527
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1528
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1529
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1530
        $this->createHydrator()->hydrate($object, $data);
1531
    }
1532
1533
    /**
1534
     * @group array-access
1535
     * @group boolean-association-array-access
1536
     */
1537
    public function testHydrateBooleanAssociationArrayAccessPropertyWithoutValue(): void
1538
    {
1539
        $object = new class {
1540
            public Fixture\BooleanAssociationCollection $value;
1541
        };
1542
1543
        $this->assertInvalidValueExceptionCount(1);
1544
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1545
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1546
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1547
        $this->createHydrator()->hydrate($object, []);
1548
    }
1549
1550
    /**
1551
     * @group array-access
1552
     * @group boolean-association-array-access
1553
     * @dataProvider notArrayProvider
1554
     */
1555
    public function testHydrateBooleanAssociationArrayAccessPropertyWithInvalidAssociation($actual): void
1556
    {
1557
        $object = new class {
1558
            public Fixture\BooleanAssociationCollection $value;
1559
        };
1560
1561
        $this->assertInvalidValueExceptionCount(1);
1562
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1563
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1564
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1565
        $this->createHydrator()->hydrate($object, ['value' => [$actual]]);
1566
    }
1567
1568
    /**
1569
     * @group array-access
1570
     * @group boolean-association-array-access
1571
     * @dataProvider strictNullDataProvider
1572
     * @dataProvider nonStrictNullDataProvider
1573
     */
1574
    public function testHydrateBooleanAssociationArrayAccessPropertyWithEmptyAssociationValue(array $data): void
1575
    {
1576
        $object = new class {
1577
            public Fixture\BooleanAssociationCollection $value;
1578
        };
1579
1580
        $this->assertInvalidValueExceptionCount(1);
1581
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1582
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1583
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value');
1584
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1585
    }
1586
1587
    /**
1588
     * @group array-access
1589
     * @group boolean-association-array-access
1590
     * @dataProvider notBooleanDataProvider
1591
     */
1592
    public function testHydrateBooleanAssociationArrayAccessPropertyWithInvalidAssociationValue(array $data): void
1593
    {
1594
        $object = new class {
1595
            public Fixture\BooleanAssociationCollection $value;
1596
        };
1597
1598
        $this->assertInvalidValueExceptionCount(1);
1599
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1600
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1601
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value');
1602
        $this->createHydrator()->hydrate($object, ['value' => [$data]]);
1603
    }
1604
1605
    /**
1606
     * @group association
1607
     */
1608
    public function testHydrateUnstantiableAssociationProperty(): void
1609
    {
1610
        $object = new class {
1611
            public Fixture\UnstantiableObject $value;
1612
        };
1613
1614
        $this->expectException(InvalidObjectException::class);
1615
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
1616
    }
1617
1618
    /**
1619
     * @group association
1620
     * @group boolean-association
1621
     * @dataProvider strictBooleanDataProvider
1622
     * @dataProvider nonStrictBooleanDataProvider
1623
     */
1624
    public function testHydrateBooleanAssociationProperty(array $data, bool $expected): void
1625
    {
1626
        $object = new class {
1627
            public Fixture\BooleanAssociation $value;
1628
        };
1629
1630
        $this->assertInvalidValueExceptionCount(0);
1631
        $this->createHydrator()->hydrate($object, ['value' => $data]);
1632
        $this->assertSame($expected, $object->value->value);
1633
    }
1634
1635
    /**
1636
     * @group association
1637
     * @group boolean-association
1638
     */
1639
    public function testHydrateNullableBooleanAssociationProperty(): void
1640
    {
1641
        $object = new class {
1642
            public ?Fixture\BooleanAssociation $value;
1643
        };
1644
1645
        $this->assertInvalidValueExceptionCount(0);
1646
        $this->createHydrator()->hydrate($object, ['value' => null]);
1647
        $this->assertNull($object->value);
1648
    }
1649
1650
    /**
1651
     * @group association
1652
     * @group boolean-association
1653
     */
1654
    public function testHydrateOptionalBooleanAssociationProperty(): void
1655
    {
1656
        $object = new class {
1657
            public ?Fixture\BooleanAssociation $value = null;
1658
        };
1659
1660
        $this->assertInvalidValueExceptionCount(0);
1661
        $this->createHydrator()->hydrate($object, []);
1662
        $this->assertNull($object->value);
1663
    }
1664
1665
    /**
1666
     * @group association
1667
     * @group boolean-association
1668
     */
1669
    public function testHydrateBooleanAssociationPropertyWithNull(): void
1670
    {
1671
        $object = new class {
1672
            public Fixture\BooleanAssociation $value;
1673
        };
1674
1675
        $this->assertInvalidValueExceptionCount(1);
1676
        $this->assertInvalidValueExceptionMessage(0, 'This value must not be empty.');
1677
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_NOT_BE_EMPTY);
1678
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1679
        $this->createHydrator()->hydrate($object, ['value' => null]);
1680
    }
1681
1682
    /**
1683
     * @group association
1684
     * @group boolean-association
1685
     * @dataProvider notArrayDataProvider
1686
     */
1687
    public function testHydrateBooleanAssociationPropertyWithInvalidValue(array $data): void
1688
    {
1689
        $object = new class {
1690
            public Fixture\BooleanAssociation $value;
1691
        };
1692
1693
        $this->assertInvalidValueExceptionCount(1);
1694
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type array.');
1695
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_ARRAY);
1696
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1697
        $this->createHydrator()->hydrate($object, $data);
1698
    }
1699
1700
    /**
1701
     * @group association
1702
     * @group boolean-association
1703
     */
1704
    public function testHydrateBooleanAssociationPropertyWithoutValue(): void
1705
    {
1706
        $object = new class {
1707
            public Fixture\BooleanAssociation $value;
1708
        };
1709
1710
        $this->assertInvalidValueExceptionCount(1);
1711
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1712
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1713
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1714
        $this->createHydrator()->hydrate($object, []);
1715
    }
1716
1717
    /**
1718
     * @group association
1719
     * @group boolean-association
1720
     */
1721
    public function testHydrateBooleanAssociationPropertyWithEmptyAssociation(): void
1722
    {
1723
        $object = new class {
1724
            public Fixture\BooleanAssociation $value;
1725
        };
1726
1727
        $this->assertInvalidValueExceptionCount(1);
1728
        $this->assertInvalidValueExceptionMessage(0, 'This value must be provided.');
1729
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_PROVIDED);
1730
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.value');
1731
        $this->createHydrator()->hydrate($object, ['value' => []]);
1732
    }
1733
1734
    /**
1735
     * @group association
1736
     * @group boolean-association
1737
     * @dataProvider notBooleanDataProvider
1738
     */
1739
    public function testHydrateBooleanAssociationPropertyWithInvalidAssociationValue(array $data): void
1740
    {
1741
        $object = new class {
1742
            public Fixture\BooleanAssociation $value;
1743
        };
1744
1745
        $this->assertInvalidValueExceptionCount(1);
1746
        $this->assertInvalidValueExceptionMessage(0, 'This value must be of type boolean.');
1747
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::MUST_BE_BOOLEAN);
1748
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.value');
1749
        $this->createHydrator()->hydrate($object, ['value' => $data]);
1750
    }
1751
1752
    /**
1753
     * @group timestamp
1754
     * @dataProvider timestampDataProvider
1755
     */
1756
    // phpcs:ignore Generic.Files.LineLength
1757
    public function testHydrateTimestampProperty(array $data, string $expected, ?string $format = null, ?string $timezone = null): void
1758
    {
1759
        $object = new class {
1760
            public DateTimeImmutable $value;
1761
        };
1762
1763
        $this->assertInvalidValueExceptionCount(0);
1764
        // phpcs:ignore Generic.Files.LineLength
1765
        $this->createHydrator([ContextKey::TIMESTAMP_FORMAT => $format, ContextKey::TIMEZONE => $timezone])->hydrate($object, $data);
1766
        $this->assertSame($expected, $object->value->format($format ?? TimestampTypeConverter::DEFAULT_FORMAT));
1767
    }
1768
1769
    /**
1770
     * @group timestamp
1771
     * @dataProvider timestampDataProvider
1772
     * @dataProvider strictNullDataProvider
1773
     * @dataProvider nonStrictNullDataProvider
1774
     */
1775
    // phpcs:ignore Generic.Files.LineLength
1776
    public function testHydrateNullableTimestampProperty(array $data, ?string $expected, ?string $format = null, ?string $timezone = null): void
1777
    {
1778
        $object = new class {
1779
            public ?DateTimeImmutable $value;
1780
        };
1781
1782
        $this->assertInvalidValueExceptionCount(0);
1783
        // phpcs:ignore Generic.Files.LineLength
1784
        $this->createHydrator([ContextKey::TIMESTAMP_FORMAT => $format, ContextKey::TIMEZONE => $timezone])->hydrate($object, $data);
1785
        // phpcs:ignore Generic.Files.LineLength
1786
        $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

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

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

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

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

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

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

2982
        /** @scrutinizer ignore-call */ 
2983
        $baz = Fixture\MyclabsEnum::BAZ();
Loading history...
2983
2984
        yield [['value' => $foo->getValue()], $foo];
2985
        yield [['value' => $bar->getValue()], $bar];
2986
        yield [['value' => $baz->getValue()], $baz];
2987
    }
2988
2989
    public function arrayDataProvider(): Generator
2990
    {
2991
        yield [['value' => []], []];
2992
        yield [['value' => ['foo']], ['foo']];
2993
    }
2994
2995
    public function notArrayDataProvider(): Generator
2996
    {
2997
        foreach ($this->notArrayProvider() as [$actual]) {
2998
            yield [['value' => $actual]];
2999
        }
3000
    }
3001
3002
    public function timestampDataProvider(): Generator
3003
    {
3004
        // default formatted timestamp
3005
        $timestamp = date(TimestampTypeConverter::DEFAULT_FORMAT);
3006
3007
        yield [['value' => $timestamp], $timestamp];
3008
        yield [['value' => $timestamp], $timestamp, TimestampTypeConverter::DEFAULT_FORMAT];
3009
3010
        yield [['value' => '700101'], '700101', 'ymd'];
3011
        yield [['value' => '000000'], '000000', 'His'];
3012
3013
        yield [['value' => '-1'], '-1', 'U'];
3014
        yield [['value' => '0'], '0', 'U'];
3015
        yield [['value' => '1'], '1', 'U'];
3016
        yield [['value' => '+1'], '1', 'U'];
3017
3018
        // Must be converted to a string...
3019
        yield [['value' => -1], '-1', 'U'];
3020
        yield [['value' => 0], '0', 'U'];
3021
        yield [['value' => 1], '1', 'U'];
3022
3023
        // The timezone must be applied...
3024
        yield [['value' => '00:00:00'], '00:00:00', 'H:i:s', 'Europe/Kiev'];
3025
3026
        // ISO 8601
3027
        yield [['value' => '00:00:00.123456'], '00:00:00.123456', 'H:i:s.u'];
3028
        yield [['value' => '00:00:00.123456+00:00'], '00:00:00.123456+00:00', 'H:i:s.uP'];
3029
        yield [['value' => 'Monday 00:00:00.123456'], 'Monday 00:00:00.123456', 'l H:i:s.u'];
3030
        yield [['value' => '00:00:00.1234567890'], '00:00:00.123456', 'H:i:s.u'];
3031
        yield [['value' => '00:00:00.1234567890+00:00'], '00:00:00.123456+00:00', 'H:i:s.uP'];
3032
        yield [['value' => 'Monday 00:00:00.1234567890'], 'Monday 00:00:00.123456', 'l H:i:s.u'];
3033
    }
3034
3035
    public function invalidTimestampDataProvider(): Generator
3036
    {
3037
        yield [
3038
            ['value' => '01/01/1970 00:00:00'],
3039
            'Y-m-d H:i:s',
3040
            ErrorCode::INVALID_TIMESTAMP,
3041
            'This value is not a valid timestamp; expected format: Y-m-d H:i:s.',
3042
        ];
3043
3044
        yield [
3045
            ['value' => '1970-01-01 00:00:00'],
3046
            'U',
3047
            ErrorCode::MUST_BE_INTEGER,
3048
            'This value must be of type integer.',
3049
        ];
3050
3051
        yield [
3052
            ['value' => 0],
3053
            'Y',
3054
            ErrorCode::MUST_BE_STRING,
3055
            'This value must be of type string.',
3056
        ];
3057
    }
3058
3059
    public function timezoneDataProvider(): Generator
3060
    {
3061
        foreach (DateTimeZone::listIdentifiers() as $timezone) {
3062
            yield [['value' => $timezone], $timezone];
3063
        }
3064
    }
3065
3066
    public function uuidDataProvider(): Generator
3067
    {
3068
        yield [['value' => '207ddb61-c300-4368-9f26-33d0a99eac00'], '207ddb61-c300-4368-9f26-33d0a99eac00'];
3069
    }
3070
3071
    private function createHydrator(array $context = []): HydratorInterface
3072
    {
3073
        $hydrator = new Hydrator($context);
3074
        if (PHP_VERSION_ID < 80000) {
3075
            $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

3075
            /** @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...
3076
        }
3077
3078
        return $hydrator;
3079
    }
3080
3081
    private function phpRequired(string $version): void
3082
    {
3083
        if (version_compare(PHP_VERSION, $version, '<')) {
3084
            $this->markTestSkipped(sprintf('PHP %s is required.', $version));
3085
        }
3086
    }
3087
3088
    private function assertInvalidValueExceptionCount(int $expectedCount): void
3089
    {
3090
        $this->invalidValueExceptionCount = $expectedCount;
3091
    }
3092
3093
    private function assertInvalidValueExceptionMessage(int $exceptionIndex, string $expectedMessage): void
3094
    {
3095
        $this->invalidValueExceptionMessage[] = [$exceptionIndex, $expectedMessage];
3096
    }
3097
3098
    private function assertInvalidValueExceptionPropertyPath(int $exceptionIndex, string $expectedPropertyPath): void
3099
    {
3100
        $this->invalidValueExceptionPropertyPath[] = [$exceptionIndex, $expectedPropertyPath];
3101
    }
3102
3103
    private function assertInvalidValueExceptionErrorCode(int $exceptionIndex, string $expectedErrorCode): void
3104
    {
3105
        $this->invalidValueExceptionErrorCode[] = [$exceptionIndex, $expectedErrorCode];
3106
    }
3107
3108
    protected function runTest(): void
3109
    {
3110
        $invalidDataExceptionHandled = false;
3111
3112
        try {
3113
            parent::runTest();
3114
        } catch (InvalidDataException $invalidDataException) {
3115
            $invalidDataExceptionMessages = [];
3116
            foreach ($invalidDataException->getExceptions() as $invalidValueException) {
3117
                $invalidDataExceptionMessages[] = sprintf(
3118
                    '[%s] %s',
3119
                    $invalidValueException->getPropertyPath(),
3120
                    $invalidValueException->getMessage(),
3121
                );
3122
            }
3123
3124
            if (isset($this->invalidValueExceptionCount)) {
3125
                $invalidDataExceptionHandled = true;
3126
                $this->assertCount(
3127
                    $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

3127
                    /** @scrutinizer ignore-type */ $this->invalidValueExceptionCount,
Loading history...
3128
                    $invalidDataException->getExceptions(),
3129
                    \join(\PHP_EOL, $invalidDataExceptionMessages),
3130
                );
3131
            }
3132
3133
            foreach ($this->invalidValueExceptionMessage as [$index, $invalidValueExceptionMessage]) {
3134
                $invalidDataExceptionHandled = true;
3135
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3136
                $this->assertSame(
3137
                    $invalidValueExceptionMessage,
3138
                    $invalidDataException->getExceptions()[$index]->getMessage(),
3139
                );
3140
            }
3141
3142
            foreach ($this->invalidValueExceptionPropertyPath as [$index, $invalidValueExceptionPropertyPath]) {
3143
                $invalidDataExceptionHandled = true;
3144
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3145
                $this->assertSame(
3146
                    $invalidValueExceptionPropertyPath,
3147
                    $invalidDataException->getExceptions()[$index]->getPropertyPath(),
3148
                );
3149
            }
3150
3151
            foreach ($this->invalidValueExceptionErrorCode as [$index, $invalidValueExceptionErrorCode]) {
3152
                $invalidDataExceptionHandled = true;
3153
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
3154
                $this->assertSame(
3155
                    $invalidValueExceptionErrorCode,
3156
                    $invalidDataException->getExceptions()[$index]->getErrorCode(),
3157
                );
3158
            }
3159
3160
            if (!$invalidDataExceptionHandled) {
3161
                throw $invalidDataException;
3162
            }
3163
        } finally {
3164
            $this->invalidValueExceptionCount = null;
3165
            $this->invalidValueExceptionMessage = [];
3166
            $this->invalidValueExceptionPropertyPath = [];
3167
            $this->invalidValueExceptionErrorCode = [];
3168
3169
            if ($invalidDataExceptionHandled) {
3170
                $this->assertTrue(true);
3171
            }
3172
        }
3173
    }
3174
}
3175