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

api-platform / core / 17158226138

22 Aug 2025 02:38PM UTC coverage: 22.203% (-0.006%) from 22.209%
17158226138

Pull #7303

github

web-flow
Merge d35038aad into 9e382e01b
Pull Request #7303: fix(test): replace `Collection|iterable` with `Collection` and add appropriate PHPDoc tags

0 of 20 new or added lines in 8 files covered. (0.0%)

7 existing lines in 7 files now uncovered.

11699 of 52692 relevant lines covered (22.2%)

24.2 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\AccessDeniedException;
19
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
20
use ApiPlatform\Metadata\Exception\ItemNotFoundException;
21
use ApiPlatform\Metadata\Get;
22
use ApiPlatform\Metadata\GetCollection;
23
use ApiPlatform\Metadata\IriConverterInterface;
24
use ApiPlatform\Metadata\Operations;
25
use ApiPlatform\Metadata\Patch;
26
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
27
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
28
use ApiPlatform\Metadata\Property\PropertyNameCollection;
29
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
30
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
31
use ApiPlatform\Metadata\ResourceAccessCheckerInterface;
32
use ApiPlatform\Metadata\ResourceClassResolverInterface;
33
use ApiPlatform\Metadata\UrlGeneratorInterface;
34
use ApiPlatform\Serializer\AbstractItemNormalizer;
35
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DtoWithNullValue;
36
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\Dummy;
37
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DummyTableInheritance;
38
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DummyTableInheritanceChild;
39
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\DummyTableInheritanceRelated;
40
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\NonCloneableDummy;
41
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\PropertyCollectionIriOnly;
42
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\PropertyCollectionIriOnlyRelation;
43
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\RelatedDummy;
44
use ApiPlatform\Serializer\Tests\Fixtures\ApiResource\SecuredDummy;
45
use Doctrine\Common\Collections\ArrayCollection;
46
use Doctrine\Common\Collections\Collection;
47
use PHPUnit\Framework\TestCase;
48
use Prophecy\Argument;
49
use Prophecy\PhpUnit\ProphecyTrait;
50
use Symfony\Component\PropertyAccess\PropertyAccessor;
51
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
52
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
53
use Symfony\Component\PropertyInfo\Type as LegacyType;
54
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
55
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
56
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
57
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
58
use Symfony\Component\Serializer\SerializerInterface;
59
use Symfony\Component\TypeInfo\Type;
60

61
/**
62
 * @author Amrouche Hamza <hamza.simperfit@gmail.com>
63
 * @author Kévin Dunglas <dunglas@gmail.com>
64
 */
65
class AbstractItemNormalizerTest extends TestCase
66
{
67
    use ProphecyTrait;
68

69
    #[\PHPUnit\Framework\Attributes\Group('legacy')]
70
    public function testSupportNormalizationAndSupportDenormalization(): void
71
    {
72
        $std = new \stdClass();
×
73
        $dummy = new Dummy();
×
74

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

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

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

86
        $this->assertTrue($normalizer->supportsNormalization($dummy));
×
87
        $this->assertFalse($normalizer->supportsNormalization($std));
×
88
        $this->assertTrue($normalizer->supportsDenormalization($dummy, Dummy::class));
×
89
        $this->assertFalse($normalizer->supportsDenormalization($std, \stdClass::class));
×
90
        $this->assertFalse($normalizer->supportsNormalization([]));
×
91
        $this->assertSame(['object' => true], $normalizer->getSupportedTypes('any'));
×
92
    }
93

94
    public function testNormalize(): void
95
    {
96
        $relatedDummy = new RelatedDummy();
×
97

98
        $dummy = new Dummy();
×
99
        $dummy->setName('foo');
×
100
        $dummy->setAlias('ignored');
×
101
        $dummy->setRelatedDummy($relatedDummy);
×
102

NEW
103
        if ($dummy->relatedDummies instanceof Collection) {
×
NEW
104
            $dummy->relatedDummies->add(new RelatedDummy());
×
105
        }
106

107
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
108

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

112
        // BC layer for api-platform/metadata < 4.1
113
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
114
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
115
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
116

117
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
118
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
119
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
120
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
121
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(false));
×
122
        } else {
123
            $relatedDummyType = Type::object(RelatedDummy::class);
×
124
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
125

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

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

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

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

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

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

158
        $expected = [
×
159
            'name' => 'foo',
×
160
            'relatedDummy' => '/dummies/2',
×
161
            'relatedDummies' => ['/dummies/2'],
×
162
        ];
×
163
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
164
            'resources' => [],
×
165
            'ignored_attributes' => ['alias'],
×
166
        ]));
×
167
    }
168

169
    public function testNormalizeWithSecuredProperty(): void
170
    {
171
        $dummy = new SecuredDummy();
×
172
        $dummy->setTitle('myPublicTitle');
×
173
        $dummy->setAdminOnlyProperty('secret');
×
174

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

178
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
179

180
        // BC layer for api-platform/metadata < 4.1
181
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
182
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
183
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
184
        } else {
185
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true));
×
186
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
187
        }
188

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

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

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

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

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

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

215
        $expected = [
×
216
            'title' => 'myPublicTitle',
×
217
        ];
×
218
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
219
            'resources' => [],
×
220
        ]));
×
221
    }
222

223
    public function testNormalizePropertyAsIriWithUriTemplate(): void
224
    {
225
        $propertyCollectionIriOnlyRelation = new PropertyCollectionIriOnlyRelation();
×
226
        $propertyCollectionIriOnlyRelation->name = 'My Relation';
×
227

228
        $propertyCollectionIriOnly = new PropertyCollectionIriOnly();
×
229
        $propertyCollectionIriOnly->addPropertyCollectionIriOnlyRelation($propertyCollectionIriOnlyRelation);
×
230

231
        $collectionOperation = new GetCollection('/property-collection-relations');
×
232
        $getIterableOperation = new GetCollection('/parent/{parentId}/another-collection-operations');
×
233
        $getToOneOperation = new Get('/parent/{parentId}/another-collection-operations/{id}');
×
234

235
        $resourceRelationMetadataCollection = new ResourceMetadataCollection(PropertyCollectionIriOnlyRelation::class, [
×
236
            (new ApiResource())->withOperations(new Operations([$collectionOperation, $getIterableOperation, $getToOneOperation])),
×
237
        ]);
×
238

239
        $resourceMetadataCollectionFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
240
        $resourceMetadataCollectionFactoryProphecy->create(PropertyCollectionIriOnlyRelation::class)->willReturn($resourceRelationMetadataCollection);
×
241

242
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
243
        $propertyNameCollectionFactoryProphecy->create(PropertyCollectionIriOnly::class, ['normalization_groups' => null, 'denormalization_groups' => null, 'operation_name' => null])->willReturn(
×
244
            new PropertyNameCollection(['propertyCollectionIriOnlyRelation', 'iterableIri', 'toOneRelation'])
×
245
        );
×
246

247
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
248

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

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

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

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

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

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

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

299
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
300
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
301
        $normalizer->setSerializer($serializerProphecy->reveal());
×
302

303
        $expected = [
×
304
            'propertyCollectionIriOnlyRelation' => '/property-collection-relations',
×
305
            'iterableIri' => '/parent/42/another-collection-operations',
×
306
            'toOneRelation' => '/parent/42/another-collection-operations/24',
×
307
        ];
×
308

309
        $this->assertSame($expected, $normalizer->normalize($propertyCollectionIriOnly, 'jsonld', [
×
310
            'resources' => [],
×
311
            'root_operation' => new GetCollection('/property_collection_iri_onlies'),
×
312
        ]));
×
313
    }
314

315
    public function testDenormalizeWithSecuredPropertyAndThrowOnAccessDeniedExtraPropertyInAttributeMetaThrowsAccessDeniedExceptionWithSecurityMessage(): void
316
    {
317
        $data = [
×
318
            'title' => 'foo',
×
319
            'adminOnlyProperty' => 'secret',
×
320
        ];
×
321
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
322
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'adminOnlyProperty']));
×
323

324
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
325

326
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
327
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
328
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withSecurityPostDenormalize('is_granted(\'ROLE_ADMIN\')')->withExtraProperties(['throw_on_access_denied' => true]));
×
329
        } else {
330
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
331
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withSecurityPostDenormalize('is_granted(\'ROLE_ADMIN\')')->withExtraProperties(['throw_on_access_denied' => true]));
×
332
        }
333

334
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
335

336
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
337

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

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

345
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
346
        $resourceAccessChecker->isGranted(
×
347
            SecuredDummy::class,
×
348
            'is_granted(\'ROLE_ADMIN\')',
×
349
            Argument::that(function (array $context) {
×
350
                return \array_key_exists('property', $context)
×
351
                    && \array_key_exists('object', $context)
×
352
                    && \array_key_exists('previous_object', $context)
×
353
                    && 'adminOnlyProperty' === $context['property']
×
354
                    && null === $context['previous_object']
×
355
                    && $context['object'] instanceof SecuredDummy;
×
356
            })
×
357
        )->willReturn(false);
×
358

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

362
        $this->expectException(AccessDeniedException::class);
×
363
        $this->expectExceptionMessage('Access denied');
×
364

365
        $normalizer->denormalize($data, SecuredDummy::class);
×
366
    }
367

368
    public function testDenormalizeWithSecuredPropertyAndThrowOnAccessDeniedExtraPropertyInOperationAndSecurityMessageInOperationThrowsAccessDeniedExceptionWithSecurityMessage(): void
369
    {
370
        $data = [
×
371
            'title' => 'foo',
×
372
            'adminOnlyProperty' => 'secret',
×
373
        ];
×
374
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
375
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'adminOnlyProperty']));
×
376

377
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
378

379
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
380
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
381
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withSecurityPostDenormalize('is_granted(\'ROLE_ADMIN\')'));
×
382
        } else {
383
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
384
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withSecurityPostDenormalize('is_granted(\'ROLE_ADMIN\')'));
×
385
        }
386

387
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
388

389
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
390

391
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
392
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
393
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
394

395
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
396
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
397

398
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
399
        $resourceAccessChecker->isGranted(
×
400
            SecuredDummy::class,
×
401
            'is_granted(\'ROLE_ADMIN\')',
×
402
            Argument::that(function (array $context) {
×
403
                return \array_key_exists('property', $context)
×
404
                    && \array_key_exists('object', $context)
×
405
                    && \array_key_exists('previous_object', $context)
×
406
                    && 'adminOnlyProperty' === $context['property']
×
407
                    && null === $context['previous_object']
×
408
                    && $context['object'] instanceof SecuredDummy;
×
409
            })
×
410
        )->willReturn(false);
×
411

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

415
        $this->expectException(AccessDeniedException::class);
×
416
        $this->expectExceptionMessage('Custom access denied message');
×
417

418
        $operation = new Patch(securityMessage: 'Custom access denied message', extraProperties: ['throw_on_access_denied' => true]);
×
419

420
        $normalizer->denormalize($data, SecuredDummy::class, 'json', [
×
421
            'operation' => $operation,
×
422
        ]);
×
423
    }
424

425
    public function testDenormalizeWithSecuredPropertyAndThrowOnAccessDeniedExtraPropertyInOperationThrowsAccessDeniedException(): void
426
    {
427
        $data = [
×
428
            'title' => 'foo',
×
429
            'adminOnlyProperty' => 'secret',
×
430
        ];
×
431
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
432
        $propertyNameCollectionFactoryProphecy->create(SecuredDummy::class, [])->willReturn(new PropertyNameCollection(['title', 'adminOnlyProperty']));
×
433

434
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
435

436
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
437
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
438
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withSecurityPostDenormalize('is_granted(\'ROLE_ADMIN\')'));
×
439
        } else {
440
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
441
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withSecurityPostDenormalize('is_granted(\'ROLE_ADMIN\')'));
×
442
        }
443

444
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
445

446
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
447

448
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
449
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
450
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
451

452
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
453
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
454

455
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
456
        $resourceAccessChecker->isGranted(
×
457
            SecuredDummy::class,
×
458
            'is_granted(\'ROLE_ADMIN\')',
×
459
            Argument::that(function (array $context) {
×
460
                return \array_key_exists('property', $context)
×
461
                    && \array_key_exists('object', $context)
×
462
                    && \array_key_exists('previous_object', $context)
×
463
                    && 'adminOnlyProperty' === $context['property']
×
464
                    && null === $context['previous_object']
×
465
                    && $context['object'] instanceof SecuredDummy;
×
466
            })
×
467
        )->willReturn(false);
×
468

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

472
        $this->expectException(AccessDeniedException::class);
×
473
        $this->expectExceptionMessage('Access denied');
×
474

475
        $operation = new Patch(extraProperties: ['throw_on_access_denied' => true]);
×
476

477
        $normalizer->denormalize($data, SecuredDummy::class, 'json', [
×
478
            'operation' => $operation,
×
479
        ]);
×
480
    }
481

482
    public function testDenormalizeWithSecuredProperty(): void
483
    {
484
        $data = [
×
485
            'title' => 'foo',
×
486
            'adminOnlyProperty' => 'secret',
×
487
        ];
×
488

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

492
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
493

494
        // BC layer for api-platform/metadata < 4.1
495
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
496
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
497
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
498
        } else {
499
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
500
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'adminOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withSecurity('is_granted(\'ROLE_ADMIN\')'));
×
501
        }
502

503
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
504

