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

api-platform / core / 17054099799

18 Aug 2025 10:28PM UTC coverage: 22.386% (-0.2%) from 22.612%
17054099799

Pull #7150

github

web-flow
Merge 973c71211 into 2d501b315
Pull Request #7150: fix: array shape in ProviderInterface

11062 of 49414 relevant lines covered (22.39%)

11.75 hits per line

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

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

16
use ApiPlatform\Laravel\Test\ApiTestAssertionsTrait;
17
use Illuminate\Contracts\Config\Repository;
18
use Illuminate\Foundation\Application;
19
use Illuminate\Foundation\Testing\RefreshDatabase;
20
use Orchestra\Testbench\Concerns\WithWorkbench;
21
use Orchestra\Testbench\TestCase;
22
use Workbench\App\Models\Author;
23
use Workbench\App\Models\Book;
24
use Workbench\Database\Factories\AuthorFactory;
25
use Workbench\Database\Factories\BookFactory;
26
use Workbench\Database\Factories\CommentFactory;
27
use Workbench\Database\Factories\PostFactory;
28
use Workbench\Database\Factories\SluggableFactory;
29
use Workbench\Database\Factories\WithAccessorFactory;
30

31
class JsonLdTest extends TestCase
32
{
33
    use ApiTestAssertionsTrait;
34
    use RefreshDatabase;
35
    use WithWorkbench;
36

37
    /**
38
     * @param Application $app
39
     */
40
    protected function defineEnvironment($app): void
41
    {
42
        tap($app['config'], function (Repository $config): void {
×
43
            $config->set('app.debug', true);
×
44
            $config->set('api-platform.resources', [app_path('Models'), app_path('ApiResource')]);
×
45
        });
×
46
    }
47

48
    public function testGetCollection(): void
49
    {
50
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
51
        $response = $this->get('/api/books', ['accept' => 'application/ld+json']);
×
52
        $response->assertStatus(200);
×
53
        $response->assertHeader('content-type', 'application/ld+json; charset=utf-8');
×
54
        $response->assertJsonFragment([
×
55
            '@context' => '/api/contexts/Book',
×
56
            '@id' => '/api/books',
×
57
            '@type' => 'Collection',
×
58
            'totalItems' => 10,
×
59
        ]);
×
60
        $response->assertJsonCount(5, 'member');
×
61
    }
62

63
    public function testGetBook(): void
64
    {
65
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
66
        $book = Book::first();
×
67
        $response = $this->get($this->getIriFromResource($book), ['accept' => 'application/ld+json']);
×
68
        $response->assertStatus(200);
×
69
        $response->assertHeader('content-type', 'application/ld+json; charset=utf-8');
×
70
        $response->assertJsonFragment([
×
71
            '@context' => '/api/contexts/Book',
×
72
            '@id' => $this->getIriFromResource($book),
×
73
            '@type' => 'Book',
×
74
            'name' => $book->name, // @phpstan-ignore-line
×
75
        ]);
×
76
    }
77

78
    public function testCreateBook(): void
79
    {
80
        AuthorFactory::new()->create();
×
81
        $author = Author::find(1);
×
82
        $response = $this->postJson(
×
83
            '/api/books',
×
84
            [
×
85
                'name' => 'Don Quichotte',
×
86
                'author' => $this->getIriFromResource($author),
×
87
                'isbn' => fake()->isbn13(),
×
88
                'publicationDate' => fake()->optional()->date(),
×
89
            ],
×
90
            [
×
91
                'accept' => 'application/ld+json',
×
92
                'content_type' => 'application/ld+json',
×
93
            ]
×
94
        );
×
95

96
        $response->assertStatus(201);
×
97
        $response->assertHeader('content-type', 'application/ld+json; charset=utf-8');
×
98
        $response->assertJsonFragment([
×
99
            '@context' => '/api/contexts/Book',
×
100
            '@type' => 'Book',
×
101
            'name' => 'Don Quichotte',
×
102
        ]);
×
103
        $this->assertMatchesRegularExpression('~^/api/books/~', $response->json('@id'));
×
104
    }
105

106
    public function testUpdateBook(): void
107
    {
108
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
109
        $book = Book::first();
×
110
        $iri = $this->getIriFromResource($book);
×
111
        $response = $this->putJson(
×
112
            $iri,
×
113
            [
×
114
                'name' => 'updated title',
×
115
            ],
×
116
            [
×
117
                'accept' => 'application/ld+json',
×
118
                'content_type' => 'application/ld+json',
×
119
            ]
×
120
        );
×
121
        $response->assertStatus(200);
×
122
        $response->assertJsonFragment([
×
123
            'name' => 'updated title',
×
124
        ]);
×
125
    }
126

127
    public function testPatchBook(): void
128
    {
129
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
130
        $book = Book::first();
×
131
        $iri = $this->getIriFromResource($book);
×
132
        $response = $this->patchJson(
×
133
            $iri,
×
134
            [
×
135
                'name' => 'updated title',
×
136
            ],
×
137
            [
×
138
                'accept' => 'application/ld+json',
×
139
                'content_type' => 'application/merge-patch+json',
×
140
            ]
×
141
        );
×
142
        $response->assertStatus(200);
×
143
        $response->assertJsonFragment([
×
144
            '@id' => $iri,
×
145
            'name' => 'updated title',
×
146
        ]);
×
147
    }
148

149
    public function testDeleteBook(): void
150
    {
151
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
152
        $book = Book::first();
×
153
        $iri = $this->getIriFromResource($book);
×
154
        $response = $this->delete($iri, headers: ['accept' => 'application/ld+json']);
×
155
        $response->assertStatus(204);
×
156
        $this->assertNull(Book::find($book->id));
×
157
    }
158

159
    public function testPatchBookAuthor(): void
160
    {
161
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
162
        $book = Book::first();
×
163
        $iri = $this->getIriFromResource($book);
×
164
        $author = Author::find(2);
×
165
        $authorIri = $this->getIriFromResource($author);
×
166
        $response = $this->patchJson(
×
167
            $iri,
×
168
            [
×
169
                'author' => $authorIri,
×
170
            ],
×
171
            [
×
172
                'accept' => 'application/ld+json',
×
173
                'content_type' => 'application/merge-patch+json',
×
174
            ]
×
175
        );
×
176
        $response->assertStatus(200);
×
177
        $response->assertJsonFragment([
×
178
            '@id' => $iri,
×
179
            'author' => $authorIri,
×
180
        ]);
×
181
    }
182

183
    public function testSkolemIris(): void
184
    {
185
        $response = $this->get('/api/outputs', ['accept' => 'application/ld+json']);
×
186
        $response->assertStatus(200);
×
187
        $response->assertHeader('content-type', 'application/ld+json; charset=utf-8');
×
188
        $response->assertJsonFragment([
×
189
            '@type' => 'NotAResource',
×
190
            'name' => 'test',
×
191
        ]);
×
192

193
        $this->assertMatchesRegularExpression('~^/api/.well-known/genid/~', $response->json('@id'));
×
194
    }
195

196
    public function testSubresourceCollection(): void
197
    {
198
        PostFactory::new()->has(CommentFactory::new()->count(10))->count(10)->create();
×
199
        $response = $this->get('/api/posts', ['accept' => 'application/ld+json']);
×
200
        $response->assertStatus(200);
×
201
        $response->assertHeader('content-type', 'application/ld+json; charset=utf-8');
×
202

203
        $response->assertJsonFragment([
×
204
            '@context' => '/api/contexts/Post',
×
205
            '@id' => '/api/posts',
×
206
            '@type' => 'Collection',
×
207
            'totalItems' => 10,
×
208
        ]);
×
209
        $response->assertJsonCount(10, 'member');
×
210
        $postIri = $response->json('member.0.@id');
×
211
        $commentsIri = $response->json('member.0.comments');
×
212
        $this->assertMatchesRegularExpression('~^/api/posts/\d+/comments$~', $commentsIri);
×
213
        $response = $this->get($commentsIri, ['accept' => 'application/ld+json']);
×
214
        $response->assertJsonFragment([
×
215
            '@context' => '/api/contexts/Comment',
×
216
            '@id' => $commentsIri,
×
217
            '@type' => 'Collection',
×
218
            'totalItems' => 10,
×
219
        ]);
×
220

221
        $commentIri = $response->json('member.0.@id');
×
222
        $response = $this->get($commentIri, ['accept' => 'application/ld+json']);
×
223
        $response->assertJsonFragment([
×
224
            '@id' => $commentIri,
×
225
            'post' => $postIri,
×
226
        ]);
×
227
    }
228

229
    public function testCreateNotValid(): void
230
    {
231
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
232
        $author = Author::find(1);
×
233
        $response = $this->postJson(
×
234
            '/api/books',
×
235
            [
×
236
                'name' => 'Don Quichotte',
×
237
                'author' => $this->getIriFromResource($author),
×
238
                'isbn' => 'test@foo',
×
239
            ],
×
240
            [
×
241
                'accept' => 'application/ld+json',
×
242
                'content_type' => 'application/ld+json',
×
243
            ]
×
244
        );
×
245

246
        $response->assertStatus(422);
×
247
        $response->assertHeader('content-type', 'application/problem+json; charset=utf-8');
×
248
        $response->assertJsonFragment([
×
249
            '@context' => '/api/contexts/ValidationError',
×
250
            '@type' => 'ValidationError',
×
251
            'description' => 'The isbn field must only contain letters and numbers.',
×
252
        ]);
×
253

254
        $violations = $response->json('violations');
×
255
        $this->assertCount(1, $violations);
×
256
        $this->assertEquals($violations[0], ['propertyPath' => 'isbn', 'message' => 'The isbn field must only contain letters and numbers.']);
×
257
    }
258

259
    public function testCreateNotValidPost(): void
260
    {
261
        $response = $this->postJson(
×
262
            '/api/posts',
×
263
            [
×
264
            ],
×
265
            [
×
266
                'accept' => 'application/ld+json',
×
267
                'content_type' => 'application/ld+json',
×
268
            ]
×
269
        );
×
270

271
        $response->assertStatus(422);
×
272
        $response->assertHeader('content-type', 'application/problem+json; charset=utf-8');
×
273
        $response->assertJsonFragment([
×
274
            '@context' => '/api/contexts/ValidationError',
×
275
            '@type' => 'ValidationError',
×
276
            'description' => 'The title field is required.',
×
277
        ]);
×
278

279
        $violations = $response->json('violations');
×
280
        $this->assertCount(1, $violations);
×
281
        $this->assertEquals($violations[0], ['propertyPath' => 'title', 'message' => 'The title field is required.']);
×
282
    }
283

284
    public function testSluggable(): void
285
    {
286
        SluggableFactory::new()->count(10)->create();
×
287
        $response = $this->get('/api/sluggables', ['accept' => 'application/ld+json']);
×
288
        $response->assertStatus(200);
×
289
        $response->assertHeader('content-type', 'application/ld+json; charset=utf-8');
×
290
        $response->assertJsonFragment([
×
291
            '@context' => '/api/contexts/Sluggable',
×
292
            '@id' => '/api/sluggables',
×
293
            '@type' => 'Collection',
×
294
            'totalItems' => 10,
×
295
        ]);
×
296
        $iri = $response->json('member.0.@id');
×
297
        $response = $this->get($iri, ['accept' => 'application/ld+json']);
×
298
        $response->assertStatus(200);
×
299
    }
300

301
    public function testApiDocsRegex(): void
302
    {
303
        $response = $this->get('/api/notexists', ['accept' => 'application/ld+json']);
×
304
        $response->assertNotFound();
×
305
    }
306

307
    public function testHidden(): void
308
    {
309
        PostFactory::new()->has(CommentFactory::new()->count(10))->count(10)->create();
×
310
        $response = $this->get('/api/posts/1/comments/1', ['accept' => 'application/ld+json']);
×
311
        $response->assertStatus(200);
×
312
        $response->assertHeader('content-type', 'application/ld+json; charset=utf-8');
×
313
        $response->assertJsonMissingPath('internalNote');
×
314
    }
315

316
    public function testVisible(): void
317
    {
318
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
319
        $response = $this->get('/api/books', ['accept' => 'application/ld+json']);
×
320
        $response->assertStatus(200);
×
321
        $response->assertHeader('content-type', 'application/ld+json; charset=utf-8');
×
322
        $this->assertStringNotContainsString('internalNote', (string) $response->getContent());
×
323
    }
324

325
    public function testError(): void
326
    {
327
        $response = $this->post('/api/books', ['content-type' => 'application/vnd.api+json']);
×
328
        $response->assertStatus(415);
×
329
        $content = $response->json();
×
330
        $this->assertArrayHasKey('trace', $content);
×
331
    }
332

333
    public function testErrorNotFound(): void
334
    {
335
        $response = $this->get('/api/books/asd', ['accept' => 'application/ld+json']);
×
336
        $response->assertStatus(404);
×
337
        $content = $response->json();
×
338
        $this->assertArrayHasKey('status', $content);
×
339
        $this->assertEquals(404, $content['status']);
×
340
    }
341

342
    public function testRelationWithGroups(): void
343
    {
344
        WithAccessorFactory::new()->create();
×
345
        $response = $this->get('/api/with_accessors/1', ['accept' => 'application/ld+json']);
×
346
        $content = $response->json();
×
347
        $this->assertArrayHasKey('relation', $content);
×
348
        $this->assertArrayHasKey('name', $content['relation']);
×
349
    }
350

351
    /**
352
     * @see https://github.com/api-platform/core/issues/6779
353
     */
354
    public function testSimilarRoutesWithFormat(): void
355
    {
356
        $response = $this->get('/api/staff_position_histories?page=1', ['accept' => 'application/ld+json']);
×
357
        $response->assertStatus(200);
×
358
        $this->assertSame('/api/staff_position_histories', $response->json()['@id']);
×
359
    }
360

361
    public function testResourceWithOptionModel(): void
362
    {
363
        $response = $this->get('/api/resource_with_models?page=1', ['accept' => 'application/ld+json']);
×
364
        $response->assertStatus(200);
×
365
        $response->assertHeader('content-type', 'application/ld+json; charset=utf-8');
×
366
        $response->assertJsonFragment([
×
367
            '@context' => '/api/contexts/ResourceWithModel',
×
368
            '@id' => '/api/resource_with_models',
×
369
            '@type' => 'Collection',
×
370
        ]);
×
371
    }
372

373
    public function testCustomRelation(): void
374
    {
375
        $response = $this->get('/api/home', headers: ['accept' => ['application/ld+json']]);
×
376
        $home = $response->json();
×
377
        $this->assertArrayHasKey('order', $home);
×
378
        $this->assertArrayHasKey('id', $home['order']);
×
379
        $this->assertArrayHasKey('number', $home['order']);
×
380
    }
381
}
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