Passed
Pull Request — main (#19)
by Anatoly
02:30
created

testHydrateDateTimePropertyWithInvalidValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 7
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Sunrise\Hydrator\Tests;
6
7
use PHPUnit\Framework\TestCase;
8
use Sunrise\Hydrator\Exception;
9
use Sunrise\Hydrator\Hydrator;
10
use Sunrise\Hydrator\HydratorInterface;
11
use InvalidArgumentException;
12
13
class HydratorTest extends TestCase
14
{
15
    public function testContracts() : void
16
    {
17
        $hydrator = new Hydrator();
18
19
        $this->assertInstanceOf(HydratorInterface::class, $hydrator);
20
    }
21
22
    public function testInvalidObject() : void
23
    {
24
        $this->expectException(InvalidArgumentException::class);
25
        $this->expectExceptionMessage('The ' . Hydrator::class . '::hydrate() method ' .
26
                                      'expects an object or name of an existing class.');
27
28
        (new Hydrator)->hydrate('Undefined', []);
29
    }
30
31
    public function testUninitializableObject() : void
32
    {
33
        $this->expectException(InvalidArgumentException::class);
34
        $this->expectExceptionMessage('The ' . Fixtures\UninitializableObject::class . ' object ' .
35
                                      'cannot be hydrated because its constructor has required parameters.');
36
37
        (new Hydrator)->hydrate(Fixtures\UninitializableObject::class, []);
38
    }
39
40
    public function testInvalidData() : void
41
    {
42
        $this->expectException(InvalidArgumentException::class);
43
        $this->expectExceptionMessage('The ' . Hydrator::class . '::hydrate(data) parameter ' .
44
                                      'expects an associative array or object.');
45
46
        (new Hydrator)->hydrate(Fixtures\ObjectWithString::class, null);
47
    }
48
49
    public function testInvalidJson() : void
50
    {
51
        $this->expectException(InvalidArgumentException::class);
52
        $this->expectExceptionMessage('Unable to decode JSON: Syntax error');
53
54
        (new Hydrator)->hydrateWithJson(Fixtures\ObjectWithString::class, '!');
55
    }
56
57
    public function testIgnoreStaticalProperty() : void
58
    {
59
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithStaticalProperty::class, ['value' => 'foo']);
60
61
        $this->assertNotSame('foo', $object::$value);
62
    }
63
64
    public function testUntypedProperty() : void
65
    {
66
        $this->expectException(Exception\UntypedPropertyException::class);
67
        $this->expectExceptionMessage('The ObjectWithUntypedProperty.value property ' .
68
                                      'is not typed.');
69
70
        (new Hydrator)->hydrate(Fixtures\ObjectWithUntypedProperty::class, []);
71
    }
72
73
    public function testUnionPropertyType() : void
74
    {
75
        if (\PHP_MAJOR_VERSION < 8) {
76
            $this->markTestSkipped('php >= 8 is required.');
77
        }
78
79
        $this->expectException(Exception\UnsupportedPropertyTypeException::class);
80
        $this->expectExceptionMessage('The ObjectWithIntOrFloat.value property ' .
81
                                      'contains an union type that is not supported.');
82
83
        (new Hydrator)->hydrate(Fixtures\ObjectWithIntOrFloat::class, []);
84
    }
85
86
    public function testHydrateAnnotatedProperty() : void
87
    {
88
        $object = (new Hydrator)
89
            ->useAnnotations()
90
            ->hydrate(Fixtures\ObjectWithAnnotatedAlias::class, ['non-normalized-value' => 'foo']);
91
92
        $this->assertSame('foo', $object->value);
93
    }
94
95
    public function testHydrateAnnotatedPropertyUsingNormalizedKey() : void
96
    {
97
        $object = (new Hydrator)
98
            ->useAnnotations()
99
            ->hydrate(Fixtures\ObjectWithAnnotatedAlias::class, ['value' => 'foo']);
100
101
        $this->assertSame('foo', $object->value);
102
    }
103
104
    public function testHydrateAnnotatedPropertyWhenDisabledAliasSupport() : void
105
    {
106
        $this->expectException(Exception\MissingRequiredValueException::class);
107
        $this->expectExceptionMessage('The ObjectWithAnnotatedAlias.value property ' .
108
                                      'is required.');
109
110
        (new Hydrator)
111
            ->useAnnotations()
112
            ->aliasSupport(false)
113
            ->hydrate(Fixtures\ObjectWithAnnotatedAlias::class, ['non-normalized-value' => 'foo']);
114
    }
115
116
    public function testHydrateAnnotatedPropertyWhenDisabledAnnotations() : void
117
    {
118
        $this->expectException(Exception\MissingRequiredValueException::class);
119
        $this->expectExceptionMessage('The ObjectWithAnnotatedAlias.value property ' .
120
                                      'is required.');
121
122
        (new Hydrator)->hydrate(Fixtures\ObjectWithAnnotatedAlias::class, ['non-normalized-value' => 'foo']);
123
    }
124
125
    public function testHydrateAttributedProperty() : void
126
    {
127
        if (\PHP_MAJOR_VERSION < 8) {
128
            $this->markTestSkipped('php >= 8 is required.');
129
        }
130
131
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithAttributedAlias::class, ['non-normalized-value' => 'foo']);
132
133
        $this->assertSame('foo', $object->value);
134
    }
135
136
    public function testHydrateAttributedPropertyUsingNormalizedKey() : void
137
    {
138
        if (\PHP_MAJOR_VERSION < 8) {
139
            $this->markTestSkipped('php >= 8 is required.');
140
        }
141
142
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithAttributedAlias::class, ['value' => 'foo']);
143
144
        $this->assertSame('foo', $object->value);
145
    }
146
147
    public function testHydrateAttributedPropertyWhenDisabledAliasSupport() : void
148
    {
149
        if (\PHP_MAJOR_VERSION < 8) {
150
            $this->markTestSkipped('php >= 8 is required.');
151
        }
152
153
        $this->expectException(Exception\MissingRequiredValueException::class);
154
        $this->expectExceptionMessage('The ObjectWithAttributedAlias.value property ' .
155
                                      'is required.');
156
157
        (new Hydrator)
158
            ->aliasSupport(false)
159
            ->hydrate(Fixtures\ObjectWithAttributedAlias::class, ['non-normalized-value' => 'foo']);
160
    }
161
162
    public function testRequiredProperty() : void
163
    {
164
        $this->expectException(Exception\MissingRequiredValueException::class);
165
        $this->expectExceptionMessage('The ObjectWithString.value property ' .
166
                                      'is required.');
167
168
        (new Hydrator)->hydrate(Fixtures\ObjectWithString::class, []);
169
    }
170
171
    public function testUnsupportedPropertyType() : void
172
    {
173
        $this->expectException(Exception\UnsupportedPropertyTypeException::class);
174
        $this->expectExceptionMessage('The ObjectWithUnsupportedType.value property ' .
175
                                      'contains an unsupported type iterable.');
176
177
        (new Hydrator)->hydrate(Fixtures\ObjectWithUnsupportedType::class, ['value' => false]);
178
    }
179
180
    public function testOptionalProperty() : void
181
    {
182
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithOptionalString::class, []);
183
184
        $this->assertSame('75c4c2a0-e352-4eda-b2ed-b7f713ffb9ff', $object->value);
185
    }
186
187
    public function testHydrateObject() : void
188
    {
189
        $source = new Fixtures\ObjectWithString();
190
191
        // should return the source object...
192
        $object = (new Hydrator)->hydrate($source, ['value' => 'foo']);
193
194
        $this->assertSame($source, $object);
195
        $this->assertSame('foo', $source->value);
196
    }
197
198
    public function testHydrateUsingDataObject() : void
199
    {
200
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithString::class, (object) ['value' => 'foo']);
201
202
        $this->assertSame('foo', $object->value);
203
    }
204
205
    public function testHydrateWithJson() : void
206
    {
207
        $object = (new Hydrator)->hydrateWithJson(Fixtures\ObjectWithString::class, '{"value": "foo"}');
208
209
        $this->assertSame('foo', $object->value);
210
    }
211
212
    public function testConvertEmptyStringToNullForNonStringType() : void
213
    {
214
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithNullableInt::class, ['value' => '']);
215
216
        $this->assertNull($object->value);
217
    }
218
219
    public function testHydrateNullablePropertyWithNull() : void
220
    {
221
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithNullableString::class, ['value' => null]);
222
223
        $this->assertNull($object->value);
224
    }
225
226
    public function testHydrateUnnullablePropertyWithNull() : void
227
    {
228
        $this->expectException(Exception\InvalidValueException::class);
229
        $this->expectExceptionMessage('The ObjectWithString.value property ' .
230
                                      'cannot accept null.');
231
232
        (new Hydrator)->hydrate(Fixtures\ObjectWithString::class, ['value' => null]);
233
    }
234
235
    /**
236
     * @dataProvider booleanValueProvider
237
     */
238
    public function testHydrateBooleanProperty($value, $expected) : void
239
    {
240
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithBool::class, ['value' => $value]);
241
242
        $this->assertSame($expected, $object->value);
243
    }
244
245
    public function booleanValueProvider() : array
246
    {
247
        return [
248
            [true, true],
249
            [1, true],
250
            ['1', true],
251
            ['on', true],
252
            ['yes', true],
253
            [false, false],
254
            [0, false],
255
            ['0', false],
256
            ['off', false],
257
            ['no', false],
258
        ];
259
    }
260
261
    public function testHydrateBooleanPropertyWithInvalidValue() : void
262
    {
263
        $this->expectException(Exception\InvalidValueException::class);
264
        $this->expectExceptionMessage('The ObjectWithBool.value property expects a boolean.');
265
266
        (new Hydrator)->hydrate(Fixtures\ObjectWithBool::class, ['value' => 'foo']);
267
    }
268
269
    /**
270
     * @dataProvider integerValueProvider
271
     */
272
    public function testHydrateIntegerProperty($value, $expected) : void
273
    {
274
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithInt::class, ['value' => $value]);
275
276
        $this->assertSame($expected, $object->value);
277
    }
278
279
    public function integerValueProvider() : array
280
    {
281
        return [
282
            [42, 42],
283
            ['42', 42],
284
        ];
285
    }
286
287
    public function testHydrateIntegerPropertyWithInvalidValue() : void
288
    {
289
        $this->expectException(Exception\InvalidValueException::class);
290
        $this->expectExceptionMessage('The ObjectWithInt.value property expects an integer.');
291
292
        (new Hydrator)->hydrate(Fixtures\ObjectWithInt::class, ['value' => 'foo']);
293
    }
294
295
    /**
296
     * @dataProvider numberValueProvider
297
     */
298
    public function testHydrateNumberProperty($value, $expected) : void
299
    {
300
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithFloat::class, ['value' => $value]);
301
302
        $this->assertSame($expected, $object->value);
303
    }
304
305
    public function numberValueProvider() : array
306
    {
307
        return [
308
            [42, 42.0],
309
            ['42', 42.0],
310
            [42.0, 42.0],
311
            ['42.0', 42.0],
312
        ];
313
    }
314
315
    public function testHydrateNumberPropertyWithInvalidValue() : void
316
    {
317
        $this->expectException(Exception\InvalidValueException::class);
318
        $this->expectExceptionMessage('The ObjectWithFloat.value property expects a number.');
319
320
        (new Hydrator)->hydrate(Fixtures\ObjectWithFloat::class, ['value' => 'foo']);
321
    }
322
323
    public function testHydrateStringableProperty() : void
324
    {
325
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithString::class, ['value' => 'foo']);
326
327
        $this->assertSame('foo', $object->value);
328
    }
329
330
    public function testHydrateStringablePropertyWithEmptyString() : void
331
    {
332
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithString::class, ['value' => '']);
333
334
        $this->assertSame('', $object->value);
335
    }
336
337
    public function testHydrateStringablePropertyWithInvalidValue() : void
338
    {
339
        $this->expectException(Exception\InvalidValueException::class);
340
        $this->expectExceptionMessage('The ObjectWithString.value property expects a string.');
341
342
        (new Hydrator)->hydrate(Fixtures\ObjectWithString::class, ['value' => 42]);
343
    }
344
345
    public function testHydrateArrayableProperty() : void
346
    {
347
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithArray::class, ['value' => ['foo']]);
348
349
        $this->assertSame(['foo'], $object->value);
350
    }
351
352
    public function testHydrateArrayablePropertyWithInvalidValue() : void
353
    {
354
        $this->expectException(Exception\InvalidValueException::class);
355
        $this->expectExceptionMessage('The ObjectWithArray.value property expects an array.');
356
357
        (new Hydrator)->hydrate(Fixtures\ObjectWithArray::class, ['value' => 'foo']);
358
    }
359
360
    public function testHydrateObjectableProperty() : void
361
    {
362
        $value = (object) ['value' => 'foo'];
363
364
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithObject::class, ['value' => $value]);
365
366
        $this->assertSame($value, $object->value);
367
    }
368
369
    public function testHydrateObjectablePropertyWithInvalidValue() : void
370
    {
371
        $this->expectException(Exception\InvalidValueException::class);
372
        $this->expectExceptionMessage('The ObjectWithObject.value property expects an object.');
373
374
        (new Hydrator)->hydrate(Fixtures\ObjectWithObject::class, ['value' => 'foo']);
375
    }
376
377
    /**
378
     * @dataProvider timestampValueProvider
379
     */
380
    public function testHydrateDateTimeProperty($value, $expected) : void
381
    {
382
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithDateTime::class, ['value' => $value]);
383
384
        $this->assertSame($expected, $object->value->format('Y-m-d'));
385
    }
386
387
    /**
388
     * @dataProvider timestampValueProvider
389
     */
390
    public function testHydrateDateTimeImmutableProperty($value, $expected) : void
391
    {
392
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithDateTimeImmutable::class, ['value' => $value]);
393
394
        $this->assertSame($expected, $object->value->format('Y-m-d'));
395
    }
396
397
    public function timestampValueProvider() : array
398
    {
399
        return [
400
            [1262304000, '2010-01-01'],
401
            ['1262304000', '2010-01-01'],
402
            ['2010-01-01', '2010-01-01'],
403
        ];
404
    }
405
406
    public function testHydrateDateTimePropertyWithInvalidValue() : void
407
    {
408
        $this->expectException(Exception\InvalidValueException::class);
409
        $this->expectExceptionMessage('The ObjectWithDateTime.value property ' .
410
                                      'expects a valid date-time string or timestamp.');
411
412
        (new Hydrator)->hydrate(Fixtures\ObjectWithDateTime::class, ['value' => 'foo']);
413
    }
414
415
    public function testHydrateDateTimeImmutablePropertyWithInvalidValue() : void
416
    {
417
        $this->expectException(Exception\InvalidValueException::class);
418
        $this->expectExceptionMessage('The ObjectWithDateTimeImmutable.value property ' .
419
                                      'expects a valid date-time string or timestamp.');
420
421
        (new Hydrator)->hydrate(Fixtures\ObjectWithDateTimeImmutable::class, ['value' => 'foo']);
422
    }
423
424
    public function testHydrateDateIntervalProperty() : void
425
    {
426
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithDateInterval::class, ['value' => 'P33Y']);
427
428
        $this->assertSame(33, $object->value->y);
429
    }
430
431
    public function testHydrateDateIntervalPropertyWithInvalidValue() : void
432
    {
433
        $this->expectException(Exception\InvalidValueException::class);
434
        $this->expectExceptionMessage('The ObjectWithDateInterval.value property ' .
435
                                      'expects a string.');
436
437
        (new Hydrator)->hydrate(Fixtures\ObjectWithDateInterval::class, ['value' => 42]);
438
    }
439
440
    public function testHydrateDateIntervalPropertyWithInvalidFormat() : void
441
    {
442
        $this->expectException(Exception\InvalidValueException::class);
443
        $this->expectExceptionMessage('The ObjectWithDateInterval.value property ' .
444
                                      'expects a valid date-interval string based on ISO 8601.');
445
446
        (new Hydrator)->hydrate(Fixtures\ObjectWithDateInterval::class, ['value' => 'foo']);
447
    }
448
449
    /**
450
     * @dataProvider stringableEnumValueProvider
451
     */
452
    public function testHydrateStringableEnumProperty($value, $expected) : void
453
    {
454
        if (\PHP_VERSION_ID < 80100) {
455
            $this->markTestSkipped('php >= 8.1 is required.');
456
        }
457
458
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithStringableEnum::class, ['value' => $value]);
459
460
        $this->assertSame($expected, $object->value);
461
    }
462
463
    public function stringableEnumValueProvider() : array
464
    {
465
        if (\PHP_VERSION_ID < 80100) {
466
            return [];
467
        }
468
469
        return [
470
            ['c1200a7e-136e-4a11-9bc3-cc937046e90f', Fixtures\StringableEnum::foo],
471
            ['a2b29b37-1c5a-4b36-9981-097ddd25c740', Fixtures\StringableEnum::bar],
472
            ['c1ea3762-9827-4c0c-808b-53be3febae6d', Fixtures\StringableEnum::baz],
473
        ];
474
    }
475
476
    public function testHydrateStringableEnumPropertyWithInvalidValue() : void
477
    {
478
        if (\PHP_VERSION_ID < 80100) {
479
            $this->markTestSkipped('php >= 8.1 is required.');
480
        }
481
482
        $this->expectException(Exception\InvalidValueException::class);
483
        $this->expectExceptionMessage('The ObjectWithStringableEnum.value property ' .
484
                                      'expects the following type: string.');
485
486
        (new Hydrator)->hydrate(Fixtures\ObjectWithStringableEnum::class, ['value' => 42]);
487
    }
488
489
    public function testHydrateStringableEnumPropertyWithInvalidUnknownCase() : void
490
    {
491
        if (\PHP_VERSION_ID < 80100) {
492
            $this->markTestSkipped('php >= 8.1 is required.');
493
        }
494
495
        $this->expectException(Exception\InvalidValueException::class);
496
        $this->expectExceptionMessage('The ObjectWithStringableEnum.value property ' .
497
                                      'expects one of the following values: ' .
498
                                      \implode(', ', Fixtures\StringableEnum::values()) . '.');
499
500
        (new Hydrator)->hydrate(Fixtures\ObjectWithStringableEnum::class, ['value' => 'foo']);
501
    }
502
503
    /**
504
     * @dataProvider numerableEnumValueProvider
505
     */
506
    public function testHydrateNumerableEnumProperty($value, $expected) : void
507
    {
508
        if (\PHP_VERSION_ID < 80100) {
509
            $this->markTestSkipped('php >= 8.1 is required.');
510
        }
511
512
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithNumerableEnum::class, ['value' => $value]);
513
514
        $this->assertSame($expected, $object->value);
515
    }
516
517
    public function numerableEnumValueProvider() : array
518
    {
519
        if (\PHP_VERSION_ID < 80100) {
520
            return [];
521
        }
522
523
        return [
524
            [1, Fixtures\NumerableEnum::foo],
525
            [2, Fixtures\NumerableEnum::bar],
526
            [3, Fixtures\NumerableEnum::baz],
527
528
            // should convert strings to integers...
529
            ['1', Fixtures\NumerableEnum::foo],
530
            ['2', Fixtures\NumerableEnum::bar],
531
            ['3', Fixtures\NumerableEnum::baz],
532
        ];
533
    }
534
535
    public function testHydrateNumerableEnumPropertyWithInvalidValue() : void
536
    {
537
        if (\PHP_VERSION_ID < 80100) {
538
            $this->markTestSkipped('php >= 8.1 is required.');
539
        }
540
541
        $this->expectException(Exception\InvalidValueException::class);
542
        $this->expectExceptionMessage('The ObjectWithNumerableEnum.value property ' .
543
                                      'expects the following type: int.');
544
545
        (new Hydrator)->hydrate(Fixtures\ObjectWithNumerableEnum::class, ['value' => 'foo']);
546
    }
547
548
    public function testHydrateNumerableEnumPropertyWithInvalidUnknownCase() : void
549
    {
550
        if (\PHP_VERSION_ID < 80100) {
551
            $this->markTestSkipped('php >= 8.1 is required.');
552
        }
553
554
        $this->expectException(Exception\InvalidValueException::class);
555
        $this->expectExceptionMessage('The ObjectWithNumerableEnum.value property ' .
556
                                      'expects one of the following values: ' .
557
                                      \implode(', ', Fixtures\NumerableEnum::values()) . '.');
558
559
        (new Hydrator)->hydrate(Fixtures\ObjectWithNumerableEnum::class, ['value' => 42]);
560
    }
561
562
    public function testHydrateAssociatedProperty() : void
563
    {
564
        $o = (new Hydrator)->hydrate(Fixtures\ObjectWithAssociation::class, ['value' => ['value' => 'foo']]);
565
566
        $this->assertSame('foo', $o->value->value);
567
    }
568
569
    public function testHydrateAssociatedPropertyUsingDataObject() : void
570
    {
571
        $o = (new Hydrator)->hydrate(Fixtures\ObjectWithAssociation::class, ['value' => (object) ['value' => 'foo']]);
572
573
        $this->assertSame('foo', $o->value->value);
574
    }
575
576
    public function testHydrateAssociatedPropertyWithInvalidData() : void
577
    {
578
        $this->expectException(Exception\InvalidValueException::class);
579
        $this->expectExceptionMessage('The ObjectWithAssociation.value property ' .
580
                                      'expects an associative array or object.');
581
582
        (new Hydrator)->hydrate(Fixtures\ObjectWithAssociation::class, ['value' => 'foo']);
583
    }
584
585
    public function testHydrateAssociationCollectionProperty() : void
586
    {
587
        $o = (new Hydrator)->hydrate(Fixtures\ObjectWithAssociations::class, ['value' => [
588
            'foo' => ['value' => 'foo'],
589
            'bar' => ['value' => 'bar'],
590
        ]]);
591
592
        $this->assertTrue($o->value->has('foo'));
593
        $this->assertSame('foo', $o->value->get('foo')->value);
594
595
        $this->assertTrue($o->value->has('bar'));
596
        $this->assertSame('bar', $o->value->get('bar')->value);
597
    }
598
599
    public function testHydrateAssociationCollectionPropertyUsingDataObject() : void
600
    {
601
        $o = (new Hydrator)->hydrate(Fixtures\ObjectWithAssociations::class, ['value' => (object) [
602
            'foo' => (object) ['value' => 'foo'],
603
            'bar' => (object) ['value' => 'bar'],
604
        ]]);
605
606
        $this->assertTrue($o->value->has('foo'));
607
        $this->assertSame('foo', $o->value->get('foo')->value);
608
609
        $this->assertTrue($o->value->has('bar'));
610
        $this->assertSame('bar', $o->value->get('bar')->value);
611
    }
612
613
    public function testHydrateAssociationCollectionPropertyWithInvalidData() : void
614
    {
615
        $this->expectException(Exception\InvalidValueException::class);
616
        $this->expectExceptionMessage('The ObjectWithAssociations.value property ' .
617
                                      'expects an associative array or object.');
618
619
        (new Hydrator)->hydrate(Fixtures\ObjectWithAssociations::class, ['value' => 'foo']);
620
    }
621
622
    public function testHydrateAssociationCollectionPropertyWithInvalidChild() : void
623
    {
624
        $this->expectException(Exception\InvalidValueException::class);
625
        $this->expectExceptionMessage('The ObjectWithAssociations.value[0] property ' .
626
                                      'expects an associative array or object.');
627
628
        (new Hydrator)->hydrate(Fixtures\ObjectWithAssociations::class, ['value' => ['foo']]);
629
    }
630
631
    public function testInvalidValueExceptionProperty() : void
632
    {
633
        try {
634
            (new Hydrator)->hydrate(Fixtures\ObjectWithString::class, ['value' => 42]);
635
        } catch (Exception\InvalidValueException $e) {
636
            $this->assertSame('value', $e->getProperty()->getName());
637
            $this->assertSame('ObjectWithString.value', $e->getPropertyPath());
638
        }
639
    }
640
641
    public function testHydrateProductWithJsonAsArray() : void
642
    {
643
        if (\PHP_VERSION_ID < 80100) {
644
            $this->markTestSkipped('php >= 8.1 is required.');
645
        }
646
647
        $json = <<<JSON
648
        {
649
            "name": "ac7ce13e-9b2e-4b09-ae7a-973769ea43df",
650
            "category": {
651
                "name": "a0127d1b-28b6-40a9-9a62-cfb2e2b44abd"
652
            },
653
            "tags": [
654
                {
655
                    "name": "a9878435-506c-4757-92b0-69ea2bd15bc3"
656
                },
657
                {
658
                    "name": "73dc4db1-7965-41b6-88cb-4dc9df6fb3ea"
659
                }
660
            ],
661
            "status": 2
662
        }
663
        JSON;
664
665
        $product = (new Hydrator)->hydrateWithJson(Fixtures\Store\Product::class, $json);
666
667
        $this->assertSame('ac7ce13e-9b2e-4b09-ae7a-973769ea43df', $product->name);
668
        $this->assertSame('a0127d1b-28b6-40a9-9a62-cfb2e2b44abd', $product->category->name);
669
        $this->assertSame('a9878435-506c-4757-92b0-69ea2bd15bc3', $product->tags->get(0)->name);
670
        $this->assertSame('73dc4db1-7965-41b6-88cb-4dc9df6fb3ea', $product->tags->get(1)->name);
671
        $this->assertSame(2, $product->status->value);
672
    }
673
674
    public function testHydrateProductWithJsonAsObject() : void
675
    {
676
        if (\PHP_VERSION_ID < 80100) {
677
            $this->markTestSkipped('php >= 8.1 is required.');
678
        }
679
680
        $json = <<<JSON
681
        {
682
            "name": "0f61ac0e-f732-4088-8082-cc396e7dcb80",
683
            "category": {
684
                "name": "d342d030-3c0c-431e-be54-2e933b722b7c"
685
            },
686
            "tags": [
687
                {
688
                    "name": "3635627a-e348-4ca4-8e62-4e5cd78043d2"
689
                },
690
                {
691
                    "name": "dccd816f-bb28-41f3-b1a9-ddaff1fdec5b"
692
                }
693
            ],
694
            "status": 2
695
        }
696
        JSON;
697
698
        $product = (new Hydrator)->hydrateWithJson(Fixtures\Store\Product::class, $json, 0);
699
700
        $this->assertSame('0f61ac0e-f732-4088-8082-cc396e7dcb80', $product->name);
701
        $this->assertSame('d342d030-3c0c-431e-be54-2e933b722b7c', $product->category->name);
702
        $this->assertSame('3635627a-e348-4ca4-8e62-4e5cd78043d2', $product->tags->get(0)->name);
703
        $this->assertSame('dccd816f-bb28-41f3-b1a9-ddaff1fdec5b', $product->tags->get(1)->name);
704
        $this->assertSame(2, $product->status->value);
705
    }
706
}
707