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

api-platform / core / 13175309672

06 Feb 2025 09:04AM UTC coverage: 7.663% (-0.2%) from 7.841%
13175309672

push

github

web-flow
fix: ensure template files have a tpl file extension (#6826) (#6829)

2 of 5 new or added lines in 4 files covered. (40.0%)

3676 existing lines in 122 files now uncovered.

13073 of 170593 relevant lines covered (7.66%)

27.3 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 Symfony\Bundle\FrameworkBundle\Console\Application;
38
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
39
use Symfony\Component\Console\Tester\ApplicationTester;
40

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

129
    /**
130
     * Test issue #5501, the locations relation inside BrokenDocs is a Resource (named Related) but its only operation is a NotExposed.
131
     * 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
132
     * directly, it is accessible through that relation.
133
     */
134
    public function testExecuteWithNotExposedResourceAndReadableLink(): void
135
    {
136
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => BrokenDocs::class, '--type' => 'output']);
×
UNCOV
137
        $result = $this->tester->getDisplay();
×
138

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

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

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

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

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

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

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

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

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

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

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

201
        $this->assertArrayHasKey('Wren.jsonapi', $json['definitions']);
×
UNCOV
202
        $this->assertArrayHasKey('Robin.jsonapi', $json['definitions']);
×
203
    }
204

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

211
        $this->assertEquals($json['definitions']['Nest.jsonhal']['properties']['owner']['anyOf'], [
×
212
            ['$ref' => '#/definitions/Wren.jsonhal'],
×
213
            ['$ref' => '#/definitions/Robin.jsonhal'],
×
214
            ['type' => 'null'],
×
UNCOV
215
        ]);
×
216

217
        $this->assertArrayHasKey('Wren.jsonhal', $json['definitions']);
×
UNCOV
218
        $this->assertArrayHasKey('Robin.jsonhal', $json['definitions']);
×
219
    }
220

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

UNCOV
230
        $this->assertEquals($json['definitions']['SaveProduct.jsonld']['properties']['codes']['items']['$ref'], '#/definitions/ProductCode.jsonld');
×
231
    }
232

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

242
        $this->assertEquals('#/definitions/DummyFriend', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['properties']['itemDto']['$ref']);
×
UNCOV
243
        $this->assertEquals('#/definitions/DummyDate', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['properties']['collectionDto']['items']['$ref']);
×
244
    }
245

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

UNCOV
256
        $this->assertArrayHasKey('@id', $json['definitions']['ThirdLevel.jsonld-friends']['properties']);
×
257
    }
258

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

268
        $this->assertArrayHasKey('answer', $properties['relationships']['properties']);
×
269
        $this->assertArrayHasKey('anyOf', $included['items']);
×
270
        $this->assertCount(1, $included['items']['anyOf']);
×
271
        $this->assertArrayHasKey('$ref', $included['items']['anyOf'][0]);
×
UNCOV
272
        $this->assertSame('#/definitions/Answer.jsonapi', $included['items']['anyOf'][0]['$ref']);
×
273

274
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => AnimalObservation::class, '--type' => 'output', '--format' => 'jsonapi']);
×
275
        $result = $this->tester->getDisplay();
×
276
        $json = json_decode($result, associative: true);
×
277
        $properties = $json['definitions']['AnimalObservation.jsonapi']['properties']['data']['properties'];
×
UNCOV
278
        $included = $json['definitions']['AnimalObservation.jsonapi']['properties']['included'];
×
279

280
        $this->assertArrayHasKey('individuals', $properties['relationships']['properties']);
×
281
        $this->assertArrayNotHasKey('individuals', $properties['attributes']['properties']);
×
282
        $this->assertArrayHasKey('anyOf', $included['items']);
×
283
        $this->assertCount(1, $included['items']['anyOf']);
×
UNCOV
284
        $this->assertSame('#/definitions/Animal.jsonapi', $included['items']['anyOf'][0]['$ref']);
×
285

286
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Animal::class, '--type' => 'output', '--format' => 'jsonapi']);
×
287
        $result = $this->tester->getDisplay();
×
288
        $json = json_decode($result, associative: true);
×
289
        $properties = $json['definitions']['Animal.jsonapi']['properties']['data']['properties'];
×
UNCOV
290
        $included = $json['definitions']['Animal.jsonapi']['properties']['included'];
×
291

292
        $this->assertArrayHasKey('species', $properties['relationships']['properties']);
×
293
        $this->assertArrayNotHasKey('species', $properties['attributes']['properties']);
×
294
        $this->assertArrayHasKey('anyOf', $included['items']);
×
295
        $this->assertCount(1, $included['items']['anyOf']);
×
UNCOV
296
        $this->assertSame('#/definitions/Species.jsonapi', $included['items']['anyOf'][0]['$ref']);
×
297

298
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => Species::class, '--type' => 'output', '--format' => 'jsonapi']);
×
299
        $result = $this->tester->getDisplay();
×
300
        $json = json_decode($result, associative: true);
×
UNCOV
301
        $properties = $json['definitions']['Species.jsonapi']['properties']['data']['properties'];
×
302

303
        $this->assertArrayHasKey('kingdom', $properties['attributes']['properties']);
×
UNCOV
304
        $this->assertArrayHasKey('phylum', $properties['attributes']['properties']);
×
305
    }
306

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

317
        $this->assertArrayHasKey('example', $properties['id']);
×
UNCOV
318
        $this->assertArrayHasKey('example', $properties['name']);
×
319
        // jsonldContext
UNCOV
320
        $this->assertArrayNotHasKey('example', $properties['ordinal']);
×
321
        // openapiContext
UNCOV
322
        $this->assertArrayNotHasKey('example', $properties['cardinal']);
×
323
    }
324

325
    public function testResourceWithEnumPropertiesSchema(): void
326
    {
327
        $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => ResourceWithEnumProperty::class, '--type' => 'output', '--format' => 'jsonld']);
×
328
        $result = $this->tester->getDisplay();
×
329
        $json = json_decode($result, associative: true);
×
UNCOV
330
        $properties = $json['definitions']['ResourceWithEnumProperty.jsonld']['properties'];
×
331

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

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

381
    /**
382
     * @dataProvider arrayPropertyTypeSyntaxProvider
383
     */
384
    public function testOpenApiSchemaGenerationForArrayProperty(string $propertyName, array $expectedProperties): void
385
    {
UNCOV
386
        $this->tester->run([
×
UNCOV
387
            'command' => 'api:json-schema:generate',
×
UNCOV
388
            'resource' => TestApiDocHashmapArrayObjectIssue::class,
×
UNCOV
389
            '--operation' => '_api_/test_api_doc_hashmap_array_object_issues{._format}_get',
×
UNCOV
390
            '--type' => 'output',
×
UNCOV
391
            '--format' => 'jsonld',
×
UNCOV
392
        ]);
×
393

UNCOV
394
        $result = $this->tester->getDisplay();
×
UNCOV
395
        $json = json_decode($result, true);
×
UNCOV
396
        $definitions = $json['definitions'];
×
UNCOV
397
        $ressourceDefinitions = $definitions['TestApiDocHashmapArrayObjectIssue.jsonld'];
×
398

UNCOV
399
        $this->assertArrayHasKey('TestApiDocHashmapArrayObjectIssue.jsonld', $definitions);
×
UNCOV
400
        $this->assertEquals('object', $ressourceDefinitions['type']);
×
UNCOV
401
        $this->assertEquals($expectedProperties, $ressourceDefinitions['properties'][$propertyName]);
×
402
    }
403

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