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

api-platform / core / 17729190580

15 Sep 2025 09:57AM UTC coverage: 22.227% (-0.4%) from 22.578%
17729190580

push

github

web-flow
fix(metadata): compute isWritable during updates (#7383)

fixes #7382

4 of 532 new or added lines in 16 files covered. (0.75%)

167 existing lines in 22 files now uncovered.

11127 of 50061 relevant lines covered (22.23%)

23.49 hits per line

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

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

16
use ApiPlatform\Hal\Serializer\ItemNormalizer;
17
use ApiPlatform\Hal\Tests\Fixtures\ApiResource\Issue5452\ActivableInterface;
18
use ApiPlatform\Hal\Tests\Fixtures\ApiResource\Issue5452\Author;
19
use ApiPlatform\Hal\Tests\Fixtures\ApiResource\Issue5452\Book;
20
use ApiPlatform\Hal\Tests\Fixtures\ApiResource\Issue5452\Library;
21
use ApiPlatform\Hal\Tests\Fixtures\ApiResource\Issue5452\TimestampableInterface;
22
use ApiPlatform\Hal\Tests\Fixtures\Dummy;
23
use ApiPlatform\Hal\Tests\Fixtures\MaxDepthDummy;
24
use ApiPlatform\Hal\Tests\Fixtures\RelatedDummy;
25
use ApiPlatform\Metadata\ApiProperty;
26
use ApiPlatform\Metadata\IriConverterInterface;
27
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
28
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
29
use ApiPlatform\Metadata\Property\PropertyNameCollection;
30
use ApiPlatform\Metadata\ResourceClassResolverInterface;
31
use PHPUnit\Framework\TestCase;
32
use Symfony\Component\PropertyInfo\Type;
33
use Symfony\Component\Serializer\Exception\LogicException;
34
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
35
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
36
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
37
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
38
use Symfony\Component\Serializer\Serializer;
39

40
/**
41
 * @author Kévin Dunglas <dunglas@gmail.com>
42
 */
43
class ItemNormalizerTest extends TestCase
44
{
45
    public function testDoesNotSupportDenormalization(): void
46
    {
NEW
47
        $this->expectException(LogicException::class);
×
NEW
48
        $this->expectExceptionMessage('jsonhal is a read-only format.');
×
49

NEW
50
        $propertyNameCollectionFactoryMock = $this->createMock(PropertyNameCollectionFactoryInterface::class);
×
NEW
51
        $propertyMetadataFactoryMock = $this->createMock(PropertyMetadataFactoryInterface::class);
×
NEW
52
        $iriConverterMock = $this->createMock(IriConverterInterface::class);
×
NEW
53
        $resourceClassResolverMock = $this->createMock(ResourceClassResolverInterface::class);
×
NEW
54
        $nameConverter = $this->createMock(NameConverterInterface::class);
×
55

NEW
56
        $normalizer = new ItemNormalizer(
×
NEW
57
            $propertyNameCollectionFactoryMock,
×
NEW
58
            $propertyMetadataFactoryMock,
×
NEW
59
            $iriConverterMock,
×
NEW
60
            $resourceClassResolverMock,
×
NEW
61
            null,
×
NEW
62
            $nameConverter
×
NEW
63
        );
×
64

NEW
65
        $this->assertFalse($normalizer->supportsDenormalization('foo', ItemNormalizer::FORMAT));
×
NEW
66
        $normalizer->denormalize(['foo'], 'Foo');
×
67
    }
68

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

NEW
75
        $propertyNameCollectionFactoryMock = $this->createMock(PropertyNameCollectionFactoryInterface::class);
×
NEW
76
        $propertyMetadataFactoryMock = $this->createMock(PropertyMetadataFactoryInterface::class);
×
NEW
77
        $iriConverterMock = $this->createMock(IriConverterInterface::class);
×
78

NEW
79
        $resourceClassResolverMock = $this->createMock(ResourceClassResolverInterface::class);
×
NEW
80
        $resourceClassResolverMock->method('isResourceClass')->willReturnMap([
×
NEW
81
            [Dummy::class, true],
×
NEW
82
            [\stdClass::class, false],
×
NEW
83
        ]);
×
84

NEW
85
        $nameConverter = $this->createMock(NameConverterInterface::class);
×
86

NEW
87
        $normalizer = new ItemNormalizer(
×
NEW
88
            $propertyNameCollectionFactoryMock,
×
NEW
89
            $propertyMetadataFactoryMock,
×
NEW
90
            $iriConverterMock,
×
NEW
91
            $resourceClassResolverMock,
×
NEW
92
            null,
×
NEW
93
            $nameConverter
×
NEW
94
        );
×
95

NEW
96
        $this->assertTrue($normalizer->supportsNormalization($dummy, $normalizer::FORMAT));
×
NEW
97
        $this->assertFalse($normalizer->supportsNormalization($dummy, 'xml'));
×
NEW
98
        $this->assertFalse($normalizer->supportsNormalization($std, $normalizer::FORMAT));
×
NEW
99
        $this->assertEmpty($normalizer->getSupportedTypes('xml'));
×
NEW
100
        $this->assertSame(['object' => true], $normalizer->getSupportedTypes($normalizer::FORMAT));
×
101
    }
102

103
    public function testNormalize(): void
104
    {
NEW
105
        $relatedDummy = new RelatedDummy();
×
NEW
106
        $dummy = new Dummy();
×
NEW
107
        $dummy->setName('hello');
×
NEW
108
        $dummy->setRelatedDummy($relatedDummy);
×
109

NEW
110
        $propertyNameCollection = new PropertyNameCollection(['name', 'relatedDummy']);
×
NEW
111
        $propertyNameCollectionFactoryMock = $this->createMock(PropertyNameCollectionFactoryInterface::class);
×
NEW
112
        $propertyNameCollectionFactoryMock->method('create')->with(Dummy::class, ['api_allow_update' => false])->willReturn($propertyNameCollection);
×
113

NEW
114
        $propertyMetadataFactoryMock = $this->createMock(PropertyMetadataFactoryInterface::class);
×
NEW
115
        $propertyMetadataFactoryMock->method('create')->willReturnMap([
×
NEW
116
            [Dummy::class, 'name', ['api_allow_update' => false], (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)],
×
NEW
117
            [Dummy::class, 'relatedDummy', ['api_allow_update' => false], (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withDescription('')->withReadable(true)->withWritable(false)->withWritableLink(false)],
×
NEW
118
        ]);
×
119

NEW
120
        $iriConverterMock = $this->createMock(IriConverterInterface::class);
×
NEW
121
        $iriConverterMock->method('getIriFromResource')->willReturnCallback(
×
NEW
122
            function ($resource) use ($dummy, $relatedDummy) {
×
NEW
123
                if ($resource === $dummy) {
×
NEW
124
                    return '/dummies/1';
×
125
                }
NEW
126
                if ($resource === $relatedDummy) {
×
NEW
127
                    return '/related-dummies/2';
×
128
                }
129

NEW
130
                return null;
×
NEW
131
            }
×
NEW
132
        );
×
133

NEW
134
        $resourceClassResolverMock = $this->createMock(ResourceClassResolverInterface::class);
×
NEW
135
        $resourceClassResolverMock->method('isResourceClass')->willReturn(true);
×
NEW
136
        $resourceClassResolverMock->method('getResourceClass')->willReturnMap([
×
NEW
137
            [$dummy, null, Dummy::class],
×
NEW
138
            [null, Dummy::class, Dummy::class],
×
NEW
139
            [$dummy, Dummy::class, Dummy::class],
×
NEW
140
            [$relatedDummy, RelatedDummy::class, RelatedDummy::class],
×
NEW
141
        ]);
×
142

NEW
143
        $serializerMock = $this->createMock(Serializer::class);
×
NEW
144
        $serializerMock->method('normalize')->with('hello', null, $this->isType('array'))->willReturn('hello');
×
145

NEW
146
        $nameConverter = $this->createMock(NameConverterInterface::class);
×
NEW
147
        $nameConverter->method('normalize')->willReturnCallback(
×
NEW
148
            static function (string $propertyName): string {
×
NEW
149
                if ('relatedDummy' === $propertyName) {
×
NEW
150
                    return 'related_dummy';
×
151
                }
152

NEW
153
                return $propertyName;
×
NEW
154
            }
×
NEW
155
        );
×
156

NEW
157
        $normalizer = new ItemNormalizer(
×
NEW
158
            $propertyNameCollectionFactoryMock,
×
NEW
159
            $propertyMetadataFactoryMock,
×
NEW
160
            $iriConverterMock,
×
NEW
161
            $resourceClassResolverMock,
×
NEW
162
            null,
×
NEW
163
            $nameConverter
×
NEW
164
        );
×
NEW
165
        $normalizer->setSerializer($serializerMock);
×
166

NEW
167
        $expected = [
×
NEW
168
            '_links' => [
×
NEW
169
                'self' => [
×
NEW
170
                    'href' => '/dummies/1',
×
NEW
171
                ],
×
NEW
172
                'related_dummy' => [
×
NEW
173
                    'href' => '/related-dummies/2',
×
NEW
174
                ],
×
NEW
175
            ],
×
NEW
176
            'name' => 'hello',
×
NEW
177
        ];
×
NEW
178
        $this->assertEquals($expected, $normalizer->normalize($dummy));
×
179
    }
180

181
    public function testNormalizeWithUnionIntersectTypes(): void
182
    {
NEW
183
        $author = new Author(id: 2, name: 'Isaac Asimov');
×
NEW
184
        $library = new Library(id: 3, name: 'Le Bâteau Livre');
×
NEW
185
        $book = new Book();
×
NEW
186
        $book->author = $author;
×
NEW
187
        $book->library = $library;
×
188

NEW
189
        $propertyNameCollection = new PropertyNameCollection(['author', 'library']);
×
NEW
190
        $propertyNameCollectionFactoryMock = $this->createMock(PropertyNameCollectionFactoryInterface::class);
×
NEW
191
        $propertyNameCollectionFactoryMock->method('create')->with(Book::class, ['api_allow_update' => false])->willReturn($propertyNameCollection);
×
192

NEW
193
        $propertyMetadataFactoryMock = $this->createMock(PropertyMetadataFactoryInterface::class);
×
NEW
194
        $propertyMetadataFactoryMock->method('create')->willReturnMap([
×
NEW
195
            [Book::class, 'author', ['api_allow_update' => false], (new ApiProperty())->withBuiltinTypes([
×
NEW
196
                new Type(Type::BUILTIN_TYPE_OBJECT, false, ActivableInterface::class),
×
NEW
197
                new Type(Type::BUILTIN_TYPE_OBJECT, false, TimestampableInterface::class),
×
NEW
198
            ])->withReadable(true)],
×
NEW
199
            [Book::class, 'library', ['api_allow_update' => false], (new ApiProperty())->withBuiltinTypes([
×
NEW
200
                new Type(Type::BUILTIN_TYPE_OBJECT, false, ActivableInterface::class),
×
NEW
201
                new Type(Type::BUILTIN_TYPE_OBJECT, false, TimestampableInterface::class),
×
NEW
202
            ])->withReadable(true)],
×
NEW
203
        ]);
×
204

NEW
205
        $iriConverterMock = $this->createMock(IriConverterInterface::class);
×
NEW
206
        $iriConverterMock->method('getIriFromResource')->with($book)->willReturn('/books/1');
×
207

NEW
208
        $resourceClassResolverMock = $this->createMock(ResourceClassResolverInterface::class);
×
NEW
209
        $resourceClassResolverMock->method('isResourceClass')->willReturnMap([
×
NEW
210
            [Book::class, true],
×
NEW
211
            [ActivableInterface::class, false],
×
NEW
212
            [TimestampableInterface::class, false],
×
NEW
213
        ]);
×
NEW
214
        $resourceClassResolverMock->method('getResourceClass')->willReturnMap([
×
NEW
215
            [$book, null, Book::class],
×
NEW
216
            [null, Book::class, Book::class],
×
NEW
217
        ]);
×
218

NEW
219
        $serializerMock = $this->createMock(Serializer::class);
×
220

NEW
221
        $nameConverter = $this->createMock(NameConverterInterface::class);
×
NEW
222
        $nameConverter->method('normalize')->willReturnCallback(
×
NEW
223
            static fn (string $propertyName): string => $propertyName
×
NEW
224
        );
×
225

NEW
226
        $normalizer = new ItemNormalizer(
×
NEW
227
            $propertyNameCollectionFactoryMock,
×
NEW
228
            $propertyMetadataFactoryMock,
×
NEW
229
            $iriConverterMock,
×
NEW
230
            $resourceClassResolverMock,
×
NEW
231
            null,
×
NEW
232
            $nameConverter
×
NEW
233
        );
×
NEW
234
        $normalizer->setSerializer($serializerMock);
×
235

NEW
236
        $expected = [
×
NEW
237
            '_links' => [
×
NEW
238
                'self' => [
×
NEW
239
                    'href' => '/books/1',
×
NEW
240
                ],
×
NEW
241
            ],
×
NEW
242
            'author' => null,
×
NEW
243
            'library' => null,
×
NEW
244
        ];
×
NEW
245
        $this->assertEquals($expected, $normalizer->normalize($book));
×
246
    }
247

248
    public function testNormalizeWithoutCache(): void
249
    {
NEW
250
        $relatedDummy = new RelatedDummy();
×
NEW
251
        $dummy = new Dummy();
×
NEW
252
        $dummy->setName('hello');
×
NEW
253
        $dummy->setRelatedDummy($relatedDummy);
×
254

NEW
255
        $propertyNameCollection = new PropertyNameCollection(['name', 'relatedDummy']);
×
NEW
256
        $propertyNameCollectionFactoryMock = $this->createMock(PropertyNameCollectionFactoryInterface::class);
×
NEW
257
        $propertyNameCollectionFactoryMock->method('create')->with(Dummy::class, ['api_allow_update' => false])->willReturn($propertyNameCollection);
×
258

NEW
259
        $propertyMetadataFactoryMock = $this->createMock(PropertyMetadataFactoryInterface::class);
×
NEW
260
        $propertyMetadataFactoryMock->method('create')->willReturnMap([
×
NEW
261
            [Dummy::class, 'name', ['api_allow_update' => false], (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)],
×
NEW
262
            [Dummy::class, 'relatedDummy', ['api_allow_update' => false], (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class)])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false)],
×
NEW
263
        ]);
×
264

NEW
265
        $iriConverterMock = $this->createMock(IriConverterInterface::class);
×
NEW
266
        $iriConverterMock->method('getIriFromResource')->willReturnCallback(
×
NEW
267
            function ($resource) use ($dummy, $relatedDummy) {
×
NEW
268
                if ($resource === $dummy) {
×
NEW
269
                    return '/dummies/1';
×
270
                }
NEW
271
                if ($resource === $relatedDummy) {
×
NEW
272
                    return '/related-dummies/2';
×
273
                }
274

NEW
275
                return null;
×
NEW
276
            }
×
NEW
277
        );
×
278

NEW
279
        $resourceClassResolverMock = $this->createMock(ResourceClassResolverInterface::class);
×
NEW
280
        $resourceClassResolverMock->method('getResourceClass')->willReturnMap([
×
NEW
281
            [$dummy, null, Dummy::class],
×
NEW
282
            [$dummy, Dummy::class, Dummy::class],
×
NEW
283
            [null, Dummy::class, Dummy::class],
×
NEW
284
            [$relatedDummy, RelatedDummy::class, RelatedDummy::class],
×
NEW
285
        ]);
×
NEW
286
        $resourceClassResolverMock->method('isResourceClass')->willReturn(true);
×
287

NEW
288
        $serializerMock = $this->createMock(Serializer::class);
×
NEW
289
        $serializerMock->method('normalize')->with('hello', null, $this->isType('array'))->willReturn('hello');
×
290

NEW
291
        $nameConverter = $this->createMock(NameConverterInterface::class);
×
NEW
292
        $nameConverter->method('normalize')->willReturnCallback(
×
NEW
293
            static function (string $propertyName): string {
×
NEW
294
                if ('relatedDummy' === $propertyName) {
×
NEW
295
                    return 'related_dummy';
×
296
                }
297

NEW
298
                return $propertyName;
×
NEW
299
            }
×
NEW
300
        );
×
301

NEW
302
        $normalizer = new ItemNormalizer(
×
NEW
303
            $propertyNameCollectionFactoryMock,
×
NEW
304
            $propertyMetadataFactoryMock,
×
NEW
305
            $iriConverterMock,
×
NEW
306
            $resourceClassResolverMock,
×
NEW
307
            null,
×
NEW
308
            $nameConverter
×
NEW
309
        );
×
NEW
310
        $normalizer->setSerializer($serializerMock);
×
311

NEW
312
        $expected = [
×
NEW
313
            '_links' => [
×
NEW
314
                'self' => [
×
NEW
315
                    'href' => '/dummies/1',
×
NEW
316
                ],
×
NEW
317
                'related_dummy' => [
×
NEW
318
                    'href' => '/related-dummies/2',
×
NEW
319
                ],
×
NEW
320
            ],
×
NEW
321
            'name' => 'hello',
×
NEW
322
        ];
×
NEW
323
        $this->assertEquals($expected, $normalizer->normalize($dummy, null, ['not_serializable' => fn () => null]));
×
324
    }
325

326
    public function testMaxDepth(): void
327
    {
NEW
328
        $setId = function (MaxDepthDummy $dummy, int $id): void {
×
NEW
329
            $prop = new \ReflectionProperty($dummy, 'id');
×
NEW
330
            $prop->setAccessible(true);
×
NEW
331
            $prop->setValue($dummy, $id);
×
NEW
332
        };
×
333

NEW
334
        $level1 = new MaxDepthDummy();
×
NEW
335
        $setId($level1, 1);
×
NEW
336
        $level1->name = 'level 1';
×
337

NEW
338
        $level2 = new MaxDepthDummy();
×
NEW
339
        $setId($level2, 2);
×
NEW
340
        $level2->name = 'level 2';
×
NEW
341
        $level1->child = $level2;
×
342

NEW
343
        $level3 = new MaxDepthDummy();
×
NEW
344
        $setId($level3, 3);
×
NEW
345
        $level3->name = 'level 3';
×
NEW
346
        $level2->child = $level3;
×
347

NEW
348
        $propertyNameCollection = new PropertyNameCollection(['id', 'name', 'child']);
×
NEW
349
        $propertyNameCollectionFactoryMock = $this->createMock(PropertyNameCollectionFactoryInterface::class);
×
NEW
350
        $propertyNameCollectionFactoryMock->method('create')->with(MaxDepthDummy::class, ['api_allow_update' => false])->willReturn($propertyNameCollection);
×
351

NEW
352
        $propertyMetadataFactoryMock = $this->createMock(PropertyMetadataFactoryInterface::class);
×
NEW
353
        $propertyMetadataFactoryMock->method('create')->willReturnMap([
×
NEW
354
            [MaxDepthDummy::class, 'id', ['api_allow_update' => false], (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_INT)])->withDescription('')->withReadable(true)],
×
NEW
355
            [MaxDepthDummy::class, 'name', ['api_allow_update' => false], (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true)],
×
NEW
356
            [MaxDepthDummy::class, 'child', ['api_allow_update' => false], (new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_OBJECT, false, MaxDepthDummy::class)])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(true)],
×
NEW
357
        ]);
×
358

NEW
359
        $iriConverterMock = $this->createMock(IriConverterInterface::class);
×
NEW
360
        $iriConverterMock->method('getIriFromResource')->willReturnCallback(
×
NEW
361
            function ($resource) use ($level1, $level2, $level3) {
×
NEW
362
                if ($resource === $level1) {
×
NEW
363
                    return '/max_depth_dummies/1';
×
364
                }
NEW
365
                if ($resource === $level2) {
×
NEW
366
                    return '/max_depth_dummies/2';
×
367
                }
NEW
368
                if ($resource === $level3) {
×
NEW
369
                    return '/max_depth_dummies/3';
×
370
                }
371

NEW
372
                return null;
×
NEW
373
            }
×
NEW
374
        );
×
375

NEW
376
        $resourceClassResolverMock = $this->createMock(ResourceClassResolverInterface::class);
×
NEW
377
        $resourceClassResolverMock->method('getResourceClass')->willReturnMap([
×
NEW
378
            [$level1, null, MaxDepthDummy::class],
×
NEW
379
            [$level1, MaxDepthDummy::class, MaxDepthDummy::class],
×
NEW
380
            [$level2, MaxDepthDummy::class, MaxDepthDummy::class],
×
NEW
381
            [$level3, MaxDepthDummy::class, MaxDepthDummy::class],
×
NEW
382
            [null, MaxDepthDummy::class, MaxDepthDummy::class],
×
NEW
383
        ]);
×
NEW
384
        $resourceClassResolverMock->method('isResourceClass')->with(MaxDepthDummy::class)->willReturn(true);
×
385

NEW
386
        $normalizer = new ItemNormalizer(
×
NEW
387
            $propertyNameCollectionFactoryMock,
×
NEW
388
            $propertyMetadataFactoryMock,
×
NEW
389
            $iriConverterMock,
×
NEW
390
            $resourceClassResolverMock,
×
NEW
391
            null,
×
NEW
392
            null,
×
NEW
393
            new ClassMetadataFactory(new AttributeLoader())
×
NEW
394
        );
×
NEW
395
        $serializer = new Serializer([$normalizer]);
×
NEW
396
        $normalizer->setSerializer($serializer);
×
397

NEW
398
        $expected = [
×
NEW
399
            '_links' => [
×
NEW
400
                'self' => [
×
NEW
401
                    'href' => '/max_depth_dummies/1',
×
NEW
402
                ],
×
NEW
403
                'child' => [
×
NEW
404
                    'href' => '/max_depth_dummies/2',
×
NEW
405
                ],
×
NEW
406
            ],
×
NEW
407
            '_embedded' => [
×
NEW
408
                'child' => [
×
NEW
409
                    '_links' => [
×
NEW
410
                        'self' => [
×
NEW
411
                            'href' => '/max_depth_dummies/2',
×
NEW
412
                        ],
×
NEW
413
                        'child' => [
×
NEW
414
                            'href' => '/max_depth_dummies/3',
×
NEW
415
                        ],
×
NEW
416
                    ],
×
NEW
417
                    '_embedded' => [
×
NEW
418
                        'child' => [
×
NEW
419
                            '_links' => [
×
NEW
420
                                'self' => [
×
NEW
421
                                    'href' => '/max_depth_dummies/3',
×
NEW
422
                                ],
×
NEW
423
                            ],
×
NEW
424
                            'id' => 3,
×
NEW
425
                            'name' => 'level 3',
×
NEW
426
                        ],
×
NEW
427
                    ],
×
NEW
428
                    'id' => 2,
×
NEW
429
                    'name' => 'level 2',
×
NEW
430
                ],
×
NEW
431
            ],
×
NEW
432
            'id' => 1,
×
NEW
433
            'name' => 'level 1',
×
NEW
434
        ];
×
435

NEW
436
        $this->assertEquals($expected, $normalizer->normalize($level1, ItemNormalizer::FORMAT));
×
NEW
437
        $this->assertEquals($expected, $normalizer->normalize($level1, ItemNormalizer::FORMAT, [ObjectNormalizer::ENABLE_MAX_DEPTH => false]));
×
438

NEW
439
        $expected = [
×
NEW
440
            '_links' => [
×
NEW
441
                'self' => [
×
NEW
442
                    'href' => '/max_depth_dummies/1',
×
NEW
443
                ],
×
NEW
444
                'child' => [
×
NEW
445
                    'href' => '/max_depth_dummies/2',
×
NEW
446
                ],
×
NEW
447
            ],
×
NEW
448
            '_embedded' => [
×
NEW
449
                'child' => [
×
NEW
450
                    '_links' => [
×
NEW
451
                        'self' => [
×
NEW
452
                            'href' => '/max_depth_dummies/2',
×
NEW
453
                        ],
×
NEW
454
                    ],
×
NEW
455
                    'id' => 2,
×
NEW
456
                    'name' => 'level 2',
×
NEW
457
                ],
×
NEW
458
            ],
×
NEW
459
            'id' => 1,
×
NEW
460
            'name' => 'level 1',
×
NEW
461
        ];
×
462

NEW
463
        $this->assertEquals($expected, $normalizer->normalize($level1, ItemNormalizer::FORMAT, [ObjectNormalizer::ENABLE_MAX_DEPTH => true]));
×
464
    }
465
}
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