• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 6417369461

05 Oct 2023 09:44AM UTC coverage: 36.79% (-0.3%) from 37.131%
6417369461

push

github

web-flow
feat: improve 'not_normalizable_value_exception' (#5844)

71 of 71 new or added lines in 3 files covered. (100.0%)

10087 of 27418 relevant lines covered (36.79%)

6.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Serializer/Tests/AbstractItemNormalizerTest.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Serializer\Tests;
15

16
use ApiPlatform\Metadata\ApiProperty;
17
use ApiPlatform\Metadata\ApiResource;
18
use ApiPlatform\Metadata\Get;
19
use ApiPlatform\Metadata\GetCollection;
20
use ApiPlatform\Metadata\IriConverterInterface;
21
use ApiPlatform\Metadata\Operations;
22
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
23
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
24
use ApiPlatform\Metadata\Property\PropertyNameCollection;
25
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
26
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
27
use ApiPlatform\Metadata\ResourceClassResolverInterface;
28
use ApiPlatform\Metadata\UrlGeneratorInterface;
29
use ApiPlatform\Serializer\AbstractItemNormalizer;
30
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DtoWithNullValue;
31
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\Dummy;
32
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DummyTableInheritance;
33
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DummyTableInheritanceChild;
34
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DummyTableInheritanceRelated;
35
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\NonCloneableDummy;
36
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\PropertyCollectionIriOnly;
37
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\PropertyCollectionIriOnlyRelation;
38
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\RelatedDummy;
39
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\SecuredDummy;
40
use ApiPlatform\Symfony\Security\ResourceAccessCheckerInterface;
41
use Doctrine\Common\Collections\ArrayCollection;
42
use PHPUnit\Framework\TestCase;
43
use Prophecy\Argument;
44
use Prophecy\PhpUnit\ProphecyTrait;
45
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
46
use Symfony\Component\PropertyAccess\PropertyAccessor;
47
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
48
use Symfony\Component\PropertyInfo\Type;
49
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
50
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
51
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
52
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
53
use Symfony\Component\Serializer\Serializer;
54
use Symfony\Component\Serializer\SerializerInterface;
55

56
/**
57
 * @author Amrouche Hamza <hamza.simperfit@gmail.com>
58
 * @author Kévin Dunglas <dunglas@gmail.com>
59
 */
60
class AbstractItemNormalizerTest extends TestCase
61
{
62
    use ExpectDeprecationTrait;
63
    use ProphecyTrait;
64

65
    /**
66
     * @group legacy
67
     */
68
    public function testSupportNormalizationAndSupportDenormalization(): void
69
    {
70
        $std = new \stdClass();
×
71
        $dummy = new Dummy();
×
72

73
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
74
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
75
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
76
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
77

78
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
79
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
80
        $resourceClassResolverProphecy->isResourceClass(\stdClass::class)->willReturn(false);
×
81

82
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
83
            $propertyNameCollectionFactoryProphecy->reveal(),
×
84
            $propertyMetadataFactoryProphecy->reveal(),
×
85
            $iriConverterProphecy->reveal(),
×
86
            $resourceClassResolverProphecy->reveal(),
×
87
            $propertyAccessorProphecy->reveal(),
×
88
            null,
×
89
            null,
×
90
            [],
×
91
            null,
×
92
            null,
×
93
        ]);
×
94

95
        $this->assertTrue($normalizer->supportsNormalization($dummy));
×
96
        $this->assertFalse($normalizer->supportsNormalization($std));
×
97
        $this->assertTrue($normalizer->supportsDenormalization($dummy, Dummy::class));
×
98
        $this->assertFalse($normalizer->supportsDenormalization($std, \stdClass::class));
×
99
        $this->assertFalse($normalizer->supportsNormalization([]));
×
100
        $this->assertSame(['object' => true], $normalizer->getSupportedTypes('any'));
×
101

102
        if (!method_exists(Serializer::class, 'getSupportedTypes')) {
×
103
            $this->assertTrue($normalizer->hasCacheableSupportsMethod());
×
104
        }
105
    }
106

107
    public function testNormalize(): void
108
    {
109
        $relatedDummy = new RelatedDummy();
×
110

111
        $dummy = new Dummy();
×
112
        $dummy->setName('foo');
×
113
        $dummy->setAlias('ignored');
×
114
        $dummy->setRelatedDummy($relatedDummy);
×
115
        $dummy->relatedDummies->add(new RelatedDummy());
×
116

117
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
118

119
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
120
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name', 'alias', 'relatedDummy', 'relatedDummies']));
×
121

122
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
123
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
124

125
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
126
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
127
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
128
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
129
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(false));
×
130

131
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
132
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
×
133
        $iriConverterProphecy->getIriFromResource($relatedDummy, Argument::cetera())->willReturn('/dummies/2');
×
134

135
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
136
        $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('foo');
×
137
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
138
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
139

140
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
141
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
142
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
143
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
144
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
145
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
146
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
147

148
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
149
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
150
        $serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo');
×
151
        $serializerProphecy->normalize(['/dummies/2'], null, Argument::type('array'))->willReturn(['/dummies/2']);
×
152

153
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
154
            $propertyNameCollectionFactoryProphecy->reveal(),
×
155
            $propertyMetadataFactoryProphecy->reveal(),
×
156
            $iriConverterProphecy->reveal(),
×
157
            $resourceClassResolverProphecy->reveal(),
×
158
            $propertyAccessorProphecy->reveal(),
×
159
            null,
×
160
            null,
×
161
            [],
×
162
            null,
×
163
            null,
×
164
        ]);
×
165
        $normalizer->setSerializer($serializerProphecy->reveal());
×
166

167
        $expected = [
×
168
            'name' => 'foo',
×
169
            'relatedDummy' => '/dummies/2',
×
170
            'relatedDummies' => ['/dummies/2'],
×
171
        ];
×
172
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
173
            'resources' => [],
×
174
            'ignored_attributes' => ['alias'],
×
175
        ]));
×
176
    }
177

178
    public function testNormalizeWithSecuredProperty(): void
179
    {
180
        $dummy = new SecuredDummy();
×
181
        $dummy->setTitle('myPublicTitle');
×
182
        $dummy->setAdminOnlyProperty('secret');
×
183

184
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
185
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'adminOnlyProperty']));
×
186

187
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
188
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
189
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
190

191
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
192
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/secured_dummies/1');
×
193

194
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
195
        $propertyAccessorProphecy->getValue($dummy, 'title')->willReturn('myPublicTitle');
