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

api-platform / core / 14980015570

12 May 2025 06:41PM UTC coverage: 26.309% (+2.6%) from 23.685%
14980015570

Pull #7140

github

web-flow
Merge 1e6b14143 into 202c60fcb
Pull Request #7140: Fix: PHPize HTTP cache headers

0 of 1 new or added line in 1 file covered. (0.0%)

4614 existing lines in 185 files now uncovered.

13550 of 51504 relevant lines covered (26.31%)

71.73 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\PropertyInfoExtractor;
49
use Symfony\Component\PropertyInfo\Type as LegacyType;
50
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
51
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
52
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
53
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
54
use Symfony\Component\Serializer\SerializerInterface;
55
use Symfony\Component\TypeInfo\Type;
56

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

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

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

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

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

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

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

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

100
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
101

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

105
        // BC layer for api-platform/metadata < 4.1
106
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
107
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
108
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
109

110
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
111
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
112
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
113
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
114
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(false));
×
115
        } else {
116
            $relatedDummyType = Type::object(RelatedDummy::class);
×
117
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
118

119
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
120
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true));
×
121
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true));
×
122
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
123
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(true)->withWritable(false)->withReadableLink(false));
×
124
        }
125

126
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
127
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
×
UNCOV
128
        $iriConverterProphecy->getIriFromResource($relatedDummy, Argument::cetera())->willReturn('/dummies/2');
×
129

130
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
131
        $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('foo');
×
132
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
UNCOV
133
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
134

135
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
136
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
137
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
138
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
139
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
140
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
141
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
142

143
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
144
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
145
        $serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo');
×
UNCOV
146
        $serializerProphecy->normalize(['/dummies/2'], null, Argument::type('array'))->willReturn(['/dummies/2']);
×
147

UNCOV
148
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
UNCOV
149
        $normalizer->setSerializer($serializerProphecy->reveal());
×
150

151
        $expected = [
×
152
            'name' => 'foo',
×
UNCOV
153
            'relatedDummy' => '/dummies/2',
×
154
            'relatedDummies' => ['/dummies/2'],
×
155
        ];
×
UNCOV
156
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
157
            'resources' => [],
×
158
            'ignored_attributes' => ['alias'],
×
159
        ]));
×
160
    }
161

162
    public function testNormalizeWithSecuredProperty(): void
163
    {
164
        $dummy = new SecuredDummy();
×
165
        $dummy->setTitle('myPublicTitle');
×
166
        $dummy->setAdminOnlyProperty('secret');
×
167

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

171
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
172

173
        // BC layer for api-platform/metadata < 4.1
174
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
175
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
176
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
177
        } else {
178
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true));
×
UNCOV
179
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
180
        }
181

182
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
UNCOV
183
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/secured_dummies/1');
×
184

185
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
UNCOV
186
        $propertyAccessorProphecy->getValue($dummy, 'title')->willReturn('myPublicTitle');
×
187
        $propertyAccessorProphecy->getValue($dummy, 'adminOnlyProperty')->willReturn('secret');
×
188

189
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
190
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(SecuredDummy::class);
×
191
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
192
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
193

UNCOV
194
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
UNCOV
195
        $resourceAccessChecker->isGranted(
×
UNCOV
196
            SecuredDummy::class,
×
197
            'is_granted(\'ROLE_ADMIN\')',
×
198
            ['object' => $dummy, 'property' => 'adminOnlyProperty']
×
UNCOV
199
        )->willReturn(false);
×
200

201
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
UNCOV
202
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
203
        $serializerProphecy->normalize('myPublicTitle', null, Argument::type('array'))->willReturn('myPublicTitle');
×
204

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

208
        $expected = [
×
209
            'title' => 'myPublicTitle',
×
UNCOV
210
        ];
×
211
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
212
            'resources' => [],
×
UNCOV
213
        ]));
×
214
    }
215

216
    public function testNormalizePropertyAsIriWithUriTemplate(): void
217
    {
UNCOV
218
        $propertyCollectionIriOnlyRelation = new PropertyCollectionIriOnlyRelation();
×
219
        $propertyCollectionIriOnlyRelation->name = 'My Relation';
×
220

221
        $propertyCollectionIriOnly = new PropertyCollectionIriOnly();
×
222
        $propertyCollectionIriOnly->addPropertyCollectionIriOnlyRelation($propertyCollectionIriOnlyRelation);
×
223

224
        $collectionOperation = new GetCollection('/property-collection-relations');
×
UNCOV
225
        $getIterableOperation = new GetCollection('/parent/{parentId}/another-collection-operations');
×
226
        $getToOneOperation = new Get('/parent/{parentId}/another-collection-operations/{id}');
×
227

228
        $resourceRelationMetadataCollection = new ResourceMetadataCollection(PropertyCollectionIriOnlyRelation::class, [
×
229
            (new ApiResource())->withOperations(new Operations([$collectionOperation, $getIterableOperation, $getToOneOperation])),
×
230
        ]);
×
231

232
        $resourceMetadataCollectionFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
233
        $resourceMetadataCollectionFactoryProphecy->create(PropertyCollectionIriOnlyRelation::class)->willReturn($resourceRelationMetadataCollection);
×
234

235
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
236
        $propertyNameCollectionFactoryProphecy->create(PropertyCollectionIriOnly::class, ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
UNCOV
237
            new PropertyNameCollection(['propertyCollectionIriOnlyRelation', 'iterableIri', 'toOneRelation'])
×
238
        );
×
239

240
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
241

242
        // BC layer for api-platform/metadata < 4.1
243
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
244
            $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'propertyCollectionIriOnlyRelation', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
245
                (new ApiProperty())->withReadable(true)->withUriTemplate('/property-collection-relations')->withBuiltinTypes([
×
246
                    new LegacyType('iterable', false, null, true, new LegacyType('int', false, null, false), new LegacyType('object', false, PropertyCollectionIriOnlyRelation::class, false)),
×
UNCOV
247
                ])
×
248
            );
×
UNCOV
249
            $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'iterableIri', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
250
                (new ApiProperty())->withReadable(true)->withUriTemplate('/parent/{parentId}/another-collection-operations')->withBuiltinTypes([
×
251
                    new LegacyType('iterable', false, null, true, new LegacyType('int', false, null, false), new LegacyType('object', false, PropertyCollectionIriOnlyRelation::class, false)),
×
UNCOV
252
                ])
×
253
            );
×
254
            $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'toOneRelation', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
UNCOV
255
                (new ApiProperty())->withReadable(true)->withUriTemplate('/parent/{parentId}/another-collection-operations/{id}')->withBuiltinTypes([
×
256
                    new LegacyType('object', false, PropertyCollectionIriOnlyRelation::class, false),
×
257
                ])