505
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
506

507
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
508
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
509
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
510

511
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
512
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
513

514
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
515
        $resourceAccessChecker->isGranted(
×
516
            SecuredDummy::class,
×
517
            'is_granted(\'ROLE_ADMIN\')',
×
518
            ['object' => null, 'property' => 'adminOnlyProperty']
×
519
        )->willReturn(false);
×
520

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

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

526
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
527

528
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
529
        $propertyAccessorProphecy->setValue($actual, 'adminOnlyProperty', 'secret')->shouldNotHaveBeenCalled();
×
530
    }
531

532
    public function testDenormalizeCreateWithDeniedPostDenormalizeSecuredProperty(): void
533
    {
534
        $data = [
×
535
            'title' => 'foo',
×
536
            'ownerOnlyProperty' => 'should not be set',
×
537
        ];
×
538

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

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

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

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

555
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
556

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

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

564
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
565
        $resourceAccessChecker->isGranted(
×
566
            SecuredDummy::class,
×
567
            'false',
×
568
            Argument::any()
×
569
        )->willReturn(false);
×
570

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

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

576
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
577

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

583
    public function testDenormalizeUpdateWithSecuredProperty(): void
584
    {
585
        $dummy = new SecuredDummy();
×
586

587
        $data = [
×
588
            'title' => 'foo',
×
589
            'ownerOnlyProperty' => 'secret',
×
590
        ];
×
591

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

595
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
596

597
        // BC layer for api-platform/metadata < 4.1
598
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
599
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
600
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('true'));
×
601
        } else {
602
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
603
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('true'));
×
604
        }
605

606
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
607

608
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
609

610
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
611
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
612
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
613
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
614

615
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
616
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
617

618
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
619
        $resourceAccessChecker->isGranted(
×
620
            SecuredDummy::class,
×
621
            'true',
×
622
            ['object' => null, 'property' => 'ownerOnlyProperty']
×
623
        )->willReturn(true);
×
624
        $resourceAccessChecker->isGranted(
×
625
            SecuredDummy::class,
×
626
            'true',
×
627
            ['object' => $dummy, 'property' => 'ownerOnlyProperty']
×
628
        )->willReturn(true);
×
629

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

633
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
634
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
635

636
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
637

638
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
639
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'secret')->shouldHaveBeenCalled();
×
640
    }
641

642
    public function testDenormalizeUpdateWithDeniedSecuredProperty(): void
643
    {
644
        $dummy = new SecuredDummy();
×
645
        $dummy->setOwnerOnlyProperty('secret');
×
646

647
        $data = [
×
648
            'title' => 'foo',
×
649
            'ownerOnlyProperty' => 'should not be set',
×
650
        ];
×
651

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

655
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
656

657
        // BC layer for api-platform/metadata < 4.1
658
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
659
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
660
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('false'));
×
661
        } else {
662
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
663
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withWritable(true)->withSecurity('false'));
×
664
        }
665

666
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
667

668
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
669

670
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
671
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
672
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
673
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
674

675
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
676
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
677

678
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
679
        $resourceAccessChecker->isGranted(
×
680
            SecuredDummy::class,
×
681
            'false',
×
682
            ['object' => null, 'property' => 'ownerOnlyProperty']
×
683
        )->willReturn(false);
×
684
        $resourceAccessChecker->isGranted(
×
685
            SecuredDummy::class,
×
686
            'false',
×
687
            ['object' => $dummy, 'property' => 'ownerOnlyProperty']
×
688
        )->willReturn(false);
×
689

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

693
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
694
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
695

696
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
697

698
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
699
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldNotHaveBeenCalled();
×
700
    }
701

702
    public function testDenormalizeUpdateWithDeniedPostDenormalizeSecuredProperty(): void
703
    {
704
        $dummy = new SecuredDummy();
×
705
        $dummy->setOwnerOnlyProperty('secret');
×
706

707
        $data = [
×
708
            'title' => 'foo',
×
709
            'ownerOnlyProperty' => 'should not be set',
×
710
        ];
×
711

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

715
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
716

717
        // BC layer for api-platform/metadata < 4.1
718
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
719
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
720
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)->withWritable(true)->withSecurityPostDenormalize('false'));
×
721
        } else {
722
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'title', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
723
            $propertyMetadataFactoryProphecy->create(SecuredDummy::class, 'ownerOnlyProperty', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true)->withWritable(true)->withSecurityPostDenormalize('false'));
×
724
        }
725

726
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
727

728
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
729
        $propertyAccessorProphecy->getValue($dummy, 'ownerOnlyProperty')->willReturn('secret');
×
730

731
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
732
        $resourceClassResolverProphecy->getResourceClass($dummy, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
733
        $resourceClassResolverProphecy->getResourceClass(null, SecuredDummy::class)->willReturn(SecuredDummy::class);
×
734
        $resourceClassResolverProphecy->isResourceClass(SecuredDummy::class)->willReturn(true);
×
735

736
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
737
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
738

739
        $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class);
×
740
        $resourceAccessChecker->isGranted(
×
741
            SecuredDummy::class,
×
742
            'false',
×
743
            ['object' => $dummy, 'previous_object' => $dummy, 'property' => 'ownerOnlyProperty']
×
744
        )->willReturn(false);
×
745

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

749
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
750
        $actual = $normalizer->denormalize($data, SecuredDummy::class, null, $context);
×
751

752
        $this->assertInstanceOf(SecuredDummy::class, $actual);
×
753

754
        $propertyAccessorProphecy->setValue($actual, 'title', 'foo')->shouldHaveBeenCalled();
×
755
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'should not be set')->shouldHaveBeenCalled();
×
756
        $propertyAccessorProphecy->setValue($actual, 'ownerOnlyProperty', 'secret')->shouldHaveBeenCalled();
×
757
    }
758

759
    public function testNormalizeReadableLinks(): void
760
    {
761
        $relatedDummy = new RelatedDummy();
×
762

763
        $dummy = new Dummy();
×
764
        $dummy->setRelatedDummy($relatedDummy);
×
765

NEW
766
        if ($dummy->relatedDummies instanceof Collection) {
×
NEW
767
            $dummy->relatedDummies->add(new RelatedDummy());
×
768
        }
769

770
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
771

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

775
        // BC layer for api-platform/metadata < 4.1
776
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
777
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
778
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
779

780
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
781
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(true)->withWritable(false)->withReadableLink(true));
×
782
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(true));
×
783
        } else {
784
            $relatedDummyType = Type::object(RelatedDummy::class);
×
785
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
786

787
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
788
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withReadable(true)->withWritable(false)->withReadableLink(true));
×
789
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(true)->withWritable(false)->withReadableLink(true));
×
790
        }
791

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

795
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
796
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
797
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
798

799
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
800
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
801
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
802
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
803
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
804
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
805
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
806

807
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
808
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
809
        $relatedDummyChildContext = Argument::allOf(
×
810
            Argument::type('array'),
×
811
            Argument::withEntry('resource_class', RelatedDummy::class),
×
812
            Argument::not(Argument::withKey('iri')),
×
813
            Argument::not(Argument::withKey('force_resource_class'))
×
814
        );
×
815
        $serializerProphecy->normalize($relatedDummy, null, $relatedDummyChildContext)->willReturn(['foo' => 'hello']);
×
816
        $serializerProphecy->normalize(['foo' => 'hello'], null, Argument::type('array'))->willReturn(['foo' => 'hello']);
×
817
        $serializerProphecy->normalize([['foo' => 'hello']], null, Argument::type('array'))->willReturn([['foo' => 'hello']]);
×
818

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

822
        $expected = [
×
823
            'relatedDummy' => ['foo' => 'hello'],
×
824
            'relatedDummies' => [['foo' => 'hello']],
×
825
        ];
×
826
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
827
            'resources' => [],
×
828
            'force_resource_class' => Dummy::class,
×
829
        ]));
×
830
    }
831

832
    public function testNormalizePolymorphicRelations(): void
833
    {
834
        $concreteDummy = new DummyTableInheritanceChild();
×
835

836
        $dummy = new DummyTableInheritanceRelated();
×
837
        $dummy->addChild($concreteDummy);
×
838

839
        $abstractDummies = new ArrayCollection([$concreteDummy]);
×
840

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

844
        // BC layer for api-platform/metadata < 4.1
845
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
846
            $abstractDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, DummyTableInheritance::class);
×
847
            $abstractDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $abstractDummyType);
×
848

849
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
850
            $propertyMetadataFactoryProphecy->create(DummyTableInheritanceRelated::class, 'children', [])->willReturn((new ApiProperty())->withBuiltinTypes([$abstractDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(true));
×
851
        } else {
852
            $abstractDummyType = Type::object(DummyTableInheritance::class);
×
853
            $abstractDummiesType = Type::collection(Type::object(ArrayCollection::class), $abstractDummyType, Type::int());
×
854

855
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
856
            $propertyMetadataFactoryProphecy->create(DummyTableInheritanceRelated::class, 'children', [])->willReturn((new ApiProperty())->withNativeType($abstractDummiesType)->withReadable(true)->withWritable(false)->withReadableLink(true));
×
857
        }
858

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

862
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
863
        $propertyAccessorProphecy->getValue($dummy, 'children')->willReturn($abstractDummies);
×
864

865
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
866
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(DummyTableInheritanceRelated::class);
×
867
        $resourceClassResolverProphecy->getResourceClass(null, DummyTableInheritanceRelated::class)->willReturn(DummyTableInheritanceRelated::class);
×
868
        $resourceClassResolverProphecy->getResourceClass($concreteDummy, DummyTableInheritance::class)->willReturn(DummyTableInheritanceChild::class);
×
869
        $resourceClassResolverProphecy->getResourceClass($abstractDummies, DummyTableInheritance::class)->willReturn(DummyTableInheritance::class);
×
870
        $resourceClassResolverProphecy->isResourceClass(DummyTableInheritanceRelated::class)->willReturn(true);
×
871
        $resourceClassResolverProphecy->isResourceClass(DummyTableInheritance::class)->willReturn(true);
×
872

873
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
874
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
875
        $concreteDummyChildContext = Argument::allOf(
×
876
            Argument::type('array'),
×
877
            Argument::not(Argument::withKey('iri'))
×
878
        );
×
879
        $serializerProphecy->normalize($concreteDummy, null, $concreteDummyChildContext)->willReturn(['foo' => 'concrete']);
×
880
        $serializerProphecy->normalize([['foo' => 'concrete']], null, Argument::type('array'))->willReturn([['foo' => 'concrete']]);
×
881

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

885
        $expected = [
×
886
            'children' => [['foo' => 'concrete']],
×
887
        ];
×
888
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
889
            'resources' => [],
×
890
        ]));
×
891
    }
892

893
    public function testDenormalize(): void
894
    {
895
        $data = [
×
896
            'name' => 'foo',
×
897
            'relatedDummy' => '/dummies/1',
×
898
            'relatedDummies' => ['/dummies/2'],
×
899
        ];
×
900

901
        $relatedDummy1 = new RelatedDummy();
×
902
        $relatedDummy2 = new RelatedDummy();
×
903

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

907
        // BC layer for api-platform/metadata < 4.1
908
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
909
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
910
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
911

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

915
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
916
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
917
        } else {
918
            $relatedDummyType = Type::object(RelatedDummy::class);
×
919
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
920

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

924
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
925
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
926
        }
927

928
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
929
        $iriConverterProphecy->getResourceFromIri('/dummies/1', Argument::type('array'))->willReturn($relatedDummy1);
×
930
        $iriConverterProphecy->getResourceFromIri('/dummies/2', Argument::type('array'))->willReturn($relatedDummy2);
×
931

932
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
933

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

940
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
941
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
942

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

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

948
        $this->assertInstanceOf(Dummy::class, $actual);
×
949

950
        $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
×
951
        $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
×
952
        $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
×
953
    }
954

955
    public function testCanDenormalizeInputClassWithDifferentFieldsThanResourceClass(): void