×
196
        $propertyAccessorProphecy->getValue($dummy, 'adminOnlyProperty')->willReturn('secret');
×
197

198
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
199
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(SecuredDummy::class);
×
200
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
201
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
202

203
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
204
        $resourceAccessChecker->isGranted(
×
205
            SecuredDummy::class,
×
206
            'is_granted(\'ROLE_ADMIN\')',
×
207
            ['object' => $dummy]
×
208
        )->willReturn(false);
×
209

210
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
211
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
212
        $serializerProphecy->normalize('myPublicTitle', null, Argument::type('array'))->willReturn('myPublicTitle');
×
213

214
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
215
            $propertyNameCollectionFactoryProphecy->reveal(),
×
216
            $propertyMetadataFactoryProphecy->reveal(),
×
217
            $iriConverterProphecy->reveal(),
×
218
            $resourceClassResolverProphecy->reveal(),
×
219
            $propertyAccessorProphecy->reveal(),
×
220
            null,
×
221
            null,
×
222
            [],
×
223
            null,
×
224
            $resourceAccessChecker->reveal(),
×
225
        ]);
×
226
        $normalizer->setSerializer($serializerProphecy->reveal());
×
227

228
        $expected = [
×
229
            'title' => 'myPublicTitle',
×
230
        ];
×
231
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
232
            'resources' => [],
×
233
        ]));
×
234
    }
235

236
    public function testNormalizePropertyAsIriWithUriTemplate(): void
237
    {
238
        $propertyCollectionIriOnlyRelation = new PropertyCollectionIriOnlyRelation();
×
239
        $propertyCollectionIriOnlyRelation->name = 'My Relation';
×
240

241
        $propertyCollectionIriOnly = new PropertyCollectionIriOnly();
×
242
        $propertyCollectionIriOnly->addPropertyCollectionIriOnlyRelation($propertyCollectionIriOnlyRelation);
×
243

244
        $collectionOperation = new GetCollection('/property-collection-relations');
×
245
        $getIterableOperation = new GetCollection('/parent/{parentId}/another-collection-operations');
×
246
        $getToOneOperation = new Get('/parent/{parentId}/another-collection-operations/{id}');
×
247

248
        $resourceRelationMetadataCollection = new ResourceMetadataCollection(PropertyCollectionIriOnlyRelation::class, [
×
249
            (new ApiResource())->withOperations(new Operations([$collectionOperation, $getIterableOperation, $getToOneOperation])),
×
250
        ]);
×
251

252
        $resourceMetadataCollectionFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
253
        $resourceMetadataCollectionFactoryProphecy->create(PropertyCollectionIriOnlyRelation::class)->willReturn($resourceRelationMetadataCollection);
×
254

255
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
256
        $propertyNameCollectionFactoryProphecy->create(PropertyCollectionIriOnly::class, ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
257
            new PropertyNameCollection(['propertyCollectionIriOnlyRelation', 'iterableIri', 'toOneRelation'])
×
258
        );
×
259

260
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
261
        $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'propertyCollectionIriOnlyRelation', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
262
            (new ApiProperty())->withReadable(true)->withUriTemplate('/property-collection-relations')->withBuiltinTypes([
×
263
                new Type('iterable', false, null, true, new Type('int', false, null, false), new Type('object', false, PropertyCollectionIriOnlyRelation::class, false)),
×
264
            ])
×
265
        );
×
266

267
        $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'iterableIri', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
268
            (new ApiProperty())->withReadable(true)->withUriTemplate('/parent/{parentId}/another-collection-operations')->withBuiltinTypes([
×
269
                new Type('iterable', false, null, true, new Type('int', false, null, false), new Type('object', false, PropertyCollectionIriOnlyRelation::class, false)),
×
270
            ])
×
271
        );
×
272

273
        $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'toOneRelation', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
274
            (new ApiProperty())->withReadable(true)->withUriTemplate('/parent/{parentId}/another-collection-operations/{id}')->withBuiltinTypes([
×
275
                new Type('object', false, PropertyCollectionIriOnlyRelation::class, false),
×
276
            ])
×
277
        );
×
278

279
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
280
        $iriConverterProphecy->getIriFromResource($propertyCollectionIriOnly, UrlGeneratorInterface::ABS_URL, null, Argument::any())->willReturn('/property-collection-relations', '/parent/42/another-collection-operations');
×
281
        $iriConverterProphecy->getIriFromResource($propertyCollectionIriOnly, UrlGeneratorInterface::ABS_PATH, Argument::type(GetCollection::class), Argument::any())->willReturn('/property-collection-relations', '/parent/42/another-collection-operations');
×
282
        $iriConverterProphecy->getIriFromResource($propertyCollectionIriOnly, UrlGeneratorInterface::ABS_PATH, Argument::type(Get::class), Argument::any())->willReturn('/parent/42/another-collection-operations/24');
×
283

284
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
285
        $propertyAccessorProphecy->getValue($propertyCollectionIriOnly, 'propertyCollectionIriOnlyRelation')->willReturn([$propertyCollectionIriOnlyRelation]);
×
286
        $propertyAccessorProphecy->getValue($propertyCollectionIriOnly, 'iterableIri')->willReturn($propertyCollectionIriOnlyRelation);
×
287
        $propertyAccessorProphecy->getValue($propertyCollectionIriOnly, 'toOneRelation')->willReturn($propertyCollectionIriOnlyRelation);
×
288

289
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
290

291
        $resourceClassResolverProphecy->isResourceClass(PropertyCollectionIriOnly::class)->willReturn(true);
×
292
        $resourceClassResolverProphecy->getResourceClass(null, PropertyCollectionIriOnly::class)->willReturn(PropertyCollectionIriOnly::class);
×
293

294
        $resourceClassResolverProphecy->isResourceClass(PropertyCollectionIriOnlyRelation::class)->willReturn(true);
×
295
        $resourceClassResolverProphecy->getResourceClass([$propertyCollectionIriOnlyRelation], PropertyCollectionIriOnlyRelation::class)->willReturn(PropertyCollectionIriOnlyRelation::class);
×
296

297
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
298
            $propertyNameCollectionFactoryProphecy->reveal(),
×
299
            $propertyMetadataFactoryProphecy->reveal(),
×
300
            $iriConverterProphecy->reveal(),
×
301
            $resourceClassResolverProphecy->reveal(),
×
302
            new PropertyAccessor(), // $propertyAccessorProphecy->reveal(),
×
303
            null,
×
304
            null,
×
305
            [],
×
306
            $resourceMetadataCollectionFactoryProphecy->reveal(),
×
307
            null,
×
308
        ]);
×
309

310
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
311
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
312
        $normalizer->setSerializer($serializerProphecy->reveal());
×
313

314
        $expected = [
×
315
            'propertyCollectionIriOnlyRelation' => '/property-collection-relations',
×
316
            'iterableIri' => '/parent/42/another-collection-operations',
×
317
            'toOneRelation' => '/parent/42/another-collection-operations/24',
×
318
        ];
×
319

320
        $this->assertSame($expected, $normalizer->normalize($propertyCollectionIriOnly, 'jsonld', [
×
321
            'resources' => [],
×
322
            'root_operation' => new GetCollection('/property_collection_iri_onlies'),
×
323
        ]));
×
324
    }
325

326
    public function testDenormalizeWithSecuredProperty(): void
327
    {
328
        $data = [
×
329
            'title' => 'foo',
×
330
            'adminOnlyProperty' => 'secret',
×
331
        ];
×
332

333
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
334
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'adminOnlyProperty']));
×
335

336
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
337
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
338
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
339

340
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
341

342
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
343

344
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
345
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
346
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
347

348
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
349
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
350

351
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
352
        $resourceAccessChecker->isGranted(
×
353
            SecuredDummy::class,
×
354
            'is_granted(\'ROLE_ADMIN\')',
×
355
            ['object' => null]
×
356
        )->willReturn(false);
×
357

358
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
359
            $propertyNameCollectionFactoryProphecy->reveal(),
×
360
            $propertyMetadataFactoryProphecy->reveal(),
×
361
            $iriConverterProphecy->reveal(),
×
362
            $resourceClassResolverProphecy->reveal(),
×
363
            $propertyAccessorProphecy->reveal(),
×
364
            null,
×
365
            null,
×
366
            [],
×
367
            null,
×
368
            $resourceAccessChecker->reveal(),
×
369
        ]);
×
370
        $normalizer->setSerializer($serializerProphecy->reveal());
×
371

372
        $actual = $normalizer->denormalize($data, SecuredDummy::class);
×
373

374
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
375

376
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
377
        $propertyAccessorProphecy->setValue($actual, 'adminOnlyProperty', 'secret')->shouldNotHaveBeenCalled();
×
378
    }
379

380
    public function testDenormalizeCreateWithDeniedPostDenormalizeSecuredProperty(): void
381
    {
382
        $data = [
×
383
            'title' => 'foo',
×
384
            'ownerOnlyProperty' => 'should not be set',
×
385
        ];
×
386

387
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
388
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'ownerOnlyProperty']));
×
389

390
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
391
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
392
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurityPostDenormalize('false')->withDefault(''));
×
393

394
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
395

396
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
397

398
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
399
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
400
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
401

402
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
403
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
404

405
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
406
        $resourceAccessChecker->isGranted(
×
407
            SecuredDummy::class,
×
408
            'false',
×
409
            Argument::any()
×
410
        )->willReturn(false);
×
411

412
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
413
            $propertyNameCollectionFactoryProphecy->reveal(),
×
414
            $propertyMetadataFactoryProphecy->reveal(),
×
415
            $iriConverterProphecy->reveal(),
×
416
            $resourceClassResolverProphecy->reveal(),
×
417
            $propertyAccessorProphecy->reveal(),
×
418
            null,
×
419
            null,
×
420
            [],
×
421
            null,
×
422
            $resourceAccessChecker->reveal(),
×
423
        ]);
×
424
        $normalizer->setSerializer($serializerProphecy->reveal());
×
425

426
        $actual = $normalizer->denormalize($data, SecuredDummy::class);
×
427

428
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
429

430
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
431
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldHaveBeenCalled();
×
432
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', '')->shouldHaveBeenCalled();
×
433
    }
434

435
    public function testDenormalizeUpdateWithSecuredProperty(): void
436
    {
437
        $dummy = new SecuredDummy();
×
438

439
        $data = [
×
440
            'title' => 'foo',
×
441
            'ownerOnlyProperty' => 'secret',
×
442
        ];
×
443

444
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
445
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'ownerOnlyProperty']));
×
446

447
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
448
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
449
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('true'));
×
450

451
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
452

453
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
454

455
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
456
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
457
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
458
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
459

460
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
461
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
462

463
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
464
        $resourceAccessChecker->isGranted(
×
465
            SecuredDummy::class,
×
466
            'true',
×
467
            ['object' => null]
×
468
        )->willReturn(true);
×
469
        $resourceAccessChecker->isGranted(
×
470
            SecuredDummy::class,
×
471
            'true',
×
472
            ['object' => $dummy]
×
473
        )->willReturn(true);
×
474

475
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
476
            $propertyNameCollectionFactoryProphecy->reveal(),
×
477
            $propertyMetadataFactoryProphecy->reveal(),
×
478
            $iriConverterProphecy->reveal(),
×
479
            $resourceClassResolverProphecy->reveal(),
×
480
            $propertyAccessorProphecy->reveal(),
×
481
            null,
×
482
            null,
×
483
            [],
×
484
            null,
×
485
            $resourceAccessChecker->reveal(),
×
486
        ]);
×
487
        $normalizer->setSerializer($serializerProphecy->reveal());
×
488

489
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
490
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
491

492
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
493

494
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
495
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'secret')->shouldHaveBeenCalled();
×
496
    }
497

498
    public function testDenormalizeUpdateWithDeniedSecuredProperty(): void
499
    {
500
        $dummy = new SecuredDummy();
×
501
        $dummy->setOwnerOnlyProperty('secret');
×
502

503
        $data = [
×
504
            'title' => 'foo',
×
505
            'ownerOnlyProperty' => 'should not be set',
×
506
        ];
×
507

508
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
509
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'ownerOnlyProperty']));
×
510

511
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
512
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
513
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('false'));
×
514

515
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
516

517
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
518

519
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
520
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
521
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
522
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
523

524
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
525
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
526

527
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
528
        $resourceAccessChecker->isGranted(
×
529
            SecuredDummy::class,
×
530
            'false',
×
531
            ['object' => null]
×
532
        )->willReturn(false);
×
533
        $resourceAccessChecker->isGranted(
×
534
            SecuredDummy::class,
×
535
            'false',
×
536
            ['object' => $dummy]
×
537
        )->willReturn(false);
×
538

539
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
540
            $propertyNameCollectionFactoryProphecy->reveal(),
×
541
            $propertyMetadataFactoryProphecy->reveal(),
×
542
            $iriConverterProphecy->reveal(),
×
543
            $resourceClassResolverProphecy->reveal(),