×
UNCOV
258
            );
×
259
        } else {
260
            $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'propertyCollectionIriOnlyRelation', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
261
                (new ApiProperty())->withReadable(true)->withUriTemplate('/property-collection-relations')->withNativeType(Type::list(Type::object(PropertyCollectionIriOnlyRelation::class)))
×
UNCOV
262
            );
×
263
            $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'iterableIri', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
264
                (new ApiProperty())->withReadable(true)->withUriTemplate('/parent/{parentId}/another-collection-operations')->withNativeType(Type::iterable(Type::object(PropertyCollectionIriOnlyRelation::class)))
×
265
            );
×
266
            $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'toOneRelation', ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
267
                (new ApiProperty())->withReadable(true)->withUriTemplate('/parent/{parentId}/another-collection-operations/{id}')->withNativeType(Type::object(PropertyCollectionIriOnlyRelation::class))
×
UNCOV
268
            );
×
269
        }
270

271
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
272
        $iriConverterProphecy->getIriFromResource($propertyCollectionIriOnly, UrlGeneratorInterface::ABS_URL, null, Argument::any())->willReturn('/property-collection-relations', '/parent/42/another-collection-operations');
×
UNCOV
273
        $iriConverterProphecy->getIriFromResource($propertyCollectionIriOnly, UrlGeneratorInterface::ABS_PATH, Argument::type(GetCollection::class), Argument::any())->willReturn('/property-collection-relations', '/parent/42/another-collection-operations');
×
UNCOV
274
        $iriConverterProphecy->getIriFromResource($propertyCollectionIriOnly, UrlGeneratorInterface::ABS_PATH, Argument::type(Get::class), Argument::any())->willReturn('/parent/42/another-collection-operations/24');
×
275

UNCOV
276
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
277
        $propertyAccessorProphecy->getValue($propertyCollectionIriOnly, 'propertyCollectionIriOnlyRelation')->willReturn([$propertyCollectionIriOnlyRelation]);
×
278
        $propertyAccessorProphecy->getValue($propertyCollectionIriOnly, 'iterableIri')->willReturn($propertyCollectionIriOnlyRelation);
×
279
        $propertyAccessorProphecy->getValue($propertyCollectionIriOnly, 'toOneRelation')->willReturn($propertyCollectionIriOnlyRelation);
×
280

UNCOV
281
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
282

283
        $resourceClassResolverProphecy->isResourceClass(PropertyCollectionIriOnly::class)->willReturn(true);
×
UNCOV
284
        $resourceClassResolverProphecy->getResourceClass(null, PropertyCollectionIriOnly::class)->willReturn(PropertyCollectionIriOnly::class);
×
285

286
        $resourceClassResolverProphecy->isResourceClass(PropertyCollectionIriOnlyRelation::class)->willReturn(true);
×
287
        $resourceClassResolverProphecy->getResourceClass([$propertyCollectionIriOnlyRelation], PropertyCollectionIriOnlyRelation::class)->willReturn(PropertyCollectionIriOnlyRelation::class);
×
288

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

UNCOV
292
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
293
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
294
        $normalizer->setSerializer($serializerProphecy->reveal());
×
295

UNCOV
296
        $expected = [
×
297
            'propertyCollectionIriOnlyRelation' => '/property-collection-relations',
×
298
            'iterableIri' => '/parent/42/another-collection-operations',
×
UNCOV
299
            'toOneRelation' => '/parent/42/another-collection-operations/24',
×
300
        ];
×
301

302
        $this->assertSame($expected, $normalizer->normalize($propertyCollectionIriOnly, 'jsonld', [
×
303
            'resources' => [],
×
304
            'root_operation' => new GetCollection('/property_collection_iri_onlies'),
×
305
        ]));
×
306
    }
307

308
    public function testDenormalizeWithSecuredProperty(): void
309
    {
310
        $data = [
×
UNCOV
311
            'title' => 'foo',
×
312
            'adminOnlyProperty' => 'secret',
×
UNCOV
313
        ];
×
314

315
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
UNCOV
316
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'adminOnlyProperty']));
×
317

UNCOV
318
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
319

320
        // BC layer for api-platform/metadata < 4.1
321
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
322
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
323
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
324
        } else {
325
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
326
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
327
        }
328

329
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
330

UNCOV
331
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
332

UNCOV
333
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
334
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
UNCOV
335
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
336

337
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
338
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
339

340
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
341
        $resourceAccessChecker->isGranted(
×
UNCOV
342
            SecuredDummy::class,
×
343
            'is_granted(\'ROLE_ADMIN\')',
×
344
            ['object' => null, 'property' => 'adminOnlyProperty']
×
345
        )->willReturn(false);
×
346

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

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

UNCOV
352
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
353

UNCOV
354
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
355
        $propertyAccessorProphecy->setValue($actual, 'adminOnlyProperty', 'secret')->shouldNotHaveBeenCalled();
×
356
    }
357

358
    public function testDenormalizeCreateWithDeniedPostDenormalizeSecuredProperty(): void
359
    {
UNCOV
360
        $data = [
×
UNCOV
361
            'title' => 'foo',
×
UNCOV
362
            'ownerOnlyProperty' => 'should not be set',
×
UNCOV
363
        ];
×
364

UNCOV
365
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
366
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'ownerOnlyProperty']));
×
367

368
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
369

370
        // BC layer for api-platform/metadata < 4.1
371
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
372
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
UNCOV
373
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurityPostDenormalize('false')->withDefault(''));
×
374
        } else {
375
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
376
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withWritable(true)->withSecurityPostDenormalize('false')->withDefault(''));
×
377
        }
378

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

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

383
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::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
            'false',
×
394
            Argument::any()
×
395
        )->willReturn(false);
×
396

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

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

402
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
403

UNCOV
404
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
405
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldHaveBeenCalled();
×
406
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', '')->shouldHaveBeenCalled();
×
407
    }
408

409
    public function testDenormalizeUpdateWithSecuredProperty(): void
410
    {
411
        $dummy = new SecuredDummy();
×
412

UNCOV
413
        $data = [
×
UNCOV
414
            'title' => 'foo',
×
UNCOV
415
            'ownerOnlyProperty' => 'secret',
×
416
        ];
×
417

UNCOV
418
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
419
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'ownerOnlyProperty']));
×
420

421
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
422

423
        // BC layer for api-platform/metadata < 4.1
424
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
425
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
UNCOV
426
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('true'));
×
427
        } else {
428
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
429
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('true'));
×
430
        }
431

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

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

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

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

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

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

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

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

464
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
UNCOV
465
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'secret')->shouldHaveBeenCalled();
×
466
    }
467

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

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

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

481
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
482

483
        // BC layer for api-platform/metadata < 4.1
