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

api-platform / core / 15394724286

02 Jun 2025 02:19PM UTC coverage: 22.201% (+0.1%) from 22.062%
15394724286

push

github

soyuka
feat(elasticsearch): add support for v9 (#7180)

Co-authored-by: darthf1 <17253332+darthf1@users.noreply.github.com>

10888 of 49042 relevant lines covered (22.2%)

10.59 hits per line

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

0.0
/src/JsonApi/Tests/Serializer/ItemNormalizerTest.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\JsonApi\Tests\Serializer;
15

16
use ApiPlatform\JsonApi\Serializer\ItemNormalizer;
17
use ApiPlatform\JsonApi\Serializer\ReservedAttributeNameConverter;
18
use ApiPlatform\JsonApi\Tests\Fixtures\CircularReference;
19
use ApiPlatform\JsonApi\Tests\Fixtures\Dummy;
20
use ApiPlatform\JsonApi\Tests\Fixtures\RelatedDummy;
21
use ApiPlatform\Metadata\ApiProperty;
22
use ApiPlatform\Metadata\ApiResource;
23
use ApiPlatform\Metadata\Get;
24
use ApiPlatform\Metadata\IriConverterInterface;
25
use ApiPlatform\Metadata\Operations;
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\ResourceClassResolverInterface;
32
use Doctrine\Common\Collections\ArrayCollection;
33
use PHPUnit\Framework\TestCase;
34
use Prophecy\Argument;
35
use Prophecy\PhpUnit\ProphecyTrait;
36
use Symfony\Component\HttpFoundation\EventStreamResponse;
37
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
38
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
39
use Symfony\Component\PropertyInfo\Type;
40
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
41
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
42
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
43
use Symfony\Component\Serializer\Serializer;
44
use Symfony\Component\Serializer\SerializerInterface;
45

46
/**
47
 * @author Amrouche Hamza <hamza.simperfit@gmail.com>
48
 */
49
class ItemNormalizerTest extends TestCase
50
{
51
    use ProphecyTrait;
52

53
    public function testNormalize(): void
54
    {
55
        $dummy = new Dummy();
×
56
        $dummy->setId(10);
×
57
        $dummy->setName('hello');
×
58

59
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
60
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::any())->willReturn(new PropertyNameCollection(['id', 'name', '\bad_property']));
×
61

62
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
63
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::any())->willReturn((new ApiProperty())->withReadable(true));
×
64
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'id', Argument::any())->willReturn((new ApiProperty())->withReadable(true)->withIdentifier(true));
×
65
        $propertyMetadataFactoryProphecy->create(Dummy::class, '\bad_property', Argument::any())->willReturn((new ApiProperty())->withReadable(true));
×
66

67
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
68
        $iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/10');
×
69

70
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
71
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
72
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
73
        $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class)->willReturn(Dummy::class);
×
74
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
75
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
76

77
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
78
        $propertyAccessorProphecy->getValue($dummy, 'id')->willReturn(10);
×
79
        $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('hello');
×
80

81
        $resourceMetadataCollectionFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
82
        $resourceMetadataCollectionFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadataCollection('Dummy', [
×
83
            (new ApiResource())
×
84
                ->withShortName('Dummy')
×
85
                ->withDescription('A dummy')
×
86
                ->withUriTemplate('/dummy')
×
87
                ->withOperations(new Operations(['get' => (new Get())->withShortName('Dummy')])),
×
88
        ]));
×
89

90
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
91
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
92
        $serializerProphecy->normalize('hello', ItemNormalizer::FORMAT, Argument::type('array'))->willReturn('hello');
×
93
        $serializerProphecy->normalize(10, ItemNormalizer::FORMAT, Argument::type('array'))->willReturn(10);
×
94
        $serializerProphecy->normalize(null, ItemNormalizer::FORMAT, Argument::type('array'))->willReturn(null);
×
95

96
        $normalizer = new ItemNormalizer(
×
97
            $propertyNameCollectionFactoryProphecy->reveal(),
×
98
            $propertyMetadataFactoryProphecy->reveal(),
×
99
            $iriConverterProphecy->reveal(),
×
100
            $resourceClassResolverProphecy->reveal(),
×
101
            $propertyAccessorProphecy->reveal(),
×
102
            new ReservedAttributeNameConverter(),
×
103
            null,
×
104
            [],
×
105
            $resourceMetadataCollectionFactoryProphecy->reveal(),
×
106
        );
×
107

108
        $normalizer->setSerializer($serializerProphecy->reveal());
×
109

110
        $expected = [
×
111
            'data' => [
×
112
                'type' => 'Dummy',
×
113
                'id' => '/dummies/10',
×
114
                'attributes' => [
×
115
                    '_id' => 10,
×
116
                    'name' => 'hello',
×
117
                ],
×
118
            ],
×
119
        ];
×
120

121
        $this->assertEquals($expected, $normalizer->normalize($dummy, ItemNormalizer::FORMAT));
×
122
    }
123

124
    public function testNormalizeCircularReference(): void
125
    {
126
        $circularReferenceEntity = new CircularReference();
×
127
        $circularReferenceEntity->id = 1;
×
128
        $circularReferenceEntity->parent = $circularReferenceEntity;
×
129

130
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
131
        $iriConverterProphecy->getIriFromResource($circularReferenceEntity, Argument::cetera())->willReturn('/circular_references/1');
×
132

133
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
134
        $resourceClassResolverProphecy->isResourceClass(CircularReference::class)->willReturn(true);
×
135
        $resourceClassResolverProphecy->getResourceClass($circularReferenceEntity, null)->willReturn(CircularReference::class);
×
136
        $resourceClassResolverProphecy->getResourceClass($circularReferenceEntity, CircularReference::class)->willReturn(CircularReference::class);
×
137
        $resourceClassResolverProphecy->isResourceClass(Argument::type('string'))->willReturn(true);
×
138

139
        $resourceMetadataCollectionFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
140
        $resourceMetadataCollectionFactoryProphecy->create(CircularReference::class)->willReturn(new ResourceMetadataCollection('CircularReference'));
×
141

142
        $normalizer = new ItemNormalizer(
×
143
            $this->prophesize(PropertyNameCollectionFactoryInterface::class)->reveal(),
×
144
            $this->prophesize(PropertyMetadataFactoryInterface::class)->reveal(),
×
145
            $iriConverterProphecy->reveal(),
×
146
            $resourceClassResolverProphecy->reveal(),
×
147
            $this->prophesize(PropertyAccessorInterface::class)->reveal(),
×
148
            new ReservedAttributeNameConverter(),
×
149
            null,
×
150
            [],
×
151
            $resourceMetadataCollectionFactoryProphecy->reveal(),
×
152
        );
×
153

154
        $normalizer->setSerializer($this->prophesize(SerializerInterface::class)->reveal());
×
155

156
        // Symfony >= 7.3
157
        $splObject = class_exists(EventStreamResponse::class) ? spl_object_id($circularReferenceEntity) : spl_object_hash($circularReferenceEntity);
×
158
        $context = [
×
159
            'circular_reference_limit' => 2,
×
160
            'circular_reference_limit_counters' => [$splObject => 2],
×
161
            'cache_error' => function (): void {},
×
162
        ];
×
163

164
        $this->assertSame('/circular_references/1', $normalizer->normalize($circularReferenceEntity, ItemNormalizer::FORMAT, $context));
×
165
    }
166

167
    public function testNormalizeNonExistentProperty(): void
168
    {
169
        $this->expectException(NoSuchPropertyException::class);
×
170

171
        $dummy = new Dummy();
×
172
        $dummy->setId(1);
×
173

174
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
175
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::any())->willReturn(new PropertyNameCollection(['bar']));
×
176

177
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
178
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'bar', Argument::any())->willReturn((new ApiProperty())->withReadable(true));
×
179

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

183
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
184
        $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
×
185
        $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class)->willReturn(Dummy::class);
×
186
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
187
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
188

189
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
190
        $propertyAccessorProphecy->getValue($dummy, 'bar')->willThrow(new NoSuchPropertyException());
×
191

192
        $serializerProphecy = $this->prophesize(Serializer::class);
×
193

194
        $resourceMetadataCollectionFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
195
        $resourceMetadataCollectionFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadataCollection('Dummy', [
×
196
            (new ApiResource())
×
197
                ->withShortName('Dummy')
×
198
                ->withOperations(new Operations(['get' => (new Get())->withShortName('Dummy')])),
×
199
        ]));
×
200

201
        $normalizer = new ItemNormalizer(
×
202
            $propertyNameCollectionFactoryProphecy->reveal(),
×
203
            $propertyMetadataFactoryProphecy->reveal(),
×
204
            $iriConverterProphecy->reveal(),
×
205
            $resourceClassResolverProphecy->reveal(),
×
206
            $propertyAccessorProphecy->reveal(),
×
207
            new ReservedAttributeNameConverter(),
×
208
            null,
×
209
            [],
×
210
            $resourceMetadataCollectionFactoryProphecy->reveal(),
×
211
        );
×
212

213
        $normalizer->setSerializer($serializerProphecy->reveal());
×
214

215
        $normalizer->normalize($dummy, ItemNormalizer::FORMAT);
×
216
    }
217

218
    public function testDenormalize(): void
219
    {
220
        $data = [
×
221
            'data' => [
×
222
                'type' => 'dummy',
×
223
                'attributes' => [
×
224
                    'name' => 'foo',
×
225
                    'ghost' => 'invisible',
×
226
                ],
×
227
                'relationships' => [
×
228
                    'relatedDummy' => [
×
229
                        'data' => [
×
230
                            'type' => 'related-dummy',
×
231
                            'id' => '/related_dummies/1',
×
232
                        ],
×
233
                    ],
×
234
                    'relatedDummies' => [
×
235
                        'data' => [
×
236
                            [
×
237
                                'type' => 'related-dummy',
×
238
                                'id' => '/related_dummies/2',
×
239
                            ],
×
240
                        ],
×
241
                    ],
×
242
                ],
×
243
            ],
×
244
        ];
×
245

246
        $relatedDummy1 = new RelatedDummy();
×
247
        $relatedDummy1->setId(1);
×
248
        $relatedDummy2 = new RelatedDummy();
×
249
        $relatedDummy2->setId(2);
×
250

251
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
252
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::any())->willReturn(new PropertyNameCollection(['name', 'ghost', 'relatedDummy', 'relatedDummies']));
×
253

254
        $relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
×
255
        $relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
×
256

257
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
258
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::any())->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
259
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'ghost', Argument::any())->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(false)->withWritable(true));
×
260
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', Argument::any())->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
261
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::any())->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
262

263
        $getItemFromIriSecondArgCallback = fn ($arg): bool => \is_array($arg) && isset($arg['fetch_data']) && true === $arg['fetch_data'];
×
264

265
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
266
        $iriConverterProphecy->getResourceFromIri('/related_dummies/1', Argument::that($getItemFromIriSecondArgCallback))->willReturn($relatedDummy1);
×
267
        $iriConverterProphecy->getResourceFromIri('/related_dummies/2', Argument::that($getItemFromIriSecondArgCallback))->willReturn($relatedDummy2);
×
268

269
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
270
        $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'name', 'foo')->will(function (): void {});
×
271
        $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'ghost', 'invisible')->willThrow(new NoSuchPropertyException());
×
272
        $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummy', $relatedDummy1)->will(function (): void {});
×
273
        $propertyAccessorProphecy->setValue(Argument::type(Dummy::class), 'relatedDummies', [$relatedDummy2])->will(function (): void {});
×
274

275
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
276
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
277
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
278
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
279
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
280

281
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
282
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
283

284
        $resourceMetadataCollectionFactory = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
285
        $resourceMetadataCollectionFactory->create(Dummy::class)->willReturn(new ResourceMetadataCollection(Dummy::class, [
×
286
            (new ApiResource())->withOperations(new Operations([new Get(name: 'get')])),
×
287
        ]
×
288
        ));
×
289
        $resourceMetadataCollectionFactory->create(RelatedDummy::class)->willReturn(new ResourceMetadataCollection(RelatedDummy::class, [
×
290
            (new ApiResource())->withOperations(new Operations([new Get(name: 'get')])),
×
291
        ]
×
292
        ));
×
293

294
        $normalizer = new ItemNormalizer(
×
295
            $propertyNameCollectionFactoryProphecy->reveal(),
×
296
            $propertyMetadataFactoryProphecy->reveal(),
×
297
            $iriConverterProphecy->reveal(),
×
298
            $resourceClassResolverProphecy->reveal(),
×
299
            $propertyAccessorProphecy->reveal(),
×
300
            new ReservedAttributeNameConverter(),
×
301
            null,
×
302
            [],
×
303
            $resourceMetadataCollectionFactory->reveal(),
×
304
        );
×
305
        $normalizer->setSerializer($serializerProphecy->reveal());
×
306

307
        $this->assertInstanceOf(Dummy::class, $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT));
×
308
    }
309

310
    public function testDenormalizeUpdateOperationNotAllowed(): void
311
    {
312
        $this->expectException(NotNormalizableValueException::class);
×
313
        $this->expectExceptionMessage('Update is not allowed for this operation.');
×
314

315
        $normalizer = new ItemNormalizer(
×
316
            $this->prophesize(PropertyNameCollectionFactoryInterface::class)->reveal(),
×
317
            $this->prophesize(PropertyMetadataFactoryInterface::class)->reveal(),
×
318
            $this->prophesize(IriConverterInterface::class)->reveal(),
×
319
            $this->prophesize(ResourceClassResolverInterface::class)->reveal(),
×
320
        );
×
321

322
        $normalizer->denormalize(
×
323
            [
×
324
                'data' => [
×
325
                    'id' => 1,
×
326
                    'type' => 'dummy',
×
327
                ],
×
328
            ],
×
329
            Dummy::class,
×
330
            ItemNormalizer::FORMAT,
×
331
            [
×
332
                'api_allow_update' => false,
×
333
            ]
×
334
        );
×
335
    }
336

337
    public function testDenormalizeCollectionIsNotArray(): void
338
    {
339
        $this->expectException(NotNormalizableValueException::class);
×
340
        $this->expectExceptionMessage('The type of the "relatedDummies" attribute must be "array", "string" given.');
×
341

342
        $data = [
×
343
            'data' => [
×
344
                'type' => 'dummy',
×
345
                'relationships' => [
×
346
                    'relatedDummies' => [
×
347
                        'data' => 'foo',
×
348
                    ],
×
349
                ],
×
350
            ],
×
351
        ];
×
352

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

356
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
357

358
        $type = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class));
×
359
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn((new ApiProperty())
×
360
                ->withBuiltinTypes([$type])
×
361
                ->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
362
        );
×
363

364
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
365

366
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
367
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
368
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
369
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
370
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
371

372
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
373

374
        $normalizer = new ItemNormalizer(
×
375
            $propertyNameCollectionFactoryProphecy->reveal(),
×
376
            $propertyMetadataFactoryProphecy->reveal(),
×
377
            $iriConverterProphecy->reveal(),
×
378
            $resourceClassResolverProphecy->reveal(),
×
379
            $propertyAccessorProphecy->reveal(),
×
380
            new ReservedAttributeNameConverter(),
×
381
            null,
×
382
            []
×
383
        );
×
384

385
        $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT);
×
386
    }
387

388
    public function testDenormalizeCollectionWithInvalidKey(): void
389
    {
390
        $this->expectException(NotNormalizableValueException::class);
×
391
        $this->expectExceptionMessage('The type of the key "0" must be "string", "integer" given.');
×
392

393
        $data = [
×
394
            'data' => [
×
395
                'type' => 'dummy',
×
396
                'relationships' => [
×
397
                    'relatedDummies' => [
×
398
                        'data' => [
×
399
                            [
×
400
                                'type' => 'related-dummy',
×
401
                                'id' => '2',
×
402
                            ],
×
403
                        ],
×
404
                    ],
×
405
                ],
×
406
            ],
×
407
        ];
×
408

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

412
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
413
        $type = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class));
×
414
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', [])->willReturn(
×
415
            (new ApiProperty())->withBuiltinTypes([$type])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
416
        );
×
417

418
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
419

420
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
421
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
422
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
423
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
424
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
425

426
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
427

428
        $normalizer = new ItemNormalizer(
×
429
            $propertyNameCollectionFactoryProphecy->reveal(),
×
430
            $propertyMetadataFactoryProphecy->reveal(),
×
431
            $iriConverterProphecy->reveal(),
×
432
            $resourceClassResolverProphecy->reveal(),
×
433
            $propertyAccessorProphecy->reveal(),
×
434
            new ReservedAttributeNameConverter(),
×
435
            null,
×
436
            []
×
437
        );
×
438

439
        $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT);
×
440
    }
441

442
    public function testDenormalizeRelationIsNotResourceLinkage(): void
443
    {
444
        $this->expectException(UnexpectedValueException::class);
×
445
        $this->expectExceptionMessage('Only resource linkage supported currently, see: http://jsonapi.org/format/#document-resource-object-linkage.');
×
446

447
        $data = [
×
448
            'data' => [
×
449
                'type' => 'dummy',
×
450
                'relationships' => [
×
451
                    'relatedDummy' => [
×
452
                        'data' => 'foo',
×
453
                    ],
×
454
                ],
×
455
            ],
×
456
        ];
×
457

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

461
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
462
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn(
×
463
            (new ApiProperty())->withBuiltinTypes([
×
464
                new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class), ])->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
465
        );
×
466

467
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
468

469
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
470
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
471
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
472
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
473
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
474

475
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
476

477
        $normalizer = new ItemNormalizer(
×
478
            $propertyNameCollectionFactoryProphecy->reveal(),
×
479
            $propertyMetadataFactoryProphecy->reveal(),
×
480
            $iriConverterProphecy->reveal(),
×
481
            $resourceClassResolverProphecy->reveal(),
×
482
            $propertyAccessorProphecy->reveal(),
×
483
            new ReservedAttributeNameConverter(),
×
484
            null,
×
485
            []
×
486
        );
×
487

488
        $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT);
×
489
    }
490
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc