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

api-platform / core / 18089937549

29 Sep 2025 07:56AM UTC coverage: 21.764% (-0.3%) from 22.093%
18089937549

Pull #7416

github

web-flow
Merge 061bcc790 into abe0438be
Pull Request #7416: fix(laravel): serializer attributes on Eloquent methods

0 of 151 new or added lines in 11 files covered. (0.0%)

5028 existing lines in 173 files now uncovered.

11889 of 54626 relevant lines covered (21.76%)

25.32 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\Serializer\Exception\NotNormalizableValueException;
40
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
41
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
42
use Symfony\Component\Serializer\Serializer;
43
use Symfony\Component\Serializer\SerializerInterface;
44
use Symfony\Component\TypeInfo\Type;
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::type('array'))->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(null, CircularReference::class)->willReturn(CircularReference::class);
×
137

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

UNCOV
141
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
142
        $propertyNameCollectionFactoryProphecy->create(CircularReference::class, [])->willReturn(new PropertyNameCollection());
×
143

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

UNCOV
156
        $normalizer->setSerializer($this->prophesize(SerializerInterface::class)->reveal());
×
157

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

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

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

UNCOV
173
        $dummy = new Dummy();
×
174
        $dummy->setId(1);
×
175

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

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

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

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

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

194
        $serializerProphecy = $this->prophesize(Serializer::class);
×
195

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

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

215
        $normalizer->setSerializer($serializerProphecy->reveal());
×
216

UNCOV
217
        $normalizer->normalize($dummy, ItemNormalizer::FORMAT);
×
218
    }
219

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

248
        $relatedDummy1 = new RelatedDummy();
×
249
        $relatedDummy1->setId(1);
×
UNCOV
250
        $relatedDummy2 = new RelatedDummy();
×
251
        $relatedDummy2->setId(2);
×
252

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

UNCOV
256
        $relatedDummyType = Type::object(RelatedDummy::class);
×
257
        $relatedDummiesType = Type::collection(Type::object(ArrayCollection::class), Type::object(RelatedDummy::class), Type::int());
×
258

259
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
260
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::any())->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
261
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'ghost', Argument::any())->willReturn((new ApiProperty())->withNativeType(Type::string())->withDescription('')->withReadable(false)->withWritable(true));
×
UNCOV
262
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', Argument::any())->willReturn((new ApiProperty())->withNativeType($relatedDummyType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
263
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::any())->willReturn((new ApiProperty())->withNativeType($relatedDummiesType)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false));
×
264

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

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

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

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

UNCOV
283
        $serializerProphecy = $this->prophesize(SerializerInterface::class);
×
284
        $serializerProphecy->willImplement(NormalizerInterface::class);
×
285

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

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

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

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

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

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

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

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

UNCOV
355
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
356
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['relatedDummies']));
×
357

358
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
359

360
        $type = Type::collection(Type::object(ArrayCollection::class), Type::object(RelatedDummy::class), Type::int());
×
361
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::type('array'))->willReturn((new ApiProperty())
×
362
                ->withNativeType($type)
×
UNCOV
363
                ->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
364
        );
×
365

366
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
367

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

374
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
375

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

UNCOV
387
        $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT);
×
388
    }
389

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

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

UNCOV
411
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
412
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['relatedDummies']));
×
413

414
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
415
        $type = Type::collection(Type::object(ArrayCollection::class), Type::object(RelatedDummy::class), Type::string());
×
416
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::type('array'))->willReturn(
×
UNCOV
417
            (new ApiProperty())->withNativeType($type)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
418
        );
×
419

420
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
421

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

428
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
429

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

UNCOV
441
        $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT);
×
442
    }
443

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

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

UNCOV
460
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
461
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['relatedDummy']));
×
462

463
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
464
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', Argument::type('array'))->willReturn(
×
465
            (new ApiProperty())->withNativeType(Type::object(RelatedDummy::class))->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
UNCOV
466
        );
×
467

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

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

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

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

UNCOV
489
        $normalizer->denormalize($data, Dummy::class, ItemNormalizer::FORMAT);
×
490
    }
491
}
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