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

api-platform / core / 14837294296

05 May 2025 01:12PM UTC coverage: 8.459% (-0.004%) from 8.463%
14837294296

push

github

web-flow
fix(serializer): throw NotNormalizableValueException when resource not found or invalid IRI on denormalization

0 of 33 new or added lines in 2 files covered. (0.0%)

116 existing lines in 57 files now uncovered.

13400 of 158414 relevant lines covered (8.46%)

22.88 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\Exception\InvalidArgumentException;
19
use ApiPlatform\Metadata\Get;
20
use ApiPlatform\Metadata\GetCollection;
21
use ApiPlatform\Metadata\IriConverterInterface;
22
use ApiPlatform\Metadata\Operations;
23
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
24
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
25
use ApiPlatform\Metadata\Property\PropertyNameCollection;
26
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
27
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
28
use ApiPlatform\Metadata\ResourceAccessCheckerInterface;
29
use ApiPlatform\Metadata\ResourceClassResolverInterface;
30
use ApiPlatform\Metadata\UrlGeneratorInterface;
31
use ApiPlatform\Serializer\AbstractItemNormalizer;
32
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DtoWithNullValue;
33
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\Dummy;
34
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DummyTableInheritance;
35
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DummyTableInheritanceChild;
36
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DummyTableInheritanceRelated;
37
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\NonCloneableDummy;
38
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\PropertyCollectionIriOnly;
39
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\PropertyCollectionIriOnlyRelation;
40
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\RelatedDummy;
41
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\SecuredDummy;
42
use Doctrine\Common\Collections\ArrayCollection;
43
use PHPUnit\Framework\TestCase;
44
use Prophecy\Argument;
45
use Prophecy\PhpUnit\ProphecyTrait;
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\SerializerInterface;
54

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

63
    #[\PHPUnit\Framework\Attributes\Group('legacy')]
64
    public function testSupportNormalizationAndSupportDenormalization(): void
65
    {
66
        $std = new \stdClass();
×
67
        $dummy = new Dummy();
×
68

69
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
70
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
71
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
72
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
73

74
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
75
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
76
        $resourceClassResolverProphecy->isResourceClass(\stdClass::class)->willReturn(false);
×
77

78
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
79

80
        $this->assertTrue($normalizer->supportsNormalization($dummy));
×
81
        $this->assertFalse($normalizer->supportsNormalization($std));
×
82
        $this->assertTrue($normalizer->supportsDenormalization($dummy, Dummy::class));
×
83
        $this->assertFalse($normalizer->supportsDenormalization($std, \stdClass::class));
×
84
        $this->assertFalse($normalizer->supportsNormalization([]));
×
85
        $this->assertSame(['object' => true], $normalizer->getSupportedTypes('any'));
×
86
    }
87

88
    public function testNormalize(): void
89
    {
90
        $relatedDummy = new RelatedDummy();
×
91

92
        $dummy = new Dummy();
×
93
        $dummy->setName('foo');
×
94
        $dummy->setAlias('ignored');
×
95
        $dummy->setRelatedDummy($relatedDummy);
×
96
        $dummy->relatedDummies->add(new RelatedDummy());
×
97

98
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
99

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

103
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
104
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
105

106
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
107
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
108
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
109
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
110
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(false));
×
111

112
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
113
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
×
114
        $iriConverterProphecy->getIriFromResource($relatedDummy, Argument::cetera())->willReturn('/dummies/2');
×
115

116
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
117
        $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('foo');
×
118
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
119
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
120

121
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
122
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
123
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
124
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
125
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
126
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
127
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
128

129
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
130
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
131
        $serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo');
×
132
        $serializerProphecy->normalize(['/dummies/2'], null, Argument::type('array'))->willReturn(['/dummies/2']);
×
133

134
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
135
        $normalizer->setSerializer($serializerProphecy->reveal());
×
136

137
        $expected = [
×
138
            'name' => 'foo',
×
139
            'relatedDummy' => '/dummies/2',
×
140
            'relatedDummies' => ['/dummies/2'],
×
141
        ];
×
142
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
143
            'resources' => [],
×
144
            'ignored_attributes' => ['alias'],
×
145
        ]));
×
146
    }
147

148
    public function testNormalizeWithSecuredProperty(): void
149
    {
150
        $dummy = new SecuredDummy();
×
151
        $dummy->setTitle('myPublicTitle');
×
152
        $dummy->setAdminOnlyProperty('secret');
×
153

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

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

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

164
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
165
        $propertyAccessorProphecy->getValue($dummy, 'title')->willReturn('myPublicTitle');
×
166
        $propertyAccessorProphecy->getValue($dummy, 'adminOnlyProperty')->willReturn('secret');
×
167

168
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
169
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(SecuredDummy::class);
×
170
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
171
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
172

173
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
174
        $resourceAccessChecker->isGranted(
×
175
            SecuredDummy::class,
×
176
            'is_granted(\'ROLE_ADMIN\')',
×
177
            ['object' => $dummy, 'property' => 'adminOnlyProperty']
×
178
        )->willReturn(false);
×
179

180
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
181
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
182
        $serializerProphecy->normalize('myPublicTitle', null, Argument::type('array'))->willReturn('myPublicTitle');
×
183

184
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, $resourceAccessChecker->reveal()) extends AbstractItemNormalizer {};
×
185
        $normalizer->setSerializer($serializerProphecy->reveal());
×
186

187
        $expected = [
×
188
            'title' => 'myPublicTitle',
×
189
        ];
×
190
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
191
            'resources' => [],
×
192
        ]));
×
193
    }
194

195
    public function testNormalizePropertyAsIriWithUriTemplate(): void