×
544
            $propertyAccessorProphecy->reveal(),
×
545
            null,
×
546
            null,
×
547
            [],
×
548
            null,
×
549
            $resourceAccessChecker->reveal(),
×
550
        ]);
×
551
        $normalizer->setSerializer($serializerProphecy->reveal());
×
552

553
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
554
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
555

556
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
557

558
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
559
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldNotHaveBeenCalled();
×
560
    }
561

562
    public function testDenormalizeUpdateWithDeniedPostDenormalizeSecuredProperty(): void
563
    {
564
        $dummy = new SecuredDummy();
×
565
        $dummy->setOwnerOnlyProperty('secret');
×
566

567
        $data = [
×
568
            'title' => 'foo',
×
569
            'ownerOnlyProperty' => 'should not be set',
×
570
        ];
×
571

572
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
573
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'ownerOnlyProperty']));
×
574

575
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
576
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
577
        $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurityPostDenormalize('false'));
×
578

579
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
580

581
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
582
        $propertyAccessorProphecy->getValue($dummy, 'ownerOnlyProperty')->willReturn('secret');
×
583

584
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
585
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
586
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
587
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
588

589
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
590
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
591

592
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
593
        $resourceAccessChecker->isGranted(
×
594
            SecuredDummy::class,
×
595
            'false',
×
596
            ['object' => $dummy, 'previous_object' => $dummy]
×
597
        )->willReturn(false);
×
598

599
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
600
            $propertyNameCollectionFactoryProphecy->reveal(),
×
601
            $propertyMetadataFactoryProphecy->reveal(),
×
602
            $iriConverterProphecy->reveal(),
×
603
            $resourceClassResolverProphecy->reveal(),
×
604
            $propertyAccessorProphecy->reveal(),
×
605
            null,
×
606
            null,
×
607
            [],
×
608
            null,
×
609
            $resourceAccessChecker->reveal(),
×
610
        ]);
×
611
        $normalizer->setSerializer($serializerProphecy->reveal());
×
612

613
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
614
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
615

616
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
617

618
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
619
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldHaveBeenCalled();
×
620
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'secret')->shouldHaveBeenCalled();
×
621
    }
622

623
    public function testNormalizeReadableLinks(): void
624
    {
625
        $relatedDummy = new RelatedDummy();
×
626

627
        $dummy = new Dummy();
×
628
        $dummy->setRelatedDummy($relatedDummy);
×
629
        $dummy->relatedDummies->add(new RelatedDummy());
×
630

631
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
632

633
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
634
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy', 'relatedDummies']));
×
635

636
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
637
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
638

639
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
640
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(true)->withWritable(false)->withReadableLink(true));
×
641
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(true));
×
642

643
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
644
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
×
645

646
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
647
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
648
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
649

650
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
651
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
652
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
653
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
654
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
655
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
656
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
657

658
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
659
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
660
        $relatedDummyChildContext = Argument::allOf(
×
661
            Argument::type('array'),
×
662
            Argument::withEntry('resource_class', RelatedDummy::class),
×
663
            Argument::not(Argument::withKey('iri')),
×
664
            Argument::not(Argument::withKey('force_resource_class'))
×
665
        );
×
666
        $serializerProphecy->normalize($relatedDummy, null, $relatedDummyChildContext)->willReturn(['foo' => 'hello']);
×
667
        $serializerProphecy->normalize(['foo' => 'hello'], null, Argument::type('array'))->willReturn(['foo' => 'hello']);
×
668
        $serializerProphecy->normalize([['foo' => 'hello']], null, Argument::type('array'))->willReturn([['foo' => 'hello']]);
×
669

670
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
671
            $propertyNameCollectionFactoryProphecy->reveal(),
×
672
            $propertyMetadataFactoryProphecy->reveal(),
×
673
            $iriConverterProphecy->reveal(),
×
674
            $resourceClassResolverProphecy->reveal(),
×
675
            $propertyAccessorProphecy->reveal(),
×
676
            null,
×
677
            null,
×
678
            [],
×
679
            null,
×
680
            null,
×
681
        ]);
×
682
        $normalizer->setSerializer($serializerProphecy->reveal());
×
683

684
        $expected = [
×
685
            'relatedDummy' => ['foo' => 'hello'],
×
686
            'relatedDummies' => [['foo' => 'hello']],
×
687
        ];
×
688
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
689
            'resources' => [],
×
690
            'force_resource_class' => Dummy::class,
×
691
        ]));
×
692
    }
693

694
    public function testNormalizePolymorphicRelations(): void
695
    {
696
        $concreteDummy = new DummyTableInheritanceChild();
×
697

698
        $dummy = new DummyTableInheritanceRelated();
×
699
        $dummy->addChild($concreteDummy);
×
700

701
        $abstractDummies = new ArrayCollection([$concreteDummy]);
×
702

703
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
704
        $propertyNameCollectionFactoryProphecy->create(DummyTableInheritanceRelated::class, [])->willReturn(new PropertyNameCollection(['children']));
×
705

706
        $abstractDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyTableInheritance::class);
×
707
        $abstractDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $abstractDummyType);
×
708

709
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
710
        $propertyMetadataFactoryProphecy->create(DummyTableInheritanceRelated::class, 'children', [])->willReturn((new ApiProperty())->withBuiltinTypes([$abstractDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(true));
×
711

712
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
713
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
×
714

715
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
716
        $propertyAccessorProphecy->getValue($dummy, 'children')->willReturn($abstractDummies);
×
717

718
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
719
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(DummyTableInheritanceRelated::class);
×
720
        $resourceClassResolverProphecy->getResourceClass(null, DummyTableInheritanceRelated::class)->willReturn(DummyTableInheritanceRelated::class);
×
721
        $resourceClassResolverProphecy->getResourceClass($concreteDummy, DummyTableInheritance::class)->willReturn(DummyTableInheritanceChild::class);
×
722
        $resourceClassResolverProphecy->getResourceClass($abstractDummies, DummyTableInheritance::class)->willReturn(DummyTableInheritance::class);
×
723
        $resourceClassResolverProphecy->isResourceClass(DummyTableInheritanceRelated::class)->willReturn(true);
×
724
        $resourceClassResolverProphecy->isResourceClass(DummyTableInheritance::class)->willReturn(true);
×
725

726
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
727
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
728
        $concreteDummyChildContext = Argument::allOf(
×
729
            Argument::type('array'),
×
730
            Argument::not(Argument::withKey('iri'))
×
731
        );
×
732
        $serializerProphecy->normalize($concreteDummy, null, $concreteDummyChildContext)->willReturn(['foo' => 'concrete']);
×
733
        $serializerProphecy->normalize([['foo' => 'concrete']], null, Argument::type('array'))->willReturn([['foo' => 'concrete']]);
×
734

735
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
736
            $propertyNameCollectionFactoryProphecy->reveal(),
×
737
            $propertyMetadataFactoryProphecy->reveal(),
×
738
            $iriConverterProphecy->reveal(),
×
739
            $resourceClassResolverProphecy->reveal(),
×
740
            $propertyAccessorProphecy->reveal(),
×
741
            null,
×
742
            null,
×
743
            [],
×
744
            null,
×
745
            null,
×
746
        ]);
×
747
        $normalizer->setSerializer($serializerProphecy->reveal());
×
748

749
        $expected = [
×
750
            'children' => [['foo' => 'concrete']],
×
751
        ];
×
752
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
753
            'resources' => [],
×
754
        ]));
×
755
    }
756

757
    public function testDenormalize(): void
758
    {
759
        $data = [
×
760
            'name' => 'foo',
×
761
            'relatedDummy' => '/dummies/1',
×
762
            'relatedDummies' => ['/dummies/2'],
×
763
        ];
×
764

765
        $relatedDummy1 = new RelatedDummy();
×
766
        $relatedDummy2 = new RelatedDummy();
×
767

768
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
769
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name', 'relatedDummy', 'relatedDummies']));
×
770

771
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
772
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
773

774
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
775
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
776

777
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
778
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
779

780
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
781
        $iriConverterProphecy->getResourceFromIri('/dummies/1', Argument::type('array'))->willReturn($relatedDummy1);
×
782
        $iriConverterProphecy->getResourceFromIri('/dummies/2', Argument::type('array'))->willReturn($relatedDummy2);
×
783

784
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
785

786
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
787
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
788
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
789
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
790
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
791

792
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
793
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
794

795
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
796
            $propertyNameCollectionFactoryProphecy->reveal(),
×
797
            $propertyMetadataFactoryProphecy->reveal(),
×
798
            $iriConverterProphecy->reveal(),
×
799
            $resourceClassResolverProphecy->reveal(),
×
800
            $propertyAccessorProphecy->reveal(),
×
801
            null,
×
802
            null,
×
803
            [],
×
804
            null,
×
805
            null,
×
806
        ]);
×
807
        $normalizer->setSerializer($serializerProphecy->reveal());
×
808

809
        $actual = $normalizer->denormalize($data, Dummy::class);
×
810

811
        $this->assertInstanceOf(Dummy::class, $actual);
×
812

813
        $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
×
814
        $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
×
815
        $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
×
816
    }
817

818
    public function testCanDenormalizeInputClassWithDifferentFieldsThanResourceClass(): void
819
    {
820
        $this->markTestSkipped('TODO: check why this test has been commented');
×
821

822
        // $data = [
823
        //     'dummyName' => 'Dummy Name',
824
        // ];
825
        //
826
        // $context = [
827
        //     'resource_class' => DummyForAdditionalFields::class,
828
        //     'input' => ['class' => DummyForAdditionalFieldsInput::class],
829
        //     'output' => ['class' => DummyForAdditionalFields::class],
830
        // ];
831
        // $augmentedContext = $context + ['api_denormalize' => true];
832
        //
833
        // $preHydratedDummy = new DummyForAdditionalFieldsInput('Name Dummy');
834
        // $cleanedContext = array_diff_key($augmentedContext, [
835
        //     'input' => null,
836
        //     'resource_class' => null,
837
        // ]);
838
        // $cleanedContextWithObjectToPopulate = array_merge($cleanedContext, [
839
        //     AbstractObjectNormalizer::OBJECT_TO_POPULATE => $preHydratedDummy,
840
        //     AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE => true,
841
        // ]);
842
        //
843
        // $dummyInputDto = new DummyForAdditionalFieldsInput('Dummy Name');
844
        // $dummy = new DummyForAdditionalFields('Dummy Name', 'name-dummy');
845
        //
846
        // $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
847
        //
848
        // $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
849
        //
850
        // $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
851
        //
852
        // $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
853
        // $resourceClassResolverProphecy->getResourceClass(null, DummyForAdditionalFields::class)->willReturn(DummyForAdditionalFields::class);
854
        //
855
        // $inputDataTransformerProphecy = $this->prophesize(DataTransformerInitializerInterface::class);
856
        // $inputDataTransformerProphecy->willImplement(DataTransformerInitializerInterface::class);
857
        // $inputDataTransformerProphecy->initialize(DummyForAdditionalFieldsInput::class, $cleanedContext)->willReturn($preHydratedDummy);
858
        // $inputDataTransformerProphecy->supportsTransformation($data, DummyForAdditionalFields::class, $augmentedContext)->willReturn(true);
859
        // $inputDataTransformerProphecy->transform($dummyInputDto, DummyForAdditionalFields::class, $augmentedContext)->willReturn($dummy);
860
        //
861
        // $serializerProphecy = $this->prophesize(SerializerInterface::class);
862
        // $serializerProphecy->willImplement(DenormalizerInterface::class);
863
        // $serializerProphecy->denormalize($data, DummyForAdditionalFieldsInput::class, 'json', $cleanedContextWithObjectToPopulate)->willReturn($dummyInputDto);
864
        //
865
        // $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), null, null, null, [], null, null) extends AbstractItemNormalizer {
866
        // };
867
        // $normalizer->setSerializer($serializerProphecy->reveal());
868
        //
869
        // $actual = $normalizer->denormalize($data, DummyForAdditionalFields::class, 'json', $context);
870
        //
871
        // $this->assertInstanceOf(DummyForAdditionalFields::class, $actual);
872
        // $this->assertSame('Dummy Name', $actual->getName());
873
    }
874

875
    public function testDenormalizeWritableLinks(): void