484
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
485
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
486
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('false'));
×
487
        } else {
UNCOV
488
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
489
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('false'));
×
490
        }
491

492
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
493

494
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
495

UNCOV
496
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
497
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
498
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
499
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
500

501
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
502
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
503

504
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
505
        $resourceAccessChecker->isGranted(
×
UNCOV
506
            SecuredDummy::class,
×
507
            'false',
×
508
            ['object' => null, 'property' => 'ownerOnlyProperty']
×
UNCOV
509
        )->willReturn(false);
×
510
        $resourceAccessChecker->isGranted(
×
UNCOV
511
            SecuredDummy::class,
×
512
            'false',
×
513
            ['object' => $dummy, 'property' => 'ownerOnlyProperty']
×
514
        )->willReturn(false);
×
515

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

519
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
UNCOV
520
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
521

522
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
523

UNCOV
524
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
525
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldNotHaveBeenCalled();
×
526
    }
527

528
    public function testDenormalizeUpdateWithDeniedPostDenormalizeSecuredProperty(): void
529
    {
530
        $dummy = new SecuredDummy();
×
531
        $dummy->setOwnerOnlyProperty('secret');
×
532

533
        $data = [
×
534
            'title' => 'foo',
×
535
            'ownerOnlyProperty' => 'should not be set',
×
UNCOV
536
        ];
×
537

538
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
UNCOV
539
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'ownerOnlyProperty']));
×
540

541
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
542

543
        // BC layer for api-platform/metadata < 4.1
544
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
545
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
546
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurityPostDenormalize('false'));
×
547
        } else {
548
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
549
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withWritable(true)->withSecurityPostDenormalize('false'));
×
550
        }
551

552
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
553

554
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
555
        $propertyAccessorProphecy->getValue($dummy, 'ownerOnlyProperty')->willReturn('secret');
×
556

557
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
558
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
559
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
560
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
561

562
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
UNCOV
563
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
564

565
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
UNCOV
566
        $resourceAccessChecker->isGranted(
×
567
            SecuredDummy::class,
×
568
            'false',
×
569
            ['object' => $dummy, 'previous_object' => $dummy, 'property' => 'ownerOnlyProperty']
×
570
        )->willReturn(false);
×
571

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

UNCOV
575
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
UNCOV
576
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
577

UNCOV
578
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
579

UNCOV
580
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
581
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldHaveBeenCalled();
×
582
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'secret')->shouldHaveBeenCalled();
×
583
    }
584

585
    public function testNormalizeReadableLinks(): void
586
    {
587
        $relatedDummy = new RelatedDummy();
×
588

589
        $dummy = new Dummy();
×
590
        $dummy->setRelatedDummy($relatedDummy);
×
UNCOV
591
        $dummy->relatedDummies->add(new RelatedDummy());
×
592

593
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
594

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

598
        // BC layer for api-platform/metadata < 4.1
599
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
600
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
601
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
602

603
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
604
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(true)->withWritable(false)->withReadableLink(true));
×
605
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(true));
×
606
        } else {
607
            $relatedDummyType = Type::object(RelatedDummy::class);
×
UNCOV
608
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
609

610
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
611
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withReadable(true)->withWritable(false)->withReadableLink(true));
×
612
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(true)->withWritable(false)->withReadableLink(true));
×
613
        }
614

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

618
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
619
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
UNCOV
620
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
621

622
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
623
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
624
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
625
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
626
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
UNCOV
627
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
UNCOV
628
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
629

UNCOV
630
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
631
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
632
        $relatedDummyChildContext = Argument::allOf(
×
633
            Argument::type('array'),
×
634
            Argument::withEntry('resource_class', RelatedDummy::class),
×
635
            Argument::not(Argument::withKey('iri')),
×
UNCOV
636
            Argument::not(Argument::withKey('force_resource_class'))
×
637
        );
×
638
        $serializerProphecy->normalize($relatedDummy, null, $relatedDummyChildContext)->willReturn(['foo' => 'hello']);
×
UNCOV
639
        $serializerProphecy->normalize(['foo' => 'hello'], null, Argument::type('array'))->willReturn(['foo' => 'hello']);
×
640
        $serializerProphecy->normalize([['foo' => 'hello']], null, Argument::type('array'))->willReturn([['foo' => 'hello']]);
×
641

UNCOV
642
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
643
        $normalizer->setSerializer($serializerProphecy->reveal());
×
644

UNCOV
645
        $expected = [
×
646
            'relatedDummy' => ['foo' => 'hello'],
×
647
            'relatedDummies' => [['foo' => 'hello']],
×
UNCOV
648
        ];
×
649
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
650
            'resources' => [],
×
UNCOV
651
            'force_resource_class' => Dummy::class,
×
652
        ]));
×
653
    }
654

655
    public function testNormalizePolymorphicRelations(): void
656
    {
UNCOV
657
        $concreteDummy = new DummyTableInheritanceChild();
×
658

659
        $dummy = new DummyTableInheritanceRelated();
×
660
        $dummy->addChild($concreteDummy);
×
661

662
        $abstractDummies = new ArrayCollection([$concreteDummy]);
×
663

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

667
        // BC layer for api-platform/metadata < 4.1
668
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
669
            $abstractDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, DummyTableInheritance::class);
×
670
            $abstractDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $abstractDummyType);
×
671

672
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
673
            $propertyMetadataFactoryProphecy->create(DummyTableInheritanceRelated::class, 'children', [])->willReturn((new ApiProperty())->withBuiltinTypes([$abstractDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(true));
×
674
        } else {
675
            $abstractDummyType = Type::object(DummyTableInheritance::class);
×
676
            $abstractDummiesType = Type::collection(Type::object(ArrayCollection::class), $abstractDummyType, Type::int());
×
677

UNCOV
678
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
679
            $propertyMetadataFactoryProphecy->create(DummyTableInheritanceRelated::class, 'children', [])->willReturn((new ApiProperty())->withNativeType($abstractDummiesType)->withReadable(true)->withWritable(false)->withReadableLink(true));
×
680
        }
681

UNCOV
682
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
UNCOV
683
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
×
684

UNCOV
685
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
UNCOV
686
        $propertyAccessorProphecy->getValue($dummy, 'children')->willReturn($abstractDummies);
×
687

UNCOV
688
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
689
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(DummyTableInheritanceRelated::class);
×
UNCOV
690
        $resourceClassResolverProphecy->getResourceClass(null, DummyTableInheritanceRelated::class)->willReturn(DummyTableInheritanceRelated::class);
×
UNCOV
691
        $resourceClassResolverProphecy->getResourceClass($concreteDummy, DummyTableInheritance::class)->willReturn(DummyTableInheritanceChild::class);
×
UNCOV
692
        $resourceClassResolverProphecy->getResourceClass($abstractDummies, DummyTableInheritance::class)->willReturn(DummyTableInheritance::class);
×
UNCOV
693
        $resourceClassResolverProphecy->isResourceClass(DummyTableInheritanceRelated::class)->willReturn(true);
×
UNCOV
694
        $resourceClassResolverProphecy->isResourceClass(DummyTableInheritance::class)->willReturn(true);
×
695

UNCOV
696
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
UNCOV
697
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
UNCOV
698
        $concreteDummyChildContext = Argument::allOf(
×
UNCOV
699
            Argument::type('array'),
×
UNCOV
700
            Argument::not(Argument::withKey('iri'))
×
UNCOV
701
        );
×
UNCOV
702
        $serializerProphecy->normalize($concreteDummy, null, $concreteDummyChildContext)->willReturn(['foo' => 'concrete']);
×
UNCOV
703
        $serializerProphecy->normalize([['foo' => 'concrete']], null, Argument::type('array'))->willReturn([['foo' => 'concrete']]);
×
704

UNCOV
705
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
UNCOV
706
        $normalizer->setSerializer($serializerProphecy->reveal());
×
707

UNCOV
708
        $expected = [
×
UNCOV
709
            'children' => [['foo' => 'concrete']],
×
UNCOV
710
        ];
×
UNCOV
711
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
UNCOV
712
            'resources' => [],
×
UNCOV
713
        ]));
×
714
    }
715

716
    public function testDenormalize(): void
717
    {
UNCOV
718
        $data = [
×
UNCOV
719
            'name' => 'foo',
×
UNCOV
720
            'relatedDummy' => '/dummies/1',
×
UNCOV
721
            'relatedDummies' => ['/dummies/2'],
×
UNCOV
722
        ];
×
723

UNCOV
724
        $relatedDummy1 = new RelatedDummy();
×
UNCOV
725
        $relatedDummy2 = new RelatedDummy();
×
726

UNCOV
727
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
UNCOV
728
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['name', 'relatedDummy', 'relatedDummies']));
×
729

730
        // BC layer for api-platform/metadata < 4.1
UNCOV
731
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
732
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
UNCOV
733
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
734

UNCOV
735
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
736
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
737

738
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
739
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
740
        } else {
741
            $relatedDummyType = Type::object(RelatedDummy::class);
×
742
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
743

UNCOV
744
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
745
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
746

747
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
748
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
749
        }
750

751
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
UNCOV
752
        $iriConverterProphecy->getResourceFromIri('/dummies/1', Argument::type('array'))->willReturn($relatedDummy1);
×
753
        $iriConverterProphecy->getResourceFromIri('/dummies/2', Argument::type('array'))->willReturn($relatedDummy2);
×
754

755
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
756

UNCOV
757
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
758
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
759
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
760
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
761
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
762

UNCOV
763
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
764
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
765

766
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
UNCOV
767
        $normalizer->setSerializer($serializerProphecy->reveal());
×
768

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

771
        $this->assertInstanceOf(Dummy::class, $actual);
×
772

UNCOV
773
        $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
×
774
        $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
×
775
        $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
×
776
    }
777

778
    public function testCanDenormalizeInputClassWithDifferentFieldsThanResourceClass(): void
779
    {
UNCOV
780
        $this->markTestSkipped('TODO: check why this test has been commented');
×
781

782
        // $data = [
783
        //     'dummyName' => 'Dummy Name',
784
        // ];
785
        //
786
        // $context = [
787
        //     'resource_class' => DummyForAdditionalFields::class,
788
        //     'input' => ['class' => DummyForAdditionalFieldsInput::class],
789
        //     'output' => ['class' => DummyForAdditionalFields::class],
790
        // ];
791
        // $augmentedContext = $context + ['api_denormalize' => true];
792
        //
793
        // $preHydratedDummy = new DummyForAdditionalFieldsInput('Name Dummy');
794
        // $cleanedContext = array_diff_key($augmentedContext, [
795
        //     'input' => null,
796
        //     'resource_class' => null,
797
        // ]);
798
        // $cleanedContextWithObjectToPopulate = array_merge($cleanedContext, [
799
        //     AbstractObjectNormalizer::OBJECT_TO_POPULATE => $preHydratedDummy,
800
        //     AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE => true,
801
        // ]);
802
        //
803
        // $dummyInputDto = new DummyForAdditionalFieldsInput('Dummy Name');
804
        // $dummy = new DummyForAdditionalFields('Dummy Name', 'name-dummy');
805
        //
806
        // $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
807
        //
808
        // $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
809
        //
810
        // $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
811
        //
812
        // $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
813
        // $resourceClassResolverProphecy->getResourceClass(null, DummyForAdditionalFields::class)->willReturn(DummyForAdditionalFields::class);
814
        //
815
        // $inputDataTransformerProphecy = $this->prophesize(DataTransformerInitializerInterface::class);
816
        // $inputDataTransformerProphecy->willImplement(DataTransformerInitializerInterface::class);
817
        // $inputDataTransformerProphecy->initialize(DummyForAdditionalFieldsInput::class, $cleanedContext)->willReturn($preHydratedDummy);
818
        // $inputDataTransformerProphecy->supportsTransformation($data, DummyForAdditionalFields::class, $augmentedContext)->willReturn(true);
819
        // $inputDataTransformerProphecy->transform($dummyInputDto, DummyForAdditionalFields::class, $augmentedContext)->willReturn($dummy);
820
        //
821
        // $serializerProphecy = $this->prophesize(SerializerInterface::class);
822
        // $serializerProphecy->willImplement(DenormalizerInterface::class);
823
        // $serializerProphecy->denormalize($data, DummyForAdditionalFieldsInput::class, 'json', $cleanedContextWithObjectToPopulate)->willReturn($dummyInputDto);
824
        //
825
        // $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), null, null, null, [], null, null) extends AbstractItemNormalizer {
826
        // };
827
        // $normalizer->setSerializer($serializerProphecy->reveal());
828
        //
829
        // $actual = $normalizer->denormalize($data, DummyForAdditionalFields::class, 'json', $context);
830
        //
831
        // $this->assertInstanceOf(DummyForAdditionalFields::class, $actual);
832
        // $this->assertSame('Dummy Name', $actual->getName());
833
    }
834

835
    public function testDenormalizeWritableLinks(): void