196
    {
197
        $propertyCollectionIriOnlyRelation = new PropertyCollectionIriOnlyRelation();
×
198
        $propertyCollectionIriOnlyRelation->name = 'My Relation';
×
199

200
        $propertyCollectionIriOnly = new PropertyCollectionIriOnly();
×
201
        $propertyCollectionIriOnly->addPropertyCollectionIriOnlyRelation($propertyCollectionIriOnlyRelation);
×
202

203
        $collectionOperation = new GetCollection('/property-collection-relations');
×
204
        $getIterableOperation = new GetCollection('/parent/{parentId}/another-collection-operations');
×
205
        $getToOneOperation = new Get('/parent/{parentId}/another-collection-operations/{id}');
×
206

207
        $resourceRelationMetadataCollection = new ResourceMetadataCollection(PropertyCollectionIriOnlyRelation::class, [
×
208
            (new ApiResource())->withOperations(new Operations([$collectionOperation, $getIterableOperation, $getToOneOperation])),
×
209
        ]);
×
210

211
        $resourceMetadataCollectionFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
212
        $resourceMetadataCollectionFactoryProphecy->create(PropertyCollectionIriOnlyRelation::class)->willReturn($resourceRelationMetadataCollection);
×
213

214
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
215
        $propertyNameCollectionFactoryProphecy->create(PropertyCollectionIriOnly::class, ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
216
            new PropertyNameCollection(['propertyCollectionIriOnlyRelation', 'iterableIri', 'toOneRelation'])
×
217
        );
×
218

219
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
220
        $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'propertyCollectionIriOnlyRelation', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
221
            (new ApiProperty())->withReadable(true)->withUriTemplate('/property-collection-relations')->withBuiltinTypes([
×
222
                new Type('iterable', false, null, true, new Type('int', false, null, false), new Type('object', false, PropertyCollectionIriOnlyRelation::class, false)),
×
223
            ])
×
224
        );
×
225

226
        $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'iterableIri', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
227
            (new ApiProperty())->withReadable(true)->withUriTemplate('/parent/{parentId}/another-collection-operations')->withBuiltinTypes([
×
228
                new Type('iterable', false, null, true, new Type('int', false, null, false), new Type('object', false, PropertyCollectionIriOnlyRelation::class, false)),
×
229
            ])
×
230
        );
×
231

232
        $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'toOneRelation', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
233
            (new ApiProperty())->withReadable(true)->withUriTemplate('/parent/{parentId}/another-collection-operations/{id}')->withBuiltinTypes([
×
234
                new Type('object', false, PropertyCollectionIriOnlyRelation::class, false),
×
235
            ])
×
236
        );
×
237

238
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
239
        $iriConverterProphecy->getIriFromResource($propertyCollectionIriOnly, UrlGeneratorInterface::ABS_URL, null, Argument::any())->willReturn('/property-collection-relations', '/parent/42/another-collection-operations');
×
240
        $iriConverterProphecy->getIriFromResource($propertyCollectionIriOnly, UrlGeneratorInterface::ABS_PATH, Argument::type(GetCollection::class), Argument::any())->willReturn('/property-collection-relations', '/parent/42/another-collection-operations');
×
241
        $iriConverterProphecy->getIriFromResource($propertyCollectionIriOnly, UrlGeneratorInterface::ABS_PATH, Argument::type(Get::class), Argument::any())->willReturn('/parent/42/another-collection-operations/24');
×
242

243
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
244
        $propertyAccessorProphecy->getValue($propertyCollectionIriOnly, 'propertyCollectionIriOnlyRelation')->willReturn([$propertyCollectionIriOnlyRelation]);
×
245
        $propertyAccessorProphecy->getValue($propertyCollectionIriOnly, 'iterableIri')->willReturn($propertyCollectionIriOnlyRelation);
×
246
        $propertyAccessorProphecy->getValue($propertyCollectionIriOnly, 'toOneRelation')->willReturn($propertyCollectionIriOnlyRelation);
×
247

248
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
249

250
        $resourceClassResolverProphecy->isResourceClass(PropertyCollectionIriOnly::class)->willReturn(true);
×
251
        $resourceClassResolverProphecy->getResourceClass(null, PropertyCollectionIriOnly::class)->willReturn(PropertyCollectionIriOnly::class);
×
252

253
        $resourceClassResolverProphecy->isResourceClass(PropertyCollectionIriOnlyRelation::class)->willReturn(true);
×
254
        $resourceClassResolverProphecy->getResourceClass([$propertyCollectionIriOnlyRelation], PropertyCollectionIriOnlyRelation::class)->willReturn(PropertyCollectionIriOnlyRelation::class);
×
255

256
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), new PropertyAccessor(), // $propertyAccessorProphecy->reveal(),
×
257
            null, null, [], $resourceMetadataCollectionFactoryProphecy->reveal(), null, ) extends AbstractItemNormalizer {};
×
258

259
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
260
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
261
        $normalizer->setSerializer($serializerProphecy->reveal());
×
262

263
        $expected = [
×
264
            'propertyCollectionIriOnlyRelation' => '/property-collection-relations',
×
265
            'iterableIri' => '/parent/42/another-collection-operations',
×
266
            'toOneRelation' => '/parent/42/another-collection-operations/24',
×
267
        ];
×
268

269
        $this->assertSame($expected, $normalizer->normalize($propertyCollectionIriOnly, 'jsonld', [
×
270
            'resources' => [],
×
271
            'root_operation' => new GetCollection('/property_collection_iri_onlies'),
×
272
        ]));
×
273
    }
274

275
    public function testDenormalizeWithSecuredProperty(): void
276
    {
277
        $data = [
×
278
            'title' => 'foo',
×
279
            'adminOnlyProperty' => 'secret',
×
280
        ];
×
281

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

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

289
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
290

291
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
292

293
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
294
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
295
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
296

297
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
298
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
299

300
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
301
        $resourceAccessChecker->isGranted(
×
302
            SecuredDummy::class,
×
303
            'is_granted(\'ROLE_ADMIN\')',
×
304
            ['object' => null, 'property' => 'adminOnlyProperty']
×
305
        )->willReturn(false);
×
306

307
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, $resourceAccessChecker->reveal()) extends AbstractItemNormalizer {};
×
308
        $normalizer->setSerializer($serializerProphecy->reveal());
×
309

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

312
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
313

314
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
315
        $propertyAccessorProphecy->setValue($actual, 'adminOnlyProperty', 'secret')->shouldNotHaveBeenCalled();
×
316
    }
317

318
    public function testDenormalizeCreateWithDeniedPostDenormalizeSecuredProperty(): void
319
    {
320
        $data = [
×
321
            'title' => 'foo',
×
322
            'ownerOnlyProperty' => 'should not be set',
×
323
        ];
×
324

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

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

332
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
333

334
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
335

336
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
337
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
338
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
339

340
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
341
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
342

343
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
344
        $resourceAccessChecker->isGranted(
×
345
            SecuredDummy::class,
×
346
            'false',
×
347
            Argument::any()
×
348
        )->willReturn(false);
×
349

350
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, $resourceAccessChecker->reveal()) extends AbstractItemNormalizer {};
×
351
        $normalizer->setSerializer($serializerProphecy->reveal());
×
352

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

355
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
356

357
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
358
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldHaveBeenCalled();
×
359
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', '')->shouldHaveBeenCalled();
×
360
    }
361

362
    public function testDenormalizeUpdateWithSecuredProperty(): void
363
    {
364
        $dummy = new SecuredDummy();
×
365

366
        $data = [
×
367
            'title' => 'foo',
×
368
            'ownerOnlyProperty' => 'secret',
×
369
        ];
×
370

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

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

378
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
379

380
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
381

382
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
383
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
384
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
385
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
386

387
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
388
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
389

390
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
391
        $resourceAccessChecker->isGranted(
×
392
            SecuredDummy::class,
×
393
            'true',
×
394
            ['object' => null, 'property' => 'ownerOnlyProperty']
×
395
        )->willReturn(true);
×
396
        $resourceAccessChecker->isGranted(
×
397
            SecuredDummy::class,
×
398
            'true',
×
399
            ['object' => $dummy, 'property' => 'ownerOnlyProperty']
×
400
        )->willReturn(true);
×
401

402
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, $resourceAccessChecker->reveal()) extends AbstractItemNormalizer {};
×
403
        $normalizer->setSerializer($serializerProphecy->reveal());
×
404

405
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
406
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
407

408
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
409

410
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
411
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'secret')->shouldHaveBeenCalled();
×
412
    }
413

414
    public function testDenormalizeUpdateWithDeniedSecuredProperty(): void
415
    {
416
        $dummy = new SecuredDummy();
×
417
        $dummy->setOwnerOnlyProperty('secret');
×
418

419
        $data = [
×
420
            'title' => 'foo',
×
421
            'ownerOnlyProperty' => 'should not be set',
×
422
        ];
×
423

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

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

431
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
432

433
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
434

435
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
436
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
437
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
438
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
439

440
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
441
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
442

443
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
444
        $resourceAccessChecker->isGranted(
×
445
            SecuredDummy::class,
×
446
            'false',
×
447
            ['object' => null, 'property' => 'ownerOnlyProperty']
×
448
        )->willReturn(false);
×
449
        $resourceAccessChecker->isGranted(
×
450
            SecuredDummy::class,
×
451
            'false',
×
452
            ['object' => $dummy, 'property' => 'ownerOnlyProperty']
×
453
        )->willReturn(false);
×
454

455
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, $resourceAccessChecker->reveal()) extends AbstractItemNormalizer {};
×
456
        $normalizer->setSerializer($serializerProphecy->reveal());
×
457

458
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
459
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
460

461
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
462

463
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
464
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldNotHaveBeenCalled();
×
465
    }
466

467
    public function testDenormalizeUpdateWithDeniedPostDenormalizeSecuredProperty(): void
468
    {
469
        $dummy = new SecuredDummy();
×
470
        $dummy->setOwnerOnlyProperty('secret');
×
471

472
        $data = [
×
473
            'title' => 'foo',
×
474
            'ownerOnlyProperty' => 'should not be set',
×
475
        ];
×
476

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

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

484
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
485

486
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
487
        $propertyAccessorProphecy->getValue($dummy, 'ownerOnlyProperty')->willReturn('secret');
×
488

489
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
490
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
491
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
492
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
493

494
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
495
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
496

497
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
498
        $resourceAccessChecker->isGranted(
×
499
            SecuredDummy::class,
×
500
            'false',
×
501
            ['object' => $dummy, 'previous_object' => $dummy, 'property' => 'ownerOnlyProperty']
×
502
        )->willReturn(false);
×
503

504
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, $resourceAccessChecker->reveal()) extends AbstractItemNormalizer {};
×
505
        $normalizer->setSerializer($serializerProphecy->reveal());
×
506

507
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
508
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
509

510
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
511

512
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
513
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldHaveBeenCalled();
×
514
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'secret')->shouldHaveBeenCalled();
×
515
    }
516

517
    public function testNormalizeReadableLinks(): void