876
    {
877
        $data = [
×
878
            'name' => 'foo',
×
879
            'relatedDummy' => ['foo' => 'bar'],
×
880
            'relatedDummies' => [['bar' => 'baz']],
×
881
        ];
×
882

883
        $relatedDummy1 = new RelatedDummy();
×
884
        $relatedDummy2 = new RelatedDummy();
×
885

886
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
887
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name', 'relatedDummy', 'relatedDummies']));
×
888

889
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
890
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
891

892
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
893
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
894
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
895
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
896

897
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
898

899
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
900

901
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
902
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
903
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
904
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
905
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
906

907
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
908
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
909
        $serializerProphecy->denormalize(['foo' => 'bar'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy1);
×
910
        $serializerProphecy->denormalize(['bar' => 'baz'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy2);
×
911

912
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
913
            $propertyNameCollectionFactoryProphecy->reveal(),
×
914
            $propertyMetadataFactoryProphecy->reveal(),
×
915
            $iriConverterProphecy->reveal(),
×
916
            $resourceClassResolverProphecy->reveal(),
×
917
            $propertyAccessorProphecy->reveal(),
×
918
            null,
×
919
            null,
×
920
            [],
×
921
            null,
×
922
            null,
×
923
        ]);
×
924
        $normalizer->setSerializer($serializerProphecy->reveal());
×
925

926
        $actual = $normalizer->denormalize($data, Dummy::class);
×
927

928
        $this->assertInstanceOf(Dummy::class, $actual);
×
929

930
        $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
×
931
        $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
×
932
        $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
×
933
    }
934

935
    public function testBadRelationType(): void
936
    {
937
        $this->expectException(NotNormalizableValueException::class);
×
938
        $this->expectExceptionMessage('The type of the "relatedDummy" attribute must be "array" (nested document) or "string" (IRI), "integer" given.');
×
939

940
        $data = [
×
941
            'relatedDummy' => 22,
×
942
        ];
×
943

944
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
945
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
×
946

947
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
948
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
949
            (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
950
        );
×
951

952
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
953

954
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
955
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
956
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
957
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
958
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
959

960
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
961

962
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
963
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
964

965
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
966
            $propertyNameCollectionFactoryProphecy->reveal(),
×
967
            $propertyMetadataFactoryProphecy->reveal(),
×
968
            $iriConverterProphecy->reveal(),
×
969
            $resourceClassResolverProphecy->reveal(),
×
970
            $propertyAccessorProphecy->reveal(),
×
971
            null,
×
972
            null,
×
973
            [],
×
974
            null,
×
975
            null,
×
976
        ]);
×
977
        $normalizer->setSerializer($serializerProphecy->reveal());
×
978

979
        $normalizer->denormalize($data, Dummy::class);
×
980
    }
981

982
    public function testBadRelationTypeWithExceptionToValidationErrors(): void
983
    {
984
        $data = [
×
985
            'relatedDummy' => 22,
×
986
        ];
×
987

988
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
989
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
×
990

991
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
992
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
993
            (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
994
        );
×
995

996
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
997

998
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
999
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1000
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1001
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1002
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1003

1004
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1005

1006
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1007
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1008

1009
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1010
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1011
            $propertyMetadataFactoryProphecy->reveal(),
×
1012
            $iriConverterProphecy->reveal(),
×
1013
            $resourceClassResolverProphecy->reveal(),
×
1014
            $propertyAccessorProphecy->reveal(),
×
1015
            null,
×
1016
            null,
×
1017
            [],
×
1018
            null,
×
1019
            null,
×
1020
        ]);
×
1021
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1022

1023
        // 'not_normalizable_value_exceptions' is set by Serializer thanks to DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS
1024
        $actual = $normalizer->denormalize($data, Dummy::class, null, ['not_normalizable_value_exceptions' => []]);
×
1025
        $this->assertNull($actual->relatedDummy);
×
1026
    }
1027

1028
    public function testInnerDocumentNotAllowed(): void
1029
    {
1030
        $this->expectException(UnexpectedValueException::class);
×
1031
        $this->expectExceptionMessage('Nested documents for attribute "relatedDummy" are not allowed. Use IRIs instead.');
×
1032

1033
        $data = [
×
1034
            'relatedDummy' => [
×
1035
                'foo' => 'bar',
×
1036
            ],
×
1037
        ];
×
1038

1039
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1040
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
×
1041

1042
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1043
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
1044
            (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
1045
        );
×
1046

1047
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1048

1049
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1050
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1051
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1052
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1053
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1054

1055
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1056

1057
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1058
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1059

1060
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1061
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1062
            $propertyMetadataFactoryProphecy->reveal(),
×
1063
            $iriConverterProphecy->reveal(),
×
1064
            $resourceClassResolverProphecy->reveal(),
×
1065
            $propertyAccessorProphecy->reveal(),
×
1066
            null,
×
1067
            null,
×
1068
            [],
×
1069
            null,
×
1070
            null,
×
1071
        ]);
×
1072
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1073

1074
        $normalizer->denormalize($data, Dummy::class);
×
1075
    }
1076

1077
    public function testBadType(): void
1078
    {
1079
        $this->expectException(UnexpectedValueException::class);
×
1080
        $this->expectExceptionMessage('The type of the "foo" attribute must be "float", "integer" given.');
×
1081

1082
        $data = [
×
1083
            'foo' => 42,
×
1084
        ];
×
1085

1086
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1087
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['foo']));
×
1088

1089
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1090
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1091

1092
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1093

1094
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1095
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1096
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1097

1098
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1099

1100
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1101
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1102

1103
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1104
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1105
            $propertyMetadataFactoryProphecy->reveal(),
×
1106
            $iriConverterProphecy->reveal(),
×
1107
            $resourceClassResolverProphecy->reveal(),
×
1108
            $propertyAccessorProphecy->reveal(),
×
1109
            null,
×
1110
            null,
×
1111
            [],
×
1112
            null,
×
1113
            null,
×
1114
        ]);
×
1115
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1116

1117
        $normalizer->denormalize($data, Dummy::class);
×
1118
    }
1119

1120
    public function testTypeChecksCanBeDisabled(): void
1121
    {
1122
        $data = [
×
1123
            'foo' => 42,
×
1124
        ];
×
1125

1126
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1127
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['foo']));
×
1128

1129
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1130
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1131

1132
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1133

1134
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1135
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1136
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1137

1138
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1139

1140
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1141
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1142

1143
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1144
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1145
            $propertyMetadataFactoryProphecy->reveal(),
×
1146
            $iriConverterProphecy->reveal(),
×
1147
            $resourceClassResolverProphecy->reveal(),
×
1148
            $propertyAccessorProphecy->reveal(),
×
1149
            null,
×
1150
            null,
×
1151
            [],
×
1152
            null,
×
1153
            null,
×
1154
        ]);
×
1155
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1156

1157
        $actual = $normalizer->denormalize($data, Dummy::class, null, ['disable_type_enforcement' => true]);
×
1158

1159
        $this->assertInstanceOf(Dummy::class, $actual);
×
1160

1161
        $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
×
1162
    }
1163

1164
    public function testJsonAllowIntAsFloat(): void
1165
    {
1166
        $data = [
×
1167
            'foo' => 42,
×
1168
        ];
×
1169

1170
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1171
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['foo']));
×
1172

