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

api-platform / core / 19799380020

30 Nov 2025 01:09PM UTC coverage: 0.0%. Remained the same
19799380020

push

github

soyuka
Merge 4.2

0 of 306 new or added lines in 35 files covered. (0.0%)

22 existing lines in 15 files now uncovered.

0 of 57173 relevant lines covered (0.0%)

0.0 hits per line

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

0.0
/src/JsonSchema/Tests/SchemaFactoryTest.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\JsonSchema\Tests;
15

16
use ApiPlatform\JsonSchema\DefinitionNameFactory;
17
use ApiPlatform\JsonSchema\Schema;
18
use ApiPlatform\JsonSchema\SchemaFactory;
19
use ApiPlatform\JsonSchema\Tests\Fixtures\ApiResource\OverriddenOperationDummy;
20
use ApiPlatform\JsonSchema\Tests\Fixtures\DummyResourceInterface;
21
use ApiPlatform\JsonSchema\Tests\Fixtures\Enum\GenderTypeEnum;
22
use ApiPlatform\JsonSchema\Tests\Fixtures\GenericChild;
23
use ApiPlatform\JsonSchema\Tests\Fixtures\NotAResource;
24
use ApiPlatform\JsonSchema\Tests\Fixtures\NotAResourceWithUnionIntersectTypes;
25
use ApiPlatform\JsonSchema\Tests\Fixtures\Serializable;
26
use ApiPlatform\Metadata\ApiProperty;
27
use ApiPlatform\Metadata\ApiResource;
28
use ApiPlatform\Metadata\Operations;
29
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
30
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
31
use ApiPlatform\Metadata\Property\PropertyNameCollection;
32
use ApiPlatform\Metadata\Put;
33
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
34
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
35
use ApiPlatform\Metadata\ResourceClassResolverInterface;
36
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
37
use PHPUnit\Framework\TestCase;
38
use Prophecy\Argument;
39
use Prophecy\PhpUnit\ProphecyTrait;
40
use Symfony\Component\PropertyInfo\Type as LegacyType;
41
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
42
use Symfony\Component\TypeInfo\Type;
43

44
class SchemaFactoryTest extends TestCase
45
{
46
    use ProphecyTrait;
47

48
    #[IgnoreDeprecations]
49
    public function testBuildSchemaForNonResourceClassLegacy(): void
50
    {
NEW
51
        if (!class_exists(LegacyType::class)) {
×
NEW
52
            $this->markTestSkipped();
×
53
        }
54
        $this->expectUserDeprecationMessage('Since api-platform/metadata 4.2: The "ApiPlatform\Metadata\ApiProperty::withBuiltinTypes()" method is deprecated, use "ApiPlatform\Metadata\ApiProperty::withNativeType()" instead.');
×
55
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
56

57
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
58
        $propertyNameCollectionFactoryProphecy->create(NotAResource::class, Argument::cetera())->willReturn(new PropertyNameCollection(['foo', 'bar', 'genderType']));
×
59

60
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
61
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'foo', Argument::cetera())->willReturn(
×
62
            (new ApiProperty())
×
63
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])
×
64
                ->withReadable(true)
×
65
                ->withSchema(['type' => 'string'])
×
66
        );
×
67
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'bar', Argument::cetera())->willReturn(
×
68
            (new ApiProperty())
×
69
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_INT)])
×
70
                ->withReadable(true)
×
71
                ->withDefault('default_bar')
×
72
                ->withExample('example_bar')
×
73
                ->withSchema(['type' => 'integer', 'default' => 'default_bar', 'example' => 'example_bar'])
×
74
        );
×
75
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'genderType', Argument::cetera())->willReturn(
×
76
            (new ApiProperty())
×
77
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT)])
×
78
                ->withReadable(true)
×
79
                ->withDefault('male')
×
80
                ->withSchema(['type' => 'object', 'default' => 'male', 'example' => 'male'])
×
81
        );
×
82

83
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
84
        $resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
×
85

86
        $definitionNameFactory = new DefinitionNameFactory();
×
87

88
        $schemaFactory = new SchemaFactory(
×
89
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
90
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
91
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
92
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
93
            definitionNameFactory: $definitionNameFactory,
×
94
        );
×
95
        $resultSchema = $schemaFactory->buildSchema(NotAResource::class);
×
96

97
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
98
        $definitions = $resultSchema->getDefinitions();
×
99

100
        $this->assertSame((new \ReflectionClass(NotAResource::class))->getShortName(), $rootDefinitionKey);
×
101
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
102
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
103
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
104
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]);
×
105
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
106
        $this->assertArrayHasKey('foo', $definitions[$rootDefinitionKey]['properties']);
×
107
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['foo']);
×
108
        $this->assertArrayNotHasKey('default', $definitions[$rootDefinitionKey]['properties']['foo']);
×
109
        $this->assertArrayNotHasKey('example', $definitions[$rootDefinitionKey]['properties']['foo']);
×
110
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['foo']['type']);
×
111
        $this->assertArrayHasKey('bar', $definitions[$rootDefinitionKey]['properties']);
×
112
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['bar']);
×
113
        $this->assertArrayHasKey('default', $definitions[$rootDefinitionKey]['properties']['bar']);
×
114
        $this->assertArrayHasKey('example', $definitions[$rootDefinitionKey]['properties']['bar']);
×
115
        $this->assertSame('integer', $definitions[$rootDefinitionKey]['properties']['bar']['type']);
×
116
        $this->assertSame('default_bar', $definitions[$rootDefinitionKey]['properties']['bar']['default']);
×
117
        $this->assertSame('example_bar', $definitions[$rootDefinitionKey]['properties']['bar']['example']);
×
118

119
        $this->assertArrayHasKey('genderType', $definitions[$rootDefinitionKey]['properties']);
×
120
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
121
        $this->assertArrayHasKey('default', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
122
        $this->assertArrayHasKey('example', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
123
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['genderType']['type']);
×
124
        $this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['default']);
×
125
        $this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['example']);
×
126
    }
127

128
    public function testBuildSchemaForNonResourceClass(): void
129
    {
130
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) { // @phpstan-ignore-line symfony/property-info 6.4 is still allowed and this may be true
×
131
            $this->markTestSkipped('This test only supports type-info component');
×
132
        }
133

134
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
135

136
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
137
        $propertyNameCollectionFactoryProphecy->create(NotAResource::class, Argument::cetera())->willReturn(new PropertyNameCollection(['foo', 'bar', 'genderType', 'items']));
×
138

139
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
140
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'foo', Argument::cetera())->willReturn(
×
141
            (new ApiProperty())
×
142
                ->withNativeType(Type::string())
×
143
                ->withReadable(true)
×
144
                ->withSchema(['type' => 'string'])
×
145
        );
×
146
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'bar', Argument::cetera())->willReturn(
×
147
            (new ApiProperty())
×
148
                ->withNativeType(Type::int())
×
149
                ->withReadable(true)
×
150
                ->withDefault('default_bar')
×
151
                ->withExample('example_bar')
×
152
                ->withSchema(['type' => 'integer', 'default' => 'default_bar', 'example' => 'example_bar'])
×
153
        );
×
154
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'genderType', Argument::cetera())->willReturn(
×
155
            (new ApiProperty())
×
156
                ->withNativeType(Type::object())
×
157
                ->withReadable(true)
×
158
                ->withDefault('male')
×
159
                ->withSchema(['type' => 'object', 'default' => 'male', 'example' => 'male'])
×
160
        );
×
161
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'items', Argument::cetera())->willReturn(
×
162
            (new ApiProperty())
×
163
                ->withNativeType(
×
164
                    Type::generic(Type::object(GenericChild::class), Type::int()),
×
165
                )
×
166
                ->withReadable(true)
×
167
                ->withSchema(['type' => Schema::UNKNOWN_TYPE])
×
168
        );
×
169

170
        $propertyNameCollectionFactoryProphecy->create(GenericChild::class, Argument::cetera())
×
171
            ->willReturn(new PropertyNameCollection(['property']));
×
172
        $propertyMetadataFactoryProphecy->create(GenericChild::class, 'property', Argument::cetera())
×
173
            ->willReturn(
×
174
                (new ApiProperty())
×
175
                    ->withNativeType(Type::string())
×
176
                    ->withReadable(true)
×
177
                    ->withSchema(['type' => 'string'])
×
178
            );
×
179

180
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
181
        $resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
×
182
        $resourceClassResolverProphecy->isResourceClass(GenericChild::class)->willReturn(false);
×
183

184
        $definitionNameFactory = new DefinitionNameFactory();
×
185

186
        $schemaFactory = new SchemaFactory(
×
187
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
188
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
189
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
190
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
191
            definitionNameFactory: $definitionNameFactory,
×
192
        );
×
193
        $resultSchema = $schemaFactory->buildSchema(NotAResource::class);
×
194

195
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
196
        $definitions = $resultSchema->getDefinitions();
×
197

198
        $this->assertSame((new \ReflectionClass(NotAResource::class))->getShortName(), $rootDefinitionKey);
×
199
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
200
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
201
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
202
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]);
×
203
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
204
        $this->assertArrayHasKey('foo', $definitions[$rootDefinitionKey]['properties']);
×
205
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['foo']);
×
206
        $this->assertArrayNotHasKey('default', $definitions[$rootDefinitionKey]['properties']['foo']);
×
207
        $this->assertArrayNotHasKey('example', $definitions[$rootDefinitionKey]['properties']['foo']);
×
208
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['foo']['type']);
×
209
        $this->assertArrayHasKey('bar', $definitions[$rootDefinitionKey]['properties']);
×
210
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['bar']);
×
211
        $this->assertArrayHasKey('default', $definitions[$rootDefinitionKey]['properties']['bar']);
×
212
        $this->assertArrayHasKey('example', $definitions[$rootDefinitionKey]['properties']['bar']);
×
213
        $this->assertSame('integer', $definitions[$rootDefinitionKey]['properties']['bar']['type']);
×
214
        $this->assertSame('default_bar', $definitions[$rootDefinitionKey]['properties']['bar']['default']);
×
215
        $this->assertSame('example_bar', $definitions[$rootDefinitionKey]['properties']['bar']['example']);
×
216

217
        $this->assertArrayHasKey('genderType', $definitions[$rootDefinitionKey]['properties']);
×
218
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
219
        $this->assertArrayHasKey('default', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
220
        $this->assertArrayHasKey('example', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
221
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['genderType']['type']);
×
222
        $this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['default']);
×
223
        $this->assertSame('male', $definitions[$rootDefinitionKey]['properties']['genderType']['example']);
×
224

225
        $this->assertArrayHasKey('items', $definitions[$rootDefinitionKey]['properties']);
×
226
        $this->assertArrayHasKey('$ref', $definitions[$rootDefinitionKey]['properties']['items']);
×
227
        $this->assertSame('#/definitions/GenericChild', $definitions[$rootDefinitionKey]['properties']['items']['$ref']);
×
228
    }
229

230
    #[IgnoreDeprecations]
231
    public function testBuildSchemaForNonResourceClassWithUnionIntersectTypesLegacy(): void
232
    {
NEW
233
        if (!class_exists(LegacyType::class)) {
×
NEW
234
            $this->markTestSkipped();
×
235
        }
236
        $this->expectUserDeprecationMessage('Since api-platform/metadata 4.2: The "ApiPlatform\Metadata\ApiProperty::withBuiltinTypes()" method is deprecated, use "ApiPlatform\Metadata\ApiProperty::withNativeType()" instead.');
×
237
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
238

239
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
240
        $propertyNameCollectionFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, Argument::cetera())->willReturn(new PropertyNameCollection(['ignoredProperty', 'unionType', 'intersectType']));
×
241

242
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
243
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'ignoredProperty', Argument::cetera())->willReturn(
×
244
            (new ApiProperty())
×
245
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING, nullable: true)])
×
246
                ->withReadable(true)
×
247
                ->withSchema(['type' => ['string', 'null']])
×
248
        );
×
249
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'unionType', Argument::cetera())->willReturn(
×
250
            (new ApiProperty())
×
251
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING, nullable: true), new LegacyType(LegacyType::BUILTIN_TYPE_INT, nullable: true), new LegacyType(LegacyType::BUILTIN_TYPE_FLOAT, nullable: true)])
×
252
                ->withReadable(true)
×
253
                ->withSchema(['oneOf' => [
×
254
                    ['type' => ['string', 'null']],
×
255
                    ['type' => ['integer', 'null']],
×
256
                ]])
×
257
        );
×
258
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'intersectType', Argument::cetera())->willReturn(
×
259
            (new ApiProperty())
×
260
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, class: Serializable::class), new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, class: DummyResourceInterface::class)])
×
261
                ->withReadable(true)
×
262
                ->withSchema(['type' => 'object'])
×
263
        );
×
264

265
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
266
        $resourceClassResolverProphecy->isResourceClass(NotAResourceWithUnionIntersectTypes::class)->willReturn(false);
×
267

268
        $definitionNameFactory = new DefinitionNameFactory();
×
269

270
        $schemaFactory = new SchemaFactory(
×
271
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
272
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
273
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
274
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
275
            definitionNameFactory: $definitionNameFactory,
×
276
        );
×
277
        $resultSchema = $schemaFactory->buildSchema(NotAResourceWithUnionIntersectTypes::class);
×
278

279
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
280
        $definitions = $resultSchema->getDefinitions();
×
281

282
        $this->assertSame((new \ReflectionClass(NotAResourceWithUnionIntersectTypes::class))->getShortName(), $rootDefinitionKey);
×
283
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
284
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
285
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
286
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]);
×
287
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
288

289
        $this->assertArrayHasKey('ignoredProperty', $definitions[$rootDefinitionKey]['properties']);
×
290
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['ignoredProperty']);
×
291
        $this->assertSame(['string', 'null'], $definitions[$rootDefinitionKey]['properties']['ignoredProperty']['type']);
×
292
        $this->assertArrayHasKey('unionType', $definitions[$rootDefinitionKey]['properties']);
×
293
        $this->assertArrayHasKey('oneOf', $definitions[$rootDefinitionKey]['properties']['unionType']);
×
294
        $this->assertCount(2, $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf']);
×
295
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][0]);
×
296
        $this->assertSame(['string', 'null'], $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][0]['type']);
×
297
        $this->assertSame(['integer', 'null'], $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][1]['type']);
×
298

299
        $this->assertArrayHasKey('intersectType', $definitions[$rootDefinitionKey]['properties']);
×
300
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['intersectType']);
×
301
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['intersectType']['type']);
×
302
    }
303

304
    public function testBuildSchemaForNonResourceClassWithUnionIntersectTypes(): void
305
    {
306
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
307

308
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
309
        $propertyNameCollectionFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, Argument::cetera())->willReturn(new PropertyNameCollection(['ignoredProperty', 'unionType', 'intersectType']));
×
310

311
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
312
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'ignoredProperty', Argument::cetera())->willReturn(
×
313
            (new ApiProperty())
×
314
                ->withNativeType(Type::nullable(Type::string()))
×
315
                ->withReadable(true)
×
316
                ->withSchema(['type' => ['string', 'null']])
×
317
        );
×
318
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'unionType', Argument::cetera())->willReturn(
×
319
            (new ApiProperty())
×
320
                ->withNativeType(Type::union(Type::string(), Type::int(), Type::float(), Type::null()))
×
321
                ->withReadable(true)
×
322
                ->withSchema(['oneOf' => [
×
323
                    ['type' => ['string', 'null']],
×
324
                    ['type' => ['integer', 'null']],
×
325
                ]])
×
326
        );
×
327
        $propertyMetadataFactoryProphecy->create(NotAResourceWithUnionIntersectTypes::class, 'intersectType', Argument::cetera())->willReturn(
×
328
            (new ApiProperty())
×
329
                ->withNativeType(Type::intersection(Type::object(Serializable::class), Type::object(DummyResourceInterface::class)))
×
330
                ->withReadable(true)
×
331
                ->withSchema(['type' => 'object'])
×
332
        );
×
333

334
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
335
        $resourceClassResolverProphecy->isResourceClass(NotAResourceWithUnionIntersectTypes::class)->willReturn(false);
×
336

337
        $definitionNameFactory = new DefinitionNameFactory();
×
338

339
        $schemaFactory = new SchemaFactory(
×
340
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
341
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
342
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
343
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
344
            definitionNameFactory: $definitionNameFactory,
×
345
        );
×
346
        $resultSchema = $schemaFactory->buildSchema(NotAResourceWithUnionIntersectTypes::class);
×
347

348
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
349
        $definitions = $resultSchema->getDefinitions();
×
350

351
        $this->assertSame((new \ReflectionClass(NotAResourceWithUnionIntersectTypes::class))->getShortName(), $rootDefinitionKey);
×
352
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
353
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
354
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
355
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]);
×
356
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
357

358
        $this->assertArrayHasKey('ignoredProperty', $definitions[$rootDefinitionKey]['properties']);
×
359
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['ignoredProperty']);
×
360
        $this->assertSame(['string', 'null'], $definitions[$rootDefinitionKey]['properties']['ignoredProperty']['type']);
×
361

362
        $this->assertArrayHasKey('unionType', $definitions[$rootDefinitionKey]['properties']);
×
363
        $this->assertArrayHasKey('oneOf', $definitions[$rootDefinitionKey]['properties']['unionType']);
×
364
        $this->assertCount(2, $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf']);
×
365
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][0]);
×
366
        $this->assertSame(['string', 'null'], $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][0]['type']);
×
367
        $this->assertSame(['integer', 'null'], $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf'][1]['type']);
×
368

369
        $this->assertArrayHasKey('intersectType', $definitions[$rootDefinitionKey]['properties']);
×
370
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['intersectType']);
×
371
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['intersectType']['type']);
×
372
    }
373

374
    #[IgnoreDeprecations]
375
    public function testBuildSchemaWithSerializerGroupsLegacy(): void
376
    {
NEW
377
        if (!class_exists(LegacyType::class)) {
×
NEW
378
            $this->markTestSkipped();
×
379
        }
380
        $this->expectUserDeprecationMessage('Since api-platform/metadata 4.2: The "ApiPlatform\Metadata\ApiProperty::withBuiltinTypes()" method is deprecated, use "ApiPlatform\Metadata\ApiProperty::withNativeType()" instead.');
×
381
        $shortName = (new \ReflectionClass(OverriddenOperationDummy::class))->getShortName();
×
382
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
383
        $operation = (new Put())->withName('put')->withNormalizationContext([
×
384
            'groups' => 'overridden_operation_dummy_put',
×
385
            AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false,
×
386
        ])->withShortName($shortName)->withValidationContext(['groups' => ['validation_groups_dummy_put']]);
×
387
        $resourceMetadataFactoryProphecy->create(OverriddenOperationDummy::class)
×
388
            ->willReturn(
×
389
                new ResourceMetadataCollection(OverriddenOperationDummy::class, [
×
390
                    (new ApiResource())->withOperations(new Operations(['put' => $operation])),
×
391
                ])
×
392
            );
×
393

394
        $serializerGroup = 'custom_operation_dummy';
×
395

396
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
397
        $propertyNameCollectionFactoryProphecy->create(OverriddenOperationDummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['alias', 'description', 'genderType']));
×
398

399
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
400
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'alias', Argument::type('array'))->willReturn(
×
401
            (new ApiProperty())
×
402
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])
×
403
                ->withReadable(true)
×
404
                ->withSchema(['type' => 'string'])
×
405
        );
×
406
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'description', Argument::type('array'))->willReturn(
×
407
            (new ApiProperty())
×
408
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_STRING)])
×
409
                ->withReadable(true)
×
410
                ->withSchema(['type' => 'string'])
×
411
        );
×
412
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'genderType', Argument::type('array'))->willReturn(
×
413
            (new ApiProperty())
×
414
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, GenderTypeEnum::class)])
×
415
                ->withReadable(true)
×
416
                ->withDefault(GenderTypeEnum::MALE)
×
417
                ->withSchema(['type' => 'object'])
×
418
        );
×
419

420
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
421
        $resourceClassResolverProphecy->isResourceClass(OverriddenOperationDummy::class)->willReturn(true);
×
422
        $resourceClassResolverProphecy->isResourceClass(GenderTypeEnum::class)->willReturn(true);
×
423

424
        $definitionNameFactory = new DefinitionNameFactory();
×
425

426
        $schemaFactory = new SchemaFactory(
×
427
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
428
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
429
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
430
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
431
            definitionNameFactory: $definitionNameFactory,
×
432
        );
×
433
        $resultSchema = $schemaFactory->buildSchema(OverriddenOperationDummy::class, 'json', Schema::TYPE_OUTPUT, null, null, ['groups' => $serializerGroup, AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false]);
×
434

435
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
436
        $definitions = $resultSchema->getDefinitions();
×
437

438
        $this->assertSame((new \ReflectionClass(OverriddenOperationDummy::class))->getShortName().'-'.$serializerGroup, $rootDefinitionKey);
×
439
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
440
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
441
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
442
        $this->assertFalse($definitions[$rootDefinitionKey]['additionalProperties']);
×
443
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
444
        $this->assertArrayHasKey('alias', $definitions[$rootDefinitionKey]['properties']);
×
445
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['alias']);
×
446
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['alias']['type']);
×
447
        $this->assertArrayHasKey('description', $definitions[$rootDefinitionKey]['properties']);
×
448
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['description']);
×
449
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['description']['type']);
×
450
        $this->assertArrayHasKey('genderType', $definitions[$rootDefinitionKey]['properties']);
×
451
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
452
        $this->assertArrayNotHasKey('default', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
453
        $this->assertArrayNotHasKey('example', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
454
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['genderType']['type']);
×
455
    }
456

457
    public function testBuildSchemaWithSerializerGroups(): void
458
    {
459
        $shortName = (new \ReflectionClass(OverriddenOperationDummy::class))->getShortName();
×
460
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
461
        $operation = (new Put())->withName('put')->withNormalizationContext([
×
462
            'groups' => 'overridden_operation_dummy_put',
×
463
            AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false,
×
464
        ])->withShortName($shortName)->withValidationContext(['groups' => ['validation_groups_dummy_put']]);
×
465
        $resourceMetadataFactoryProphecy->create(OverriddenOperationDummy::class)
×
466
            ->willReturn(
×
467
                new ResourceMetadataCollection(OverriddenOperationDummy::class, [
×
468
                    (new ApiResource())->withOperations(new Operations(['put' => $operation])),
×
469
                ])
×
470
            );
×
471

472
        $serializerGroup = 'custom_operation_dummy';
×
473

474
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
475
        $propertyNameCollectionFactoryProphecy->create(OverriddenOperationDummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['alias', 'description', 'genderType']));
×
476

477
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
478
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'alias', Argument::type('array'))->willReturn(
×
479
            (new ApiProperty())
×
480
                ->withNativeType(Type::string())
×
481
                ->withReadable(true)
×
482
                ->withSchema(['type' => 'string'])
×
483
        );
×
484
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'description', Argument::type('array'))->willReturn(
×
485
            (new ApiProperty())
×
486
                ->withNativeType(Type::string())
×
487
                ->withReadable(true)
×
488
                ->withSchema(['type' => 'string'])
×
489
        );
×
490
        $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'genderType', Argument::type('array'))->willReturn(
×
491
            (new ApiProperty())
×
492
                ->withNativeType(Type::enum(GenderTypeEnum::class))
×
493
                ->withReadable(true)
×
494
                ->withDefault(GenderTypeEnum::MALE)
×
495
                ->withSchema(['type' => 'object'])
×
496
        );
×
497
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
498
        $resourceClassResolverProphecy->isResourceClass(OverriddenOperationDummy::class)->willReturn(true);
×
499
        $resourceClassResolverProphecy->isResourceClass(GenderTypeEnum::class)->willReturn(true);
×
500

501
        $definitionNameFactory = new DefinitionNameFactory();
×
502

503
        $schemaFactory = new SchemaFactory(
×
504
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
505
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
506
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
507
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
508
            definitionNameFactory: $definitionNameFactory,
×
509
        );
×
510
        $resultSchema = $schemaFactory->buildSchema(OverriddenOperationDummy::class, 'json', Schema::TYPE_OUTPUT, null, null, ['groups' => $serializerGroup, AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false]);
×
511

512
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
513
        $definitions = $resultSchema->getDefinitions();
×
514

515
        $this->assertSame((new \ReflectionClass(OverriddenOperationDummy::class))->getShortName().'-'.$serializerGroup, $rootDefinitionKey);
×
516
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
517
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]);
×
518
        $this->assertSame('object', $definitions[$rootDefinitionKey]['type']);
×
519
        $this->assertFalse($definitions[$rootDefinitionKey]['additionalProperties']);
×
520
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
521
        $this->assertArrayHasKey('alias', $definitions[$rootDefinitionKey]['properties']);
×
522
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['alias']);
×
523
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['alias']['type']);
×
524
        $this->assertArrayHasKey('description', $definitions[$rootDefinitionKey]['properties']);
×
525
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['description']);
×
526
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['description']['type']);
×
527
        $this->assertArrayHasKey('genderType', $definitions[$rootDefinitionKey]['properties']);
×
528
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
529
        $this->assertArrayNotHasKey('default', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
530
        $this->assertArrayNotHasKey('example', $definitions[$rootDefinitionKey]['properties']['genderType']);
×
531
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['genderType']['type']);
×
532
    }
533

534
    #[IgnoreDeprecations]
535
    public function testBuildSchemaForAssociativeArrayLegacy(): void
536
    {
NEW
537
        if (!class_exists(LegacyType::class)) {
×
NEW
538
            $this->markTestSkipped();
×
539
        }
540
        $this->expectUserDeprecationMessage('Since api-platform/metadata 4.2: The "ApiPlatform\Metadata\ApiProperty::withBuiltinTypes()" method is deprecated, use "ApiPlatform\Metadata\ApiProperty::withNativeType()" instead.');
×
541
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
542

543
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
544
        $propertyNameCollectionFactoryProphecy->create(NotAResource::class, Argument::cetera())->willReturn(new PropertyNameCollection(['foo', 'bar']));
×
545

546
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
547
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'foo', Argument::cetera())->willReturn(
×
548
            (new ApiProperty())
×
549
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, false, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), new LegacyType(LegacyType::BUILTIN_TYPE_STRING))])
×
550
                ->withReadable(true)
×
551
                ->withSchema(['type' => 'array', 'items' => ['string', 'int']])
×
552
        );
×
553
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'bar', Argument::cetera())->willReturn(
×
554
            (new ApiProperty())
×
555
                ->withBuiltinTypes([new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, false, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_STRING), new LegacyType(LegacyType::BUILTIN_TYPE_STRING))])
×
556
                ->withReadable(true)
×
557
                ->withSchema(['type' => 'object', 'additionalProperties' => 'string'])
×
558
        );
×
559

560
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
561
        $resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
×
562

563
        $definitionNameFactory = new DefinitionNameFactory();
×
564

565
        $schemaFactory = new SchemaFactory(
×
566
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
567
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
568
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
569
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
570
            definitionNameFactory: $definitionNameFactory,
×
571
        );
×
572
        $resultSchema = $schemaFactory->buildSchema(NotAResource::class);
×
573

574
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
575
        $definitions = $resultSchema->getDefinitions();
×
576

577
        $this->assertSame((new \ReflectionClass(NotAResource::class))->getShortName(), $rootDefinitionKey);
×
578
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
579
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
580
        $this->assertArrayHasKey('foo', $definitions[$rootDefinitionKey]['properties']);
×
581
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['foo']);
×
582
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]['properties']['foo']);
×
583
        $this->assertSame('array', $definitions[$rootDefinitionKey]['properties']['foo']['type']);
×
584
        $this->assertArrayHasKey('bar', $definitions[$rootDefinitionKey]['properties']);
×
585
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['bar']);
×
586
        $this->assertArrayHasKey('additionalProperties', $definitions[$rootDefinitionKey]['properties']['bar']);
×
587
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['bar']['type']);
×
588
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['bar']['additionalProperties']);
×
589
    }
590

591
    public function testBuildSchemaForAssociativeArray(): void
592
    {
593
        $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
594

595
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
596
        $propertyNameCollectionFactoryProphecy->create(NotAResource::class, Argument::cetera())->willReturn(new PropertyNameCollection(['foo', 'bar']));
×
597

598
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
599
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'foo', Argument::cetera())->willReturn(
×
600
            (new ApiProperty())
×
601
                ->withNativeType(Type::list(Type::string()))
×
602
                ->withReadable(true)
×
603
                ->withSchema(['type' => 'array', 'items' => ['string', 'int']])
×
604
        );
×
605
        $propertyMetadataFactoryProphecy->create(NotAResource::class, 'bar', Argument::cetera())->willReturn(
×
606
            (new ApiProperty())
×
607
                ->withNativeType(Type::dict(Type::string()))
×
608
                ->withReadable(true)
×
609
                ->withSchema(['type' => 'object', 'additionalProperties' => 'string'])
×
610
        );
×
611

612
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
613
        $resourceClassResolverProphecy->isResourceClass(NotAResource::class)->willReturn(false);
×
614

615
        $definitionNameFactory = new DefinitionNameFactory();
×
616

617
        $schemaFactory = new SchemaFactory(
×
618
            resourceMetadataFactory: $resourceMetadataFactoryProphecy->reveal(),
×
619
            propertyNameCollectionFactory: $propertyNameCollectionFactoryProphecy->reveal(),
×
620
            propertyMetadataFactory: $propertyMetadataFactoryProphecy->reveal(),
×
621
            resourceClassResolver: $resourceClassResolverProphecy->reveal(),
×
622
            definitionNameFactory: $definitionNameFactory,
×
623
        );
×
624
        $resultSchema = $schemaFactory->buildSchema(NotAResource::class);
×
625

626
        $rootDefinitionKey = $resultSchema->getRootDefinitionKey();
×
627
        $definitions = $resultSchema->getDefinitions();
×
628

629
        $this->assertSame((new \ReflectionClass(NotAResource::class))->getShortName(), $rootDefinitionKey);
×
630
        $this->assertTrue(isset($definitions[$rootDefinitionKey]));
×
631
        $this->assertArrayHasKey('properties', $definitions[$rootDefinitionKey]);
×
632
        $this->assertArrayHasKey('foo', $definitions[$rootDefinitionKey]['properties']);
×
633
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['foo']);
×
634
        $this->assertArrayNotHasKey('additionalProperties', $definitions[$rootDefinitionKey]['properties']['foo']);
×
635
        $this->assertSame('array', $definitions[$rootDefinitionKey]['properties']['foo']['type']);
×
636
        $this->assertArrayHasKey('bar', $definitions[$rootDefinitionKey]['properties']);
×
637
        $this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['bar']);
×
638
        $this->assertArrayHasKey('additionalProperties', $definitions[$rootDefinitionKey]['properties']['bar']);
×
639
        $this->assertSame('object', $definitions[$rootDefinitionKey]['properties']['bar']['type']);
×
640
        $this->assertSame('string', $definitions[$rootDefinitionKey]['properties']['bar']['additionalProperties']);
×
641
    }
642
}
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