518
    {
519
        $relatedDummy = new RelatedDummy();
×
520

521
        $dummy = new Dummy();
×
522
        $dummy->setRelatedDummy($relatedDummy);
×
523
        $dummy->relatedDummies->add(new RelatedDummy());
×
524

525
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
526

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

530
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
531
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
532

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

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

540
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
541
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
542
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
543

544
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
545
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
546
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
547
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
548
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
549
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
550
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
551

552
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
553
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
554
        $relatedDummyChildContext = Argument::allOf(
×
555
            Argument::type('array'),
×
556
            Argument::withEntry('resource_class', RelatedDummy::class),
×
557
            Argument::not(Argument::withKey('iri')),
×
558
            Argument::not(Argument::withKey('force_resource_class'))
×
559
        );
×
560
        $serializerProphecy->normalize($relatedDummy, null, $relatedDummyChildContext)->willReturn(['foo' => 'hello']);
×
561
        $serializerProphecy->normalize(['foo' => 'hello'], null, Argument::type('array'))->willReturn(['foo' => 'hello']);
×
562
        $serializerProphecy->normalize([['foo' => 'hello']], null, Argument::type('array'))->willReturn([['foo' => 'hello']]);
×
563

564
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
565
        $normalizer->setSerializer($serializerProphecy->reveal());
×
566

567
        $expected = [
×
568
            'relatedDummy' => ['foo' => 'hello'],
×
569
            'relatedDummies' => [['foo' => 'hello']],
×
570
        ];
×
571
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
572
            'resources' => [],
×
573
            'force_resource_class' => Dummy::class,
×
574
        ]));
×
575
    }
576

577
    public function testNormalizePolymorphicRelations(): void
578
    {
579
        $concreteDummy = new DummyTableInheritanceChild();
×
580

581
        $dummy = new DummyTableInheritanceRelated();
×
582
        $dummy->addChild($concreteDummy);
×
583

584
        $abstractDummies = new ArrayCollection([$concreteDummy]);
×
585

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

589
        $abstractDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, DummyTableInheritance::class);
×
590
        $abstractDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $abstractDummyType);
×
591

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

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

598
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
599
        $propertyAccessorProphecy->getValue($dummy, 'children')->willReturn($abstractDummies);
×
600

601
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
602
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(DummyTableInheritanceRelated::class);
×
603
        $resourceClassResolverProphecy->getResourceClass(null, DummyTableInheritanceRelated::class)->willReturn(DummyTableInheritanceRelated::class);
×
604
        $resourceClassResolverProphecy->getResourceClass($concreteDummy, DummyTableInheritance::class)->willReturn(DummyTableInheritanceChild::class);
×
605
        $resourceClassResolverProphecy->getResourceClass($abstractDummies, DummyTableInheritance::class)->willReturn(DummyTableInheritance::class);
×
606
        $resourceClassResolverProphecy->isResourceClass(DummyTableInheritanceRelated::class)->willReturn(true);
×
607
        $resourceClassResolverProphecy->isResourceClass(DummyTableInheritance::class)->willReturn(true);
×
608

609
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
610
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
611
        $concreteDummyChildContext = Argument::allOf(
×
612
            Argument::type('array'),
×
613
            Argument::not(Argument::withKey('iri'))
×
614
        );
×
615
        $serializerProphecy->normalize($concreteDummy, null, $concreteDummyChildContext)->willReturn(['foo' => 'concrete']);
×
616
        $serializerProphecy->normalize([['foo' => 'concrete']], null, Argument::type('array'))->willReturn([['foo' => 'concrete']]);
×
617

618
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
619
        $normalizer->setSerializer($serializerProphecy->reveal());
×
620

621
        $expected = [
×
622
            'children' => [['foo' => 'concrete']],
×
623
        ];
×
624
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
625
            'resources' => [],
×
626
        ]));
×
627
    }
628

629
    public function testDenormalize(): void
630
    {
631
        $data = [
×
632
            'name' => 'foo',
×
633
            'relatedDummy' => '/dummies/1',
×
634
            'relatedDummies' => ['/dummies/2'],
×
635
        ];
×
636

637
        $relatedDummy1 = new RelatedDummy();
×
638
        $relatedDummy2 = new RelatedDummy();
×
639

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

643
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
644
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
645

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

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

652
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
653
        $iriConverterProphecy->getResourceFromIri('/dummies/1', Argument::type('array'))->willReturn($relatedDummy1);
×
654
        $iriConverterProphecy->getResourceFromIri('/dummies/2', Argument::type('array'))->willReturn($relatedDummy2);
×
655

656
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
657

658
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
659
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
660
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
661
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
662
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
663

664
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
665
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
666

667
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
668
        $normalizer->setSerializer($serializerProphecy->reveal());
×
669

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

672
        $this->assertInstanceOf(Dummy::class, $actual);
×
673

674
        $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
×
675
        $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
×
676
        $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
×
677
    }
678

679
    public function testCanDenormalizeInputClassWithDifferentFieldsThanResourceClass(): void
680
    {
681
        $this->markTestSkipped('TODO: check why this test has been commented');
×
682

683
        // $data = [
684
        //     'dummyName' => 'Dummy Name',
685
        // ];
686
        //
687
        // $context = [
688
        //     'resource_class' => DummyForAdditionalFields::class,
689
        //     'input' => ['class' => DummyForAdditionalFieldsInput::class],
690
        //     'output' => ['class' => DummyForAdditionalFields::class],
691
        // ];
692
        // $augmentedContext = $context + ['api_denormalize' => true];
693
        //
694
        // $preHydratedDummy = new DummyForAdditionalFieldsInput('Name Dummy');
695
        // $cleanedContext = array_diff_key($augmentedContext, [
696
        //     'input' => null,
697
        //     'resource_class' => null,
698
        // ]);
699
        // $cleanedContextWithObjectToPopulate = array_merge($cleanedContext, [
700
        //     AbstractObjectNormalizer::OBJECT_TO_POPULATE => $preHydratedDummy,
701
        //     AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE => true,
702
        // ]);
703
        //
704
        // $dummyInputDto = new DummyForAdditionalFieldsInput('Dummy Name');
705
        // $dummy = new DummyForAdditionalFields('Dummy Name', 'name-dummy');
706
        //
707
        // $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
708
        //
709
        // $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
710
        //
711
        // $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
712
        //
713
        // $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
714
        // $resourceClassResolverProphecy->getResourceClass(null, DummyForAdditionalFields::class)->willReturn(DummyForAdditionalFields::class);
715
        //
716
        // $inputDataTransformerProphecy = $this->prophesize(DataTransformerInitializerInterface::class);
717
        // $inputDataTransformerProphecy->willImplement(DataTransformerInitializerInterface::class);
718
        // $inputDataTransformerProphecy->initialize(DummyForAdditionalFieldsInput::class, $cleanedContext)->willReturn($preHydratedDummy);
719
        // $inputDataTransformerProphecy->supportsTransformation($data, DummyForAdditionalFields::class, $augmentedContext)->willReturn(true);
720
        // $inputDataTransformerProphecy->transform($dummyInputDto, DummyForAdditionalFields::class, $augmentedContext)->willReturn($dummy);
721
        //
722
        // $serializerProphecy = $this->prophesize(SerializerInterface::class);
723
        // $serializerProphecy->willImplement(DenormalizerInterface::class);
724
        // $serializerProphecy->denormalize($data, DummyForAdditionalFieldsInput::class, 'json', $cleanedContextWithObjectToPopulate)->willReturn($dummyInputDto);
725
        //
726
        // $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), null, null, null, [], null, null) extends AbstractItemNormalizer {
727
        // };
728
        // $normalizer->setSerializer($serializerProphecy->reveal());
729
        //
730
        // $actual = $normalizer->denormalize($data, DummyForAdditionalFields::class, 'json', $context);
731
        //
732
        // $this->assertInstanceOf(DummyForAdditionalFields::class, $actual);
733
        // $this->assertSame('Dummy Name', $actual->getName());
734
    }
735

736
    public function testDenormalizeWritableLinks(): void
737
    {
738
        $data = [
×
739
            'name' => 'foo',
×
740
            'relatedDummy' => ['foo' => 'bar'],
×
741
            'relatedDummies' => [['bar' => 'baz']],
×
742
            'relatedDummiesWithUnionTypes' => [0 => ['bar' => 'qux'], 1. => ['bar' => 'quux']],
×
743
        ];
×
744

745
        $relatedDummy1 = new RelatedDummy();
×
746
        $relatedDummy2 = new RelatedDummy();
×
747
        $relatedDummy3 = new RelatedDummy();
×
748
        $relatedDummy4 = new RelatedDummy();
×
749

750
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
751
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name', 'relatedDummy', 'relatedDummies', 'relatedDummiesWithUnionTypes']));
×
752

753
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
754
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
755
        $relatedDummiesWithUnionTypesIntType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
756
        $relatedDummiesWithUnionTypesFloatType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_FLOAT), $relatedDummyType);
×
757

758
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
759
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
760
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
761
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
762
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummiesWithUnionTypes', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesWithUnionTypesIntType, $relatedDummiesWithUnionTypesFloatType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
763

764
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
765

766
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
767

768
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
769
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
770
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
771
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
772
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
773

774
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
775
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
776
        $serializerProphecy->denormalize(['foo' => 'bar'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy1);
×
777
        $serializerProphecy->denormalize(['bar' => 'baz'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy2);
×
778
        $serializerProphecy->denormalize(['bar' => 'qux'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy3);
×
779
        $serializerProphecy->denormalize(['bar' => 'quux'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy4);
×
780

781
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
782
        $normalizer->setSerializer($serializerProphecy->reveal());
×
783

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

786
        $this->assertInstanceOf(Dummy::class, $actual);
×
787

788
        $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
×
789
        $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
×
790
        $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
×
791
        $propertyAccessorProphecy->setValue($actual, 'relatedDummiesWithUnionTypes', [0 => $relatedDummy3, 1. => $relatedDummy4])->shouldHaveBeenCalled();
×
792
    }
793

794
    public function testBadRelationType(): void
795
    {
796
        $this->expectException(NotNormalizableValueException::class);
×
797
        $this->expectExceptionMessage('The type of the "relatedDummy" attribute must be "array" (nested document) or "string" (IRI), "integer" given.');
×
798

799
        $data = [
×
800
            'relatedDummy' => 22,
×
801
        ];
×
802

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

806
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
807
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
808
            (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
809
        );
×
810

811
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
812

813
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
814
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
815
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
816
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
817
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
818

819
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
820

821
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
822
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
823

824
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
825
        $normalizer->setSerializer($serializerProphecy->reveal());
×
826

827
        $normalizer->denormalize($data, Dummy::class);
×
828
    }
829

830
    public function testBadRelationTypeWithExceptionToValidationErrors(): void
831
    {
832
        $data = [
×
833
            'relatedDummy' => 22,
×
834
        ];
×
835

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

839
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
840
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
841
            (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
842
        );
×
843

844
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
845

846
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
847
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
848
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
849
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
850
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
851

852
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
853

854
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
855
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
856

857
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
858
        $normalizer->setSerializer($serializerProphecy->reveal());
×
859

860
        // 'not_normalizable_value_exceptions' is set by Serializer thanks to DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS
861
        $actual = $normalizer->denormalize($data, Dummy::class, null, ['not_normalizable_value_exceptions' => []]);
×
862
        $this->assertNull($actual->relatedDummy);
×
863
    }
864

865
    public function testDeserializationPathForNotDenormalizableRelations(): void
866
    {
867
        $data = [
×
868
            'relatedDummies' => ['wrong'],
×
869
        ];
×
870

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

874
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
875
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
×
876
            (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, null, true, null, new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class))])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true)
×
877
        );
×
878

879
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
880
        $iriConverterProphecy->getResourceFromIri(Argument::cetera())->willThrow(new InvalidArgumentException('Invalid IRI'));
×
881

882
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
883
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
884
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
885
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
886
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
887

888
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
889

890
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
891
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
892

893
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
894
            $propertyNameCollectionFactoryProphecy->reveal(),
×
895
            $propertyMetadataFactoryProphecy->reveal(),
×
896
            $iriConverterProphecy->reveal(),
×
897
            $resourceClassResolverProphecy->reveal(),
×
898
            $propertyAccessorProphecy->reveal(),
×
899
            null,
×
900
            null,
×
901
            [],
×
902
            null,
×
903
            null,
×
904
        ]);
×
905
        $normalizer->setSerializer($serializerProphecy->reveal());
×
906

907
        $errors = [];
×
908
        $actual = $normalizer->denormalize($data, Dummy::class, null, ['not_normalizable_value_exceptions' => &$errors]);
×
909
        $this->assertEmpty($actual->relatedDummies);
×
910
        $this->assertCount(1, $errors); // @phpstan-ignore-line method.impossibleType (false positive)
×
911
        $this->assertInstanceOf(NotNormalizableValueException::class, $errors[0]);
×
912
        $this->assertSame('relatedDummies[0]', $errors[0]->getPath());
×
913
    }
914

915
    public function testDeserializationPathForNotDenormalizableResource(): void
916
    {
NEW
917
        $this->expectException(NotNormalizableValueException::class);
×
918

NEW
919
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
920

NEW
921
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
922

NEW
923
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
NEW
924
        $iriConverterProphecy->getResourceFromIri(Argument::cetera())->willThrow(new InvalidArgumentException('Invalid IRI'));
×
925

NEW
926
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
NEW
927
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
NEW
928
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
929

NEW
930
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
931

NEW
932
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
NEW
933
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
934

NEW
935
        $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
×
NEW
936
            $propertyNameCollectionFactoryProphecy->reveal(),
×
NEW
937
            $propertyMetadataFactoryProphecy->reveal(),
×
NEW
938
            $iriConverterProphecy->reveal(),
×
NEW
939
            $resourceClassResolverProphecy->reveal(),
×
NEW
940
            $propertyAccessorProphecy->reveal(),
×
NEW
941
            null,
×
NEW
942
            null,
×
NEW
943
            [],
×
NEW
944
            null,
×
NEW
945
            null,
×
NEW
946
        ]);
×
NEW
947
        $normalizer->setSerializer($serializerProphecy->reveal());
×
948

NEW
949
        $normalizer->denormalize('wrong IRI', Dummy::class, null, ['not_normalizable_value_exceptions' => []]);
×
950
    }
951

952
    public function testInnerDocumentNotAllowed(): void
953
    {
954
        $this->expectException(UnexpectedValueException::class);
×
955
        $this->expectExceptionMessage('Nested documents for attribute "relatedDummy" are not allowed. Use IRIs instead.');
×
956

957
        $data = [
×
958
            'relatedDummy' => [
×
959
                'foo' => 'bar',
×
960
            ],
×
961
        ];
×
962

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

966
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
967
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
968
            (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
969
        );
×
970

971
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
972

973
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
974
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
975
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
976
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
977
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
978

979
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
980

981
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
982
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
983

984
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
985
        $normalizer->setSerializer($serializerProphecy->reveal());
×
986

987
        $normalizer->denormalize($data, Dummy::class);
×
988
    }
989

990
    public function testBadType(): void
991
    {
992
        $this->expectException(UnexpectedValueException::class);
×
993
        $this->expectExceptionMessage('The type of the "foo" attribute must be "float", "integer" given.');
×
994

995
        $data = [
×
996
            'foo' => 42,
×
997
        ];
×
998

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

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

1005
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1006

1007
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1008
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1009
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1010

1011
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1012

1013
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1014
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1015

1016
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1017
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1018

1019
        $normalizer->denormalize($data, Dummy::class);
×
1020
    }
1021

1022
    public function testTypeChecksCanBeDisabled(): void
1023
    {
1024
        $data = [
×
1025
            'foo' => 42,
×
1026
        ];
×
1027

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

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

1034
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1035

1036
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1037
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1038
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1039

1040
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1041

1042
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1043
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1044

1045
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1046
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1047

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

1050
        $this->assertInstanceOf(Dummy::class, $actual);
×
1051

1052
        $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
×
1053
    }
1054

1055
    public function testJsonAllowIntAsFloat(): void
1056
    {
1057
        $data = [
×
1058
            'foo' => 42,
×
1059
        ];
×
1060

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

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

1067
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1068

1069
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1070
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1071
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1072

1073
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1074

1075
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1076
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1077

1078
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1079
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1080

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

1083
        $this->assertInstanceOf(Dummy::class, $actual);
×
1084

1085
        $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
×
1086
    }
1087

1088
    public function testDenormalizeBadKeyType(): void
1089
    {
1090
        $this->expectException(NotNormalizableValueException::class);
×
1091
        $this->expectExceptionMessage('The type of the key "a" must be "int", "string" given.');
×
1092

1093
        $data = [
×
1094
            'name' => 'foo',
×
1095
            'relatedDummy' => [
×
1096
                'foo' => 'bar',
×
1097
            ],
×
1098
            'relatedDummies' => [
×
1099
                'a' => [
×
1100
                    'bar' => 'baz',
×
1101
                ],
×
1102
            ],
×
1103
            'relatedDummiesWithUnionTypes' => [
×
1104
                'a' => [
×
1105
                    'bar' => 'baz',
×
1106
                ],
×
1107
            ],
×
1108
        ];
×
1109

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

1113
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1114
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
1115
        $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));
×
1116

1117
        $type = new Type(
×
1118
            Type::BUILTIN_TYPE_OBJECT,
×
1119
            false,
×
1120
            ArrayCollection::class,
×
1121
            true,
×
1122
            new Type(Type::BUILTIN_TYPE_INT),
×
1123
            new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)
×
1124
        );
×
1125
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$type])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1126

1127
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1128

1129
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1130
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1131
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1132
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1133
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1134

1135
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1136

1137
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1138
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1139

1140
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1141
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1142

1143
        $normalizer->denormalize($data, Dummy::class);
×
1144
    }
1145

1146
    public function testNullable(): void
1147
    {
1148
        $data = [
×
1149
            'name' => null,
×
1150
        ];
×
1151

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

1155
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1156
        $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));
×
1157

1158
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1159

1160
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1161
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1162
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1163

1164
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1165

1166
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1167
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1168

1169
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1170
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1171

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

1174
        $this->assertInstanceOf(Dummy::class, $actual);
×
1175

1176
        $propertyAccessorProphecy->setValue($actual, 'name', null)->shouldHaveBeenCalled();
×
1177
    }
1178

1179
    public function testDenormalizeBasicTypePropertiesFromXml(): void
1180
    {
1181
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1182
        $propertyNameCollectionFactoryProphecy->create(ObjectWithBasicProperties::class, [])->willReturn(new PropertyNameCollection([
×
1183
            'boolTrue1',
×
1184
            'boolFalse1',
×
1185
            'boolTrue2',
×
1186
            'boolFalse2',
×
1187
            'int1',
×
1188
            'int2',
×
1189
            'float1',
×
1190
            'float2',
×
1191
            'float3',
×
1192
            'floatNaN',
×
1193
            'floatInf',
×
1194
            'floatNegInf',
×
1195
        ]));
×
1196

1197
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1198
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1199
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1200
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1201
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1202
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_INT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1203
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_INT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1204
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1205
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1206
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float3', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1207
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNaN', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1208
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatInf', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1209
        $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNegInf', [])->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1210

1211
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1212

1213
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1214
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolTrue1', true)->shouldBeCalled();
×
1215
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolFalse1', false)->shouldBeCalled();
×
1216
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolTrue2', true)->shouldBeCalled();
×
1217
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolFalse2', false)->shouldBeCalled();
×
1218
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'int1', 4711)->shouldBeCalled();
×
1219
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'int2', -4711)->shouldBeCalled();
×
1220
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float1', Argument::approximate(123.456, 0))->shouldBeCalled();
×
1221
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float2', Argument::approximate(-1.2344e56, 1))->shouldBeCalled();
×
1222
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float3', Argument::approximate(45E-6, 1))->shouldBeCalled();
×
1223
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatNaN', Argument::that(static fn (float $arg) => is_nan($arg)))->shouldBeCalled();
×
1224
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatInf', \INF)->shouldBeCalled();
×
1225
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatNegInf', -\INF)->shouldBeCalled();
×
1226

1227
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1228
        $resourceClassResolverProphecy->getResourceClass(null, ObjectWithBasicProperties::class)->willReturn(ObjectWithBasicProperties::class);
×
1229
        $resourceClassResolverProphecy->isResourceClass(ObjectWithBasicProperties::class)->willReturn(true);
×
1230

1231
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1232
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1233

1234
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1235
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1236

1237
        $objectWithBasicProperties = $normalizer->denormalize(
×
1238
            [
×
1239
                'boolTrue1' => 'true',
×
1240
                'boolFalse1' => 'false',
×
1241
                'boolTrue2' => '1',
×
1242
                'boolFalse2' => '0',
×
1243
                'int1' => '4711',
×
1244
                'int2' => '-4711',
×
1245
                'float1' => '123.456',
×
1246
                'float2' => '-1.2344e56',
×
1247
                'float3' => '45E-6',
×
1248
                'floatNaN' => 'NaN',
×
1249
                'floatInf' => 'INF',
×
1250
                'floatNegInf' => '-INF',
×
1251
            ],
×
1252
            ObjectWithBasicProperties::class,
×
1253
            'xml'
×
1254
        );
×
1255

1256
        $this->assertInstanceOf(ObjectWithBasicProperties::class, $objectWithBasicProperties);
×
1257
    }
1258

1259
    public function testDenormalizeCollectionDecodedFromXmlWithOneChild(): void
1260
    {
1261
        $data = [
×
1262
            'relatedDummies' => [
×
1263
                'name' => 'foo',
×
1264
            ],
×
1265
        ];
×
1266

1267
        $relatedDummy = new RelatedDummy();
×
1268
        $relatedDummy->setName('foo');
×
1269

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

1273
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
1274
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
1275

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

1279
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1280

1281
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1282
        $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', Argument::type('array'))->shouldBeCalled();
×
1283

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

1290
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1291
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1292
        $serializerProphecy->denormalize(['name' => 'foo'], RelatedDummy::class, 'xml', Argument::type('array'))->willReturn($relatedDummy);
×
1293

1294
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1295
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1296

1297
        $normalizer->denormalize($data, Dummy::class, 'xml');
×
1298
    }
1299

1300
    public function testDenormalizePopulatingNonCloneableObject(): void
1301
    {
1302
        $dummy = new NonCloneableDummy();
×
1303
        $dummy->setName('foo');
×
1304

1305
        $data = [
×
1306
            'name' => 'bar',
×
1307
        ];
×
1308

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

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

1315
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1316
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1317
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1318
        $resourceClassResolverProphecy->getResourceClass(null, NonCloneableDummy::class)->willReturn(NonCloneableDummy::class);
×
1319
        $resourceClassResolverProphecy->getResourceClass($dummy, NonCloneableDummy::class)->willReturn(NonCloneableDummy::class);
×
1320
        $resourceClassResolverProphecy->isResourceClass(NonCloneableDummy::class)->willReturn(true);
×
1321

1322
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1323
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1324

1325
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1326
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1327
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1328

1329
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
1330
        $actual = $normalizer->denormalize($data, NonCloneableDummy::class, null, $context);
×
1331

1332
        $this->assertInstanceOf(NonCloneableDummy::class, $actual);
×
1333
        $this->assertSame($dummy, $actual);
×
1334
        $propertyAccessorProphecy->setValue($actual, 'name', 'bar')->shouldHaveBeenCalled();
×
1335
    }
1336

1337
    public function testDenormalizeObjectWithNullDisabledTypeEnforcement(): void
1338
    {
1339
        $data = [
×
1340
            'dummy' => null,
×
1341
        ];
×
1342

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

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

1349
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1350
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1351
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1352
        $resourceClassResolverProphecy->getResourceClass(null, DtoWithNullValue::class)->willReturn(DtoWithNullValue::class);
×
1353
        $resourceClassResolverProphecy->isResourceClass(DtoWithNullValue::class)->willReturn(true);
×
1354

1355
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1356
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1357

1358
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1359
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1360

1361
        $context = [AbstractItemNormalizer::DISABLE_TYPE_ENFORCEMENT => true];
×
1362
        $actual = $normalizer->denormalize($data, DtoWithNullValue::class, null, $context);
×
1363

1364
        $this->assertInstanceOf(DtoWithNullValue::class, $actual);
×
1365
        $this->assertEquals(new DtoWithNullValue(), $actual);
×
1366
    }
1367

1368
    public function testCacheKey(): void
1369
    {
1370
        $relatedDummy = new RelatedDummy();
×
1371

1372
        $dummy = new Dummy();
×
1373
        $dummy->setName('foo');
×
1374
        $dummy->setAlias('ignored');
×
1375
        $dummy->setRelatedDummy($relatedDummy);
×
1376
        $dummy->relatedDummies->add(new RelatedDummy());
×
1377

1378
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
1379

1380
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1381
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['name', 'alias', 'relatedDummy', 'relatedDummies']));
×
1382

1383
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
1384
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
1385

1386
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1387
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
1388
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
1389
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
1390
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(false));
×
1391

1392
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1393
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
×
1394
        $iriConverterProphecy->getIriFromResource($relatedDummy, Argument::cetera())->willReturn('/dummies/2');
×
1395

1396
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1397
        $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('foo');
×
1398
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
1399
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
1400

1401
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1402
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1403
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
1404
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1405
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1406
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1407
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1408

1409
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1410
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1411
        $serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo');
×
1412
        $serializerProphecy->normalize(['/dummies/2'], null, Argument::type('array'))->willReturn(['/dummies/2']);
×
1413

1414
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1415
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1416

1417
        $expected = [
×
1418
            'name' => 'foo',
×
1419
            'relatedDummy' => '/dummies/2',
×
1420
            'relatedDummies' => ['/dummies/2'],
×
1421
        ];
×
1422
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
1423
            'resources' => [],
×
1424
            'groups' => ['group'],
×
1425
            'ignored_attributes' => ['alias'],
×
1426
            'operation_name' => 'operation_name',
×
1427
            'root_operation_name' => 'root_operation_name',
×
1428
        ]));
×
1429

1430
        $operationCacheKey = (new \ReflectionClass($normalizer))->getProperty('localFactoryOptionsCache')->getValue($normalizer);
×
1431
        $this->assertEquals(array_keys($operationCacheKey), [\sprintf('%s%s%s%s', Dummy::class, 'operation_name', 'root_operation_name', 'n')]);
×
1432
        $this->assertEquals(current($operationCacheKey), ['serializer_groups' => ['group']]);
×
1433
    }
1434
}
1435

1436
class ObjectWithBasicProperties
1437
{
1438
    /** @var bool */
1439
    public $boolTrue1;
1440

1441
    /** @var bool */
1442
    public $boolFalse1;
1443

1444
    /** @var bool */
1445
    public $boolTrue2;
1446

1447
    /** @var bool */
1448
    public $boolFalse2;
1449

1450
    /** @var int */
1451
    public $int1;
1452

1453
    /** @var int */
1454
    public $int2;
1455

1456
    /** @var float */
1457
    public $float1;
1458

1459
    /** @var float */
1460
    public $float2;
1461

1462
    /** @var float */
1463
    public $float3;
1464

1465
    /** @var float */
1466
    public $floatNaN;
1467

1468
    /** @var float */
1469
    public $floatInf;
1470

1471
    /** @var float */
1472
    public $floatNegInf;
1473
}
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