1173
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1174
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1175

1176
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1177

1178
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1179
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1180
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1181

1182
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1183

1184
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1185
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1186

1187
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1188
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1189
            $propertyMetadataFactoryProphecy->reveal(),
×
1190
            $iriConverterProphecy->reveal(),
×
1191
            $resourceClassResolverProphecy->reveal(),
×
1192
            $propertyAccessorProphecy->reveal(),
×
1193
            null,
×
1194
            null,
×
1195
            [],
×
1196
            null,
×
1197
            null,
×
1198
        ]);
×
1199
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1200

1201
        $actual = $normalizer->denormalize($data, Dummy::class, 'jsonfoo');
×
1202

1203
        $this->assertInstanceOf(Dummy::class, $actual);
×
1204

1205
        $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
×
1206
    }
1207

1208
    public function testDenormalizeBadKeyType(): void
1209
    {
1210
        $this->expectException(NotNormalizableValueException::class);
×
1211
        $this->expectExceptionMessage('The type of the key "a" must be "int", "string" given.');
×
1212

1213
        $data = [
×
1214
            'name' => 'foo',
×
1215
            'relatedDummy' => [
×
1216
                'foo' => 'bar',
×
1217
            ],
×
1218
            'relatedDummies' => [
×
1219
                'a' => [
×
1220
                    'bar' => 'baz',
×
1221
                ],
×
1222
            ],
×
1223
        ];
×
1224

1225
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1226
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']));
×
1227

1228
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1229
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
1230
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1231

1232
        $type = new Type(
×
1233
            Type::BUILTIN_TYPE_OBJECT,
×
1234
            false,
×
1235
            ArrayCollection::class,
×
1236
            true,
×
1237
            new Type(Type::BUILTIN_TYPE_INT),
×
1238
            new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)
×
1239
        );
×
1240
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$type])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1241

1242
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1243

1244
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1245
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1246
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1247
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1248
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1249

1250
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1251

1252
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1253
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1254

1255
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1256
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1257
            $propertyMetadataFactoryProphecy->reveal(),
×
1258
            $iriConverterProphecy->reveal(),
×
1259
            $resourceClassResolverProphecy->reveal(),
×
1260
            $propertyAccessorProphecy->reveal(),
×
1261
            null,
×
1262
            null,
×
1263
            [],
×
1264
            null,
×
1265
            null,
×
1266
        ]);
×
1267
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1268

1269
        $normalizer->denormalize($data, Dummy::class);
×
1270
    }
1271

1272
    public function testNullable(): void
1273
    {
1274
        $data = [
×
1275
            'name' => null,
×
1276
        ];
×
1277

1278
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1279
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name']));
×
1280

1281
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1282
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING, true)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1283

1284
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1285

1286
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1287
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1288
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1289

1290
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1291

1292
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1293
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1294

1295
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1296
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1297
            $propertyMetadataFactoryProphecy->reveal(),
×
1298
            $iriConverterProphecy->reveal(),
×
1299
            $resourceClassResolverProphecy->reveal(),
×
1300
            $propertyAccessorProphecy->reveal(),
×
1301
            null,
×
1302
            null,
×
1303
            [],
×
1304
            null,
×
1305
            null,
×
1306
        ]);
×
1307
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1308

1309
        $actual = $normalizer->denormalize($data, Dummy::class);
×
1310

1311
        $this->assertInstanceOf(Dummy::class, $actual);
×
1312

1313
        $propertyAccessorProphecy->setValue($actual, 'name', null)->shouldHaveBeenCalled();
×
1314
    }
1315

1316
    public function testDenormalizeBasicTypePropertiesFromXml(): void
1317
    {
1318
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1319
        $propertyNameCollectionFactoryProphecy->create(ObjectWithBasicProperties::class, [])->willReturn(new PropertyNameCollection([
×
1320
            'boolTrue1',
×
1321
            'boolFalse1',
×
1322
            'boolTrue2',
×
1323
            'boolFalse2',
×
1324
            'int1',
×
1325
            'int2',
×
1326
            'float1',
×
1327
            'float2',
×
1328
            'float3',
×
1329
            'floatNaN',
×
1330
            'floatInf',
×
1331
            'floatNegInf',
×
1332
        ]));
×
1333

1334
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1335
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1336
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1337
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1338
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1339
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_INT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1340
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_INT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1341
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1342
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1343
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float3', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1344
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNaN', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1345
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatInf', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1346
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNegInf', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1347

1348
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1349

1350
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1351
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolTrue1', true)->shouldBeCalled();
×
1352
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolFalse1', false)->shouldBeCalled();
×
1353
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolTrue2', true)->shouldBeCalled();
×
1354
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolFalse2', false)->shouldBeCalled();
×
1355
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'int1', 4711)->shouldBeCalled();
×
1356
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'int2', -4711)->shouldBeCalled();
×
1357
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float1', Argument::approximate(123.456, 0))->shouldBeCalled();
×
1358
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float2', Argument::approximate(-1.2344e56, 1))->shouldBeCalled();
×
1359
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float3', Argument::approximate(45E-6, 1))->shouldBeCalled();
×
1360
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatNaN', Argument::that(static fn (float $arg) => is_nan($arg)))->shouldBeCalled();
×
1361
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatInf', \INF)->shouldBeCalled();
×
1362
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatNegInf', -\INF)->shouldBeCalled();
×
1363

1364
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1365
        $resourceClassResolverProphecy->getResourceClass(null, ObjectWithBasicProperties::class)->willReturn(ObjectWithBasicProperties::class);
×
1366
        $resourceClassResolverProphecy->isResourceClass(ObjectWithBasicProperties::class)->willReturn(true);
×
1367

1368
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1369
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1370

1371
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1372
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1373
            $propertyMetadataFactoryProphecy->reveal(),
×
1374
            $iriConverterProphecy->reveal(),
×
1375
            $resourceClassResolverProphecy->reveal(),
×
1376
            $propertyAccessorProphecy->reveal(),
×
1377
            null,
×
1378
            null,
×
1379
            [],
×
1380
            null,
×
1381
            null,
×
1382
        ]);
×
1383
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1384

