Passed
Pull Request — main (#24)
by Anatoly
04:51 queued 15s
created

HydratorTest   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 1721
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
wmc 58
eloc 709
c 8
b 0
f 0
dl 0
loc 1721
rs 4.4509

175 Methods

Rating   Name   Duplication   Size   Complexity  
A hp$0 ➔ createHydrator() 0 8 2
A hp$0 ➔ timestampDataProvider() 0 4 1
A hp$0 ➔ strictNullDataProvider() 0 4 1
A testHydrateRelationshipsPropertyWithNotArray() 0 7 1
nonStrictIntegerDataProvider() 0 6 ?
A hp$0 ➔ notIntegerDataProvider() 0 8 1
A testHydrateNonNullableRelationshipPropertyWithNull() 0 7 1
A testHydrateOptionalNumericProperty() 0 5 1
A hp$0 ➔ assertInvalidValueExceptionCount() 0 3 1
A testHydrateRelationshipsPropertyWithNotStringForStringValue() 0 7 1
A hp$0 ➔ testStaticalProperty() 0 5 1
A testHydrateObjectWithNonObjectableJson() 0 5 1
A testHydrateOptionalTimestampProperty() 0 5 2
A hp$0 ➔ notUnixTimeStampDataProvider() 0 7 1
A testHydrateNonNullableRelationshipsPropertyWithNull() 0 7 1
A hp$0 ➔ testIgnoredProperty() 0 5 1
A testHydrateNumericPropertyWithNotNumber() 0 7 1
runTest() 0 63 ?
A testHydrateNullableIntegerProperty() 0 5 1
A hp$0 ➔ strictNumberDataProvider() 0 12 1
A testHydrateStringEnumerationPropertyWithNotInteger() 0 8 1
A testHydrateLimitedRelationshipsProperty() 0 9 1
A testHydrateNonNullableStringEnumerationPropertyWithNull() 0 8 1
nonStrictNumberDataProvider() 0 27 ?
strictIntegerEnumerationDataProvider() 0 14 ?
testUntypedProperty() 0 10 ?
A testHydrateOptionalArrayProperty() 0 5 1
strictNullDataProvider() 0 4 ?
A testHydrateObjectWithJson() 0 5 1
testInstantedObject() 0 9 ?
A testHydrateNullableIntegerEnumerationProperty() 0 6 1
A testHydrateStringEnumerationProperty() 0 6 1
nonStrictNullDataProvider() 0 5 ?
A testHydrateUnformattedTimestampProperty() 0 12 1
A testHydrateTimestampPropertyWithInvalidTimestamp() 0 8 1
A hp$0 ➔ arrayDataProvider() 0 4 1
assertInvalidValueExceptionPropertyPath() 0 3 ?
invalidTimestampDataProvider() 0 4 ?
A hp$0 ➔ notArrayDataProvider() 0 8 1
strictBooleanDataProvider() 0 5 ?
A testHydrateRequiredUnixTimeStampPropertyWithoutValue() 0 7 1
testAttributedAlias() 0 16 ?
A hp$0 ➔ nonStrictIntegerEnumerationDataProvider() 0 14 2
stringEnumerationDataProvider() 0 14 ?
A testHydrateRelationshipWithNonNullablePropertyWithNull() 0 7 1
A hp$0 ➔ nonStrictBooleanDataProvider() 0 11 1
A testHydrateArrayProperty() 0 5 1
A hp$0 ➔ notStringDataProvider() 0 8 1
A testHydrateNonNullableTimestampPropertyWithNull() 0 7 1
nonStrictUnixTimeStampDataProvider() 0 6 ?
A testHydrateIntegerEnumerationPropertyWithInvalidChoice() 0 9 1
testUnstantiableObject() 0 9 ?
A testHydrateNonNullableIntegerEnumerationPropertyWithNull() 0 8 1
A testHydrateStringPropertyWithNotString() 0 7 1
A testHydrateRequiredRelationshipPropertyWithoutValue() 0 7 1
A hp$0 ➔ strictIntegerDataProvider() 0 6 1
A testHydrateNullableUnixTimeStampProperty() 0 5 2
stringDataProvider() 0 22 ?
notBooleanDataProvider() 0 9 ?
A testHydrateUnixTimeStampProperty() 0 5 1
A hp$0 ➔ testAttributedAlias() 0 16 1
A testHydrateBooleanPropertyWithNonBooleanValue() 0 7 1
A hp$0 ➔ testInstantedObject() 0 9 1
A testHydrateOptionalUnixTimeStampProperty() 0 5 2
A testHydrateBooleanProperty() 0 5 1
notUnixTimeStampDataProvider() 0 7 ?
A testHydrateRequiredStringPropertyWithoutValue() 0 7 1
notTimestampDataProvider() 0 8 ?
A testHydrateIntegerPropertyWithNotInteger() 0 7 1
A hp$0 ➔ notTimestampDataProvider() 0 8 1
A hp$0 ➔ stringEnumerationDataProvider() 0 14 2
testSymfonyViolations() 0 15 ?
A testHydrateUnixTimeStampPropertyWithNotInteger() 0 7 1
A hp$0 ➔ nonStrictUnixTimeStampDataProvider() 0 6 1
testUnsupportedPropertyType() 0 11 ?
A testHydrateRelationshipsPropertyWithUnstantiableObject() 0 13 1
strictNumberDataProvider() 0 12 ?
phpRequired() 0 4 ?
A testHydrateOptionalStringEnumerationProperty() 0 6 1
A testHydrateNullableArrayProperty() 0 5 1
A testHydrateNonNullableUnixTimeStampPropertyWithNull() 0 7 1
A testHydrateNumericProperty() 0 5 1
A testHydrateStringProperty() 0 5 1
A testHydrateNullableRelationshipProperty() 0 5 1
A testHydrateOptionalRelationshipsProperty() 0 5 1
A testHydrateRequiredIntegerPropertyWithoutValue() 0 7 1
A testHydrateIntegerEnumerationProperty() 0 6 1
A testHydrateIntegerEnumerationPropertyWithNotInteger() 0 8 1
A testHydrateRelationshipsPropertyWithNullsForNonNullableValues() 0 7 1
notIntegerDataProvider() 0 8 ?
A hp$0 ➔ testUnsupportedPropertyTypeNotation() 0 13 1
testHydrateStore() 0 30 ?
A testHydrateOptionalBooleanProperty() 0 5 1
A testHydrateNullableStringProperty() 0 5 1
A hp$0 ➔ strictUnixTimeStampDataProvider() 0 6 1
A hp$0 ➔ testUnsupportedPropertyType() 0 11 1
A testSeveralErrorsWhenHydratingRelationshipsProperty() 0 16 1
arrayDataProvider() 0 4 ?
A testHydrateRelationshipsProperty() 0 11 1
A hp$0 ➔ assertInvalidValueExceptionErrorCode() 0 3 1
timestampDataProvider() 0 4 ?
A testHydrateStringEnumerationPropertyWithInvalidChoice() 0 9 1
A hp$0 ➔ notBooleanDataProvider() 0 9 1
B hp$0 ➔ runTest() 0 63 9
A testHydrateOptionalIntegerProperty() 0 5 1
createHydrator() 0 8 ?
A testHydrateRelationshipWithNullableStringProperty() 0 5 1
A testHydrateNullableNumericProperty() 0 5 1
A hp$0 ➔ testAnnotationReader() 0 14 1
notStringDataProvider() 0 8 ?
A hp$0 ➔ strictIntegerEnumerationDataProvider() 0 14 2
A testHydrateNonNullableIntegerPropertyWithNull() 0 7 1
A hp$0 ➔ assertInvalidValueExceptionPropertyPath() 0 3 1
A testHydrateRelationshipWithStringProperty() 0 5 1
A testHydrateNullableStringEnumerationProperty() 0 6 1
A testHydrateRequiredStringEnumerationPropertyWithoutValue() 0 7 1
assertInvalidValueExceptionCount() 0 3 ?
nonStrictIntegerEnumerationDataProvider() 0 14 ?
strictUnixTimeStampDataProvider() 0 6 ?
A testHydrateRelationshipPropertyWithNotArray() 0 7 1
A testHydrateArrayPropertyWithNotArray() 0 7 1
A testHydrateNullableBooleanProperty() 0 5 1
A testHydrateObjectWithInvalidJson() 0 5 1
A testHydrateRelationshipWithOptionalStringProperty() 0 5 1
A testHydrateOptionalIntegerEnumerationProperty() 0 6 1
A testHydrateNonNullableBooleanPropertyWithNull() 0 7 1
A testHydrateNonNullableStringPropertyWithNull() 0 7 1
strictIntegerDataProvider() 0 6 ?
emptyDataProvider() 0 4 ?
testAnnotationReader() 0 14 ?
A hp$0 ➔ assertInvalidValueExceptionMessage() 0 3 1
A testHydrateRequiredTimestampPropertyWithoutValue() 0 7 1
A testHydrateRelationshipProperty() 0 5 1
A hp$0 ➔ nonStrictIntegerDataProvider() 0 6 1
A hp$0 ➔ invalidTimestampDataProvider() 0 4 1
assertInvalidValueExceptionMessage() 0 3 ?
A testHydrateNonNullableNumericPropertyWithNull() 0 7 1
notArrayDataProvider() 0 8 ?
A hp$0 ➔ strictBooleanDataProvider() 0 5 1
A hp$0 ➔ nonStrictNumberDataProvider() 0 27 1
A testHydrateRelationshipWithStringPropertyWithNotString() 0 7 1
A hp$0 ➔ testUntypedProperty() 0 10 1
A hp$0 ➔ testHydrateStore() 0 30 1
A testHydrateOptionalStringProperty() 0 5 1
A testHydrateRequiredNumericPropertyWithoutValue() 0 7 1
assertInvalidValueExceptionErrorCode() 0 3 ?
A testHydrateRequiredBooleanPropertyWithoutValue() 0 7 1
A testHydrateRelationshipsPropertyWithNotArrayForRelation() 0 7 1
A testHydrateIntegerProperty() 0 5 1
A testHydrateRequiredRelationshipsPropertyWithoutValue() 0 7 1
A hp$0 ➔ testUnstantiableObject() 0 9 1
A testHydrateNullableTimestampProperty() 0 5 2
A testHydrateNullableRelationshipsProperty() 0 5 1
A hp$0 ➔ phpRequired() 0 4 2
A hp$0 ➔ emptyDataProvider() 0 4 1
A testHydrateRequiredIntegerEnumerationPropertyWithoutValue() 0 7 1
testUnsupportedPropertyTypeNotation() 0 13 ?
A hp$0 ➔ testAnnotatedAlias() 0 15 1
testStaticalProperty() 0 5 ?
A testHydrateUnstantiableRelationshipProperty() 0 11 1
A testHydrateNonNullableArrayPropertyWithNull() 0 7 1
A hp$0 ➔ nonStrictNullDataProvider() 0 5 1
A hp$0 ➔ stringDataProvider() 0 22 1
A hp$0 ➔ notNumberDataProvider() 0 7 1
A testHydrateTimestampPropertyWithNotString() 0 7 1
testAnnotatedAlias() 0 15 ?
A testHydrateRequiredArrayPropertyWithoutValue() 0 7 1
A testHydrateTimestampProperty() 0 5 1
testIgnoredProperty() 0 5 ?
A testHydrateRelationshipWithRequiredStringPropertyWithoutValue() 0 7 1
nonStrictBooleanDataProvider() 0 11 ?
A testHydrateOptionalRelationshipProperty() 0 5 1
A testHydrateRelationshipsPropertyWithoutValueForRequiredValue() 0 7 1
notNumberDataProvider() 0 7 ?
A hp$0 ➔ testSymfonyViolations() 0 15 2

How to fix   Complexity   

Complex Class

Complex classes like HydratorTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HydratorTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Sunrise\Hydrator\Tests;
6
7
use Doctrine\Common\Annotations\Reader;
8
use PHPUnit\Framework\TestCase;
9
use Sunrise\Hydrator\Annotation\Format;
10
use Sunrise\Hydrator\Dictionary\ErrorCode;
11
use Sunrise\Hydrator\Exception\InvalidDataException;
12
use Sunrise\Hydrator\Exception\UninitializableObjectException;
13
use Sunrise\Hydrator\Exception\UnsupportedPropertyTypeException;
14
use Sunrise\Hydrator\Exception\UntypedPropertyException;
15
use Sunrise\Hydrator\Hydrator;
16
use Sunrise\Hydrator\HydratorInterface;
17
use Sunrise\Hydrator\Tests\Fixtures\IntegerEnum;
18
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithAnnotatedAlias;
19
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithArray;
20
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithAttributedAlias;
21
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithBoolean;
22
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithIgnoredProperty;
23
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithInteger;
24
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithIntegerEnum;
25
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableArray;
26
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableBoolean;
27
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableInteger;
28
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableIntegerEnum;
29
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableNumber;
30
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableRelationship;
31
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableRelationships;
32
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableString;
33
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableStringEnum;
34
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableTimestamp;
35
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNullableUnixTimeStamp;
36
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithNumber;
37
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalArray;
38
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalBoolean;
39
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalInteger;
40
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalIntegerEnum;
41
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalNumber;
42
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalRelationship;
43
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalRelationships;
44
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalString;
45
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalStringEnum;
46
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalTimestamp;
47
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithOptionalUnixTimeStamp;
48
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithRelationship;
49
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithRelationshipWithNullableString;
50
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithRelationshipWithOptionalString;
51
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithRelationshipWithString;
52
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithRelationships;
53
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithRelationshipsWithLimit;
54
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithRelationshipsWithUnstantiableObject;
55
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithStaticalProperty;
56
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithString;
57
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithStringEnum;
58
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithTimestamp;
59
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithUnformattedTimestampProperty;
60
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithUnixTimeStamp;
61
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithUnstantiableRelationship;
62
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithUnsupportedProperty;
63
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithUnsupportedPropertyNotation;
64
use Sunrise\Hydrator\Tests\Fixtures\ObjectWithUntypedProperty;
65
use Sunrise\Hydrator\Tests\Fixtures\Store\Product;
66
use Sunrise\Hydrator\Tests\Fixtures\Store\Status;
67
use Sunrise\Hydrator\Tests\Fixtures\StringEnum;
68
use Sunrise\Hydrator\Tests\Fixtures\UnstantiableObject;
69
70
use function sprintf;
71
use function version_compare;
72
73
use const PHP_VERSION;
74
use const PHP_VERSION_ID;
75
76
class HydratorTest extends TestCase
77
{
78
    private ?int $invalidValueExceptionCount = null;
79
    private array $invalidValueExceptionMessage = [];
80
    private array $invalidValueExceptionPropertyPath = [];
81
    private array $invalidValueExceptionErrorCode = [];
82
83
    /**
84
     * @group boolean
85
     * @dataProvider strictBooleanDataProvider
86
     * @dataProvider nonStrictBooleanDataProvider
87
     */
88
    public function testHydrateBooleanProperty(array $data, bool $expected): void
89
    {
90
        $this->assertInvalidValueExceptionCount(0);
91
        $object = $this->createHydrator()->hydrate(ObjectWithBoolean::class, $data);
92
        $this->assertSame($expected, $object->value);
93
    }
94
95
    /**
96
     * @group boolean
97
     * @dataProvider strictBooleanDataProvider
98
     * @dataProvider nonStrictBooleanDataProvider
99
     * @dataProvider strictNullDataProvider
100
     * @dataProvider nonStrictNullDataProvider
101
     */
102
    public function testHydrateNullableBooleanProperty(array $data, ?bool $expected): void
103
    {
104
        $this->assertInvalidValueExceptionCount(0);
105
        $object = $this->createHydrator()->hydrate(ObjectWithNullableBoolean::class, $data);
106
        $this->assertSame($expected, $object->value);
107
    }
108
109
    /**
110
     * @group boolean
111
     * @dataProvider strictBooleanDataProvider
112
     * @dataProvider nonStrictBooleanDataProvider
113
     * @dataProvider emptyDataProvider
114
     */
115
    public function testHydrateOptionalBooleanProperty(array $data, bool $expected = true): void
116
    {
117
        $this->assertInvalidValueExceptionCount(0);
118
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalBoolean::class, $data);
119
        $this->assertSame($expected, $object->value);
120
    }
121
122
    /**
123
     * @group boolean
124
     * @dataProvider strictNullDataProvider
125
     * @dataProvider nonStrictNullDataProvider
126
     */
127
    public function testHydrateNonNullableBooleanPropertyWithNull(array $data): void
128
    {
129
        $this->assertInvalidValueExceptionCount(1);
130
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
131
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
132
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
133
        $this->createHydrator()->hydrate(ObjectWithBoolean::class, $data);
134
    }
135
136
    /**
137
     * @group boolean
138
     * @dataProvider notBooleanDataProvider
139
     */
140
    public function testHydrateBooleanPropertyWithNonBooleanValue(array $data): void
141
    {
142
        $this->assertInvalidValueExceptionCount(1);
143
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type boolean.');
144
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_BOOLEAN);
145
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
146
        $this->createHydrator()->hydrate(ObjectWithBoolean::class, $data);
147
    }
148
149
    /**
150
     * @group boolean
151
     */
152
    public function testHydrateRequiredBooleanPropertyWithoutValue(): void
153
    {
154
        $this->assertInvalidValueExceptionCount(1);
155
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
156
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
157
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
158
        $this->createHydrator()->hydrate(ObjectWithBoolean::class, []);
159
    }
160
161
    /**
162
     * @group integer
163
     * @dataProvider strictIntegerDataProvider
164
     * @dataProvider nonStrictIntegerDataProvider
165
     */
166
    public function testHydrateIntegerProperty(array $data, int $expected): void
167
    {
168
        $this->assertInvalidValueExceptionCount(0);
169
        $object = $this->createHydrator()->hydrate(ObjectWithInteger::class, $data);
170
        $this->assertSame($expected, $object->value);
171
    }
172
173
    /**
174
     * @group integer
175
     * @dataProvider strictIntegerDataProvider
176
     * @dataProvider nonStrictIntegerDataProvider
177
     * @dataProvider strictNullDataProvider
178
     * @dataProvider nonStrictNullDataProvider
179
     */
180
    public function testHydrateNullableIntegerProperty(array $data, ?int $expected): void
181
    {
182
        $this->assertInvalidValueExceptionCount(0);
183
        $object = $this->createHydrator()->hydrate(ObjectWithNullableInteger::class, $data);
184
        $this->assertSame($expected, $object->value);
185
    }
186
187
    /**
188
     * @group integer
189
     * @dataProvider strictIntegerDataProvider
190
     * @dataProvider nonStrictIntegerDataProvider
191
     * @dataProvider emptyDataProvider
192
     */
193
    public function testHydrateOptionalIntegerProperty(array $data, int $expected = 42): void
194
    {
195
        $this->assertInvalidValueExceptionCount(0);
196
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalInteger::class, $data);
197
        $this->assertSame($expected, $object->value);
198
    }
199
200
    /**
201
     * @group integer
202
     * @dataProvider strictNullDataProvider
203
     * @dataProvider nonStrictNullDataProvider
204
     */
205
    public function testHydrateNonNullableIntegerPropertyWithNull(array $data): void
206
    {
207
        $this->assertInvalidValueExceptionCount(1);
208
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
209
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
210
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
211
        $this->createHydrator()->hydrate(ObjectWithInteger::class, $data);
212
    }
213
214
    /**
215
     * @group integer
216
     * @dataProvider notIntegerDataProvider
217
     */
218
    public function testHydrateIntegerPropertyWithNotInteger(array $data): void
219
    {
220
        $this->assertInvalidValueExceptionCount(1);
221
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type integer.');
222
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_INTEGER);
223
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
224
        $this->createHydrator()->hydrate(ObjectWithInteger::class, $data);
225
    }
226
227
    /**
228
     * @group integer
229
     */
230
    public function testHydrateRequiredIntegerPropertyWithoutValue(): void
231
    {
232
        $this->assertInvalidValueExceptionCount(1);
233
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
234
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
235
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
236
        $this->createHydrator()->hydrate(ObjectWithInteger::class, []);
237
    }
238
239
    /**
240
     * @group number
241
     * @dataProvider strictNumberDataProvider
242
     * @dataProvider nonStrictNumberDataProvider
243
     */
244
    public function testHydrateNumericProperty(array $data, float $expected): void
245
    {
246
        $this->assertInvalidValueExceptionCount(0);
247
        $object = $this->createHydrator()->hydrate(ObjectWithNumber::class, $data);
248
        $this->assertSame($expected, $object->value);
249
    }
250
251
    /**
252
     * @group number
253
     * @dataProvider strictNumberDataProvider
254
     * @dataProvider nonStrictNumberDataProvider
255
     * @dataProvider strictNullDataProvider
256
     * @dataProvider nonStrictNullDataProvider
257
     */
258
    public function testHydrateNullableNumericProperty(array $data, ?float $expected): void
259
    {
260
        $this->assertInvalidValueExceptionCount(0);
261
        $object = $this->createHydrator()->hydrate(ObjectWithNullableNumber::class, $data);
262
        $this->assertSame($expected, $object->value);
263
    }
264
265
    /**
266
     * @group number
267
     * @dataProvider strictNumberDataProvider
268
     * @dataProvider nonStrictNumberDataProvider
269
     * @dataProvider emptyDataProvider
270
     */
271
    public function testHydrateOptionalNumericProperty(array $data, float $expected = 3.14159): void
272
    {
273
        $this->assertInvalidValueExceptionCount(0);
274
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalNumber::class, $data);
275
        $this->assertSame($expected, $object->value);
276
    }
277
278
    /**
279
     * @group number
280
     * @dataProvider strictNullDataProvider
281
     * @dataProvider nonStrictNullDataProvider
282
     */
283
    public function testHydrateNonNullableNumericPropertyWithNull(array $data): void
284
    {
285
        $this->assertInvalidValueExceptionCount(1);
286
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
287
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
288
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
289
        $this->createHydrator()->hydrate(ObjectWithNumber::class, $data);
290
    }
291
292
    /**
293
     * @group number
294
     * @dataProvider notNumberDataProvider
295
     */
296
    public function testHydrateNumericPropertyWithNotNumber(array $data): void
297
    {
298
        $this->assertInvalidValueExceptionCount(1);
299
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type number.');
300
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_NUMBER);
301
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
302
        $this->createHydrator()->hydrate(ObjectWithNumber::class, $data);
303
    }
304
305
    /**
306
     * @group number
307
     */
308
    public function testHydrateRequiredNumericPropertyWithoutValue(): void
309
    {
310
        $this->assertInvalidValueExceptionCount(1);
311
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
312
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
313
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
314
        $this->createHydrator()->hydrate(ObjectWithNumber::class, []);
315
    }
316
317
    /**
318
     * @group string
319
     * @dataProvider stringDataProvider
320
     */
321
    public function testHydrateStringProperty(array $data, string $expected): void
322
    {
323
        $this->assertInvalidValueExceptionCount(0);
324
        $object = $this->createHydrator()->hydrate(ObjectWithString::class, $data);
325
        $this->assertSame($expected, $object->value);
326
    }
327
328
    /**
329
     * @group string
330
     * @dataProvider stringDataProvider
331
     * @dataProvider strictNullDataProvider
332
     */
333
    public function testHydrateNullableStringProperty(array $data, ?string $expected): void
334
    {
335
        $this->assertInvalidValueExceptionCount(0);
336
        $object = $this->createHydrator()->hydrate(ObjectWithNullableString::class, $data);
337
        $this->assertSame($expected, $object->value);
338
    }
339
340
    /**
341
     * @group string
342
     * @dataProvider stringDataProvider
343
     * @dataProvider emptyDataProvider
344
     */
345
    public function testHydrateOptionalStringProperty(array $data, string $expected = 'default'): void
346
    {
347
        $this->assertInvalidValueExceptionCount(0);
348
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalString::class, $data);
349
        $this->assertSame($expected, $object->value);
350
    }
351
352
    /**
353
     * @group string
354
     * @dataProvider strictNullDataProvider
355
     */
356
    public function testHydrateNonNullableStringPropertyWithNull(array $data): void
357
    {
358
        $this->assertInvalidValueExceptionCount(1);
359
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
360
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
361
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
362
        $this->createHydrator()->hydrate(ObjectWithString::class, $data);
363
    }
364
365
    /**
366
     * @group string
367
     * @dataProvider notStringDataProvider
368
     */
369
    public function testHydrateStringPropertyWithNotString(array $data): void
370
    {
371
        $this->assertInvalidValueExceptionCount(1);
372
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type string.');
373
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_STRING);
374
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
375
        $this->createHydrator()->hydrate(ObjectWithString::class, $data);
376
    }
377
378
    /**
379
     * @group string
380
     */
381
    public function testHydrateRequiredStringPropertyWithoutValue(): void
382
    {
383
        $this->assertInvalidValueExceptionCount(1);
384
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
385
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
386
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
387
        $this->createHydrator()->hydrate(ObjectWithString::class, []);
388
    }
389
390
    /**
391
     * @group array
392
     * @dataProvider arrayDataProvider
393
     */
394
    public function testHydrateArrayProperty(array $data, array $expected): void
395
    {
396
        $this->assertInvalidValueExceptionCount(0);
397
        $object = $this->createHydrator()->hydrate(ObjectWithArray::class, $data);
398
        $this->assertSame($expected, $object->value);
399
    }
400
401
    /**
402
     * @group array
403
     * @dataProvider arrayDataProvider
404
     * @dataProvider strictNullDataProvider
405
     */
406
    public function testHydrateNullableArrayProperty(array $data, ?array $expected): void
407
    {
408
        $this->assertInvalidValueExceptionCount(0);
409
        $object = $this->createHydrator()->hydrate(ObjectWithNullableArray::class, $data);
410
        $this->assertSame($expected, $object->value);
411
    }
412
413
    /**
414
     * @group array
415
     * @dataProvider arrayDataProvider
416
     * @dataProvider emptyDataProvider
417
     */
418
    public function testHydrateOptionalArrayProperty(array $data, array $expected = []): void
419
    {
420
        $this->assertInvalidValueExceptionCount(0);
421
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalArray::class, $data);
422
        $this->assertSame($expected, $object->value);
423
    }
424
425
    /**
426
     * @group array
427
     * @dataProvider strictNullDataProvider
428
     */
429
    public function testHydrateNonNullableArrayPropertyWithNull(array $data): void
430
    {
431
        $this->assertInvalidValueExceptionCount(1);
432
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
433
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
434
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
435
        $this->createHydrator()->hydrate(ObjectWithArray::class, $data);
436
    }
437
438
    /**
439
     * @group array
440
     * @dataProvider notArrayDataProvider
441
     */
442
    public function testHydrateArrayPropertyWithNotArray(array $data): void
443
    {
444
        $this->assertInvalidValueExceptionCount(1);
445
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type array.');
446
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_ARRAY);
447
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
448
        $this->createHydrator()->hydrate(ObjectWithArray::class, $data);
449
    }
450
451
    /**
452
     * @group array
453
     */
454
    public function testHydrateRequiredArrayPropertyWithoutValue(): void
455
    {
456
        $this->assertInvalidValueExceptionCount(1);
457
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
458
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
459
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
460
        $this->createHydrator()->hydrate(ObjectWithArray::class, []);
461
    }
462
463
    /**
464
     * @group datetimeTimestamp
465
     * @dataProvider timestampDataProvider
466
     */
467
    public function testHydrateTimestampProperty(array $data, string $expected, string $format): void
468
    {
469
        $this->assertInvalidValueExceptionCount(0);
470
        $object = $this->createHydrator()->hydrate(ObjectWithTimestamp::class, $data);
471
        $this->assertSame($expected, $object->value->format($format));
472
    }
473
474
    /**
475
     * @group datetimeTimestamp
476
     * @dataProvider timestampDataProvider
477
     * @dataProvider strictNullDataProvider
478
     * @dataProvider nonStrictNullDataProvider
479
     */
480
    // phpcs:ignore Generic.Files.LineLength
481
    public function testHydrateNullableTimestampProperty(array $data, ?string $expected = null, ?string $format = null): void
482
    {
483
        $this->assertInvalidValueExceptionCount(0);
484
        $object = $this->createHydrator()->hydrate(ObjectWithNullableTimestamp::class, $data);
485
        $this->assertSame($expected, isset($object->value, $format) ? $object->value->format($format) : null);
486
    }
487
488
    /**
489
     * @group datetimeTimestamp
490
     * @dataProvider timestampDataProvider
491
     * @dataProvider emptyDataProvider
492
     */
493
    // phpcs:ignore Generic.Files.LineLength
494
    public function testHydrateOptionalTimestampProperty(array $data, ?string $expected = null, ?string $format = null): void
495
    {
496
        $this->assertInvalidValueExceptionCount(0);
497
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalTimestamp::class, $data);
498
        $this->assertSame($expected, isset($object->value, $format) ? $object->value->format($format) : null);
499
    }
500
501
    /**
502
     * @group datetimeTimestamp
503
     * @dataProvider strictNullDataProvider
504
     * @dataProvider nonStrictNullDataProvider
505
     */
506
    public function testHydrateNonNullableTimestampPropertyWithNull(array $data): void
507
    {
508
        $this->assertInvalidValueExceptionCount(1);
509
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
510
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
511
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
512
        $this->createHydrator()->hydrate(ObjectWithTimestamp::class, $data);
513
    }
514
515
    /**
516
     * @group datetimeTimestamp
517
     * @dataProvider notStringDataProvider
518
     */
519
    public function testHydrateTimestampPropertyWithNotString(array $data): void
520
    {
521
        $this->assertInvalidValueExceptionCount(1);
522
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type string.');
523
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_STRING);
524
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
525
        $this->createHydrator()->hydrate(ObjectWithTimestamp::class, $data);
526
    }
527
528
    /**
529
     * @group datetimeTimestamp
530
     * @dataProvider invalidTimestampDataProvider
531
     */
532
    public function testHydrateTimestampPropertyWithInvalidTimestamp(array $data): void
533
    {
534
        $this->assertInvalidValueExceptionCount(1);
535
        $message = sprintf('This value is not a valid timestamp, expected format: %s.', ObjectWithTimestamp::FORMAT);
536
        $this->assertInvalidValueExceptionMessage(0, $message);
537
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_TIMESTAMP);
538
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
539
        $this->createHydrator()->hydrate(ObjectWithTimestamp::class, $data);
540
    }
541
542
    /**
543
     * @group datetimeTimestamp
544
     */
545
    public function testHydrateRequiredTimestampPropertyWithoutValue(): void
546
    {
547
        $this->assertInvalidValueExceptionCount(1);
548
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
549
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
550
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
551
        $this->createHydrator()->hydrate(ObjectWithTimestamp::class, []);
552
    }
553
554
    /**
555
     * @group datetimeTimestamp
556
     * @dataProvider timestampDataProvider
557
     */
558
    public function testHydrateUnformattedTimestampProperty(array $data): void
559
    {
560
        $this->expectException(UnsupportedPropertyTypeException::class);
561
        $this->expectExceptionMessage(sprintf(
562
            'The property %1$s.%2$s must contain the attribute %3$s, ' .
563
            'for example: #[\%3$s(\DateTimeInterface::DATE_RFC3339)].',
564
            ObjectWithUnformattedTimestampProperty::class,
565
            'value',
566
            Format::class,
567
        ));
568
569
        $this->createHydrator()->hydrate(ObjectWithUnformattedTimestampProperty::class, $data);
570
    }
571
572
    /**
573
     * @group unixTimestamp
574
     * @dataProvider strictUnixTimeStampDataProvider
575
     * @dataProvider nonStrictUnixTimeStampDataProvider
576
     */
577
    public function testHydrateUnixTimeStampProperty(array $data, string $expected, string $format): void
578
    {
579
        $this->assertInvalidValueExceptionCount(0);
580
        $object = $this->createHydrator()->hydrate(ObjectWithUnixTimeStamp::class, $data);
581
        $this->assertSame($expected, $object->value->format($format));
582
    }
583
584
    /**
585
     * @group unixTimestamp
586
     * @dataProvider strictUnixTimeStampDataProvider
587
     * @dataProvider nonStrictUnixTimeStampDataProvider
588
     * @dataProvider strictNullDataProvider
589
     * @dataProvider nonStrictNullDataProvider
590
     */
591
    // phpcs:ignore Generic.Files.LineLength
592
    public function testHydrateNullableUnixTimeStampProperty(array $data, ?string $expected = null, ?string $format = null): void
593
    {
594
        $this->assertInvalidValueExceptionCount(0);
595
        $object = $this->createHydrator()->hydrate(ObjectWithNullableUnixTimeStamp::class, $data);
596
        $this->assertSame($expected, isset($object->value, $format) ? $object->value->format($format) : null);
597
    }
598
599
    /**
600
     * @group unixTimestamp
601
     * @dataProvider strictUnixTimeStampDataProvider
602
     * @dataProvider nonStrictUnixTimeStampDataProvider
603
     * @dataProvider emptyDataProvider
604
     */
605
    // phpcs:ignore Generic.Files.LineLength
606
    public function testHydrateOptionalUnixTimeStampProperty(array $data, ?string $expected = null, ?string $format = null): void
607
    {
608
        $this->assertInvalidValueExceptionCount(0);
609
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalUnixTimeStamp::class, $data);
610
        $this->assertSame($expected, isset($object->value, $format) ? $object->value->format($format) : null);
611
    }
612
613
    /**
614
     * @group unixTimestamp
615
     * @dataProvider strictNullDataProvider
616
     * @dataProvider nonStrictNullDataProvider
617
     */
618
    public function testHydrateNonNullableUnixTimeStampPropertyWithNull(array $data): void
619
    {
620
        $this->assertInvalidValueExceptionCount(1);
621
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
622
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
623
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
624
        $this->createHydrator()->hydrate(ObjectWithUnixTimeStamp::class, $data);
625
    }
626
627
    /**
628
     * @group unixTimestamp
629
     * @dataProvider notIntegerDataProvider
630
     */
631
    public function testHydrateUnixTimeStampPropertyWithNotInteger(array $data): void
632
    {
633
        $this->assertInvalidValueExceptionCount(1);
634
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type integer.');
635
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_INTEGER);
636
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
637
        $this->createHydrator()->hydrate(ObjectWithUnixTimeStamp::class, $data);
638
    }
639
640
    /**
641
     * @group unixTimestamp
642
     */
643
    public function testHydrateRequiredUnixTimeStampPropertyWithoutValue(): void
644
    {
645
        $this->assertInvalidValueExceptionCount(1);
646
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
647
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
648
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
649
        $this->createHydrator()->hydrate(ObjectWithUnixTimeStamp::class, []);
650
    }
651
652
    /**
653
     * @group integerEnumeration
654
     * @dataProvider strictIntegerEnumerationDataProvider
655
     * @dataProvider nonStrictIntegerEnumerationDataProvider
656
     * @param IntegerEnum $expected
657
     */
658
    public function testHydrateIntegerEnumerationProperty(array $data, $expected): void
659
    {
660
        $this->phpRequired('8.1');
661
        $this->assertInvalidValueExceptionCount(0);
662
        $object = $this->createHydrator()->hydrate(ObjectWithIntegerEnum::class, $data);
663
        $this->assertSame($expected, $object->value);
664
    }
665
666
    /**
667
     * @group integerEnumeration
668
     * @dataProvider strictIntegerEnumerationDataProvider
669
     * @dataProvider nonStrictIntegerEnumerationDataProvider
670
     * @dataProvider strictNullDataProvider
671
     * @dataProvider nonStrictNullDataProvider
672
     * @param IntegerEnum|null $expected
673
     */
674
    public function testHydrateNullableIntegerEnumerationProperty(array $data, $expected): void
675
    {
676
        $this->phpRequired('8.1');
677
        $this->assertInvalidValueExceptionCount(0);
678
        $object = $this->createHydrator()->hydrate(ObjectWithNullableIntegerEnum::class, $data);
679
        $this->assertSame($expected, $object->value);
680
    }
681
682
    /**
683
     * @group integerEnumeration
684
     * @dataProvider strictIntegerEnumerationDataProvider
685
     * @dataProvider nonStrictIntegerEnumerationDataProvider
686
     * @dataProvider emptyDataProvider
687
     * @param IntegerEnum|null $expected
688
     */
689
    // phpcs:ignore Generic.Files.LineLength
690
    public function testHydrateOptionalIntegerEnumerationProperty(array $data, $expected = null): void
691
    {
692
        $this->phpRequired('8.1');
693
        $this->assertInvalidValueExceptionCount(0);
694
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalIntegerEnum::class, $data);
695
        $this->assertSame($expected ?? IntegerEnum::FOO, $object->value);
696
    }
697
698
    /**
699
     * @group integerEnumeration
700
     * @dataProvider strictNullDataProvider
701
     * @dataProvider nonStrictNullDataProvider
702
     */
703
    public function testHydrateNonNullableIntegerEnumerationPropertyWithNull(array $data): void
704
    {
705
        $this->phpRequired('8.1');
706
        $this->assertInvalidValueExceptionCount(1);
707
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
708
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
709
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
710
        $this->createHydrator()->hydrate(ObjectWithIntegerEnum::class, $data);
711
    }
712
713
    /**
714
     * @group integerEnumeration
715
     * @dataProvider notIntegerDataProvider
716
     */
717
    public function testHydrateIntegerEnumerationPropertyWithNotInteger(array $data): void
718
    {
719
        $this->phpRequired('8.1');
720
        $this->assertInvalidValueExceptionCount(1);
721
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type integer.');
722
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_INTEGER);
723
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
724
        $this->createHydrator()->hydrate(ObjectWithIntegerEnum::class, $data);
725
    }
726
727
    /**
728
     * @group integerEnumeration
729
     */
730
    public function testHydrateRequiredIntegerEnumerationPropertyWithoutValue(): void
731
    {
732
        $this->assertInvalidValueExceptionCount(1);
733
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
734
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
735
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
736
        $this->createHydrator()->hydrate(ObjectWithIntegerEnum::class, []);
737
    }
738
739
    /**
740
     * @group stringEnumeration
741
     */
742
    public function testHydrateIntegerEnumerationPropertyWithInvalidChoice(): void
743
    {
744
        $this->phpRequired('8.1');
745
        $this->assertInvalidValueExceptionCount(1);
746
        // phpcs:ignore Generic.Files.LineLength
747
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid choice, expected choices: 1, 2, 3.');
748
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_CHOICE);
749
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
750
        $this->createHydrator()->hydrate(ObjectWithIntegerEnum::class, ['value' => 42]);
751
    }
752
753
    /**
754
     * @group stringEnumeration
755
     * @dataProvider stringEnumerationDataProvider
756
     * @param StringEnum $expected
757
     */
758
    public function testHydrateStringEnumerationProperty(array $data, $expected): void
759
    {
760
        $this->phpRequired('8.1');
761
        $this->assertInvalidValueExceptionCount(0);
762
        $object = $this->createHydrator()->hydrate(ObjectWithStringEnum::class, $data);
763
        $this->assertSame($expected, $object->value);
764
    }
765
766
    /**
767
     * @group stringEnumeration
768
     * @dataProvider stringEnumerationDataProvider
769
     * @dataProvider strictNullDataProvider
770
     * @dataProvider nonStrictNullDataProvider
771
     * @param StringEnum|null $expected
772
     */
773
    public function testHydrateNullableStringEnumerationProperty(array $data, $expected): void
774
    {
775
        $this->phpRequired('8.1');
776
        $this->assertInvalidValueExceptionCount(0);
777
        $object = $this->createHydrator()->hydrate(ObjectWithNullableStringEnum::class, $data);
778
        $this->assertSame($expected, $object->value);
779
    }
780
781
    /**
782
     * @group stringEnumeration
783
     * @dataProvider stringEnumerationDataProvider
784
     * @dataProvider emptyDataProvider
785
     * @param StringEnum|null $expected
786
     */
787
    // phpcs:ignore Generic.Files.LineLength
788
    public function testHydrateOptionalStringEnumerationProperty(array $data, $expected = null): void
789
    {
790
        $this->phpRequired('8.1');
791
        $this->assertInvalidValueExceptionCount(0);
792
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalStringEnum::class, $data);
793
        $this->assertSame($expected ?? StringEnum::FOO, $object->value);
794
    }
795
796
    /**
797
     * @group stringEnumeration
798
     * @dataProvider strictNullDataProvider
799
     * @dataProvider nonStrictNullDataProvider
800
     */
801
    public function testHydrateNonNullableStringEnumerationPropertyWithNull(array $data): void
802
    {
803
        $this->phpRequired('8.1');
804
        $this->assertInvalidValueExceptionCount(1);
805
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
806
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
807
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
808
        $this->createHydrator()->hydrate(ObjectWithStringEnum::class, $data);
809
    }
810
811
    /**
812
     * @group stringEnumeration
813
     * @dataProvider notStringDataProvider
814
     */
815
    public function testHydrateStringEnumerationPropertyWithNotInteger(array $data): void
816
    {
817
        $this->phpRequired('8.1');
818
        $this->assertInvalidValueExceptionCount(1);
819
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type string.');
820
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_STRING);
821
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
822
        $this->createHydrator()->hydrate(ObjectWithStringEnum::class, $data);
823
    }
824
825
    /**
826
     * @group stringEnumeration
827
     */
828
    public function testHydrateStringEnumerationPropertyWithInvalidChoice(): void
829
    {
830
        $this->phpRequired('8.1');
831
        $this->assertInvalidValueExceptionCount(1);
832
        // phpcs:ignore Generic.Files.LineLength
833
        $this->assertInvalidValueExceptionMessage(0, 'This value is not a valid choice, expected choices: foo, bar, baz.');
834
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::INVALID_CHOICE);
835
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
836
        $this->createHydrator()->hydrate(ObjectWithStringEnum::class, ['value' => 'unknown']);
837
    }
838
839
    /**
840
     * @group stringEnumeration
841
     */
842
    public function testHydrateRequiredStringEnumerationPropertyWithoutValue(): void
843
    {
844
        $this->assertInvalidValueExceptionCount(1);
845
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
846
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
847
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
848
        $this->createHydrator()->hydrate(ObjectWithStringEnum::class, []);
849
    }
850
851
    /**
852
     * @group relationship
853
     * @dataProvider stringDataProvider
854
     */
855
    public function testHydrateRelationshipProperty(array $data, string $expected): void
856
    {
857
        $this->assertInvalidValueExceptionCount(0);
858
        $object = $this->createHydrator()->hydrate(ObjectWithRelationship::class, ['value' => $data]);
859
        $this->assertSame($expected, $object->value->value);
860
    }
861
862
    /**
863
     * @group relationship
864
     * @dataProvider strictNullDataProvider
865
     */
866
    public function testHydrateNullableRelationshipProperty(array $data): void
867
    {
868
        $this->assertInvalidValueExceptionCount(0);
869
        $object = $this->createHydrator()->hydrate(ObjectWithNullableRelationship::class, $data);
870
        $this->assertNull($object->value);
871
    }
872
873
    /**
874
     * @group relationship
875
     * @dataProvider emptyDataProvider
876
     */
877
    public function testHydrateOptionalRelationshipProperty(array $data): void
878
    {
879
        $this->assertInvalidValueExceptionCount(0);
880
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalRelationship::class, $data);
881
        $this->assertNull($object->value);
882
    }
883
884
    /**
885
     * @group relationship
886
     * @dataProvider strictNullDataProvider
887
     */
888
    public function testHydrateNonNullableRelationshipPropertyWithNull(array $data): void
889
    {
890
        $this->assertInvalidValueExceptionCount(1);
891
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
892
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
893
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
894
        $this->createHydrator()->hydrate(ObjectWithRelationship::class, $data);
895
    }
896
897
    /**
898
     * @group relationship
899
     * @dataProvider notArrayDataProvider
900
     */
901
    public function testHydrateRelationshipPropertyWithNotArray(array $data): void
902
    {
903
        $this->assertInvalidValueExceptionCount(1);
904
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type array.');
905
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_ARRAY);
906
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
907
        $this->createHydrator()->hydrate(ObjectWithRelationship::class, $data);
908
    }
909
910
    /**
911
     * @group relationship
912
     */
913
    public function testHydrateRequiredRelationshipPropertyWithoutValue(): void
914
    {
915
        $this->assertInvalidValueExceptionCount(1);
916
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
917
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
918
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
919
        $this->createHydrator()->hydrate(ObjectWithRelationship::class, []);
920
    }
921
922
    /**
923
     * @group relationship
924
     * @dataProvider stringDataProvider
925
     */
926
    public function testHydrateRelationshipWithStringProperty(array $data, string $expected): void
927
    {
928
        $this->assertInvalidValueExceptionCount(0);
929
        $object = $this->createHydrator()->hydrate(ObjectWithRelationshipWithString::class, ['value' => $data]);
930
        $this->assertSame($expected, $object->value->value);
931
    }
932
933
    /**
934
     * @group relationship
935
     * @dataProvider stringDataProvider
936
     * @dataProvider strictNullDataProvider
937
     */
938
    public function testHydrateRelationshipWithNullableStringProperty(array $data, ?string $expected): void
939
    {
940
        $this->assertInvalidValueExceptionCount(0);
941
        $object = $this->createHydrator()->hydrate(ObjectWithRelationshipWithNullableString::class, ['value' => $data]);
942
        $this->assertSame($expected, $object->value->value);
943
    }
944
945
    /**
946
     * @group relationship
947
     * @dataProvider stringDataProvider
948
     * @dataProvider emptyDataProvider
949
     */
950
    public function testHydrateRelationshipWithOptionalStringProperty(array $data, string $expected = 'default'): void
951
    {
952
        $this->assertInvalidValueExceptionCount(0);
953
        $object = $this->createHydrator()->hydrate(ObjectWithRelationshipWithOptionalString::class, ['value' => $data]);
954
        $this->assertSame($expected, $object->value->value);
955
    }
956
957
    /**
958
     * @group relationship
959
     * @dataProvider strictNullDataProvider
960
     */
961
    public function testHydrateRelationshipWithNonNullablePropertyWithNull(array $data): void
962
    {
963
        $this->assertInvalidValueExceptionCount(1);
964
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
965
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
966
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.value');
967
        $this->createHydrator()->hydrate(ObjectWithRelationshipWithString::class, ['value' => $data]);
968
    }
969
970
    /**
971
     * @group relationship
972
     * @dataProvider notStringDataProvider
973
     */
974
    public function testHydrateRelationshipWithStringPropertyWithNotString(array $data): void
975
    {
976
        $this->assertInvalidValueExceptionCount(1);
977
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type string.');
978
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_STRING);
979
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.value');
980
        $this->createHydrator()->hydrate(ObjectWithRelationshipWithString::class, ['value' => $data]);
981
    }
982
983
    /**
984
     * @group relationship
985
     */
986
    public function testHydrateRelationshipWithRequiredStringPropertyWithoutValue(): void
987
    {
988
        $this->assertInvalidValueExceptionCount(1);
989
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
990
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
991
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.value');
992
        $this->createHydrator()->hydrate(ObjectWithRelationshipWithString::class, ['value' => []]);
993
    }
994
995
    /**
996
     * @group relationship
997
     */
998
    public function testHydrateUnstantiableRelationshipProperty(): void
999
    {
1000
        $this->expectException(UnsupportedPropertyTypeException::class);
1001
        $this->expectExceptionMessage(sprintf(
1002
            'The property %s.%s refers to a non-instantiable class %s.',
1003
            ObjectWithUnstantiableRelationship::class,
1004
            'value',
1005
            UnstantiableObject::class,
1006
        ));
1007
1008
        $this->createHydrator()->hydrate(ObjectWithUnstantiableRelationship::class, ['value' => []]);
1009
    }
1010
1011
    /**
1012
     * @group relationships
1013
     */
1014
    public function testHydrateRelationshipsProperty(): void
1015
    {
1016
        $data = ['value' => [['value' => ['value' => 'foo']], ['value' => ['value' => 'bar']]]];
1017
1018
        $this->assertInvalidValueExceptionCount(0);
1019
        $object = $this->createHydrator()->hydrate(ObjectWithRelationships::class, $data);
1020
        $this->assertCount(2, $object->value);
1021
        $this->assertArrayHasKey(0, $object->value);
1022
        $this->assertSame($data['value'][0]['value']['value'], $object->value[0]->value->value);
1023
        $this->assertArrayHasKey(1, $object->value);
1024
        $this->assertSame($data['value'][1]['value']['value'], $object->value[1]->value->value);
1025
    }
1026
1027
    /**
1028
     * @group relationships
1029
     * @dataProvider strictNullDataProvider
1030
     */
1031
    public function testHydrateNullableRelationshipsProperty(array $data): void
1032
    {
1033
        $this->assertInvalidValueExceptionCount(0);
1034
        $object = $this->createHydrator()->hydrate(ObjectWithNullableRelationships::class, $data);
1035
        $this->assertNull($object->value);
1036
    }
1037
1038
    /**
1039
     * @group relationships
1040
     * @dataProvider emptyDataProvider
1041
     */
1042
    public function testHydrateOptionalRelationshipsProperty(array $data): void
1043
    {
1044
        $this->assertInvalidValueExceptionCount(0);
1045
        $object = $this->createHydrator()->hydrate(ObjectWithOptionalRelationships::class, $data);
1046
        $this->assertSame([], $object->value);
1047
    }
1048
1049
    /**
1050
     * @group relationships
1051
     * @dataProvider strictNullDataProvider
1052
     */
1053
    public function testHydrateNonNullableRelationshipsPropertyWithNull(array $data): void
1054
    {
1055
        $this->assertInvalidValueExceptionCount(1);
1056
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
1057
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
1058
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1059
        $this->createHydrator()->hydrate(ObjectWithRelationships::class, $data);
1060
    }
1061
1062
    /**
1063
     * @group relationships
1064
     * @dataProvider notArrayDataProvider
1065
     */
1066
    public function testHydrateRelationshipsPropertyWithNotArray(array $data): void
1067
    {
1068
        $this->assertInvalidValueExceptionCount(1);
1069
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type array.');
1070
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_ARRAY);
1071
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1072
        $this->createHydrator()->hydrate(ObjectWithRelationships::class, $data);
1073
    }
1074
1075
    /**
1076
     * @group relationships
1077
     */
1078
    public function testHydrateRequiredRelationshipsPropertyWithoutValue(): void
1079
    {
1080
        $this->assertInvalidValueExceptionCount(1);
1081
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
1082
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
1083
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1084
        $this->createHydrator()->hydrate(ObjectWithRelationships::class, []);
1085
    }
1086
1087
    /**
1088
     * @group relationships
1089
     * @dataProvider strictNullDataProvider
1090
     */
1091
    public function testHydrateRelationshipsPropertyWithNullsForNonNullableValues(array $data): void
1092
    {
1093
        $this->assertInvalidValueExceptionCount(1);
1094
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
1095
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
1096
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value.value');
1097
        $this->createHydrator()->hydrate(ObjectWithRelationships::class, ['value' => [['value' => $data]]]);
1098
    }
1099
1100
    /**
1101
     * @group relationships
1102
     * @dataProvider notStringDataProvider
1103
     */
1104
    public function testHydrateRelationshipsPropertyWithNotStringForStringValue(array $data): void
1105
    {
1106
        $this->assertInvalidValueExceptionCount(1);
1107
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type string.');
1108
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_STRING);
1109
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value.value');
1110
        $this->createHydrator()->hydrate(ObjectWithRelationships::class, ['value' => [['value' => $data]]]);
1111
    }
1112
1113
    /**
1114
     * @group relationships
1115
     */
1116
    public function testHydrateRelationshipsPropertyWithoutValueForRequiredValue(): void
1117
    {
1118
        $this->assertInvalidValueExceptionCount(1);
1119
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
1120
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
1121
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value.value');
1122
        $this->createHydrator()->hydrate(ObjectWithRelationships::class, ['value' => [['value' => []]]]);
1123
    }
1124
1125
    /**
1126
     * @group relationships
1127
     */
1128
    public function testHydrateRelationshipsPropertyWithNotArrayForRelation(): void
1129
    {
1130
        $this->assertInvalidValueExceptionCount(1);
1131
        $this->assertInvalidValueExceptionMessage(0, 'This value should be of type array.');
1132
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_ARRAY);
1133
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0');
1134
        $this->createHydrator()->hydrate(ObjectWithRelationships::class, ['value' => [null]]);
1135
    }
1136
1137
    /**
1138
     * @group relationships
1139
     */
1140
    public function testSeveralErrorsWhenHydratingRelationshipsProperty(): void
1141
    {
1142
        $this->assertInvalidValueExceptionCount(3);
1143
        $this->assertInvalidValueExceptionMessage(0, 'This value should not be empty.');
1144
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_NOT_BE_EMPTY);
1145
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.0.value.value');
1146
        $this->assertInvalidValueExceptionMessage(1, 'This value should be of type string.');
1147
        $this->assertInvalidValueExceptionErrorCode(1, ErrorCode::VALUE_SHOULD_BE_STRING);
1148
        $this->assertInvalidValueExceptionPropertyPath(1, 'value.1.value.value');
1149
        $this->assertInvalidValueExceptionMessage(2, 'This value should be provided.');
1150
        $this->assertInvalidValueExceptionErrorCode(2, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
1151
        $this->assertInvalidValueExceptionPropertyPath(2, 'value.2.value.value');
1152
        $this->createHydrator()->hydrate(ObjectWithRelationships::class, ['value' => [
1153
            ['value' => ['value' => null]],
1154
            ['value' => ['value' => []]],
1155
            ['value' => []]
1156
        ]]);
1157
    }
1158
1159
    /**
1160
     * @group relationships
1161
     */
1162
    public function testHydrateLimitedRelationshipsProperty(): void
1163
    {
1164
        $this->assertInvalidValueExceptionCount(1);
1165
        $this->assertInvalidValueExceptionMessage(0, 'This element is redundant, limit: 1.');
1166
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::REDUNDANT_ELEMENT);
1167
        $this->assertInvalidValueExceptionPropertyPath(0, 'value.1');
1168
        $this->createHydrator()->hydrate(ObjectWithRelationshipsWithLimit::class, ['value' => [
1169
            ['value' => 'foo'],
1170
            ['value' => 'bar'],
1171
        ]]);
1172
    }
1173
1174
    /**
1175
     * @group relationships
1176
     */
1177
    public function testHydrateRelationshipsPropertyWithUnstantiableObject(): void
1178
    {
1179
        $this->expectException(UnsupportedPropertyTypeException::class);
1180
        $this->expectExceptionMessage(sprintf(
1181
            'The property %s.%s refers to a non-instantiable class %s.',
1182
            ObjectWithRelationshipsWithUnstantiableObject::class,
1183
            'value',
1184
            UnstantiableObject::class,
1185
        ));
1186
1187
        $this->createHydrator()->hydrate(
1188
            ObjectWithRelationshipsWithUnstantiableObject::class,
1189
            ['value' => [['value' => []]]],
1190
        );
1191
    }
1192
1193
    /**
1194
     * @group json
1195
     */
1196
    public function testHydrateObjectWithJson(): void
1197
    {
1198
        $this->assertInvalidValueExceptionCount(0);
1199
        $object = $this->createHydrator()->hydrateWithJson(ObjectWithString::class, '{"value": "foo"}');
1200
        $this->assertSame('foo', $object->value);
1201
    }
1202
1203
    /**
1204
     * @group json
1205
     */
1206
    public function testHydrateObjectWithInvalidJson(): void
1207
    {
1208
        $this->expectException(InvalidDataException::class);
1209
        $this->expectExceptionMessage('Invalid JSON: Maximum stack depth exceeded');
1210
        $this->createHydrator()->hydrateWithJson(ObjectWithString::class, '[]', 0, 1);
1211
    }
1212
1213
    /**
1214
     * @group json
1215
     */
1216
    public function testHydrateObjectWithNonObjectableJson(): void
1217
    {
1218
        $this->expectException(InvalidDataException::class);
1219
        $this->expectExceptionMessage('JSON must be an object.');
1220
        $this->createHydrator()->hydrateWithJson(ObjectWithString::class, 'null');
1221
    }
1222
1223
    public function testInstantedObject(): void
1224
    {
1225
        $object = new class {
1226
            public string $value;
1227
        };
1228
1229
        $this->assertInvalidValueExceptionCount(0);
1230
        $this->createHydrator()->hydrate($object, ['value' => 'foo']);
1231
        $this->assertSame('foo', $object->value);
1232
    }
1233
1234
    public function testUnstantiableObject(): void
1235
    {
1236
        $this->expectException(UninitializableObjectException::class);
1237
        $this->expectExceptionMessage(sprintf(
1238
            'The class %s cannot be hydrated because it is an uninstantiable class.',
1239
            UnstantiableObject::class,
1240
        ));
1241
1242
        $this->createHydrator()->hydrate(UnstantiableObject::class, []);
1243
    }
1244
1245
    public function testStaticalProperty(): void
1246
    {
1247
        $this->assertInvalidValueExceptionCount(0);
1248
        $this->createHydrator()->hydrate(ObjectWithStaticalProperty::class, ['value' => 'foo']);
1249
        $this->assertNotSame('foo', ObjectWithStaticalProperty::$value);
1250
    }
1251
1252
    public function testIgnoredProperty(): void
1253
    {
1254
        $this->assertInvalidValueExceptionCount(0);
1255
        $object = $this->createHydrator()->hydrate(ObjectWithIgnoredProperty::class, ['value' => 'foo']);
1256
        $this->assertNotSame('foo', $object->value);
1257
    }
1258
1259
    public function testUnsupportedPropertyType(): void
1260
    {
1261
        $this->expectException(UnsupportedPropertyTypeException::class);
1262
        $this->expectExceptionMessage(sprintf(
1263
            'The property %s.%s contains an unsupported type %s.',
1264
            ObjectWithUnsupportedProperty::class,
1265
            'value',
1266
            'iterable',
1267
        ));
1268
1269
        $this->createHydrator()->hydrate(ObjectWithUnsupportedProperty::class, ['value' => []]);
1270
    }
1271
1272
    public function testUnsupportedPropertyTypeNotation(): void
1273
    {
1274
        $this->phpRequired('8.0');
1275
1276
        $this->expectException(UnsupportedPropertyTypeException::class);
1277
        $this->expectExceptionMessage(sprintf(
1278
            'The property %s.%s contains an unsupported type %s.',
1279
            ObjectWithUnsupportedPropertyNotation::class,
1280
            'value',
1281
            'int|float',
1282
        ));
1283
1284
        $this->createHydrator()->hydrate(ObjectWithUnsupportedPropertyNotation::class, ['value' => []]);
1285
    }
1286
1287
    public function testUntypedProperty(): void
1288
    {
1289
        $this->expectException(UntypedPropertyException::class);
1290
        $this->expectExceptionMessage(sprintf(
1291
            'The property %s.%s is not typed.',
1292
            ObjectWithUntypedProperty::class,
1293
            'value',
1294
        ));
1295
1296
        $this->createHydrator()->hydrate(ObjectWithUntypedProperty::class, ['value' => []]);
1297
    }
1298
1299
    public function testAnnotatedAlias(): void
1300
    {
1301
        /** @var Hydrator $hydrator */
1302
        $hydrator = $this->createHydrator();
1303
        $hydrator->useDefaultAnnotationReader();
1304
1305
        $this->assertInvalidValueExceptionCount(0);
1306
        $object = $hydrator->hydrate(ObjectWithAnnotatedAlias::class, ['non-normalized-value' => 'foo']);
1307
        $this->assertSame('foo', $object->value);
1308
1309
        $this->assertInvalidValueExceptionCount(1);
1310
        $hydrator->hydrate(ObjectWithAnnotatedAlias::class, ['value' => 'foo']);
1311
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
1312
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
1313
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1314
    }
1315
1316
    public function testAttributedAlias(): void
1317
    {
1318
        $this->phpRequired('8.0');
1319
1320
        /** @var Hydrator $hydrator */
1321
        $hydrator = $this->createHydrator();
1322
1323
        $this->assertInvalidValueExceptionCount(0);
1324
        $object = $hydrator->hydrate(ObjectWithAttributedAlias::class, ['non-normalized-value' => 'foo']);
1325
        $this->assertSame('foo', $object->value);
1326
1327
        $this->assertInvalidValueExceptionCount(1);
1328
        $hydrator->hydrate(ObjectWithAttributedAlias::class, ['value' => 'foo']);
1329
        $this->assertInvalidValueExceptionMessage(0, 'This value should be provided.');
1330
        $this->assertInvalidValueExceptionErrorCode(0, ErrorCode::VALUE_SHOULD_BE_PROVIDED);
1331
        $this->assertInvalidValueExceptionPropertyPath(0, 'value');
1332
    }
1333
1334
    public function testAnnotationReader(): void
1335
    {
1336
        /** @var Hydrator $hydrator */
1337
        $hydrator = $this->createHydrator();
1338
1339
        $hydrator->setAnnotationReader(null);
1340
        $this->assertNull($hydrator->getAnnotationReader());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $hydrator->getAnnotationReader() targeting Sunrise\Hydrator\Hydrator::getAnnotationReader() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1341
1342
        $reader = $this->createMock(Reader::class);
1343
        $hydrator->setAnnotationReader($reader);
1344
        $this->assertSame($reader, $hydrator->getAnnotationReader());
1345
1346
        $hydrator->setAnnotationReader(null);
1347
        $this->assertNull($hydrator->getAnnotationReader());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $hydrator->getAnnotationReader() targeting Sunrise\Hydrator\Hydrator::getAnnotationReader() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1348
    }
1349
1350
    public function testHydrateStore(): void
1351
    {
1352
        $this->phpRequired('8.1');
1353
1354
        $sold = Status::SOLD;
1355
1356
        $data = [];
1357
        $data['name'] = 'Some product';
1358
        $data['category']['name'] = 'Some category';
1359
        $data['tags'][]['name'] = 'foo';
1360
        $data['tags'][]['name'] = 'bar';
1361
        $data['status'] = $sold->value;
1362
        $data['createdAt'] = '2000-01-01 00:00:00';
1363
1364
        $this->assertInvalidValueExceptionCount(0);
1365
        $product = $this->createHydrator()->hydrate(Product::class, $data);
1366
        $this->assertSame('Some product', $product->name);
1367
        $this->assertSame('Some category', $product->category->name);
1368
        $this->assertCount(2, $product->tags);
1369
        $this->assertArrayHasKey(0, $product->tags);
1370
        $this->assertSame('foo', $product->tags[0]->name);
1371
        $this->assertArrayHasKey(1, $product->tags);
1372
        $this->assertSame('bar', $product->tags[1]->name);
1373
        $this->assertSame(Status::SOLD, $product->status);
1374
        $this->assertSame('2000-01-01 00:00:00', $product->createdAt->format('Y-m-d H:i:s'));
1375
1376
        unset($data['createdAt']);
1377
        $this->assertInvalidValueExceptionCount(0);
1378
        $product = $this->createHydrator()->hydrate(Product::class, $data);
1379
        $this->assertSame('2020-01-01 12:00:00', $product->createdAt->format('Y-m-d H:i:s'));
1380
    }
1381
1382
    public function testSymfonyViolations(): void
1383
    {
1384
        $violations = null;
1385
        try {
1386
            $this->createHydrator()->hydrate(ObjectWithString::class, []);
1387
        } catch (InvalidDataException $e) {
1388
            $violations = $e->getViolations();
1389
        }
1390
1391
        $this->assertNotNull($violations);
1392
        $this->assertCount(1, $violations);
1393
        $this->assertTrue($violations->has(0));
1394
        $this->assertSame(ErrorCode::VALUE_SHOULD_BE_PROVIDED, $violations->get(0)->getCode());
1395
        $this->assertSame('This value should be provided.', $violations->get(0)->getMessage());
1396
        $this->assertSame('value', $violations->get(0)->getPropertyPath());
1397
    }
1398
1399
    public function emptyDataProvider(): array
1400
    {
1401
        return [
1402
            [[]],
1403
        ];
1404
    }
1405
1406
    public function strictNullDataProvider(): array
1407
    {
1408
        return [
1409
            [['value' => null], null],
1410
        ];
1411
    }
1412
1413
    public function nonStrictNullDataProvider(): array
1414
    {
1415
        return [
1416
            [['value' => ''], null],
1417
            [['value' => ' '], null],
1418
        ];
1419
    }
1420
1421
    public function strictBooleanDataProvider(): array
1422
    {
1423
        return [
1424
            [['value' => true], true],
1425
            [['value' => false], false],
1426
        ];
1427
    }
1428
1429
    public function nonStrictBooleanDataProvider(): array
1430
    {
1431
        return [
1432
            [['value' => '1'], true],
1433
            [['value' => '0'], false],
1434
            [['value' => 'true'], true],
1435
            [['value' => 'false'], false],
1436
            [['value' => 'yes'], true],
1437
            [['value' => 'no'], false],
1438
            [['value' => 'on'], true],
1439
            [['value' => 'off'], false],
1440
        ];
1441
    }
1442
1443
    public function notBooleanDataProvider(): array
1444
    {
1445
        return [
1446
            [['value' => 0]],
1447
            [['value' => 1]],
1448
            [['value' => 42]],
1449
            [['value' => 3.14159]],
1450
            [['value' => 'foo']],
1451
            [['value' => []]],
1452
        ];
1453
    }
1454
1455
    public function strictIntegerDataProvider(): array
1456
    {
1457
        return [
1458
            [['value' => -1], -1],
1459
            [['value' => 0], 0],
1460
            [['value' => 1], 1],
1461
        ];
1462
    }
1463
1464
    public function nonStrictIntegerDataProvider(): array
1465
    {
1466
        return [
1467
            [['value' => '-1'], -1],
1468
            [['value' => '0'], 0],
1469
            [['value' => '+1'], 1],
1470
        ];
1471
    }
1472
1473
    public function notIntegerDataProvider(): array
1474
    {
1475
        return [
1476
            [['value' => true]],
1477
            [['value' => false]],
1478
            [['value' => 3.14159]],
1479
            [['value' => 'foo']],
1480
            [['value' => []]],
1481
        ];
1482
    }
1483
1484
    public function strictNumberDataProvider(): array
1485
    {
1486
        return [
1487
            [['value' => -1], -1.],
1488
            [['value' => 0], 0.],
1489
            [['value' => 1], 1.],
1490
            [['value' => -1.], -1.],
1491
            [['value' => 0.], 0.],
1492
            [['value' => 1.], 1.],
1493
            [['value' => -.1], -.1],
1494
            [['value' => .0], .0],
1495
            [['value' => .1], .1],
1496
        ];
1497
    }
1498
1499
    public function nonStrictNumberDataProvider(): array
1500
    {
1501
        return [
1502
            [['value' => '-1'], -1.],
1503
            [['value' => '0'], 0.],
1504
            [['value' => '+1'], 1.],
1505
            [['value' => '-1.'], -1.],
1506
            [['value' => '0.'], 0.],
1507
            [['value' => '+1.'], 1.],
1508
            [['value' => '-.1'], -.1],
1509
            [['value' => '.0'], .0],
1510
            [['value' => '+.1'], .1],
1511
            [['value' => '-1.0'], -1.],
1512
            [['value' => '0.0'], 0.],
1513
            [['value' => '+1.0'], 1.],
1514
            [['value' => '1e-1'], .1],
1515
            [['value' => '1e1'], 10.],
1516
            [['value' => '1e+1'], 10.],
1517
            [['value' => '1.e-1'], .1],
1518
            [['value' => '1.e1'], 10.],
1519
            [['value' => '1.e+1'], 10.],
1520
            [['value' => '.1e-1'], .01],
1521
            [['value' => '.1e1'], 1.],
1522
            [['value' => '.1e+1'], 1.],
1523
            [['value' => '1.0e-1'], .1],
1524
            [['value' => '1.0e1'], 10.],
1525
            [['value' => '1.0e+1'], 10.],
1526
        ];
1527
    }
1528
1529
    public function notNumberDataProvider(): array
1530
    {
1531
        return [
1532
            [['value' => true]],
1533
            [['value' => false]],
1534
            [['value' => 'foo']],
1535
            [['value' => []]],
1536
        ];
1537
    }
1538
1539
    public function stringDataProvider(): array
1540
    {
1541
        return [
1542
            [['value' => 'foo'], 'foo'],
1543
1544
            // Should not be cast to a null
1545
            [['value' => ''], ''],
1546
            [['value' => ' '], ' '],
1547
1548
            // Should not be cast to a boolean type
1549
            [['value' => '1'], '1'],
1550
            [['value' => '0'], '0'],
1551
            [['value' => 'true'], 'true'],
1552
            [['value' => 'false'], 'false'],
1553
            [['value' => 'yes'], 'yes'],
1554
            [['value' => 'no'], 'no'],
1555
            [['value' => 'on'], 'on'],
1556
            [['value' => 'off'], 'off'],
1557
1558
            // Should not be cast to a number
1559
            [['value' => '42'], '42'],
1560
            [['value' => '3.14159'], '3.14159'],
1561
        ];
1562
    }
1563
1564
    public function notStringDataProvider(): array
1565
    {
1566
        return [
1567
            [['value' => true]],
1568
            [['value' => false]],
1569
            [['value' => 42]],
1570
            [['value' => 3.14159]],
1571
            [['value' => []]],
1572
        ];
1573
    }
1574
1575
    public function arrayDataProvider(): array
1576
    {
1577
        return [
1578
            [['value' => ['foo']], ['foo']]
1579
        ];
1580
    }
1581
1582
    public function notArrayDataProvider(): array
1583
    {
1584
        return [
1585
            [['value' => true]],
1586
            [['value' => false]],
1587
            [['value' => 42]],
1588
            [['value' => 3.14159]],
1589
            [['value' => 'foo']],
1590
        ];
1591
    }
1592
1593
    public function timestampDataProvider(): array
1594
    {
1595
        return [
1596
            [['value' => '1970-01-01 00:00:00'], '1970-01-01 00:00:00', 'Y-m-d H:i:s'],
1597
        ];
1598
    }
1599
1600
    public function invalidTimestampDataProvider(): array
1601
    {
1602
        return [
1603
            [['value' => 'Tue, 06 Jun 23 16:50:23']],
1604
        ];
1605
    }
1606
1607
    public function notTimestampDataProvider(): array
1608
    {
1609
        return [
1610
            [['value' => true]],
1611
            [['value' => false]],
1612
            [['value' => 42]],
1613
            [['value' => 3.14159]],
1614
            [['value' => []]],
1615
        ];
1616
    }
1617
1618
    public function strictUnixTimeStampDataProvider(): array
1619
    {
1620
        return [
1621
            [['value' => -1], '1969-12-31 23:59:59', 'Y-m-d H:i:s'],
1622
            [['value' => 0], '1970-01-01 00:00:00', 'Y-m-d H:i:s'],
1623
            [['value' => 1], '1970-01-01 00:00:01', 'Y-m-d H:i:s'],
1624
        ];
1625
    }
1626
1627
    public function nonStrictUnixTimeStampDataProvider(): array
1628
    {
1629
        return [
1630
            [['value' => '-1'], '1969-12-31 23:59:59', 'Y-m-d H:i:s'],
1631
            [['value' => '0'], '1970-01-01 00:00:00', 'Y-m-d H:i:s'],
1632
            [['value' => '1'], '1970-01-01 00:00:01', 'Y-m-d H:i:s'],
1633
        ];
1634
    }
1635
1636
    public function notUnixTimeStampDataProvider(): array
1637
    {
1638
        return [
1639
            [['value' => true]],
1640
            [['value' => false]],
1641
            [['value' => 'foo']],
1642
            [['value' => []]],
1643
        ];
1644
    }
1645
1646
    public function strictIntegerEnumerationDataProvider(): array
1647
    {
1648
        if (PHP_VERSION_ID < 80100) {
1649
            return [[[], null]];
1650
        }
1651
1652
        $foo = IntegerEnum::FOO;
1653
        $bar = IntegerEnum::BAR;
1654
        $baz = IntegerEnum::BAZ;
1655
1656
        return [
1657
            [['value' => $foo->value], $foo],
1658
            [['value' => $bar->value], $bar],
1659
            [['value' => $baz->value], $baz],
1660
        ];
1661
    }
1662
1663
    public function nonStrictIntegerEnumerationDataProvider(): array
1664
    {
1665
        if (PHP_VERSION_ID < 80100) {
1666
            return [[[], null]];
1667
        }
1668
1669
        $foo = IntegerEnum::FOO;
1670
        $bar = IntegerEnum::BAR;
1671
        $baz = IntegerEnum::BAZ;
1672
1673
        return [
1674
            [['value' => (string) $foo->value], $foo],
1675
            [['value' => (string) $bar->value], $bar],
1676
            [['value' => (string) $baz->value], $baz],
1677
        ];
1678
    }
1679
1680
    public function stringEnumerationDataProvider(): array
1681
    {
1682
        if (PHP_VERSION_ID < 80100) {
1683
            return [[[], null]];
1684
        }
1685
1686
        $foo = StringEnum::FOO;
1687
        $bar = StringEnum::BAR;
1688
        $baz = StringEnum::BAZ;
1689
1690
        return [
1691
            [['value' => $foo->value], $foo],
1692
            [['value' => $bar->value], $bar],
1693
            [['value' => $baz->value], $baz],
1694
        ];
1695
    }
1696
1697
    private function createHydrator(): HydratorInterface
1698
    {
1699
        $hydrator = new Hydrator();
1700
        if (PHP_VERSION_ID < 80000) {
1701
            $hydrator->useDefaultAnnotationReader();
1702
        }
1703
1704
        return $hydrator;
1705
    }
1706
1707
    private function phpRequired(string $version): void
1708
    {
1709
        if (version_compare(PHP_VERSION, $version, '<')) {
1710
            $this->markTestSkipped(sprintf('PHP %s is required.', $version));
1711
        }
1712
    }
1713
1714
    private function assertInvalidValueExceptionCount(int $expectedCount): void
1715
    {
1716
        $this->invalidValueExceptionCount = $expectedCount;
1717
    }
1718
1719
    private function assertInvalidValueExceptionMessage(int $exceptionIndex, string $expectedMessage): void
1720
    {
1721
        $this->invalidValueExceptionMessage[] = [$exceptionIndex, $expectedMessage];
1722
    }
1723
1724
    private function assertInvalidValueExceptionPropertyPath(int $exceptionIndex, string $expectedPropertyPath): void
1725
    {
1726
        $this->invalidValueExceptionPropertyPath[] = [$exceptionIndex, $expectedPropertyPath];
1727
    }
1728
1729
    private function assertInvalidValueExceptionErrorCode(int $exceptionIndex, string $expectedErrorCode): void
1730
    {
1731
        $this->invalidValueExceptionErrorCode[] = [$exceptionIndex, $expectedErrorCode];
1732
    }
1733
1734
    protected function runTest(): void
1735
    {
1736
        $invalidDataExceptionHandled = false;
1737
1738
        try {
1739
            parent::runTest();
1740
        } catch (InvalidDataException $invalidDataException) {
1741
            $invalidDataExceptionMessages = [];
1742
            foreach ($invalidDataException->getExceptions() as $invalidValueException) {
1743
                $invalidDataExceptionMessages[] = sprintf(
1744
                    '[%s] %s',
1745
                    $invalidValueException->getPropertyPath(),
1746
                    $invalidValueException->getMessage(),
1747
                );
1748
            }
1749
1750
            if (isset($this->invalidValueExceptionCount)) {
1751
                $invalidDataExceptionHandled = true;
1752
                $this->assertCount(
1753
                    $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

1753
                    /** @scrutinizer ignore-type */ $this->invalidValueExceptionCount,
Loading history...
1754
                    $invalidDataException->getExceptions(),
1755
                    \join(\PHP_EOL, $invalidDataExceptionMessages),
1756
                );
1757
            }
1758
1759
            foreach ($this->invalidValueExceptionMessage as [$index, $invalidValueExceptionMessage]) {
1760
                $invalidDataExceptionHandled = true;
1761
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
1762
                $this->assertSame(
1763
                    $invalidValueExceptionMessage,
1764
                    $invalidDataException->getExceptions()[$index]->getMessage(),
1765
                );
1766
            }
1767
1768
            foreach ($this->invalidValueExceptionPropertyPath as [$index, $invalidValueExceptionPropertyPath]) {
1769
                $invalidDataExceptionHandled = true;
1770
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
1771
                $this->assertSame(
1772
                    $invalidValueExceptionPropertyPath,
1773
                    $invalidDataException->getExceptions()[$index]->getPropertyPath(),
1774
                );
1775
            }
1776
1777
            foreach ($this->invalidValueExceptionErrorCode as [$index, $invalidValueExceptionErrorCode]) {
1778
                $invalidDataExceptionHandled = true;
1779
                $this->assertArrayHasKey($index, $invalidDataException->getExceptions());
1780
                $this->assertSame(
1781
                    $invalidValueExceptionErrorCode,
1782
                    $invalidDataException->getExceptions()[$index]->getErrorCode(),
1783
                );
1784
            }
1785
1786
            if (!$invalidDataExceptionHandled) {
1787
                throw $invalidDataException;
1788
            }
1789
        } finally {
1790
            $this->invalidValueExceptionCount = null;
1791
            $this->invalidValueExceptionMessage = [];
1792
            $this->invalidValueExceptionPropertyPath = [];
1793
            $this->invalidValueExceptionErrorCode = [];
1794
1795
            if ($invalidDataExceptionHandled) {
1796
                $this->assertTrue(true);
1797
            }
1798
        }
1799
    }
1800
}
1801