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

api-platform / core / 14500959057

16 Apr 2025 07:29PM UTC coverage: 8.532% (+0.3%) from 8.189%
14500959057

push

github

web-flow
feat: Use `Type` of `TypeInfo` instead of `PropertyInfo` (#6979)

Co-authored-by: soyuka <soyuka@users.noreply.github.com>

scopes: metadata, doctrine, json-schema

300 of 616 new or added lines in 25 files covered. (48.7%)

285 existing lines in 18 files now uncovered.

13513 of 158381 relevant lines covered (8.53%)

22.97 hits per line

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

0.0
/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.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\JsonSchema\Command;
15

16
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Animal;
17
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\AnimalObservation;
18
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\BackedEnumIntegerResource;
19
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\BackedEnumStringResource;
20
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue5501\BrokenDocs;
21
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6299\Issue6299;
22
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6317\Issue6317;
23
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6800\TestApiDocHashmapArrayObjectIssue;
24
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\ResourceWithEnumProperty;
25
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Species;
26
use ApiPlatform\Tests\Fixtures\TestBundle\Document\Dummy as DocumentDummy;
27
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Answer;
28
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Dummy;
29
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5793\BagOfTests;
30
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5998\Issue5998Product;
31
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5998\ProductCode;
32
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5998\SaveProduct;
33
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest;
34
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Question;
35
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\RelatedDummy;
36
use ApiPlatform\Tests\SetupClassResourcesTrait;
37
use PHPUnit\Framework\Attributes\DataProvider;
38
use Symfony\Bundle\FrameworkBundle\Console\Application;
39
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
40
use Symfony\Component\Console\Tester\ApplicationTester;
41

42
/**
43
 * @author Jacques Lefebvre <jacques@les-tilleuls.coop>
44
 */
45
class JsonSchemaGenerateCommandTest extends KernelTestCase
46
{
47
    use SetupClassResourcesTrait;
48
    private ApplicationTester $tester;
49
    private string $entityClass;
50

51
    protected function setUp(): void
52
    {
53
        $kernel = self::bootKernel();
×
54

55
        $application = new Application(static::$kernel);
×
56
        $application->setCatchExceptions(true);
×
57
        $application->setAutoExit(false);
×
58

59
        $this->entityClass = 'mongodb' === $kernel->getEnvironment() ? DocumentDummy::class : Dummy::class;
×
60
        $this->tester = new ApplicationTester($application);
×
61
    }
62

63
    /**
64
     * @return class-string[]
65
     */
66
    public static function getResources(): array
67
    {
68
        return [
×
69
            Dummy::class,
×
70
            BrokenDocs::class,
×
71
            Nest::class,
×
72
            BagOfTests::class,
×
73
            ResourceWithEnumProperty::class,
×
74
            Issue6299::class,
×
75
            RelatedDummy::class,
×
76
            Question::class,
×
77
            Answer::class,
×
78
            AnimalObservation::class,
×
79
            Animal::class,
×
80
            Species::class,
×
81
            Issue6317::class,
×
82
            ProductCode::class,
×
83
            Issue5998Product::class,
×
84
            BackedEnumIntegerResource::class,
×
85
            BackedEnumStringResource::class,
×
86
        ];
×
87
    }
88

89
    public function testExecuteWithoutOption(): void
90
    {
91
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => $this->entityClass]);
×
92

93
        $this->assertJson($this->tester->getDisplay());
×
94
    }
95

96
    public function testExecuteWithItemOperationGet(): void
97
    {
98
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => $this->entityClass, '--operation' => '_api_/dummies/{id}{._format}_get', '--type' => 'output']);
×
99

100
        $this->assertJson($this->tester->getDisplay());
×
101
    }
102

103
    public function testExecuteWithCollectionOperationGet(): void
104
    {
105
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => $this->entityClass, '--operation' => '_api_/dummies{._format}_get_collection', '--type' => 'output']);
×
106

107
        $this->assertJson($this->tester->getDisplay());
×
108
    }
109

110
    public function testExecuteWithJsonldFormatOption(): void
111
    {
112
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => $this->entityClass, '--operation' => '_api_/dummies{._format}_post', '--format' => 'jsonld', '--type' => 'output']);
×
113
        $result = $this->tester->getDisplay();
×
114

115
        $this->assertStringContainsString('@id', $result);
×
116
        $this->assertStringContainsString('@context', $result);
×
117
        $this->assertStringContainsString('@type', $result);
×
118
    }
119

120
    public function testExecuteWithJsonldTypeInput(): void
121
    {
122
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => $this->entityClass, '--operation' => '_api_/dummies{._format}_post', '--format' => 'jsonld', '--type' => 'input']);
×
123
        $result = $this->tester->getDisplay();
×
124

125
        $this->assertStringNotContainsString('@id', $result);
×
126
        $this->assertStringNotContainsString('@context', $result);
×
127
        $this->assertStringNotContainsString('@type', $result);
×
128
    }
129

130
    /**
131
     * Test issue #5501, the locations relation inside BrokenDocs is a Resource (named Related) but its only operation is a NotExposed.
132
     * Still, serializer groups are set, and therefore it is a "readableLink" so we actually want to compute the schema, even if it's not accessible
133
     * directly, it is accessible through that relation.
134
     */
135
    public function testExecuteWithNotExposedResourceAndReadableLink(): void
136
    {
137
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => BrokenDocs::class, '--type' => 'output']);
×
138
        $result = $this->tester->getDisplay();
×
139

140
        $this->assertStringContainsString('Related.jsonld-location.read_collection', $result);
×
141
    }
142

143
    /**
144
     * When serializer groups are present the Schema should have an embed resource. #5470 breaks array references when serializer groups are present.
145
     */
146
    #[\PHPUnit\Framework\Attributes\Group('orm')]
147
    public function testArraySchemaWithReference(): void
148
    {
149
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => BagOfTests::class, '--type' => 'input']);
×
150
        $result = $this->tester->getDisplay();
×
151
        $json = json_decode($result, associative: true);
×
152

153
        $this->assertEquals($json['definitions']['BagOfTests.jsonld-write']['properties']['tests'], [
×
154
            'type' => 'string',
×
155
            'foo' => 'bar',
×
156
        ]);
×
157

158
        $this->assertEquals($json['definitions']['BagOfTests.jsonld-write']['properties']['nonResourceTests'], [
×
159
            'type' => 'array',
×
160
            'items' => [
×
161
                '$ref' => '#/definitions/NonResourceTestEntity.jsonld-write',
×
162
            ],
×
163
        ]);
×
164

165
        $this->assertEquals($json['definitions']['BagOfTests.jsonld-write']['properties']['description'], [
×
166
            'maxLength' => 255,
×
167
        ]);
×
168

169
        $this->assertEquals($json['definitions']['BagOfTests.jsonld-write']['properties']['type'], [
×
170
            '$ref' => '#/definitions/TestEntity.jsonld-write',
×
171
        ]);
×
172
    }
173

174
    public function testArraySchemaWithMultipleUnionTypesJsonLd(): void
175
    {
176
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Nest::class, '--type' => 'output', '--format' => 'jsonld']);
×
177
        $result = $this->tester->getDisplay();
×
178
        $json = json_decode($result, associative: true);
×
179

NEW
180
        $this->assertContains(['$ref' => '#/definitions/Robin.jsonld'], $json['definitions']['Nest.jsonld']['properties']['owner']['anyOf']);
×
NEW
181
        $this->assertContains(['$ref' => '#/definitions/Wren.jsonld'], $json['definitions']['Nest.jsonld']['properties']['owner']['anyOf']);
×
NEW
182
        $this->assertContains(['type' => 'null'], $json['definitions']['Nest.jsonld']['properties']['owner']['anyOf']);
×
183

184
        $this->assertArrayHasKey('Wren.jsonld', $json['definitions']);
×
185
        $this->assertArrayHasKey('Robin.jsonld', $json['definitions']);
×
186
    }
187

188
    public function testArraySchemaWithMultipleUnionTypesJsonApi(): void
189
    {
190
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Nest::class, '--type' => 'output', '--format' => 'jsonapi']);
×
191
        $result = $this->tester->getDisplay();
×
192
        $json = json_decode($result, associative: true);
×
193

NEW
194
        $this->assertContains(['$ref' => '#/definitions/Robin.jsonapi'], $json['definitions']['Nest.jsonapi']['properties']['data']['properties']['attributes']['properties']['owner']['anyOf']);
×
NEW
195
        $this->assertContains(['$ref' => '#/definitions/Wren.jsonapi'], $json['definitions']['Nest.jsonapi']['properties']['data']['properties']['attributes']['properties']['owner']['anyOf']);
×
NEW
196
        $this->assertContains(['type' => 'null'], $json['definitions']['Nest.jsonapi']['properties']['data']['properties']['attributes']['properties']['owner']['anyOf']);
×
197

198
        $this->assertArrayHasKey('Wren.jsonapi', $json['definitions']);
×
199
        $this->assertArrayHasKey('Robin.jsonapi', $json['definitions']);
×
200
    }
201

202
    public function testArraySchemaWithMultipleUnionTypesJsonHal(): void
203
    {
204
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Nest::class, '--type' => 'output', '--format' => 'jsonhal']);
×
205
        $result = $this->tester->getDisplay();
×
206
        $json = json_decode($result, associative: true);
×
207

NEW
208
        $this->assertContains(['$ref' => '#/definitions/Robin.jsonhal'], $json['definitions']['Nest.jsonhal']['properties']['owner']['anyOf']);
×
NEW
209
        $this->assertContains(['$ref' => '#/definitions/Wren.jsonhal'], $json['definitions']['Nest.jsonhal']['properties']['owner']['anyOf']);
×
NEW
210
        $this->assertContains(['type' => 'null'], $json['definitions']['Nest.jsonhal']['properties']['owner']['anyOf']);
×
211

212
        $this->assertArrayHasKey('Wren.jsonhal', $json['definitions']);
×
213
        $this->assertArrayHasKey('Robin.jsonhal', $json['definitions']);
×
214
    }
215

216
    /**
217
     * Test issue #5998.
218
     */
219
    public function testWritableNonResourceRef(): void
220
    {
221
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => SaveProduct::class, '--type' => 'input']);
×
222
        $result = $this->tester->getDisplay();
×
223
        $json = json_decode($result, associative: true);
×
224

225
        $this->assertEquals($json['definitions']['SaveProduct.jsonld']['properties']['codes']['items']['$ref'], '#/definitions/ProductCode.jsonld');
×
226
    }
227

228
    /**
229
     * Test issue #6299.
230
     */
231
    public function testOpenApiResourceRefIsNotOverwritten(): void
232
    {
233
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Issue6299::class, '--type' => 'output']);
×
234
        $result = $this->tester->getDisplay();
×
235
        $json = json_decode($result, associative: true);
×
236

237
        $this->assertEquals('#/definitions/DummyFriend', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['properties']['itemDto']['$ref']);
×
238
        $this->assertEquals('#/definitions/DummyDate', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['properties']['collectionDto']['items']['$ref']);
×
239
    }
240

241
    /**
242
     * Test related Schema keeps json-ld context.
243
     */
244
    #[\PHPUnit\Framework\Attributes\Group('orm')]
245
    public function testSubSchemaJsonLd(): void
246
    {
247
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => RelatedDummy::class, '--type' => 'output', '--format' => 'jsonld']);
×
248
        $result = $this->tester->getDisplay();
×
249
        $json = json_decode($result, associative: true);
×
250

251
        $this->assertArrayHasKey('@id', $json['definitions']['ThirdLevel.jsonld-friends']['properties']);
×
252
    }
253

254
    #[\PHPUnit\Framework\Attributes\Group('orm')]
255
    public function testJsonApiIncludesSchema(): void
256
    {
257
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Question::class, '--type' => 'output', '--format' => 'jsonapi']);
×
258
        $result = $this->tester->getDisplay();
×
259
        $json = json_decode($result, associative: true);
×
260
        $properties = $json['definitions']['Question.jsonapi']['properties']['data']['properties'];
×
261
        $included = $json['definitions']['Question.jsonapi']['properties']['included'];
×
262

263
        $this->assertArrayHasKey('answer', $properties['relationships']['properties']);
×
264
        $this->assertArrayHasKey('anyOf', $included['items']);
×
265
        $this->assertCount(1, $included['items']['anyOf']);
×
266
        $this->assertArrayHasKey('$ref', $included['items']['anyOf'][0]);
×
267
        $this->assertSame('#/definitions/Answer.jsonapi', $included['items']['anyOf'][0]['$ref']);
×
268

269
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => AnimalObservation::class, '--type' => 'output', '--format' => 'jsonapi']);
×
270
        $result = $this->tester->getDisplay();
×
271
        $json = json_decode($result, associative: true);
×
272
        $properties = $json['definitions']['AnimalObservation.jsonapi']['properties']['data']['properties'];
×
273
        $included = $json['definitions']['AnimalObservation.jsonapi']['properties']['included'];
×
274

275
        $this->assertArrayHasKey('individuals', $properties['relationships']['properties']);
×
276
        $this->assertArrayNotHasKey('individuals', $properties['attributes']['properties']);
×
277
        $this->assertArrayHasKey('anyOf', $included['items']);
×
278
        $this->assertCount(1, $included['items']['anyOf']);
×
279
        $this->assertSame('#/definitions/Animal.jsonapi', $included['items']['anyOf'][0]['$ref']);
×
280

281
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Animal::class, '--type' => 'output', '--format' => 'jsonapi']);
×
282
        $result = $this->tester->getDisplay();
×
283
        $json = json_decode($result, associative: true);
×
284
        $properties = $json['definitions']['Animal.jsonapi']['properties']['data']['properties'];
×
285
        $included = $json['definitions']['Animal.jsonapi']['properties']['included'];
×
286

287
        $this->assertArrayHasKey('species', $properties['relationships']['properties']);
×
288
        $this->assertArrayNotHasKey('species', $properties['attributes']['properties']);
×
289
        $this->assertArrayHasKey('anyOf', $included['items']);
×
290
        $this->assertCount(1, $included['items']['anyOf']);
×
291
        $this->assertSame('#/definitions/Species.jsonapi', $included['items']['anyOf'][0]['$ref']);
×
292

293
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Species::class, '--type' => 'output', '--format' => 'jsonapi']);
×
294
        $result = $this->tester->getDisplay();
×
295
        $json = json_decode($result, associative: true);
×
296
        $properties = $json['definitions']['Species.jsonapi']['properties']['data']['properties'];
×
297

298
        $this->assertArrayHasKey('kingdom', $properties['attributes']['properties']);
×
299
        $this->assertArrayHasKey('phylum', $properties['attributes']['properties']);
×
300
    }
301

302
    /**
303
     * Test issue #6317.
304
     */
305
    public function testBackedEnumExamplesAreNotLost(): void
306
    {
307
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Issue6317::class, '--type' => 'output', '--format' => 'jsonld']);
×
308
        $result = $this->tester->getDisplay();
×
309
        $json = json_decode($result, associative: true);
×
310
        $properties = $json['definitions']['Issue6317.jsonld']['properties'];
×
311

312
        $this->assertArrayHasKey('example', $properties['id']);
×
313
        $this->assertArrayHasKey('example', $properties['name']);
×
314
        // jsonldContext
315
        $this->assertArrayNotHasKey('example', $properties['ordinal']);
×
316
        // openapiContext
317
        $this->assertArrayNotHasKey('example', $properties['cardinal']);
×
318
    }
319

320
    public function testResourceWithEnumPropertiesSchema(): void
321
    {
322
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => ResourceWithEnumProperty::class, '--type' => 'output', '--format' => 'jsonld']);
×
323
        $result = $this->tester->getDisplay();
×
324
        $json = json_decode($result, associative: true);
×
325
        $properties = $json['definitions']['ResourceWithEnumProperty.jsonld']['properties'];
×
326

327
        $this->assertSame(
×
328
            [
×
329
                'type' => ['string', 'null'],
×
330
                'format' => 'iri-reference',
×
331
                'example' => 'https://example.com/',
×
332
            ],
×
333
            $properties['intEnum']
×
334
        );
×
335
        $this->assertSame(
×
336
            [
×
337
                'type' => 'array',
×
338
                'items' => [
×
339
                    'type' => 'string',
×
340
                    'format' => 'iri-reference',
×
341
                    'example' => 'https://example.com/',
×
342
                ],
×
343
            ],
×
344
            $properties['stringEnum']
×
345
        );
×
346
        $this->assertSame(
×
347
            [
×
348
                'type' => ['string', 'null'],
×
349
                'enum' => ['male', 'female', null],
×
350
            ],
×
351
            $properties['gender']
×
352
        );
×
353
        $this->assertSame(
×
354
            [
×
355
                'type' => 'array',
×
356
                'items' => [
×
357
                    'type' => 'string',
×
358
                    'enum' => ['male', 'female'],
×
359
                ],
×
360
            ],
×
361
            $properties['genders']
×
362
        );
×
363
    }
364

365
    /**
366
     * Test feature #6716.
367
     */
368
    public function testGenId(): void
369
    {
370
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\DisableIdGeneration', '--type' => 'output', '--format' => 'jsonld']);
×
371
        $result = $this->tester->getDisplay();
×
372
        $json = json_decode($result, associative: true);
×
373
        $this->assertArrayNotHasKey('@id', $json['definitions']['DisableIdGenerationItem.jsonld']['properties']);
×
374
    }
375

376
    #[DataProvider('arrayPropertyTypeSyntaxProvider')]
377
    public function testOpenApiSchemaGenerationForArrayProperty(string $propertyName, array $expectedProperties): void
378
    {
379
        $this->tester->run([
×
380
            'command' => 'api:json-schema:generate',
×
381
            'resource' => TestApiDocHashmapArrayObjectIssue::class,
×
382
            '--operation' => '_api_/test_api_doc_hashmap_array_object_issues{._format}_get',
×
383
            '--type' => 'output',
×
384
            '--format' => 'jsonld',
×
385
        ]);
×
386

387
        $result = $this->tester->getDisplay();
×
388
        $json = json_decode($result, true);
×
389
        $definitions = $json['definitions'];
×
390
        $ressourceDefinitions = $definitions['TestApiDocHashmapArrayObjectIssue.jsonld'];
×
391

392
        $this->assertArrayHasKey('TestApiDocHashmapArrayObjectIssue.jsonld', $definitions);
×
393
        $this->assertEquals('object', $ressourceDefinitions['type']);
×
394
        $this->assertEquals($expectedProperties, $ressourceDefinitions['properties'][$propertyName]);
×
395
    }
396

397
    public static function arrayPropertyTypeSyntaxProvider(): \Generator
398
    {
399
        yield 'Array of Foo objects using array<Foo> syntax' => [
×
400
            'foos',
×
401
            [
×
402
                'type' => 'array',
×
403
                'items' => [
×
404
                    '$ref' => '#/definitions/Foo.jsonld',
×
405
                ],
×
406
            ],
×
407
        ];
×
408
        yield 'Array of Foo objects using Foo[] syntax' => [
×
409
            'fooOtherSyntax',
×
410
            [
×
411
                'type' => 'array',
×
412
                'items' => [
×
413
                    '$ref' => '#/definitions/Foo.jsonld',
×
414
                ],
×
415
            ],
×
416
        ];
×
417
        yield 'Hashmap of Foo objects using array<string, Foo> syntax' => [
×
418
            'fooHashmaps',
×
419
            [
×
420
                'type' => 'object',
×
421
                'additionalProperties' => [
×
422
                    '$ref' => '#/definitions/Foo.jsonld',
×
423
                ],
×
424
            ],
×
425
        ];
×
426
    }
427
}
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