1385
        $objectWithBasicProperties = $normalizer->denormalize(
×
1386
            [
×
1387
                'boolTrue1' => 'true',
×
1388
                'boolFalse1' => 'false',
×
1389
                'boolTrue2' => '1',
×
1390
                'boolFalse2' => '0',
×
1391
                'int1' => '4711',
×
1392
                'int2' => '-4711',
×
1393
                'float1' => '123.456',
×
1394
                'float2' => '-1.2344e56',
×
1395
                'float3' => '45E-6',
×
1396
                'floatNaN' => 'NaN',
×
1397
                'floatInf' => 'INF',
×
1398
                'floatNegInf' => '-INF',
×
1399
            ],
×
1400
            ObjectWithBasicProperties::class,
×
1401
            'xml'
×
1402
        );
×
1403

1404
        $this->assertInstanceOf(ObjectWithBasicProperties::class, $objectWithBasicProperties);
×
1405
    }
1406

1407
    public function testDenormalizeCollectionDecodedFromXmlWithOneChild(): void
1408
    {
1409
        $data = [
×
1410
            'relatedDummies' => [
×
1411
                'name' => 'foo',
×
1412
            ],
×
1413
        ];
×
1414

1415
        $relatedDummy = new RelatedDummy();
×
1416
        $relatedDummy->setName('foo');
×
1417

1418
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1419
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']));
×
1420

1421
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
1422
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
1423

1424
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1425
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1426

1427
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1428

1429
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1430
        $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', Argument::type('array'))->shouldBeCalled();
×
1431

1432
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1433
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1434
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1435
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1436
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1437

1438
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1439
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1440
        $serializerProphecy->denormalize(['name' => 'foo'], RelatedDummy::class, 'xml', Argument::type('array'))->willReturn($relatedDummy);
×
1441

1442
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1443
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1444
            $propertyMetadataFactoryProphecy->reveal(),
×
1445
            $iriConverterProphecy->reveal(),
×
1446
            $resourceClassResolverProphecy->reveal(),
×
1447
            $propertyAccessorProphecy->reveal(),
×
1448
            null,
×
1449
            null,
×
1450
            [],
×
1451
            null,
×
1452
            null,
×
1453
        ]);
×
1454
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1455

1456
        $normalizer->denormalize($data, Dummy::class, 'xml');
×
1457
    }
1458

1459
    public function testDenormalizePopulatingNonCloneableObject(): void
1460
    {
1461
        $dummy = new NonCloneableDummy();
×
1462
        $dummy->setName('foo');
×
1463

1464
        $data = [
×
1465
            'name' => 'bar',
×
1466
        ];
×
1467

1468
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1469
        $propertyNameCollectionFactoryProphecy->create(NonCloneableDummy::class, [])->willReturn(new PropertyNameCollection(['name']));
×
1470

1471
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1472
        $propertyMetadataFactoryProphecy->create(NonCloneableDummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
1473

1474
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1475
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1476
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1477
        $resourceClassResolverProphecy->getResourceClass(null, NonCloneableDummy::class)->willReturn(NonCloneableDummy::class);
×
1478
        $resourceClassResolverProphecy->getResourceClass($dummy, NonCloneableDummy::class)->willReturn(NonCloneableDummy::class);
×
1479
        $resourceClassResolverProphecy->isResourceClass(NonCloneableDummy::class)->willReturn(true);
×
1480

1481
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1482
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1483

1484
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1485
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1486
            $propertyMetadataFactoryProphecy->reveal(),
×
1487
            $iriConverterProphecy->reveal(),
×
1488
            $resourceClassResolverProphecy->reveal(),
×
1489
            $propertyAccessorProphecy->reveal(),
×
1490
            null,
×
1491
            null,
×
1492
            [],
×
1493
            null,
×
1494
            null,
×
1495
        ]);
×
1496
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1497
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1498

1499
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
1500
        $actual = $normalizer->denormalize($data, NonCloneableDummy::class, null, $context);
×
1501

1502
        $this->assertInstanceOf(NonCloneableDummy::class, $actual);
×
1503
        $this->assertSame($dummy, $actual);
×
1504
        $propertyAccessorProphecy->setValue($actual, 'name', 'bar')->shouldHaveBeenCalled();
×
1505
    }
1506

1507
    public function testDenormalizeObjectWithNullDisabledTypeEnforcement(): void
1508
    {
1509
        $data = [
×
1510
            'dummy' => null,
×
1511
        ];
×
1512

1513
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1514
        $propertyNameCollectionFactoryProphecy->create(DtoWithNullValue::class, [])->willReturn(new PropertyNameCollection(['dummy']));
×
1515

1516
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1517
        $propertyMetadataFactoryProphecy->create(DtoWithNullValue::class, 'dummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, nullable: true)])->withDescription('')->withReadable(true)->withWritable(true));
×
1518

1519
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1520
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1521
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1522
        $resourceClassResolverProphecy->getResourceClass(null, DtoWithNullValue::class)->willReturn(DtoWithNullValue::class);
×
1523
        $resourceClassResolverProphecy->isResourceClass(DtoWithNullValue::class)->willReturn(true);
×
1524

1525
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1526
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1527

1528
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
1529
            $propertyNameCollectionFactoryProphecy->reveal(),
×
1530
            $propertyMetadataFactoryProphecy->reveal(),
×
1531
            $iriConverterProphecy->reveal(),
×
1532
            $resourceClassResolverProphecy->reveal(),
×
1533
            $propertyAccessorProphecy->reveal(),
×
1534
            null,
×
1535
            null,
×
1536
            [],
×
1537
            null,
×
1538
            null,
×
1539
        ]);
×
1540
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1541

1542
        $context = [AbstractItemNormalizer::DISABLE_TYPE_ENFORCEMENT => true];
×
1543
        $actual = $normalizer->denormalize($data, DtoWithNullValue::class, null, $context);
×
1544

1545
        $this->assertInstanceOf(DtoWithNullValue::class, $actual);
×
1546
        $this->assertEquals(new DtoWithNullValue(), $actual);
×
1547
    }
1548
}
1549

1550
class ObjectWithBasicProperties
1551
{
1552
    /** @var bool */
1553
    public $boolTrue1;
1554

1555
    /** @var bool */
1556
    public $boolFalse1;
1557

1558
    /** @var bool */
1559
    public $boolTrue2;
1560

1561
    /** @var bool */
1562
    public $boolFalse2;
1563

1564
    /** @var int */
1565
    public $int1;
1566

1567
    /** @var int */
1568
    public $int2;
1569

1570
    /** @var float */
1571
    public $float1;
1572

1573
    /** @var float */
1574
    public $float2;
1575

1576
    /** @var float */
1577
    public $float3;
1578

1579
    /** @var float */
1580
    public $floatNaN;
1581

1582
    /** @var float */
1583
    public $floatInf;
1584

1585
    /** @var float */
1586
    public $floatNegInf;
1587
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc