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

api-platform / core / 15775135891

20 Jun 2025 08:42AM UTC coverage: 22.065% (+0.2%) from 21.876%
15775135891

push

github

soyuka
Merge 4.1

13 of 103 new or added lines in 10 files covered. (12.62%)

868 existing lines in 35 files now uncovered.

11487 of 52060 relevant lines covered (22.06%)

21.72 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::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(null, CircularReference::class)->willReturn(CircularReference::class);
×
137

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

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
            [],
×
153
            $resourceMetadataCollectionFactoryProphecy->reveal(),
×
154
        );
×
155

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 = [
×
UNCOV
161
            'circular_reference_limit' => 2,
×
162
            'circular_reference_limit_counters' => [$splObject => 2],
×
163
            'cache_error' => function (): void {},
×
164
        ];
×
165

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

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

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

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

UNCOV
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

UNCOV
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

UNCOV
203
        $normalizer = new ItemNormalizer(
×
204
            $propertyNameCollectionFactoryProphecy->reveal(),
×
UNCOV
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

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

220
    public function testDenormalize(): void
221
    {
222
        $data = [
×
223
            'data' => [
×
UNCOV
224
                'type' => 'dummy',
×
225
                'attributes' => [
×
UNCOV
226
                    'name' => 'foo',
×
227
                    'ghost' => 'invisible',
×
UNCOV
228
                ],
×
UNCOV
229
                'relationships' => [
×
UNCOV
230
                    'relatedDummy' => [
×
UNCOV
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
                ],
×
245
            ],
×
246
        ];
×
247

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

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

256
        $relatedDummyType = Type::object(RelatedDummy::class);
×
UNCOV
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

UNCOV
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

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

UNCOV
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, [
×
UNCOV
292
            (new ApiResource())->withOperations(new Operations([new Get(name: 'get')])),
×
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
            [],
×
UNCOV
305
            $resourceMetadataCollectionFactory->reveal(),
×
306
        );
×
307
        $normalizer->setSerializer($serializerProphecy->reveal());
×
308

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

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

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

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

339
    public function testDenormalizeCollectionIsNotArray(): void
340
    {
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',
×
UNCOV
347
                'relationships' => [
×
UNCOV
348
                    'relatedDummies' => [
×
UNCOV
349
                        'data' => 'foo',
×
UNCOV
350
                    ],
×
351
                ],
×
352
            ],
×
UNCOV
353
        ];
×
354

355
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
356
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->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', [])->willReturn((new ApiProperty())
×
362
                ->withNativeType($type)
×
363
                ->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
UNCOV
364
        );
×
365

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

368
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
369
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
370
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
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(
×
UNCOV
377
            $propertyNameCollectionFactoryProphecy->reveal(),
×
378
            $propertyMetadataFactoryProphecy->reveal(),
×
379
            $iriConverterProphecy->reveal(),
×
380
            $resourceClassResolverProphecy->reveal(),
×
381
            $propertyAccessorProphecy->reveal(),
×
382
            new ReservedAttributeNameConverter(),
×
UNCOV
383
            null,
×
384
            []
×
UNCOV
385
        );
×
386

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

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

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

411
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
412
        $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->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', [])->willReturn(
×
417
            (new ApiProperty())->withNativeType($type)->withReadable(false)->withWritable(true)->withReadableLink(false)->withWritableLink(false)
×
418
        );
×
419

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

422
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
UNCOV
423
        $resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
×
424
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
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(
×
UNCOV
431
            $propertyNameCollectionFactoryProphecy->reveal(),
×
432
            $propertyMetadataFactoryProphecy->reveal(),
×
433
            $iriConverterProphecy->reveal(),
×
434
            $resourceClassResolverProphecy->reveal(),
×
435
            $propertyAccessorProphecy->reveal(),
×
436
            new ReservedAttributeNameConverter(),
×
UNCOV
437
            null,
×
438
            []
×
UNCOV
439
        );
×
440

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

444
    public function testDenormalizeRelationIsNotResourceLinkage(): void
445
    {
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 = [
×
UNCOV
450
            'data' => [
×
451
                'type' => 'dummy',
×
UNCOV
452
                'relationships' => [
×
UNCOV
453
                    'relatedDummy' => [
×
UNCOV
454
                        'data' => 'foo',
×
UNCOV
455
                    ],
×
456
                ],
×
457
            ],
×
UNCOV
458
        ];
×
459

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

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

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

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

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

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

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

© 2025 Coveralls, Inc