Passed
Push — main ( d57b17...28c7e4 )
by Anatoly
09:38 queued 06:40
created

HydratorTest::testHydratePropertyWithEmptyString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 7
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 testHydrate() : void
23
    {
24
        $data = [];
25
        $data['statical'] = '813ea72c-6763-4596-a4d6-b478efed61bb';
26
        $data['nullable'] = null;
27
        $data['required'] = '9f5c273e-1dca-4c2d-ac81-7d6b03b169f4';
28
        $data['boolean'] = true;
29
        $data['integer'] = 42;
30
        $data['number'] = 123.45;
31
        $data['string'] = 'db7614d4-0a81-437b-b2cf-c536ad229c97';
32
        $data['array'] = ['foo' => 'bar'];
33
        $data['object'] = (object) ['foo' => 'bar'];
34
        $data['dateTime'] = '2038-01-19 03:14:08';
35
        $data['dateTimeImmutable'] = '2038-01-19 03:14:08';
36
        $data['bar'] = ['value' => '9898fb3b-ffb0-406c-bda6-b516423abde7'];
37
        $data['barCollection'][] = ['value' => 'd85c17b6-6e2c-4e2d-9eba-e1dd59b75fe3'];
38
        $data['barCollection'][] = ['value' => '5a8019aa-1c15-4c7c-8beb-1783c3d8996b'];
39
        $data['non-normalized'] = 'f76c4656-431a-4337-9ba9-5440611b37f1';
40
41
        $object = (new Hydrator)
42
            ->useAnnotations()
43
            ->useAnnotations() // CC
44
            ->hydrate(new Fixtures\Foo(), $data);
45
46
        $this->assertNotSame($data['statical'], $object::$statical);
47
        $this->assertSame($data['nullable'], $object->nullable);
48
        $this->assertSame($data['required'], $object->required);
49
        $this->assertSame($data['boolean'], $object->boolean);
50
        $this->assertSame($data['integer'], $object->integer);
51
        $this->assertSame($data['number'], $object->number);
52
        $this->assertSame($data['string'], $object->string);
53
        $this->assertSame($data['array'], $object->array);
54
        $this->assertSame($data['object'], $object->object);
55
        $this->assertSame($data['dateTime'], $object->dateTime->format('Y-m-d H:i:s'));
56
        $this->assertSame($data['dateTimeImmutable'], $object->dateTimeImmutable->format('Y-m-d H:i:s'));
57
        $this->assertSame($data['bar']['value'], $object->bar->value);
58
        $this->assertSame($data['barCollection'][0]['value'], $object->barCollection->get(0)->value);
59
        $this->assertSame($data['barCollection'][1]['value'], $object->barCollection->get(1)->value);
60
        $this->assertSame($data['non-normalized'], $object->normalized);
61
    }
62
63
    public function testHydrateUndefinedObject() : void
64
    {
65
        $this->expectException(InvalidArgumentException::class);
66
        $this->expectExceptionMessage('The method ' . Hydrator::class . '::hydrate() ' .
67
                                      'expects an object or name of an existing class.');
68
69
        (new Hydrator)->hydrate('Undefined', []);
70
    }
71
72
    public function testHydrateUninitializableObject() : void
73
    {
74
        $this->expectException(InvalidArgumentException::class);
75
        $this->expectExceptionMessage('The object ' . Fixtures\UninitializableObject::class . ' ' .
76
                                      'cannot be hydrated because its constructor has required parameters.');
77
78
        (new Hydrator)->hydrate(Fixtures\UninitializableObject::class, []);
79
    }
80
81
    public function testHydrateWithJson() : void
82
    {
83
        $json = '{"value": "4c1e3453-7b76-4d5d-b4b8-bc6b0afcd835"}';
84
85
        $object = (new Hydrator)->useAnnotations()->hydrateWithJson(Fixtures\Bar::class, $json);
86
87
        $this->assertSame($object->value, '4c1e3453-7b76-4d5d-b4b8-bc6b0afcd835');
88
    }
89
90
    public function testHydrateWithInvalidJson() : void
91
    {
92
        $this->expectException(InvalidArgumentException::class);
93
        $this->expectExceptionMessage('Unable to decode JSON: Syntax error');
94
95
        (new Hydrator)->useAnnotations()->hydrateWithJson(Fixtures\Bar::class, '!');
96
    }
97
98
    public function testUntypedProperty() : void
99
    {
100
        $this->expectException(Exception\UntypedPropertyException::class);
101
        $this->expectExceptionMessage('The ObjectWithUntypedProperty.value property is not typed.');
102
103
        (new Hydrator)->hydrate(Fixtures\ObjectWithUntypedProperty::class, []);
104
    }
105
106
    public function testUnsupportedPropertyType() : void
107
    {
108
        $this->expectException(Exception\UnsupportedPropertyTypeException::class);
109
        $this->expectExceptionMessage('The ObjectWithUnsupportedPropertyType.value property ' .
110
                                      'contains an unsupported type iterable.');
111
112
        (new Hydrator)->hydrate(Fixtures\ObjectWithUnsupportedPropertyType::class, [
113
            'value' => 'b25c08e8-4771-42c0-b01b-fe7fd3689602',
114
        ]);
115
    }
116
117
    public function testUnionPropertyType() : void
118
    {
119
        if (8 > \PHP_MAJOR_VERSION) {
120
            $this->markTestSkipped('PHP 8 is required...');
121
            return;
122
        }
123
124
        $this->expectException(Exception\UnsupportedPropertyTypeException::class);
125
        $this->expectExceptionMessage('The ObjectWithUnionPropertyType.value property ' .
126
                                      'contains an union type that is not supported.');
127
128
        (new Hydrator)->hydrate(Fixtures\ObjectWithUnionPropertyType::class, []);
129
    }
130
131
    public function testRequiredProperty() : void
132
    {
133
        $this->expectException(Exception\MissingRequiredValueException::class);
134
        $this->expectExceptionMessage('The ObjectWithRequiredProperty.value property ' .
135
                                      'is required.');
136
137
        try {
138
            (new Hydrator)->hydrate(Fixtures\ObjectWithRequiredProperty::class, []);
139
        } catch (Exception\MissingRequiredValueException $e) {
140
            $this->assertSame('value', $e->getProperty()->getName());
141
142
            throw $e;
143
        }
144
    }
145
146
    public function testAnnotatedProperty() : void
147
    {
148
        $data = [
149
            'non-normalized-value' => '87019bbd-643b-45b8-94f8-0abd56be9851',
150
        ];
151
152
        $object = (new Hydrator)->useAnnotations()->hydrate(Fixtures\ObjectWithAnnotatedProperty::class, $data);
153
154
        $this->assertSame($object->value, '87019bbd-643b-45b8-94f8-0abd56be9851');
155
    }
156
157
    public function testUnnullableProperty() : void
158
    {
159
        $this->expectException(Exception\InvalidValueException::class);
160
        $this->expectExceptionMessage('The Bar.value property cannot accept null.');
161
162
        (new Hydrator)->hydrate(Fixtures\Bar::class, [
163
            'value' => null,
164
        ]);
165
    }
166
167
    public function testHydratePropertyWithStringBooleanValue() : void
168
    {
169
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithBooleanProperty::class, [
170
            'value' => 'yes',
171
        ]);
172
173
        $this->assertTrue($object->value);
174
    }
175
176
    public function testHydratePropertyWithStringIntegerNumber() : void
177
    {
178
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithIntegerProperty::class, [
179
            'value' => '42',
180
        ]);
181
182
        $this->assertSame(42, $object->value);
183
    }
184
185
    public function testHydratePropertyWithStringableNumber() : void
186
    {
187
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithNumberProperty::class, [
188
            'value' => '123.45',
189
        ]);
190
191
        $this->assertSame(123.45, $object->value);
192
    }
193
194
    public function testHydratePropertyWithIntegerTimestamp() : void
195
    {
196
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithTimestampProperty::class, [
197
            'value' => 1262304000,
198
        ]);
199
200
        $this->assertSame('2010-01-01', $object->value->format('Y-m-d'));
201
    }
202
203
    public function testHydratePropertyWithStringIntegerTimestamp() : void
204
    {
205
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithTimestampProperty::class, [
206
            'value' => '1262304000',
207
        ]);
208
209
        $this->assertSame('2010-01-01', $object->value->format('Y-m-d'));
210
    }
211
212
    public function testHydratePropertyWithStringDateTime() : void
213
    {
214
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithTimestampProperty::class, [
215
            'value' => '2010-01-01',
216
        ]);
217
218
        $this->assertSame('2010-01-01', $object->value->format('Y-m-d'));
219
    }
220
221
    public function testHydratePropertyWithInvalidBooleanValue() : void
222
    {
223
        $this->expectException(Exception\InvalidValueException::class);
224
        $this->expectExceptionMessage('The ObjectWithBooleanProperty.value property accepts a boolean value only.');
225
226
        (new Hydrator)->hydrate(Fixtures\ObjectWithBooleanProperty::class, [
227
            'value' => [],
228
        ]);
229
    }
230
231
    public function testHydratePropertyWithInvalidIntegerNumber() : void
232
    {
233
        $this->expectException(Exception\InvalidValueException::class);
234
        $this->expectExceptionMessage('The ObjectWithIntegerProperty.value property accepts an integer number only.');
235
236
        (new Hydrator)->hydrate(Fixtures\ObjectWithIntegerProperty::class, [
237
            'value' => [],
238
        ]);
239
    }
240
241
    public function testHydratePropertyWithInvalidNumber() : void
242
    {
243
        $this->expectException(Exception\InvalidValueException::class);
244
        $this->expectExceptionMessage('The ObjectWithNumberProperty.value property accepts a number only.');
245
246
        (new Hydrator)->hydrate(Fixtures\ObjectWithNumberProperty::class, [
247
            'value' => [],
248
        ]);
249
    }
250
251
    public function testHydratePropertyWithInvalidString() : void
252
    {
253
        $this->expectException(Exception\InvalidValueException::class);
254
        $this->expectExceptionMessage('The ObjectWithStringProperty.value property accepts a string only.');
255
256
        (new Hydrator)->hydrate(Fixtures\ObjectWithStringProperty::class, [
257
            'value' => [],
258
        ]);
259
    }
260
261
    public function testHydratePropertyWithInvalidArray() : void
262
    {
263
        $this->expectException(Exception\InvalidValueException::class);
264
        $this->expectExceptionMessage('The ObjectWithArrayProperty.value property accepts an array only.');
265
266
        (new Hydrator)->hydrate(Fixtures\ObjectWithArrayProperty::class, [
267
            'value' => 0,
268
        ]);
269
    }
270
271
    public function testHydratePropertyWithInvalidObject() : void
272
    {
273
        $this->expectException(Exception\InvalidValueException::class);
274
        $this->expectExceptionMessage('The ObjectWithObjectProperty.value property accepts an object only.');
275
276
        (new Hydrator)->hydrate(Fixtures\ObjectWithObjectProperty::class, [
277
            'value' => 0,
278
        ]);
279
    }
280
281
    public function testHydratePropertyWithInvalidTimestamp() : void
282
    {
283
        $this->expectException(Exception\InvalidValueException::class);
284
        $this->expectExceptionMessage('The ObjectWithTimestampProperty.value property ' .
285
                                      'accepts a valid date-time string or a timestamp only.');
286
287
        (new Hydrator)->hydrate(Fixtures\ObjectWithTimestampProperty::class, [
288
            'value' => [],
289
        ]);
290
    }
291
292
    public function testHydratePropertyWithInvalidAssociation() : void
293
    {
294
        $this->expectException(Exception\InvalidValueException::class);
295
        $this->expectExceptionMessage('The ObjectWithAssociation.value property accepts an array only.');
296
297
        (new Hydrator)->hydrate(Fixtures\ObjectWithAssociation::class, [
298
            'value' => 0,
299
        ]);
300
    }
301
302
    public function testHydratePropertyWithInvalidAssociations() : void
303
    {
304
        $this->expectException(Exception\InvalidValueException::class);
305
        $this->expectExceptionMessage('The ObjectWithAssociations.value property accepts an array only.');
306
307
        (new Hydrator)->hydrate(Fixtures\ObjectWithAssociations::class, [
308
            'value' => 0,
309
        ]);
310
    }
311
312
    public function testHydratePropertyWithInvalidOneOfAssociations() : void
313
    {
314
        $this->expectException(Exception\InvalidValueException::class);
315
        $this->expectExceptionMessage('The ObjectWithAssociations.value property accepts an array with arrays only.');
316
317
        (new Hydrator)->hydrate(Fixtures\ObjectWithAssociations::class, [
318
            'value' => [0],
319
        ]);
320
    }
321
322
    public function testHydratePropertyWithInterval() : void
323
    {
324
        $object = (new Hydrator)->hydrate(Fixtures\Baz::class, [
325
            'duration' => 'P1W2D',
326
        ]);
327
328
        $this->assertInstanceOf(\DateInterval::class, $object->duration);
329
    }
330
331
    public function testHydratePropertyWithInvalidIntervalType() : void
332
    {
333
        $this->expectException(Exception\InvalidValueException::class);
334
        $this->expectExceptionMessage('The Baz.duration property accepts a string only.');
335
336
        (new Hydrator)->hydrate(Fixtures\Baz::class, [
337
            'duration' => 0,
338
        ]);
339
    }
340
341
    public function testHydratePropertyWithInvalidInterval() : void
342
    {
343
        $this->expectException(Exception\InvalidValueException::class);
344
        $this->expectExceptionMessage('The Baz.duration property accepts a valid interval based on ISO 8601.');
345
346
        (new Hydrator)->hydrate(Fixtures\Baz::class, [
347
            'duration' => 'fuuu',
348
        ]);
349
    }
350
351
    public function testHydratePropertyWithEmptyStringIntegerNumber() : void
352
    {
353
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithNullableIntegerProperty::class, [
354
            'value' => '',
355
        ]);
356
357
        $this->assertNull($object->value);
358
    }
359
360
    public function testHydratePropertyWithEmptyString() : void
361
    {
362
        $object = (new Hydrator)->hydrate(Fixtures\ObjectWithStringProperty::class, [
363
            'value' => '',
364
        ]);
365
366
        $this->assertSame('', $object->value);
367
    }
368
}
369