836
    {
837
        $data = [
×
UNCOV
838
            'name' => 'foo',
×
839
            'relatedDummy' => ['foo' => 'bar'],
×
840
            'relatedDummies' => [['bar' => 'baz']],
×
841
            'relatedDummiesWithUnionTypes' => [0 => ['bar' => 'qux'], 1. => ['bar' => 'quux']],
×
842
        ];
×
843

844
        $relatedDummy1 = new RelatedDummy();
×
UNCOV
845
        $relatedDummy2 = new RelatedDummy();
×
846
        $relatedDummy3 = new RelatedDummy();
×
847
        $relatedDummy4 = new RelatedDummy();
×
848

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

852
        // BC layer for api-platform/metadata < 4.1
UNCOV
853
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
854
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
855
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
UNCOV
856
            $relatedDummiesWithUnionTypesIntType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
857
            $relatedDummiesWithUnionTypesFloatType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT), $relatedDummyType);
×
858

UNCOV
859
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
860
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
861
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
862
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
UNCOV
863
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummiesWithUnionTypes', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesWithUnionTypesIntType, $relatedDummiesWithUnionTypesFloatType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
864
        } else {
UNCOV
865
            $relatedDummyType = Type::object(RelatedDummy::class);
×
UNCOV
866
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
867
            $relatedDummiesWithUnionTypesIntType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
868
            $relatedDummiesWithUnionTypesFloatType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::float());
×
869

UNCOV
870
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
871
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
872
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
UNCOV
873
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
874
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummiesWithUnionTypes', [])->willReturn((new ApiProperty())->withNativeType(Type::union($relatedDummiesWithUnionTypesIntType, $relatedDummiesWithUnionTypesFloatType))->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
875
        }
876

877
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
878

879
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
880

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

888
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
UNCOV
889
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
890
        $serializerProphecy->denormalize(['foo' => 'bar'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy1);
×
891
        $serializerProphecy->denormalize(['bar' => 'baz'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy2);
×
UNCOV
892
        $serializerProphecy->denormalize(['bar' => 'qux'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy3);
×
893
        $serializerProphecy->denormalize(['bar' => 'quux'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy4);
×
894

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

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

900
        $this->assertInstanceOf(Dummy::class, $actual);
×
901

902
        $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
×
903
        $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
×
904
        $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
×
905
        $propertyAccessorProphecy->setValue($actual, 'relatedDummiesWithUnionTypes', [0 => $relatedDummy3, 1. => $relatedDummy4])->shouldHaveBeenCalled();
×
906
    }
907

908
    public function testBadRelationType(): void
909
    {
910
        $this->expectException(NotNormalizableValueException::class);
×
911
        $this->expectExceptionMessage('The type of the "relatedDummy" attribute must be "array" (nested document) or "string" (IRI), "integer" given.');
×
912

UNCOV
913
        $data = [
×
UNCOV
914
            'relatedDummy' => 22,
×
UNCOV
915
        ];
×
916

917
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
UNCOV
918
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
×
919

UNCOV
920
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
921

922
        // BC layer for api-platform/metadata < 4.1
923
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
924
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
UNCOV
925
                (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
926
            );
×
927
        } else {
928
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
UNCOV
929
                (new ApiProperty())->withNativeType(Type::object(RelatedDummy::class))->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
930
            );
×
931
        }
932

933
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
934

935
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
936
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
937
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
938
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
939
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
940

941
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
942

943
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
944
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
945

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

949
        $normalizer->denormalize($data, Dummy::class);
×
950
    }
951

952
    public function testBadRelationTypeWithExceptionToValidationErrors(): void
953
    {
954
        $data = [
×
955
            'relatedDummy' => 22,
×
UNCOV
956
        ];
×
957

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

961
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
962

963
        // BC layer for api-platform/metadata < 4.1
964
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
965
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
966
                (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
967
            );
×
968
        } else {
969
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
UNCOV
970
                (new ApiProperty())->withNativeType(Type::object(RelatedDummy::class))->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
971
            );
×
972
        }
973

974
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
975

976
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
977
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
UNCOV
978
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
979
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
UNCOV
980
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
981

982
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
983

984
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
985
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
986

987
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
UNCOV
988
        $normalizer->setSerializer($serializerProphecy->reveal());
×
989

990
        // 'not_normalizable_value_exceptions' is set by Serializer thanks to DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS
UNCOV
991
        $actual = $normalizer->denormalize($data, Dummy::class, null, ['not_normalizable_value_exceptions' => []]);
×
992
        $this->assertNull($actual->relatedDummy);
×
993
    }
994

995
    public function testDeserializationPathForNotDenormalizableRelations(): void
996
    {
997
        $data = [
×
UNCOV
998
            'relatedDummies' => ['wrong'],
×
999
        ];
×
1000

UNCOV
1001
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1002
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']));
×
1003

UNCOV
1004
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1005

1006
        // BC layer for api-platform/metadata < 4.1
1007
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1008
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
×
1009
                (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, null, new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class))])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true)
×
UNCOV
1010
            );
×
1011
        } else {
UNCOV
1012
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
×
1013
                (new ApiProperty())->withNativeType(Type::collection(Type::object(ArrayCollection::class), Type::object(RelatedDummy::class)))->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true)
×
1014
            );
×
1015
        }
1016

1017
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
UNCOV
1018
        $iriConverterProphecy->getResourceFromIri(Argument::cetera())->willThrow(new InvalidArgumentException('Invalid IRI'));
×
1019

UNCOV
1020
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
1021
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
UNCOV
1022
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
UNCOV
1023
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1024
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1025

1026
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1027

1028
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1029
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1030

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

1034
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1035

1036
        $errors = [];
×
1037
        $actual = $normalizer->denormalize($data, Dummy::class, null, ['not_normalizable_value_exceptions' => &$errors]);
×
1038
        $this->assertEmpty($actual->relatedDummies);
×
UNCOV
1039
        $this->assertCount(1, $errors); // @phpstan-ignore-line method.impossibleType (false positive)
×
1040
        $this->assertInstanceOf(NotNormalizableValueException::class, $errors[0]);
×
UNCOV
1041
        $this->assertSame('relatedDummies[0]', $errors[0]->getPath());
×
1042
    }
1043

1044
    public function testDeserializationPathForNotDenormalizableResource(): void
1045
    {
1046
        $this->expectException(NotNormalizableValueException::class);
×
1047

1048
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1049

1050
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1051

1052
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
UNCOV
1053
        $iriConverterProphecy->getResourceFromIri(Argument::cetera())->willThrow(new InvalidArgumentException('Invalid IRI'));
×
1054

UNCOV
1055
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
1056
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1057
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1058

1059
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1060

1061
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1062
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1063

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

1078
        $normalizer->denormalize('wrong IRI', Dummy::class, null, ['not_normalizable_value_exceptions' => []]);
×
1079
    }
1080

1081
    public function testInnerDocumentNotAllowed(): void
1082
    {
1083
        $this->expectException(UnexpectedValueException::class);
×
UNCOV
1084
        $this->expectExceptionMessage('Nested documents for attribute "relatedDummy" are not allowed. Use IRIs instead.');
×
1085

UNCOV
1086
        $data = [
×
UNCOV
1087
            'relatedDummy' => [
×
UNCOV
1088
                'foo' => 'bar',
×
UNCOV
1089
            ],
×
1090
        ];
×
1091

UNCOV
1092
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1093
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummy']));
×
1094

1095
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1096

1097
        // BC layer for api-platform/metadata < 4.1
1098
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1099
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
1100
                (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
1101
            );
×
1102
        } else {
1103
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
1104
                (new ApiProperty())->withNativeType(Type::object(RelatedDummy::class))->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
1105
            );
×
1106
        }
1107

1108
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1109

1110
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1111
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
UNCOV
1112
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1113
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1114
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1115

UNCOV
1116
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1117

1118
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1119
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1120

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

1124
        $normalizer->denormalize($data, Dummy::class);
×
1125
    }
1126

1127
    public function testBadType(): void
1128
    {
1129
        $this->expectException(UnexpectedValueException::class);
×
1130
        $this->expectExceptionMessage('The type of the "foo" attribute must be "float", "integer" given.');
×
1131

1132
        $data = [
×
1133
            'foo' => 42,
×
UNCOV
1134
        ];
×
1135

UNCOV
1136
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1137
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['foo']));
×
1138

UNCOV
1139
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1140

1141
        // BC layer for api-platform/metadata < 4.1
UNCOV
1142
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1143
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1144
        } else {
UNCOV
1145
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1146
        }
1147

1148
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1149

1150
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
1151
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1152
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1153

UNCOV
1154
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1155

1156
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
UNCOV
1157
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1158

UNCOV
1159
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1160
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1161

1162
        $normalizer->denormalize($data, Dummy::class);
×
1163
    }
1164

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

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

1174
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1175

1176
        // BC layer for api-platform/metadata < 4.1
UNCOV
1177
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
1178
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1179
        } else {
UNCOV
1180
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1181
        }
1182

1183
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1184

1185
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1186
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1187
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1188

1189
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1190

1191
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1192
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1193

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

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

1199
        $this->assertInstanceOf(Dummy::class, $actual);
×
1200

1201
        $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
×
1202
    }
1203

1204
    public function testJsonAllowIntAsFloat(): void
1205
    {
1206
        $data = [
×
1207
            'foo' => 42,
×
1208
        ];
×
1209

UNCOV
1210
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1211
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['foo']));
×
1212

1213
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1214

1215
        // BC layer for api-platform/metadata < 4.1
1216
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1217
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1218
        } else {
1219
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1220
        }
1221

1222
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1223

1224
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1225
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
UNCOV
1226
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1227

1228
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1229

UNCOV
1230
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1231
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1232

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

UNCOV
1236
        $actual = $normalizer->denormalize($data, Dummy::class, 'jsonfoo');
×
1237

1238
        $this->assertInstanceOf(Dummy::class, $actual);
×
1239

1240
        $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
×
1241
    }
1242

1243
    public function testDenormalizeBadKeyType(): void
1244
    {
1245
        $this->expectException(NotNormalizableValueException::class);
×
1246
        $this->expectExceptionMessage('The type of the key "a" must be "int", "string" given.');
×
1247

1248
        $data = [
×
1249
            'name' => 'foo',
×
1250
            'relatedDummy' => [
×
1251
                'foo' => 'bar',
×
1252
            ],
×
1253
            'relatedDummies' => [
×
1254
                'a' => [
×
UNCOV
1255
                    'bar' => 'baz',
×
1256
                ],
×
UNCOV
1257
            ],
×
UNCOV
1258
            'relatedDummiesWithUnionTypes' => [
×
UNCOV
1259
                'a' => [
×
UNCOV
1260
                    'bar' => 'baz',
×
1261
                ],
×
1262
            ],
×
1263
        ];
×
1264

1265
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
UNCOV
1266
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']));
×
1267

1268
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1269

1270
        // BC layer for api-platform/metadata < 4.1
1271
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
1272
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
1273
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1274

UNCOV
1275
            $type = new LegacyType(
×
1276
                LegacyType::BUILTIN_TYPE_OBJECT,
×
1277
                false,
×
UNCOV
1278
                ArrayCollection::class,
×
1279
                true,
×
UNCOV
1280
                new LegacyType(LegacyType::BUILTIN_TYPE_INT),
×
1281
                new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)
×
1282
            );
×
UNCOV
1283
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$type])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1284
        } else {
1285
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
1286
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withNativeType(Type::object(RelatedDummy::class))->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1287

1288
            $type = Type::collection(Type::object(ArrayCollection::class), Type::object(RelatedDummy::class), Type::int());
×
UNCOV
1289
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($type)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1290
        }
1291

1292
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1293

1294
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1295
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
UNCOV
1296
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1297
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
UNCOV
1298
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1299

UNCOV
1300
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1301

1302
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1303
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1304

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

UNCOV
1308
        $normalizer->denormalize($data, Dummy::class);
×
1309
    }
1310

1311
    public function testNullable(): void
1312
    {
1313
        $data = [
×
UNCOV
1314
            'name' => null,
×
1315
        ];
×
1316

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

1320
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1321

1322
        // BC layer for api-platform/metadata < 4.1
1323
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
1324
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING, true)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1325
        } else {
1326
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::nullable(Type::string()))->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)); // @phpstan-ignore-line
×
1327
        }
1328

1329
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1330

UNCOV
1331
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1332
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1333
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1334

UNCOV
1335
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1336

UNCOV
1337
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
UNCOV
1338
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1339

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

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

UNCOV
1345
        $this->assertInstanceOf(Dummy::class, $actual);
×
1346

1347
        $propertyAccessorProphecy->setValue($actual, 'name', null)->shouldHaveBeenCalled();
×
1348
    }
1349

1350
    public function testDenormalizeBasicTypePropertiesFromXml(): void
1351
    {
1352
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1353
        $propertyNameCollectionFactoryProphecy->create(ObjectWithBasicProperties::class, [])->willReturn(new PropertyNameCollection([
×
UNCOV
1354
            'boolTrue1',
×
1355
            'boolFalse1',
×
1356
            'boolTrue2',
×
UNCOV
1357
            'boolFalse2',
×
1358
            'int1',
×
1359
            'int2',
×
UNCOV
1360
            'float1',
×
1361
            'float2',
×
1362
            'float3',
×
UNCOV
1363
            'floatNaN',
×
1364
            'floatInf',
×
1365
            'floatNegInf',
×
UNCOV
1366
        ]));
×
1367

UNCOV
1368
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1369

1370
        // BC layer for api-platform/metadata < 4.1
UNCOV
1371
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1372
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1373
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1374
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1375
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1376
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)])->withDescription('')->withReadable(false)->withWritable(true));
×
UNCOV
1377
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1378
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
UNCOV
1379
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1380
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float3', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1381
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNaN', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
UNCOV
1382
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatInf', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1383
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNegInf', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1384
        } else {
UNCOV
1385
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue1', [])->willReturn((new ApiProperty())->withNativeType(Type::bool())->withDescription('')->withReadable(false)->withWritable(true));
×
1386
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse1', [])->willReturn((new ApiProperty())->withNativeType(Type::bool())->withDescription('')->withReadable(false)->withWritable(true));
×
1387
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue2', [])->willReturn((new ApiProperty())->withNativeType(Type::bool())->withDescription('')->withReadable(false)->withWritable(true));
×
1388
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse2', [])->willReturn((new ApiProperty())->withNativeType(Type::bool())->withDescription('')->withReadable(false)->withWritable(true));
×
1389
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int1', [])->willReturn((new ApiProperty())->withNativeType(Type::int())->withDescription('')->withReadable(false)->withWritable(true));
×
1390
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int2', [])->willReturn((new ApiProperty())->withNativeType(Type::int())->withDescription('')->withReadable(false)->withWritable(true));
×
UNCOV
1391
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float1', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1392
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float2', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1393
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float3', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1394
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNaN', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
UNCOV
1395
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatInf', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1396
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNegInf', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1397
        }
1398

1399
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1400

1401
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1402
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolTrue1', true)->shouldBeCalled();
×
1403
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolFalse1', false)->shouldBeCalled();
×
1404
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolTrue2', true)->shouldBeCalled();
×
1405
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolFalse2', false)->shouldBeCalled();
×
1406
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'int1', 4711)->shouldBeCalled();
×
1407
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'int2', -4711)->shouldBeCalled();
×
UNCOV
1408
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float1', Argument::approximate(123.456, 0))->shouldBeCalled();
×
1409
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float2', Argument::approximate(-1.2344e56, 1))->shouldBeCalled();
×
1410
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float3', Argument::approximate(45E-6, 1))->shouldBeCalled();
×
1411
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatNaN', Argument::that(static fn (float $arg) => is_nan($arg)))->shouldBeCalled();
×
1412
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatInf', \INF)->shouldBeCalled();
×
UNCOV
1413
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatNegInf', -\INF)->shouldBeCalled();
×
1414

1415
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
1416
        $resourceClassResolverProphecy->getResourceClass(null, ObjectWithBasicProperties::class)->willReturn(ObjectWithBasicProperties::class);
×
1417
        $resourceClassResolverProphecy->isResourceClass(ObjectWithBasicProperties::class)->willReturn(true);
×
1418

1419
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1420
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1421

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

1425
        $objectWithBasicProperties = $normalizer->denormalize(
×
1426
            [
×
1427
                'boolTrue1' => 'true',
×
1428
                'boolFalse1' => 'false',
×
UNCOV
1429
                'boolTrue2' => '1',
×
1430
                'boolFalse2' => '0',
×
1431
                'int1' => '4711',
×
1432
                'int2' => '-4711',
×
UNCOV
1433
                'float1' => '123.456',
×
UNCOV
1434
                'float2' => '-1.2344e56',
×
UNCOV
1435
                'float3' => '45E-6',
×
UNCOV
1436
                'floatNaN' => 'NaN',
×
UNCOV
1437
                'floatInf' => 'INF',
×
UNCOV
1438
                'floatNegInf' => '-INF',
×
UNCOV
1439
            ],
×
UNCOV
1440
            ObjectWithBasicProperties::class,
×
UNCOV
1441
            'xml'
×
UNCOV
1442
        );
×
1443

UNCOV
1444
        $this->assertInstanceOf(ObjectWithBasicProperties::class, $objectWithBasicProperties);
×
1445
    }
1446

1447
    public function testDenormalizeCollectionDecodedFromXmlWithOneChild(): void
1448
    {
UNCOV
1449
        $data = [
×
UNCOV
1450
            'relatedDummies' => [
×
UNCOV
1451
                'name' => 'foo',
×
UNCOV
1452
            ],
×
UNCOV
1453
        ];
×
1454

UNCOV
1455
        $relatedDummy = new RelatedDummy();
×
UNCOV
1456
        $relatedDummy->setName('foo');
×
1457

UNCOV
1458
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
UNCOV
1459
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn(new PropertyNameCollection(['relatedDummies']));
×
1460

1461
        // BC layer for api-platform/metadata < 4.1
UNCOV
1462
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
1463
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
UNCOV
1464
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
1465

UNCOV
1466
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
1467
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1468
        } else {
UNCOV
1469
            $relatedDummyType = Type::object(RelatedDummy::class);
×
UNCOV
1470
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
1471

UNCOV
1472
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
1473
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1474
        }
1475

UNCOV
1476
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1477

UNCOV
1478
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
UNCOV
1479
        $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', Argument::type('array'))->shouldBeCalled();
×
1480

UNCOV
1481
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
1482
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
UNCOV
1483
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
UNCOV
1484
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
UNCOV
1485
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1486

UNCOV
1487
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
UNCOV
1488
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
UNCOV
1489
        $serializerProphecy->denormalize(['name' => 'foo'], RelatedDummy::class, 'xml', Argument::type('array'))->willReturn($relatedDummy);
×
1490

UNCOV
1491
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
UNCOV
1492
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1493

UNCOV
1494
        $normalizer->denormalize($data, Dummy::class, 'xml');
×
1495
    }
1496

1497
    public function testDenormalizePopulatingNonCloneableObject(): void
1498
    {
UNCOV
1499
        $dummy = new NonCloneableDummy();
×
UNCOV
1500
        $dummy->setName('foo');
×
1501

UNCOV
1502
        $data = [
×
UNCOV
1503
            'name' => 'bar',
×
UNCOV
1504
        ];
×
1505

UNCOV
1506
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
UNCOV
1507
        $propertyNameCollectionFactoryProphecy->create(NonCloneableDummy::class, [])->willReturn(new PropertyNameCollection(['name']));
×
1508

UNCOV
1509
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1510

1511
        // BC layer for api-platform/metadata < 4.1
UNCOV
1512
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
1513
            $propertyMetadataFactoryProphecy->create(NonCloneableDummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
1514
        } else {
UNCOV
1515
            $propertyMetadataFactoryProphecy->create(NonCloneableDummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
1516
        }
1517

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

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

UNCOV
1528
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
UNCOV
1529
        $normalizer->setSerializer($serializerProphecy->reveal());
×
UNCOV
1530
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1531

UNCOV
1532
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
UNCOV
1533
        $actual = $normalizer->denormalize($data, NonCloneableDummy::class, null, $context);
×
1534

UNCOV
1535
        $this->assertInstanceOf(NonCloneableDummy::class, $actual);
×
UNCOV
1536
        $this->assertSame($dummy, $actual);
×
UNCOV
1537
        $propertyAccessorProphecy->setValue($actual, 'name', 'bar')->shouldHaveBeenCalled();
×
1538
    }
1539

1540
    public function testDenormalizeObjectWithNullDisabledTypeEnforcement(): void
1541
    {
UNCOV
1542
        $data = [
×
UNCOV
1543
            'dummy' => null,
×
UNCOV
1544
        ];
×
1545

UNCOV
1546
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
UNCOV
1547
        $propertyNameCollectionFactoryProphecy->create(DtoWithNullValue::class, [])->willReturn(new PropertyNameCollection(['dummy']));
×
1548

UNCOV
1549
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1550

1551
        // BC layer for api-platform/metadata < 4.1
UNCOV
1552
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
1553
            $propertyMetadataFactoryProphecy->create(DtoWithNullValue::class, 'dummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, nullable: true)])->withDescription('')->withReadable(true)->withWritable(true));
×
1554
        } else {
UNCOV
1555
            $propertyMetadataFactoryProphecy->create(DtoWithNullValue::class, 'dummy', [])->willReturn((new ApiProperty())->withNativeType(Type::nullable(Type::object()))->withDescription('')->withReadable(true)->withWritable(true)); // @phpstan-ignore-line
×
1556
        }
1557

UNCOV
1558
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
UNCOV
1559
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
UNCOV
1560
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
1561
        $resourceClassResolverProphecy->getResourceClass(null, DtoWithNullValue::class)->willReturn(DtoWithNullValue::class);
×
UNCOV
1562
        $resourceClassResolverProphecy->isResourceClass(DtoWithNullValue::class)->willReturn(true);
×
1563

UNCOV
1564
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
UNCOV
1565
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1566

UNCOV
1567
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
UNCOV
1568
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1569

UNCOV
1570
        $context = [AbstractItemNormalizer::DISABLE_TYPE_ENFORCEMENT => true];
×
UNCOV
1571
        $actual = $normalizer->denormalize($data, DtoWithNullValue::class, null, $context);
×
1572

UNCOV
1573
        $this->assertInstanceOf(DtoWithNullValue::class, $actual);
×
UNCOV
1574
        $this->assertEquals(new DtoWithNullValue(), $actual);
×
1575
    }
1576

1577
    public function testCacheKey(): void
1578
    {
UNCOV
1579
        $relatedDummy = new RelatedDummy();
×
1580

UNCOV
1581
        $dummy = new Dummy();
×
UNCOV
1582
        $dummy->setName('foo');
×
UNCOV
1583
        $dummy->setAlias('ignored');
×
UNCOV
1584
        $dummy->setRelatedDummy($relatedDummy);
×
UNCOV
1585
        $dummy->relatedDummies->add(new RelatedDummy());
×
1586

UNCOV
1587
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
1588

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

1592
        // BC layer for api-platform/metadata < 4.1
UNCOV
1593
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
UNCOV
1594
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
UNCOV
1595
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
1596

UNCOV
1597
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
1598
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
UNCOV
1599
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
UNCOV
1600
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
UNCOV
1601
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(false));
×
1602
        } else {
UNCOV
1603
            $relatedDummyType = Type::object(RelatedDummy::class);
×
UNCOV
1604
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
1605

UNCOV
1606
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
UNCOV
1607
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::type('array'))->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true));
×
UNCOV
1608
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', Argument::type('array'))->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true));
×
UNCOV
1609
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', Argument::type('array'))->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
UNCOV
1610
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::type('array'))->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(true)->withWritable(false)->withReadableLink(false));
×
1611
        }
1612

UNCOV
1613
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
UNCOV
1614
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
×
UNCOV
1615
        $iriConverterProphecy->getIriFromResource($relatedDummy, Argument::cetera())->willReturn('/dummies/2');
×
1616

UNCOV
1617
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
UNCOV
1618
        $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('foo');
×
UNCOV
1619
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
UNCOV
1620
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
1621

UNCOV
1622
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
1623
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
UNCOV
1624
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
UNCOV
1625
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
UNCOV
1626
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
UNCOV
1627
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
UNCOV
1628
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1629

UNCOV
1630
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
UNCOV
1631
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
UNCOV
1632
        $serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo');
×
UNCOV
1633
        $serializerProphecy->normalize(['/dummies/2'], null, Argument::type('array'))->willReturn(['/dummies/2']);
×
1634

UNCOV
1635
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
UNCOV
1636
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1637

UNCOV
1638
        $expected = [
×
UNCOV
1639
            'name' => 'foo',
×
UNCOV
1640
            'relatedDummy' => '/dummies/2',
×
UNCOV
1641
            'relatedDummies' => ['/dummies/2'],
×
UNCOV
1642
        ];
×
UNCOV
1643
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
UNCOV
1644
            'resources' => [],
×
UNCOV
1645
            'groups' => ['group'],
×
UNCOV
1646
            'ignored_attributes' => ['alias'],
×
UNCOV
1647
            'operation_name' => 'operation_name',
×
UNCOV
1648
            'root_operation_name' => 'root_operation_name',
×
UNCOV
1649
        ]));
×
1650

UNCOV
1651
        $operationCacheKey = (new \ReflectionClass($normalizer))->getProperty('localFactoryOptionsCache')->getValue($normalizer);
×
UNCOV
1652
        $this->assertEquals(array_keys($operationCacheKey), [\sprintf('%s%s%s%s', Dummy::class, 'operation_name', 'root_operation_name', 'n')]);
×
UNCOV
1653
        $this->assertEquals(current($operationCacheKey), ['serializer_groups' => ['group']]);
×
1654
    }
1655
}
1656

1657
class ObjectWithBasicProperties
1658
{
1659
    /** @var bool */
1660
    public $boolTrue1;
1661

1662
    /** @var bool */
1663
    public $boolFalse1;
1664

1665
    /** @var bool */
1666
    public $boolTrue2;
1667

1668
    /** @var bool */
1669
    public $boolFalse2;
1670

1671
    /** @var int */
1672
    public $int1;
1673

1674
    /** @var int */
1675
    public $int2;
1676

1677
    /** @var float */
1678
    public $float1;
1679

1680
    /** @var float */
1681
    public $float2;
1682

1683
    /** @var float */
1684
    public $float3;
1685

1686
    /** @var float */
1687
    public $floatNaN;
1688

1689
    /** @var float */
1690
    public $floatInf;
1691

1692
    /** @var float */
1693
    public $floatNegInf;
1694
}
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

© 2026 Coveralls, Inc