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

api-platform / core / 20528769615

26 Dec 2025 08:17PM UTC coverage: 25.119%. First build
20528769615

Pull #7629

github

web-flow
Merge 4691c25d0 into 38d474d1b
Pull Request #7629: fix: add support for normalization/denormalization with attributes

24 of 236 new or added lines in 8 files covered. (10.17%)

14638 of 58274 relevant lines covered (25.12%)

29.5 hits per line

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

0.0
/src/Metadata/Tests/Property/Factory/SerializerPropertyMetadataFactoryTest.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\Metadata\Tests\Property\Factory;
15

16
use ApiPlatform\Metadata\ApiProperty;
17
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
18
use ApiPlatform\Metadata\Property\Factory\SerializerPropertyMetadataFactory;
19
use ApiPlatform\Metadata\ResourceClassResolverInterface;
20
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\Dummy;
21
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\DummyCar;
22
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\RelatedDummy;
23
use ApiPlatform\Metadata\Tests\Fixtures\DummyIgnoreProperty;
24
use PHPUnit\Framework\TestCase;
25
use Prophecy\PhpUnit\ProphecyTrait;
26
use Symfony\Component\Serializer\Mapping\AttributeMetadata as SerializerAttributeMetadata;
27
use Symfony\Component\Serializer\Mapping\ClassMetadata as SerializerClassMetadata;
28
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface as SerializerClassMetadataFactoryInterface;
29
use Symfony\Component\TypeInfo\Type;
30

31
class SerializerPropertyMetadataFactoryTest extends TestCase
32
{
33
    use ProphecyTrait;
34

35
    public static function groupsProvider(): array
36
    {
37
        return [
×
38
            [['dummy_read'], ['dummy_write']],
×
39
            ['dummy_read', 'dummy_write'],
×
40
        ];
×
41
    }
42

43
    #[\PHPUnit\Framework\Attributes\DataProvider('groupsProvider')]
44
    public function testCreateWithGroups($readGroups, $writeGroups): void
45
    {
46
        $serializerClassMetadataFactoryProphecy = $this->prophesize(SerializerClassMetadataFactoryInterface::class);
×
47
        $dummySerializerClassMetadata = new SerializerClassMetadata(Dummy::class);
×
48
        $fooSerializerAttributeMetadata = new SerializerAttributeMetadata('foo');
×
49
        $fooSerializerAttributeMetadata->addGroup('dummy_read');
×
50
        $fooSerializerAttributeMetadata->addGroup('dummy_write');
×
51
        $dummySerializerClassMetadata->addAttributeMetadata($fooSerializerAttributeMetadata);
×
52
        $relatedDummySerializerAttributeMetadata = new SerializerAttributeMetadata('relatedDummy');
×
53
        $relatedDummySerializerAttributeMetadata->addGroup('dummy_read');
×
54
        $relatedDummySerializerAttributeMetadata->addGroup('dummy_write');
×
55
        $dummySerializerClassMetadata->addAttributeMetadata($relatedDummySerializerAttributeMetadata);
×
56
        $nameConvertedSerializerAttributeMetadata = new SerializerAttributeMetadata('nameConverted');
×
57
        $dummySerializerClassMetadata->addAttributeMetadata($nameConvertedSerializerAttributeMetadata);
×
58
        $serializerClassMetadataFactoryProphecy->getMetadataFor(Dummy::class)->willReturn($dummySerializerClassMetadata);
×
59
        $relatedDummySerializerClassMetadata = new SerializerClassMetadata(RelatedDummy::class);
×
60
        $nameSerializerAttributeMetadata = new SerializerAttributeMetadata('name');
×
61
        $nameSerializerAttributeMetadata->addGroup('dummy_read');
×
62
        $relatedDummySerializerClassMetadata->addAttributeMetadata($nameSerializerAttributeMetadata);
×
63
        $serializerClassMetadataFactoryProphecy->getMetadataFor(RelatedDummy::class)->willReturn($relatedDummySerializerClassMetadata);
×
64
        $dummyCarSerializerClassMetadata = new SerializerClassMetadata(DummyCar::class);
×
65
        $nameSerializerAttributeMetadata = new SerializerAttributeMetadata('name');
×
66
        $nameSerializerAttributeMetadata->addGroup('dummy_car_read');
×
67
        $nameSerializerAttributeMetadata->addGroup('dummy_write');
×
68
        $dummyCarSerializerClassMetadata->addAttributeMetadata($nameSerializerAttributeMetadata);
×
69
        $serializerClassMetadataFactoryProphecy->getMetadataFor(DummyCar::class)->willReturn($dummyCarSerializerClassMetadata);
×
70

71
        $context = ['normalization_groups' => $readGroups, 'denormalization_groups' => $writeGroups];
×
72

73
        $decoratedProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
74
        $fooPropertyMetadata = (new ApiProperty())
×
75
            ->withNativeType(Type::nullable(Type::array()))
×
76
            ->withReadable(false)
×
77
            ->withWritable(true);
×
78
        $decoratedProphecy->create(Dummy::class, 'foo', $context)->willReturn($fooPropertyMetadata);
×
79
        $relatedDummyPropertyMetadata = (new ApiProperty())
×
80
            ->withNativeType(Type::nullable(Type::object(RelatedDummy::class)));
×
81
        $decoratedProphecy->create(Dummy::class, 'relatedDummy', $context)->willReturn($relatedDummyPropertyMetadata);
×
82
        $nameConvertedPropertyMetadata = (new ApiProperty())
×
83
            ->withNativeType(Type::nullable(Type::string()));
×
84
        $decoratedProphecy->create(Dummy::class, 'nameConverted', $context)->willReturn($nameConvertedPropertyMetadata);
×
85

86
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
87
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
88
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
89
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
90

91
        $serializerPropertyMetadataFactory = new SerializerPropertyMetadataFactory($serializerClassMetadataFactoryProphecy->reveal(), $decoratedProphecy->reveal(), $resourceClassResolverProphecy->reveal());
×
92

93
        $actual = [];
×
94
        $actual[] = $serializerPropertyMetadataFactory->create(Dummy::class, 'foo', $context);
×
95
        $actual[] = $serializerPropertyMetadataFactory->create(Dummy::class, 'relatedDummy', $context);
×
96
        $actual[] = $serializerPropertyMetadataFactory->create(Dummy::class, 'nameConverted', $context);
×
97

98
        $this->assertInstanceOf(ApiProperty::class, $actual[0]);
×
99
        $this->assertFalse($actual[0]->isReadable());
×
100
        $this->assertTrue($actual[0]->isWritable());
×
101

102
        $this->assertInstanceOf(ApiProperty::class, $actual[1]);
×
103
        $this->assertTrue($actual[1]->isReadable());
×
104
        $this->assertTrue($actual[1]->isWritable());
×
105
        $this->assertTrue($actual[1]->isReadableLink());
×
106
        $this->assertFalse($actual[1]->isWritableLink());
×
107

108
        $this->assertInstanceOf(ApiProperty::class, $actual[2]);
×
109
        $this->assertFalse($actual[2]->isReadable());
×
110
        $this->assertFalse($actual[2]->isWritable());
×
111
    }
112

113
    public static function attributesProvider(): array
114
    {
NEW
115
        return [
×
NEW
116
            [['foo', 'relatedDummy'], ['foo']],
×
NEW
117
            [['foo', 'relatedDummy' => ['name']], ['foo' => []]],
×
NEW
118
        ];
×
119
    }
120

121
    #[\PHPUnit\Framework\Attributes\DataProvider('attributesProvider')]
122
    public function testCreateWithAttributes($readAttributes, $writeAttributes): void
123
    {
NEW
124
        $serializerClassMetadataFactoryProphecy = $this->prophesize(SerializerClassMetadataFactoryInterface::class);
×
NEW
125
        $dummySerializerClassMetadata = new SerializerClassMetadata(Dummy::class);
×
NEW
126
        $dummySerializerClassMetadata->addAttributeMetadata(new SerializerAttributeMetadata('foo'));
×
NEW
127
        $dummySerializerClassMetadata->addAttributeMetadata(new SerializerAttributeMetadata('relatedDummy'));
×
NEW
128
        $dummySerializerClassMetadata->addAttributeMetadata(new SerializerAttributeMetadata('notIncluded'));
×
NEW
129
        $serializerClassMetadataFactoryProphecy->getMetadataFor(Dummy::class)->willReturn($dummySerializerClassMetadata);
×
NEW
130
        $relatedDummySerializerClassMetadata = new SerializerClassMetadata(RelatedDummy::class);
×
NEW
131
        $serializerClassMetadataFactoryProphecy->getMetadataFor(RelatedDummy::class)->willReturn($relatedDummySerializerClassMetadata);
×
132

NEW
133
        $context = ['normalization_attributes' => $readAttributes, 'denormalization_attributes' => $writeAttributes];
×
134

NEW
135
        $decoratedProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
NEW
136
        $fooPropertyMetadata = (new ApiProperty())
×
NEW
137
            ->withNativeType(Type::nullable(Type::array()))
×
NEW
138
            ->withReadable(true)
×
NEW
139
            ->withWritable(true);
×
NEW
140
        $decoratedProphecy->create(Dummy::class, 'foo', $context)->willReturn($fooPropertyMetadata);
×
NEW
141
        $relatedDummyPropertyMetadata = (new ApiProperty())
×
NEW
142
            ->withNativeType(Type::nullable(Type::object(RelatedDummy::class)))
×
NEW
143
            ->withReadable(true)
×
NEW
144
            ->withWritable(true);
×
NEW
145
        $decoratedProphecy->create(Dummy::class, 'relatedDummy', $context)->willReturn($relatedDummyPropertyMetadata);
×
NEW
146
        $notIncludedPropertyMetadata = (new ApiProperty())
×
NEW
147
            ->withNativeType(Type::nullable(Type::string()))
×
NEW
148
            ->withReadable(true)
×
NEW
149
            ->withWritable(true);
×
NEW
150
        $decoratedProphecy->create(Dummy::class, 'notIncluded', $context)->willReturn($notIncludedPropertyMetadata);
×
151

NEW
152
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
NEW
153
        $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
×
NEW
154
        $resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
×
NEW
155
        $resourceClassResolverProphecy->getResourceClass(null, RelatedDummy::class)->willReturn(RelatedDummy::class);
×
156

NEW
157
        $serializerPropertyMetadataFactory = new SerializerPropertyMetadataFactory($serializerClassMetadataFactoryProphecy->reveal(), $decoratedProphecy->reveal(), $resourceClassResolverProphecy->reveal());
×
158

NEW
159
        $actual = [];
×
NEW
160
        $actual[] = $serializerPropertyMetadataFactory->create(Dummy::class, 'foo', $context);
×
NEW
161
        $actual[] = $serializerPropertyMetadataFactory->create(Dummy::class, 'relatedDummy', $context);
×
NEW
162
        $actual[] = $serializerPropertyMetadataFactory->create(Dummy::class, 'notIncluded', $context);
×
163

NEW
164
        $this->assertInstanceOf(ApiProperty::class, $actual[0]);
×
NEW
165
        $this->assertTrue($actual[0]->isReadable());
×
NEW
166
        $this->assertTrue($actual[0]->isWritable());
×
167

NEW
168
        $this->assertInstanceOf(ApiProperty::class, $actual[1]);
×
NEW
169
        $this->assertTrue($actual[1]->isReadable());
×
NEW
170
        $this->assertFalse($actual[1]->isWritable());
×
NEW
171
        $this->assertTrue($actual[1]->isReadableLink());
×
172

NEW
173
        $this->assertInstanceOf(ApiProperty::class, $actual[2]);
×
NEW
174
        $this->assertFalse($actual[2]->isReadable());
×
NEW
175
        $this->assertFalse($actual[2]->isWritable());
×
176
    }
177

178
    public function testCreateWithIgnoredProperty(): void
179
    {
180
        $ignoredSerializerAttributeMetadata = new SerializerAttributeMetadata('ignored');
×
181
        $ignoredSerializerAttributeMetadata->addGroup('dummy');
×
182
        $ignoredSerializerAttributeMetadata->addGroup('dummy');
×
183
        $ignoredSerializerAttributeMetadata->setIgnore(true);
×
184

185
        $dummyIgnorePropertySerializerClassMetadata = new SerializerClassMetadata(DummyIgnoreProperty::class);
×
186
        $dummyIgnorePropertySerializerClassMetadata->addAttributeMetadata($ignoredSerializerAttributeMetadata);
×
187

188
        $serializerClassMetadataFactoryProphecy = $this->prophesize(SerializerClassMetadataFactoryInterface::class);
×
189
        $serializerClassMetadataFactoryProphecy->getMetadataFor(DummyIgnoreProperty::class)->willReturn($dummyIgnorePropertySerializerClassMetadata);
×
190

191
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
192
        $resourceClassResolverProphecy->isResourceClass(DummyIgnoreProperty::class)->willReturn(true);
×
193

194
        $ignoredPropertyMetadata = (new ApiProperty())->withNativeType(Type::nullable(Type::string()));
×
195

196
        $options = [
×
197
            'normalization_groups' => ['dummy'],
×
198
            'denormalization_groups' => ['dummy'],
×
199
        ];
×
200

201
        $decoratedProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
202
        $decoratedProphecy->create(DummyIgnoreProperty::class, 'ignored', $options)->willReturn($ignoredPropertyMetadata);
×
203

204
        $serializerPropertyMetadataFactory = new SerializerPropertyMetadataFactory(
×
205
            $serializerClassMetadataFactoryProphecy->reveal(),
×
206
            $decoratedProphecy->reveal(),
×
207
            $resourceClassResolverProphecy->reveal()
×
208
        );
×
209

210
        $result = $serializerPropertyMetadataFactory->create(DummyIgnoreProperty::class, 'ignored', $options);
×
211

212
        self::assertFalse($result->isReadable());
×
213
        self::assertFalse($result->isWritable());
×
214
    }
215
}
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