956
    {
957
        $this->markTestSkipped('TODO: check why this test has been commented');
×
958

959
        // $data = [
960
        //     'dummyName' => 'Dummy Name',
961
        // ];
962
        //
963
        // $context = [
964
        //     'resource_class' => DummyForAdditionalFields::class,
965
        //     'input' => ['class' => DummyForAdditionalFieldsInput::class],
966
        //     'output' => ['class' => DummyForAdditionalFields::class],
967
        // ];
968
        // $augmentedContext = $context + ['api_denormalize' => true];
969
        //
970
        // $preHydratedDummy = new DummyForAdditionalFieldsInput('Name Dummy');
971
        // $cleanedContext = array_diff_key($augmentedContext, [
972
        //     'input' => null,
973
        //     'resource_class' => null,
974
        // ]);
975
        // $cleanedContextWithObjectToPopulate = array_merge($cleanedContext, [
976
        //     AbstractObjectNormalizer::OBJECT_TO_POPULATE => $preHydratedDummy,
977
        //     AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE => true,
978
        // ]);
979
        //
980
        // $dummyInputDto = new DummyForAdditionalFieldsInput('Dummy Name');
981
        // $dummy = new DummyForAdditionalFields('Dummy Name', 'name-dummy');
982
        //
983
        // $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
984
        //
985
        // $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
986
        //
987
        // $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
988
        //
989
        // $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
990
        // $resourceClassResolverProphecy->getResourceClass(null, DummyForAdditionalFields::class)->willReturn(DummyForAdditionalFields::class);
991
        //
992
        // $inputDataTransformerProphecy = $this->prophesize(DataTransformerInitializerInterface::class);
993
        // $inputDataTransformerProphecy->willImplement(DataTransformerInitializerInterface::class);
994
        // $inputDataTransformerProphecy->initialize(DummyForAdditionalFieldsInput::class, $cleanedContext)->willReturn($preHydratedDummy);
995
        // $inputDataTransformerProphecy->supportsTransformation($data, DummyForAdditionalFields::class, $augmentedContext)->willReturn(true);
996
        // $inputDataTransformerProphecy->transform($dummyInputDto, DummyForAdditionalFields::class, $augmentedContext)->willReturn($dummy);
997
        //
998
        // $serializerProphecy = $this->prophesize(SerializerInterface::class);
999
        // $serializerProphecy->willImplement(DenormalizerInterface::class);
1000
        // $serializerProphecy->denormalize($data, DummyForAdditionalFieldsInput::class, 'json', $cleanedContextWithObjectToPopulate)->willReturn($dummyInputDto);
1001
        //
1002
        // $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), null, null, null, [], null, null) extends AbstractItemNormalizer {
1003
        // };
1004
        // $normalizer->setSerializer($serializerProphecy->reveal());
1005
        //
1006
        // $actual = $normalizer->denormalize($data, DummyForAdditionalFields::class, 'json', $context);
1007
        //
1008
        // $this->assertInstanceOf(DummyForAdditionalFields::class, $actual);
1009
        // $this->assertSame('Dummy Name', $actual->getName());
1010
    }
1011

1012
    public function testDenormalizeWritableLinks(): void
1013
    {
1014
        $data = [
×
1015
            'name' => 'foo',
×
1016
            'relatedDummy' => ['foo' => 'bar'],
×
1017
            'relatedDummies' => [['bar' => 'baz']],
×
1018
            'relatedDummiesWithUnionTypes' => [0 => ['bar' => 'qux'], 1. => ['bar' => 'quux']],
×
1019
        ];
×
1020

1021
        $relatedDummy1 = new RelatedDummy();
×
1022
        $relatedDummy2 = new RelatedDummy();
×
1023
        $relatedDummy3 = new RelatedDummy();
×
1024
        $relatedDummy4 = new RelatedDummy();
×
1025

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

1029
        // BC layer for api-platform/metadata < 4.1
1030
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1031
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
1032
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
1033
            $relatedDummiesWithUnionTypesIntType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
1034
            $relatedDummiesWithUnionTypesFloatType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT), $relatedDummyType);
×
1035

1036
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1037
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
1038
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1039
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1040
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummiesWithUnionTypes', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesWithUnionTypesIntType, $relatedDummiesWithUnionTypesFloatType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1041
        } else {
1042
            $relatedDummyType = Type::object(RelatedDummy::class);
×
1043
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
1044
            $relatedDummiesWithUnionTypesIntType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
1045
            $relatedDummiesWithUnionTypesFloatType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::float());
×
1046

1047
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1048
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
1049
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1050
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1051
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummiesWithUnionTypes', [])->willReturn((new ApiProperty())->withNativeType(Type::union($relatedDummiesWithUnionTypesIntType, $relatedDummiesWithUnionTypesFloatType))->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1052
        }
1053

1054
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1055

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

1058
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1059
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1060
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1061
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1062
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1063
        $resourceClassResolverProphecy->isResourceClass(ArrayCollection::class)->willReturn(false);
×
1064

1065
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1066
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1067
        $serializerProphecy->denormalize(['foo' => 'bar'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy1);
×
1068
        $serializerProphecy->denormalize(['bar' => 'baz'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy2);
×
1069
        $serializerProphecy->denormalize(['bar' => 'qux'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy3);
×
1070
        $serializerProphecy->denormalize(['bar' => 'quux'], RelatedDummy::class, null, Argument::type('array'))->willReturn($relatedDummy4);
×
1071

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

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

1077
        $this->assertInstanceOf(Dummy::class, $actual);
×
1078

1079
        $propertyAccessorProphecy->setValue($actual, 'name', 'foo')->shouldHaveBeenCalled();
×
1080
        $propertyAccessorProphecy->setValue($actual, 'relatedDummy', $relatedDummy1)->shouldHaveBeenCalled();
×
1081
        $propertyAccessorProphecy->setValue($actual, 'relatedDummies', [$relatedDummy2])->shouldHaveBeenCalled();
×
1082
        $propertyAccessorProphecy->setValue($actual, 'relatedDummiesWithUnionTypes', [0 => $relatedDummy3, 1. => $relatedDummy4])->shouldHaveBeenCalled();
×
1083
    }
1084

1085
    public function testBadRelationType(): void
1086
    {
1087
        $this->expectException(NotNormalizableValueException::class);
×
1088
        $this->expectExceptionMessage('The type of the "relatedDummy" attribute must be "array" (nested document) or "string" (IRI), "integer" given.');
×
1089

1090
        $data = [
×
1091
            'relatedDummy' => 22,
×
1092
        ];
×
1093

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

1097
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1098

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

1110
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1111

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

1118
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1119

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

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

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

1129
    public function testBadRelationTypeWithExceptionToValidationErrors(): void
1130
    {
1131
        $data = [
×
1132
            'relatedDummy' => 22,
×
1133
        ];
×
1134

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

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

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

1151
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1152

1153
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1154
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1155
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1156
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1157
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1158

1159
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1160

1161
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1162
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1163

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

1167
        // 'not_normalizable_value_exceptions' is set by Serializer thanks to DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS
1168
        $actual = $normalizer->denormalize($data, Dummy::class, null, ['not_normalizable_value_exceptions' => []]);
×
1169
        $this->assertNull($actual->relatedDummy);
×
1170
    }
1171

1172
    public function testDeserializationPathForNotDenormalizableRelations(): void
1173
    {
1174
        $data = [
×
1175
            'relatedDummies' => ['wrong'],
×
1176
        ];
×
1177

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

1181
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1182

1183
        // BC layer for api-platform/metadata < 4.1
1184
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1185
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
×
1186
                (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)
×
1187
            );
×
1188
        } else {
1189
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
×
1190
                (new ApiProperty())->withNativeType(Type::collection(Type::object(ArrayCollection::class), Type::object(RelatedDummy::class)))->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true)
×
1191
            );
×
1192
        }
1193

1194
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1195
        $iriConverterProphecy->getResourceFromIri(Argument::cetera())->willThrow(new InvalidArgumentException('Invalid IRI'));
×
1196

1197
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1198
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1199
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1200
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1201
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1202

1203
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1204

1205
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1206
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1207

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

1211
        $errors = [];
×
1212
        $actual = $normalizer->denormalize($data, Dummy::class, null, ['not_normalizable_value_exceptions' => &$errors]);
×
1213
        $this->assertEmpty($actual->relatedDummies);
×
1214
        $this->assertCount(1, $errors);
×
1215
        $this->assertInstanceOf(NotNormalizableValueException::class, $errors[0]);
×
1216
        $this->assertSame('relatedDummies[0]', $errors[0]->getPath());
×
1217
        $this->assertSame('Invalid IRI "wrong".', $errors[0]->getMessage());
×
1218
    }
1219

1220
    public function testDeserializationPathForNotDenormalizableResource(): void
1221
    {
1222
        $this->expectException(NotNormalizableValueException::class);
×
1223
        $this->expectExceptionMessage('Invalid IRI "wrong IRI".');
×
1224

1225
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1226

1227
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1228

1229
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1230
        $iriConverterProphecy->getResourceFromIri(Argument::cetera())->willThrow(new InvalidArgumentException('Invalid IRI'));
×
1231

1232
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1233
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1234
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1235

1236
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1237

1238
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1239
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1240

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

1244
        $normalizer->denormalize('wrong IRI', Dummy::class, null, ['not_normalizable_value_exceptions' => []]);
×
1245
    }
1246

1247
    public function testDeserializationPathForNotFoundResource(): void
1248
    {
1249
        $this->expectException(NotNormalizableValueException::class);
×
1250
        $this->expectExceptionMessage('Some item not found exception.');
×
1251

1252
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1253

1254
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1255

1256
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1257
        $iriConverterProphecy->getResourceFromIri(Argument::cetera())->willThrow(new ItemNotFoundException('Some item not found exception.'));
×
1258

1259
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1260
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1261
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1262

1263
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1264

1265
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1266
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1267

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

1271
        $normalizer->denormalize('/some-iri', Dummy::class, null, ['not_normalizable_value_exceptions' => []]);
×
1272
    }
1273

1274
    public function testInnerDocumentNotAllowed(): void
1275
    {
1276
        $this->expectException(UnexpectedValueException::class);
×
1277
        $this->expectExceptionMessage('Nested documents for attribute "relatedDummy" are not allowed. Use IRIs instead.');
×
1278

1279
        $data = [
×
1280
            'relatedDummy' => [
×
1281
                'foo' => 'bar',
×
1282
            ],
×
1283
        ];
×
1284

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

1288
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1289

1290
        // BC layer for api-platform/metadata < 4.1
1291
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1292
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
1293
                (new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
1294
            );
×
1295
        } else {
1296
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
1297
                (new ApiProperty())->withNativeType(Type::object(RelatedDummy::class))->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
1298
            );
×
1299
        }
1300

1301
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1302

1303
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1304
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1305
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1306
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1307
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1308

1309
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1310

1311
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1312
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1313

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

1317
        $normalizer->denormalize($data, Dummy::class);
×
1318
    }
1319

1320
    public function testBadType(): void
1321
    {
1322
        $this->expectException(UnexpectedValueException::class);
×
1323
        $this->expectExceptionMessage('The type of the "foo" attribute must be "float", "integer" given.');
×
1324

1325
        $data = [
×
1326
            'foo' => 42,
×
1327
        ];
×
1328

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

1332
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1333

1334
        // BC layer for api-platform/metadata < 4.1
1335
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1336
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1337
        } else {
1338
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1339
        }
1340

1341
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1342

1343
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1344
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1345
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1346

1347
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1348

1349
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1350
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1351

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

1355
        $normalizer->denormalize($data, Dummy::class);
×
1356
    }
1357

1358
    public function testTypeChecksCanBeDisabled(): void
1359
    {
1360
        $data = [
×
1361
            'foo' => 42,
×
1362
        ];
×
1363

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

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

1369
        // BC layer for api-platform/metadata < 4.1
1370
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1371
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1372
        } else {
1373
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1374
        }
1375

1376
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1377

1378
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1379
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1380
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1381

1382
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1383

1384
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1385
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1386

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

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

1392
        $this->assertInstanceOf(Dummy::class, $actual);
×
1393

1394
        $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
×
1395
    }
1396

1397
    public function testJsonAllowIntAsFloat(): void
1398
    {
1399
        $data = [
×
1400
            'foo' => 42,
×
1401
        ];
×
1402

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

1406
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1407

1408
        // BC layer for api-platform/metadata < 4.1
1409
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1410
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1411
        } else {
1412
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1413
        }
1414

1415
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1416

1417
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1418
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1419
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1420

1421
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1422

1423
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1424
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1425

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

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

1431
        $this->assertInstanceOf(Dummy::class, $actual);
×
1432

1433
        $propertyAccessorProphecy->setValue($actual, 'foo', 42)->shouldHaveBeenCalled();
×
1434
    }
1435

1436
    public function testDenormalizeBadKeyType(): void
1437
    {
1438
        $this->expectException(NotNormalizableValueException::class);
×
1439
        $this->expectExceptionMessage('The type of the key "a" must be "int", "string" given.');
×
1440

1441
        $data = [
×
1442
            'name' => 'foo',
×
1443
            'relatedDummy' => [
×
1444
                'foo' => 'bar',
×
1445
            ],
×
1446
            'relatedDummies' => [
×
1447
                'a' => [
×
1448
                    'bar' => 'baz',
×
1449
                ],
×
1450
            ],
×
1451
            'relatedDummiesWithUnionTypes' => [
×
1452
                'a' => [
×
1453
                    'bar' => 'baz',
×
1454
                ],
×
1455
            ],
×
1456
        ];
×
1457

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

1461
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1462

1463
        // BC layer for api-platform/metadata < 4.1
1464
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1465
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
1466
            $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));
×
1467

1468
            $type = new LegacyType(
×
1469
                LegacyType::BUILTIN_TYPE_OBJECT,
×
1470
                false,
×
1471
                ArrayCollection::class,
×
1472
                true,
×
1473
                new LegacyType(LegacyType::BUILTIN_TYPE_INT),
×
1474
                new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)
×
1475
            );
×
1476
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$type])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1477
        } else {
1478
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
1479
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn((new ApiProperty())->withNativeType(Type::object(RelatedDummy::class))->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1480

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

1485
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1486

1487
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1488
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1489
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1490
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1491
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1492

1493
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1494

1495
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1496
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1497

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

1501
        $normalizer->denormalize($data, Dummy::class);
×
1502
    }
1503

1504
    public function testNullable(): void
1505
    {
1506
        $data = [
×
1507
            'name' => null,
×
1508
        ];
×
1509

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

1513
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1514

1515
        // BC layer for api-platform/metadata < 4.1
1516
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1517
            $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));
×
1518
        } else {
1519
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::nullable(Type::string()))->withDescription('')->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
1520
        }
1521

1522
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1523

1524
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1525
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1526
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1527

1528
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1529

1530
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1531
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1532

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

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

1538
        $this->assertInstanceOf(Dummy::class, $actual);
×
1539

1540
        $propertyAccessorProphecy->setValue($actual, 'name', null)->shouldHaveBeenCalled();
×
1541
    }
1542

1543
    public function testDenormalizeBasicTypePropertiesFromXml(): void
1544
    {
1545
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
1546
        $propertyNameCollectionFactoryProphecy->create(ObjectWithBasicProperties::class, [])->willReturn(new PropertyNameCollection([
×
1547
            'boolTrue1',
×
1548
            'boolFalse1',
×
1549
            'boolTrue2',
×
1550
            'boolFalse2',
×
1551
            'int1',
×
1552
            'int2',
×
1553
            'float1',
×
1554
            'float2',
×
1555
            'float3',
×
1556
            'floatNaN',
×
1557
            'floatInf',
×
1558
            'floatNegInf',
×
1559
        ]));
×
1560

1561
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1562

1563
        // BC layer for api-platform/metadata < 4.1
1564
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1565
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1566
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1567
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1568
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_BOOL)])->withDescription('')->withReadable(false)->withWritable(true));
×
1569
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1570
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1571
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float1', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1572
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float2', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1573
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float3', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1574
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNaN', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1575
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatInf', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1576
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNegInf', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT)])->withDescription('')->withReadable(false)->withWritable(true));
×
1577
        } else {
1578
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue1', [])->willReturn((new ApiProperty())->withNativeType(Type::bool())->withDescription('')->withReadable(false)->withWritable(true));
×
1579
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse1', [])->willReturn((new ApiProperty())->withNativeType(Type::bool())->withDescription('')->withReadable(false)->withWritable(true));
×
1580
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolTrue2', [])->willReturn((new ApiProperty())->withNativeType(Type::bool())->withDescription('')->withReadable(false)->withWritable(true));
×
1581
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'boolFalse2', [])->willReturn((new ApiProperty())->withNativeType(Type::bool())->withDescription('')->withReadable(false)->withWritable(true));
×
1582
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int1', [])->willReturn((new ApiProperty())->withNativeType(Type::int())->withDescription('')->withReadable(false)->withWritable(true));
×
1583
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'int2', [])->willReturn((new ApiProperty())->withNativeType(Type::int())->withDescription('')->withReadable(false)->withWritable(true));
×
1584
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float1', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1585
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float2', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1586
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'float3', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1587
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNaN', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1588
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatInf', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1589
            $propertyMetadataFactoryProphecy->create(ObjectWithBasicProperties::class, 'floatNegInf', [])->willReturn((new ApiProperty())->withNativeType(Type::float())->withDescription('')->withReadable(false)->withWritable(true));
×
1590
        }
1591

1592
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1593

1594
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1595
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolTrue1', true)->shouldBeCalled();
×
1596
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolFalse1', false)->shouldBeCalled();
×
1597
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolTrue2', true)->shouldBeCalled();
×
1598
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'boolFalse2', false)->shouldBeCalled();
×
1599
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'int1', 4711)->shouldBeCalled();
×
1600
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'int2', -4711)->shouldBeCalled();
×
1601
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float1', Argument::approximate(123.456, 0))->shouldBeCalled();
×
1602
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float2', Argument::approximate(-1.2344e56, 1))->shouldBeCalled();
×
1603
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'float3', Argument::approximate(45E-6, 1))->shouldBeCalled();
×
1604
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatNaN', Argument::that(static fn (float $arg) => is_nan($arg)))->shouldBeCalled();
×
1605
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatInf', \INF)->shouldBeCalled();
×
1606
        $propertyAccessorProphecy->setValue(Argument::type(ObjectWithBasicProperties::class), 'floatNegInf', -\INF)->shouldBeCalled();
×
1607

1608
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1609
        $resourceClassResolverProphecy->getResourceClass(null, ObjectWithBasicProperties::class)->willReturn(ObjectWithBasicProperties::class);
×
1610
        $resourceClassResolverProphecy->isResourceClass(ObjectWithBasicProperties::class)->willReturn(true);
×
1611

1612
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1613
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1614

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

1618
        $objectWithBasicProperties = $normalizer->denormalize(
×
1619
            [
×
1620
                'boolTrue1' => 'true',
×
1621
                'boolFalse1' => 'false',
×
1622
                'boolTrue2' => '1',
×
1623
                'boolFalse2' => '0',
×
1624
                'int1' => '4711',
×
1625
                'int2' => '-4711',
×
1626
                'float1' => '123.456',
×
1627
                'float2' => '-1.2344e56',
×
1628
                'float3' => '45E-6',
×
1629
                'floatNaN' => 'NaN',
×
1630
                'floatInf' => 'INF',
×
1631
                'floatNegInf' => '-INF',
×
1632
            ],
×
1633
            ObjectWithBasicProperties::class,
×
1634
            'xml'
×
1635
        );
×
1636

1637
        $this->assertInstanceOf(ObjectWithBasicProperties::class, $objectWithBasicProperties);
×
1638
    }
1639

1640
    public function testDenormalizeCollectionDecodedFromXmlWithOneChild(): void
1641
    {
1642
        $data = [
×
1643
            'relatedDummies' => [
×
1644
                'name' => 'foo',
×
1645
            ],
×
1646
        ];
×
1647

1648
        $relatedDummy = new RelatedDummy();
×
1649
        $relatedDummy->setName('foo');
×
1650

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

1654
        // BC layer for api-platform/metadata < 4.1
1655
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1656
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
1657
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
1658

1659
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1660
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1661
        } else {
1662
            $relatedDummyType = Type::object(RelatedDummy::class);
×
1663
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
1664

1665
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1666
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(true));
×
1667
        }
1668

1669
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1670

1671
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1672
        $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', Argument::type('array'))->shouldBeCalled();
×
1673

1674
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1675
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1676
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1677
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1678
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1679

1680
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1681
        $serializerProphecy->willImplement(DenormalizerInterface::class);
×
1682
        $serializerProphecy->denormalize(['name' => 'foo'], RelatedDummy::class, 'xml', Argument::type('array'))->willReturn($relatedDummy);
×
1683

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

1687
        $normalizer->denormalize($data, Dummy::class, 'xml');
×
1688
    }
1689

1690
    public function testDenormalizePopulatingNonCloneableObject(): void
1691
    {
1692
        $dummy = new NonCloneableDummy();
×
1693
        $dummy->setName('foo');
×
1694

1695
        $data = [
×
1696
            'name' => 'bar',
×
1697
        ];
×
1698

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

1702
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1703

1704
        // BC layer for api-platform/metadata < 4.1
1705
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1706
            $propertyMetadataFactoryProphecy->create(NonCloneableDummy::class, 'name', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
1707
        } else {
1708
            $propertyMetadataFactoryProphecy->create(NonCloneableDummy::class, 'name', [])->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
1709
        }
1710

1711
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1712
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1713
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1714
        $resourceClassResolverProphecy->getResourceClass(null, NonCloneableDummy::class)->willReturn(NonCloneableDummy::class);
×
1715
        $resourceClassResolverProphecy->getResourceClass($dummy, NonCloneableDummy::class)->willReturn(NonCloneableDummy::class);
×
1716
        $resourceClassResolverProphecy->isResourceClass(NonCloneableDummy::class)->willReturn(true);
×
1717

1718
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1719
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1720

1721
        $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal(), null, null, [], null, null) extends AbstractItemNormalizer {};
×
1722
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1723
        $normalizer->setSerializer($serializerProphecy->reveal());
×
1724

1725
        $context = [AbstractItemNormalizer::OBJECT_TO_POPULATE => $dummy];
×
1726
        $actual = $normalizer->denormalize($data, NonCloneableDummy::class, null, $context);
×
1727

1728
        $this->assertInstanceOf(NonCloneableDummy::class, $actual);
×
1729
        $this->assertSame($dummy, $actual);
×
1730
        $propertyAccessorProphecy->setValue($actual, 'name', 'bar')->shouldHaveBeenCalled();
×
1731
    }
1732

1733
    public function testDenormalizeObjectWithNullDisabledTypeEnforcement(): void
1734
    {
1735
        $data = [
×
1736
            'dummy' => null,
×
1737
        ];
×
1738

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

1742
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1743

1744
        // BC layer for api-platform/metadata < 4.1
1745
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1746
            $propertyMetadataFactoryProphecy->create(DtoWithNullValue::class, 'dummy', [])->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, nullable: true)])->withDescription('')->withReadable(true)->withWritable(true));
×
1747
        } else {
1748
            $propertyMetadataFactoryProphecy->create(DtoWithNullValue::class, 'dummy', [])->willReturn((new ApiProperty())->withNativeType(Type::nullable(Type::object()))->withDescription('')->withReadable(true)->withWritable(true));
×
1749
        }
1750

1751
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1752
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1753
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1754
        $resourceClassResolverProphecy->getResourceClass(null, DtoWithNullValue::class)->willReturn(DtoWithNullValue::class);
×
1755
        $resourceClassResolverProphecy->isResourceClass(DtoWithNullValue::class)->willReturn(true);
×
1756

1757
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1758
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1759

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

1763
        $context = [AbstractItemNormalizer::DISABLE_TYPE_ENFORCEMENT => true];
×
1764
        $actual = $normalizer->denormalize($data, DtoWithNullValue::class, null, $context);
×
1765

1766
        $this->assertInstanceOf(DtoWithNullValue::class, $actual);
×
1767
        $this->assertEquals(new DtoWithNullValue(), $actual);
×
1768
    }
1769

1770
    public function testCacheKey(): void
1771
    {
1772
        $relatedDummy = new RelatedDummy();
×
1773

1774
        $dummy = new Dummy();
×
1775
        $dummy->setName('foo');
×
1776
        $dummy->setAlias('ignored');
×
1777
        $dummy->setRelatedDummy($relatedDummy);
×
1778

NEW
1779
        if ($dummy->relatedDummies instanceof Collection) {
×
NEW
1780
            $dummy->relatedDummies->add(new RelatedDummy());
×
1781
        }
1782

1783
        $relatedDummies = new ArrayCollection([$relatedDummy]);
×
1784

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

1788
        // BC layer for api-platform/metadata < 4.1
1789
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
1790
            $relatedDummyType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
1791
            $relatedDummiesType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $relatedDummyType);
×
1792

1793
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1794
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
1795
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
×
1796
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
1797
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(false));
×
1798
        } else {
1799
            $relatedDummyType = Type::object(RelatedDummy::class);
×
1800
            $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), $relatedDummyType, Type::int());
×
1801

1802
            $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
1803
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::type('array'))->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true));
×
1804
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', Argument::type('array'))->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(true));
×
1805
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', Argument::type('array'))->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
×
1806
            $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::type('array'))->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(true)->withWritable(false)->withReadableLink(false));
×
1807
        }
1808

1809
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
1810
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
×
1811
        $iriConverterProphecy->getIriFromResource($relatedDummy, Argument::cetera())->willReturn('/dummies/2');
×
1812

1813
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
1814
        $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('foo');
×
1815
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
×
1816
        $propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
×
1817

1818
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
1819
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
1820
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
1821
        $resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1822
        $resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
1823
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
1824
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
1825

1826
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
1827
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
1828
        $serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo');
×
1829
        $serializerProphecy->normalize(['/dummies/2'], null, Argument::type('array'))->willReturn(['/dummies/2']);
×
1830

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

1834
        $expected = [
×
1835
            'name' => 'foo',
×
1836
            'relatedDummy' => '/dummies/2',
×
1837
            'relatedDummies' => ['/dummies/2'],
×
1838
        ];
×
1839
        $this->assertSame($expected, $normalizer->normalize($dummy, null, [
×
1840
            'resources' => [],
×
1841
            'groups' => ['group'],
×
1842
            'ignored_attributes' => ['alias'],
×
1843
            'operation_name' => 'operation_name',
×
1844
            'root_operation_name' => 'root_operation_name',
×
1845
        ]));
×
1846

1847
        $operationCacheKey = (new \ReflectionClass($normalizer))->getProperty('localFactoryOptionsCache')->getValue($normalizer);
×
1848
        $this->assertEquals(array_keys($operationCacheKey), [\sprintf('%s%s%s%s', Dummy::class, 'operation_name', 'root_operation_name', 'n')]);
×
1849
        $this->assertEquals(current($operationCacheKey), ['serializer_groups' => ['group']]);
×
1850
    }
1851
}
1852

1853
class ObjectWithBasicProperties
1854
{
1855
    /** @var bool */
1856
    public $boolTrue1;
1857

1858
    /** @var bool */
1859
    public $boolFalse1;
1860

1861
    /** @var bool */
1862
    public $boolTrue2;
1863

1864
    /** @var bool */
1865
    public $boolFalse2;
1866

1867
    /** @var int */
1868
    public $int1;
1869

1870
    /** @var int */
1871
    public $int2;
1872

1873
    /** @var float */
1874
    public $float1;
1875

1876
    /** @var float */
1877
    public $float2;
1878

1879
    /** @var float */
1880
    public $float3;
1881

1882
    /** @var float */
1883
    public $floatNaN;
1884

1885
    /** @var float */
1886
    public $floatInf;
1887

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

© 2025 Coveralls, Inc