Passed
Push — main ( 76dcd0...9f35d8 )
by Anatoly
10:37 queued 04:41
created

php$27 ➔ testHydrateIntegerEnumPropertyWithEmptyValue()   A

Complexity

Conditions 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

2817
        /** @scrutinizer ignore-call */ 
2818
        $baz = Stub\MyclabsEnum::BAZ();
Loading history...
2818
2819
        yield [['value' => $foo->getValue()], $foo];
2820
        yield [['value' => $bar->getValue()], $bar];
2821
        yield [['value' => $baz->getValue()], $baz];
2822
    }
2823
2824
    public function arrayDataProvider(): Generator
2825
    {
2826
        yield [['value' => []], []];
2827
        yield [['value' => ['foo']], ['foo']];
2828
    }
2829
2830
    public function notArrayDataProvider(): Generator
2831
    {
2832
        foreach ($this->notArrayProvider() as [$actual]) {
2833
            yield [['value' => $actual]];
2834
        }
2835
    }
2836
2837
    public function timestampDataProvider(): Generator
2838
    {
2839
        // default formatted timestamp
2840
        $timestamp = date(TimestampTypeConverter::DEFAULT_FORMAT);
2841
2842
        yield [['value' => $timestamp], $timestamp];
2843
        yield [['value' => $timestamp], $timestamp, TimestampTypeConverter::DEFAULT_FORMAT];
2844
2845
        yield [['value' => '700101'], '700101', 'ymd'];
2846
        yield [['value' => '000000'], '000000', 'His'];
2847
2848
        yield [['value' => '-1'], '-1', 'U'];
2849
        yield [['value' => '0'], '0', 'U'];
2850
        yield [['value' => '1'], '1', 'U'];
2851
        yield [['value' => '+1'], '1', 'U'];
2852
2853
        // Must be converted to a string...
2854
        yield [['value' => -1], '-1', 'U'];
2855
        yield [['value' => 0], '0', 'U'];
2856
        yield [['value' => 1], '1', 'U'];
2857
2858
        // The timezone must be applied...
2859
        yield [['value' => '00:00:00'], '00:00:00', 'H:i:s', 'Europe/Kiev'];
2860
    }
2861
2862
    public function invalidTimestampDataProvider(): Generator
2863
    {
2864
        yield [
2865
            ['value' => '01/01/1970 00:00:00'],
2866
            'Y-m-d H:i:s',
2867
            ErrorCode::INVALID_TIMESTAMP,
2868
            'This value is not a valid timestamp.',
2869
        ];
2870
2871
        yield [
2872
            ['value' => '1970-01-01 00:00:00'],
2873
            'U',
2874
            ErrorCode::MUST_BE_INTEGER,
2875
            'This value must be of type integer.',
2876
        ];
2877
2878
        yield [
2879
            ['value' => 0],
2880
            'Y',
2881
            ErrorCode::MUST_BE_STRING,
2882
            'This value must be of type string.',
2883
        ];
2884
    }
2885
2886
    public function timezoneDataProvider(): Generator
2887
    {
2888
        foreach (DateTimeZone::listIdentifiers() as $timezone) {
2889
            yield [['value' => $timezone], $timezone];
2890
        }
2891
    }
2892
2893
    public function uuidDataProvider(): Generator
2894
    {
2895
        yield [['value' => '207ddb61-c300-4368-9f26-33d0a99eac00'], '207ddb61-c300-4368-9f26-33d0a99eac00'];
2896
    }
2897
2898
    private function createHydrator(array $context = []): HydratorInterface
2899
    {
2900
        $hydrator = new Hydrator($context);
2901
        if (PHP_VERSION_ID < 80000) {
2902
            $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

2902
            /** @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...
2903
        }
2904
2905
        return $hydrator;
2906
    }
2907
2908
    private function phpRequired(string $version): void
2909
    {
2910
        if (version_compare(PHP_VERSION, $version, '<')) {
2911
            $this->markTestSkipped(sprintf('PHP %s is required.', $version));
2912
        }
2913
    }
2914
2915
    private function assertInvalidValueExceptionCount(int $expectedCount): void
2916
    {
2917
        $this->invalidValueExceptionCount = $expectedCount;
2918
    }
2919
2920
    private function assertInvalidValueExceptionMessage(int $exceptionIndex, string $expectedMessage): void
2921
    {
2922
        $this->invalidValueExceptionMessage[] = [$exceptionIndex, $expectedMessage];
2923
    }
2924
2925
    private function assertInvalidValueExceptionPropertyPath(int $exceptionIndex, string $expectedPropertyPath): void
2926
    {
2927
        $this->invalidValueExceptionPropertyPath[] = [$exceptionIndex, $expectedPropertyPath];
2928
    }
2929
2930
    private function assertInvalidValueExceptionErrorCode(int $exceptionIndex, string $expectedErrorCode): void
2931
    {
2932
        $this->invalidValueExceptionErrorCode[] = [$exceptionIndex, $expectedErrorCode];
2933
    }
2934
2935
    protected function runTest(): void
2936
    {
2937
        $invalidDataExceptionHandled = false;
2938
2939
        try {
2940
            parent::runTest();
2941
        } catch (InvalidDataException $invalidDataException) {
2942
            $invalidDataExceptionMessages = [];
2943
            foreach ($invalidDataException->getExceptions() as $invalidValueException) {
2944
                $invalidDataExceptionMessages[] = sprintf(
2945
                    '[%s] %s',
2946
                    $invalidValueException->getPropertyPath(),
2947
                    $invalidValueException->getMessage(),
2948
                );
2949
            }
2950
2951
            if (isset($this->invalidValueExceptionCount)) {
2952
                $invalidDataExceptionHandled = true;
2953
                $this->assertCount(
2954
                    $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

2954
                    /** @scrutinizer ignore-type */ $this->invalidValueExceptionCount,
Loading history...
2955
                    $invalidDataException->getExceptions(),
2956
                    \join(\PHP_EOL, $invalidDataExceptionMessages),
2957
                );
2958
            }
2959
2960
            foreach ($this->invalidValueExceptionMessage as [$index, $invalidValueExceptionMessage]) {
2961
                $invalidDataExceptionHandled = true;
2962
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
2963
                $this->assertSame(
2964
                    $invalidValueExceptionMessage,
2965
                    $invalidDataException->getExceptions()[$index]->getMessage(),
2966
                );
2967
            }
2968
2969
            foreach ($this->invalidValueExceptionPropertyPath as [$index, $invalidValueExceptionPropertyPath]) {
2970
                $invalidDataExceptionHandled = true;
2971
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
2972
                $this->assertSame(
2973
                    $invalidValueExceptionPropertyPath,
2974
                    $invalidDataException->getExceptions()[$index]->getPropertyPath(),
2975
                );
2976
            }
2977
2978
            foreach ($this->invalidValueExceptionErrorCode as [$index, $invalidValueExceptionErrorCode]) {
2979
                $invalidDataExceptionHandled = true;
2980
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
2981
                $this->assertSame(
2982
                    $invalidValueExceptionErrorCode,
2983
                    $invalidDataException->getExceptions()[$index]->getErrorCode(),
2984
                );
2985
            }
2986
2987
            if (!$invalidDataExceptionHandled) {
2988
                throw $invalidDataException;
2989
            }
2990
        } finally {
2991
            $this->invalidValueExceptionCount = null;
2992
            $this->invalidValueExceptionMessage = [];
2993
            $this->invalidValueExceptionPropertyPath = [];
2994
            $this->invalidValueExceptionErrorCode = [];
2995
2996
            if ($invalidDataExceptionHandled) {
2997
                $this->assertTrue(true);
2998
            }
2999
        }
3000
    }
3001
}
3002