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

api-platform / core / 14969517179

12 May 2025 10:08AM UTC coverage: 7.243% (-1.2%) from 8.418%
14969517179

push

github

web-flow
feat(serializer): (un)set object-to-populate through denormalization context (#7124)

14 of 95 new or added lines in 4 files covered. (14.74%)

1 existing line in 1 file now uncovered.

11006 of 151963 relevant lines covered (7.24%)

6.55 hits per line

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

0.0
/src/State/Tests/Provider/DeserializeProviderTest.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\State\Tests\Provider;
15

16
use ApiPlatform\Metadata\Get;
17
use ApiPlatform\Metadata\HttpOperation;
18
use ApiPlatform\Metadata\Patch;
19
use ApiPlatform\Metadata\Post;
20
use ApiPlatform\Metadata\Put;
21
use ApiPlatform\State\Provider\DeserializeProvider;
22
use ApiPlatform\State\ProviderInterface;
23
use ApiPlatform\State\SerializerContextBuilderInterface;
24
use PHPUnit\Framework\Attributes\DataProvider;
25
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
26
use PHPUnit\Framework\TestCase;
27
use Symfony\Component\HttpFoundation\Request;
28
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
29
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
30
use Symfony\Component\Serializer\SerializerInterface;
31

32
class DeserializeProviderTest extends TestCase
33
{
34
    #[IgnoreDeprecations]
35
    public function testDeserialize(): void
36
    {
NEW
37
        $this->expectUserDeprecationMessage('Since api-platform/core 5.0: To assign an object to populate you should set "api_assign_object_to_populate" in your denormalizationContext, not defining it is deprecated.');
×
38
        $objectToPopulate = new \stdClass();
×
39
        $serializerContext = [];
×
40
        $operation = new Post(deserialize: true, class: 'Test');
×
41
        $decorated = $this->createStub(ProviderInterface::class);
×
42
        $decorated->method('provide')->willReturn($objectToPopulate);
×
43

44
        $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
×
45
        $serializerContextBuilder->expects($this->once())->method('createFromRequest')->willReturn($serializerContext);
×
46
        $serializer = $this->createMock(SerializerInterface::class);
×
47
        $serializer->expects($this->once())->method('deserialize')->with('test', 'Test', 'format', ['uri_variables' => ['id' => 1], AbstractNormalizer::OBJECT_TO_POPULATE => $objectToPopulate] + $serializerContext)->willReturn(new \stdClass());
×
48

49
        $provider = new DeserializeProvider($decorated, $serializer, $serializerContextBuilder);
×
50
        $request = new Request(content: 'test');
×
51
        $request->headers->set('CONTENT_TYPE', 'ok');
×
52
        $request->attributes->set('input_format', 'format');
×
53
        $provider->provide($operation, ['id' => 1], ['request' => $request]);
×
54
    }
55

56
    public function testDeserializeNoContentType(): void
57
    {
58
        $this->expectException(UnsupportedMediaTypeHttpException::class);
×
59
        $operation = new Get(deserialize: true, class: 'Test');
×
60
        $decorated = $this->createStub(ProviderInterface::class);
×
61
        $decorated->method('provide')->willReturn(null);
×
62

63
        $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
×
64
        $serializer = $this->createMock(SerializerInterface::class);
×
65

66
        $provider = new DeserializeProvider($decorated, $serializer, $serializerContextBuilder);
×
67
        $request = new Request(content: 'test');
×
68
        $request->attributes->set('input_format', 'format');
×
69
        $provider->provide($operation, ['id' => 1], ['request' => $request]);
×
70
    }
71

72
    public function testDeserializeNoInput(): void
73
    {
74
        $this->expectException(UnsupportedMediaTypeHttpException::class);
×
75
        $operation = new Get(deserialize: true, class: 'Test');
×
76
        $decorated = $this->createStub(ProviderInterface::class);
×
77
        $decorated->method('provide')->willReturn(null);
×
78

79
        $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
×
80
        $serializer = $this->createMock(SerializerInterface::class);
×
81

82
        $provider = new DeserializeProvider($decorated, $serializer, $serializerContextBuilder);
×
83
        $request = new Request(content: 'test');
×
84
        $request->headers->set('CONTENT_TYPE', 'ok');
×
85
        $provider->provide($operation, ['id' => 1], ['request' => $request]);
×
86
    }
87

88
    public function testDeserializeWithContextClass(): void
89
    {
90
        $serializerContext = ['deserializer_type' => 'Test'];
×
91
        $operation = new Get(deserialize: true);
×
92
        $decorated = $this->createStub(ProviderInterface::class);
×
93
        $decorated->method('provide')->willReturn(null);
×
94

95
        $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
×
96
        $serializerContextBuilder->expects($this->once())->method('createFromRequest')->willReturn($serializerContext);
×
97
        $serializer = $this->createMock(SerializerInterface::class);
×
98
        $serializer->expects($this->once())->method('deserialize')->with('test', 'Test', 'format', ['uri_variables' => ['id' => 1]] + $serializerContext)->willReturn(new \stdClass());
×
99

100
        $provider = new DeserializeProvider($decorated, $serializer, $serializerContextBuilder);
×
101
        $request = new Request(content: 'test');
×
102
        $request->headers->set('CONTENT_TYPE', 'ok');
×
103
        $request->attributes->set('input_format', 'format');
×
104
        $provider->provide($operation, ['id' => 1], ['request' => $request]);
×
105
    }
106

107
    public function testRequestWithEmptyContentType(): void
108
    {
109
        $expectedResult = new \stdClass();
×
110
        $decorated = $this->createMock(ProviderInterface::class);
×
111
        $decorated->method('provide')->willReturn($expectedResult);
×
112

113
        $serializer = $this->createStub(SerializerInterface::class);
×
114
        $serializerContextBuilder = $this->createStub(SerializerContextBuilderInterface::class);
×
115

116
        $provider = new DeserializeProvider($decorated, $serializer, $serializerContextBuilder);
×
117

118
        // in Symfony (at least up to 7.0.2, 6.4.2, 6.3.11, 5.4.34), a request
119
        // without a content-type and content-length header will result in the
120
        // variables set to an empty string, not null
121

122
        $request = new Request(
×
123
            server: [
×
124
                'REQUEST_METHOD' => 'POST',
×
125
                'REQUEST_URI' => '/',
×
126
                'CONTENT_TYPE' => '',
×
127
                'CONTENT_LENGTH' => '',
×
128
            ],
×
129
            content: ''
×
130
        );
×
131

132
        $operation = new Post(deserialize: true);
×
133
        $context = ['request' => $request];
×
134

135
        $this->expectException(UnsupportedMediaTypeHttpException::class);
×
136
        $provider->provide($operation, [], $context);
×
137
    }
138

139
    #[DataProvider('provideMethodsTriggeringDeprecation')]
140
    #[IgnoreDeprecations]
141
    public function testDeserializeTriggersDeprecationWhenContextNotSet(HttpOperation $operation): void
142
    {
NEW
143
        $this->expectUserDeprecationMessage('Since api-platform/core 5.0: To assign an object to populate you should set "api_assign_object_to_populate" in your denormalizationContext, not defining it is deprecated.');
×
144

NEW
145
        $objectToPopulate = new \stdClass();
×
NEW
146
        $serializerContext = [];
×
NEW
147
        $decorated = $this->createStub(ProviderInterface::class);
×
NEW
148
        $decorated->method('provide')->willReturn($objectToPopulate);
×
149

NEW
150
        $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
×
NEW
151
        $serializerContextBuilder->method('createFromRequest')->willReturn($serializerContext);
×
152

NEW
153
        $serializer = $this->createMock(SerializerInterface::class);
×
NEW
154
        $serializer->expects($this->once())->method('deserialize')->with(
×
NEW
155
            'test',
×
NEW
156
            'Test',
×
NEW
157
            'format',
×
NEW
158
            ['uri_variables' => ['id' => 1], 'object_to_populate' => $objectToPopulate] + $serializerContext
×
NEW
159
        )->willReturn(new \stdClass());
×
160

NEW
161
        $provider = new DeserializeProvider($decorated, $serializer, $serializerContextBuilder);
×
NEW
162
        $request = new Request(content: 'test');
×
NEW
163
        $request->headers->set('CONTENT_TYPE', 'ok');
×
NEW
164
        $request->attributes->set('input_format', 'format');
×
NEW
165
        $provider->provide($operation, ['id' => 1], ['request' => $request]);
×
166
    }
167

168
    public static function provideMethodsTriggeringDeprecation(): iterable
169
    {
NEW
170
        yield 'POST method' => [new Post(deserialize: true, class: 'Test')];
×
NEW
171
        yield 'PATCH method' => [new Patch(deserialize: true, class: 'Test')];
×
NEW
172
        yield 'PUT method (non-standard)' => [new Put(deserialize: true, class: 'Test', extraProperties: ['standard_put' => false])];
×
173
    }
174

175
    public function testDeserializeSetsObjectToPopulateWhenContextIsTrue(): void
176
    {
NEW
177
        $objectToPopulate = new \stdClass();
×
NEW
178
        $serializerContext = [SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE => true];
×
NEW
179
        $operation = new Post(deserialize: true, class: 'Test');
×
NEW
180
        $decorated = $this->createStub(ProviderInterface::class);
×
NEW
181
        $decorated->method('provide')->willReturn($objectToPopulate);
×
182

NEW
183
        $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
×
NEW
184
        $serializerContextBuilder->method('createFromRequest')->willReturn($serializerContext);
×
185

NEW
186
        $serializer = $this->createMock(SerializerInterface::class);
×
NEW
187
        $serializer->expects($this->once())->method('deserialize')->with(
×
NEW
188
            'test',
×
NEW
189
            'Test',
×
NEW
190
            'format',
×
NEW
191
            $this->callback(function (array $context) use ($objectToPopulate) {
×
NEW
192
                $this->assertArrayHasKey(AbstractNormalizer::OBJECT_TO_POPULATE, $context);
×
NEW
193
                $this->assertSame($objectToPopulate, $context[AbstractNormalizer::OBJECT_TO_POPULATE]);
×
194

NEW
195
                return true;
×
NEW
196
            })
×
NEW
197
        )->willReturn(new \stdClass());
×
198

NEW
199
        $provider = new DeserializeProvider($decorated, $serializer, $serializerContextBuilder);
×
NEW
200
        $request = new Request(content: 'test');
×
NEW
201
        $request->headers->set('CONTENT_TYPE', 'ok');
×
NEW
202
        $request->attributes->set('input_format', 'format');
×
NEW
203
        $provider->provide($operation, ['id' => 1], ['request' => $request]);
×
204
    }
205

206
    public function testDeserializeDoesNotSetObjectToPopulateWhenContextIsFalse(): void
207
    {
NEW
208
        $objectToPopulate = new \stdClass();
×
NEW
209
        $serializerContext = [SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE => false];
×
NEW
210
        $operation = new Post(deserialize: true, class: 'Test');
×
NEW
211
        $decorated = $this->createStub(ProviderInterface::class);
×
NEW
212
        $decorated->method('provide')->willReturn($objectToPopulate);
×
213

NEW
214
        $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
×
NEW
215
        $serializerContextBuilder->method('createFromRequest')->willReturn($serializerContext);
×
216

NEW
217
        $serializer = $this->createMock(SerializerInterface::class);
×
NEW
218
        $serializer->expects($this->once())->method('deserialize')->with(
×
NEW
219
            'test',
×
NEW
220
            'Test',
×
NEW
221
            'format',
×
NEW
222
            $this->callback(function (array $context) {
×
NEW
223
                $this->assertArrayNotHasKey(AbstractNormalizer::OBJECT_TO_POPULATE, $context);
×
224

NEW
225
                return true;
×
NEW
226
            })
×
NEW
227
        )->willReturn(new \stdClass());
×
228

NEW
229
        $provider = new DeserializeProvider($decorated, $serializer, $serializerContextBuilder);
×
NEW
230
        $request = new Request(content: 'test');
×
NEW
231
        $request->headers->set('CONTENT_TYPE', 'ok');
×
NEW
232
        $request->attributes->set('input_format', 'format');
×
NEW
233
        $provider->provide($operation, ['id' => 1], ['request' => $request]);
×
234
    }
235
}
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