]> BookStack Code Mirror - bookstack/blob - tests/Api/ImageGalleryApiTest.php
Users API: Fixed sending invite when using form requests
[bookstack] / tests / Api / ImageGalleryApiTest.php
1 <?php
2
3 namespace Tests\Api;
4
5 use BookStack\Entities\Models\Page;
6 use BookStack\Uploads\Image;
7 use Tests\TestCase;
8
9 class ImageGalleryApiTest extends TestCase
10 {
11     use TestsApi;
12
13     protected string $baseEndpoint = '/api/image-gallery';
14
15     public function test_index_endpoint_returns_expected_image_and_count()
16     {
17         $this->actingAsApiAdmin();
18         $imagePage = $this->entities->page();
19         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
20         $image = Image::findOrFail($data['response']->id);
21
22         $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
23         $resp->assertJson(['data' => [
24             [
25                 'id' => $image->id,
26                 'name' => $image->name,
27                 'url' => $image->url,
28                 'path' => $image->path,
29                 'type' => 'gallery',
30                 'uploaded_to' => $imagePage->id,
31                 'created_by' => $this->users->admin()->id,
32                 'updated_by' => $this->users->admin()->id,
33             ],
34         ]]);
35
36         $resp->assertJson(['total' => Image::query()->count()]);
37     }
38
39     public function test_index_endpoint_doesnt_show_images_for_those_uploaded_to_non_visible_pages()
40     {
41         $this->actingAsApiEditor();
42         $imagePage = $this->entities->page();
43         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
44         $image = Image::findOrFail($data['response']->id);
45
46         $resp = $this->getJson($this->baseEndpoint . '?filter[id]=' . $image->id);
47         $resp->assertJsonCount(1, 'data');
48         $resp->assertJson(['total' => 1]);
49
50         $this->permissions->disableEntityInheritedPermissions($imagePage);
51
52         $resp = $this->getJson($this->baseEndpoint . '?filter[id]=' . $image->id);
53         $resp->assertJsonCount(0, 'data');
54         $resp->assertJson(['total' => 0]);
55     }
56
57     public function test_index_endpoint_doesnt_show_other_image_types()
58     {
59         $this->actingAsApiEditor();
60         $imagePage = $this->entities->page();
61         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
62         $image = Image::findOrFail($data['response']->id);
63
64         $typesByCountExpectation = [
65             'cover_book' => 0,
66             'drawio' => 1,
67             'gallery' => 1,
68             'user' => 0,
69             'system' => 0,
70         ];
71
72         foreach ($typesByCountExpectation as $type => $count) {
73             $image->type = $type;
74             $image->save();
75
76             $resp = $this->getJson($this->baseEndpoint . '?filter[id]=' . $image->id);
77             $resp->assertJsonCount($count, 'data');
78             $resp->assertJson(['total' => $count]);
79         }
80     }
81
82     public function test_create_endpoint()
83     {
84         $this->actingAsApiAdmin();
85
86         $imagePage = $this->entities->page();
87         $resp = $this->call('POST', $this->baseEndpoint, [
88             'type' => 'gallery',
89             'uploaded_to' => $imagePage->id,
90             'name' => 'My awesome image!',
91         ], [], [
92             'image' => $this->files->uploadedImage('my-cool-image.png'),
93         ]);
94
95         $resp->assertStatus(200);
96
97         $image = Image::query()->where('uploaded_to', '=', $imagePage->id)->first();
98         $expectedUser = [
99             'id' => $this->users->admin()->id,
100             'name' => $this->users->admin()->name,
101             'slug' => $this->users->admin()->slug,
102         ];
103         $resp->assertJson([
104             'id' => $image->id,
105             'name' => 'My awesome image!',
106             'url' => $image->url,
107             'path' => $image->path,
108             'type' => 'gallery',
109             'uploaded_to' => $imagePage->id,
110             'created_by' => $expectedUser,
111             'updated_by' => $expectedUser,
112         ]);
113     }
114
115     public function test_create_endpoint_requires_image_create_permissions()
116     {
117         $user = $this->users->editor();
118         $this->actingAsForApi($user);
119         $this->permissions->removeUserRolePermissions($user, ['image-create-all']);
120
121         $makeRequest = function () {
122             return $this->call('POST', $this->baseEndpoint, []);
123         };
124
125         $resp = $makeRequest();
126         $resp->assertStatus(403);
127
128         $this->permissions->grantUserRolePermissions($user, ['image-create-all']);
129
130         $resp = $makeRequest();
131         $resp->assertStatus(422);
132     }
133
134     public function test_create_fails_if_uploaded_to_not_visible_or_not_exists()
135     {
136         $this->actingAsApiEditor();
137
138         $makeRequest = function (int $uploadedTo) {
139             return $this->call('POST', $this->baseEndpoint, [
140                 'type' => 'gallery',
141                 'uploaded_to' => $uploadedTo,
142                 'name' => 'My awesome image!',
143             ], [], [
144                 'image' => $this->files->uploadedImage('my-cool-image.png'),
145             ]);
146         };
147
148         $page = $this->entities->page();
149         $this->permissions->disableEntityInheritedPermissions($page);
150         $resp = $makeRequest($page->id);
151         $resp->assertStatus(404);
152
153         $resp = $makeRequest(Page::query()->max('id') + 55);
154         $resp->assertStatus(404);
155     }
156
157     public function test_create_has_restricted_types()
158     {
159         $this->actingAsApiEditor();
160
161         $typesByStatusExpectation = [
162             'cover_book' => 422,
163             'drawio' => 200,
164             'gallery' => 200,
165             'user' => 422,
166             'system' => 422,
167         ];
168
169         $makeRequest = function (string $type) {
170             return $this->call('POST', $this->baseEndpoint, [
171                 'type' => $type,
172                 'uploaded_to' => $this->entities->page()->id,
173                 'name' => 'My awesome image!',
174             ], [], [
175                 'image' => $this->files->uploadedImage('my-cool-image.png'),
176             ]);
177         };
178
179         foreach ($typesByStatusExpectation as $type => $status) {
180             $resp = $makeRequest($type);
181             $resp->assertStatus($status);
182         }
183     }
184
185     public function test_create_will_use_file_name_if_no_name_provided_in_request()
186     {
187         $this->actingAsApiEditor();
188
189         $imagePage = $this->entities->page();
190         $resp = $this->call('POST', $this->baseEndpoint, [
191             'type' => 'gallery',
192             'uploaded_to' => $imagePage->id,
193         ], [], [
194             'image' => $this->files->uploadedImage('my-cool-image.png'),
195         ]);
196         $resp->assertStatus(200);
197
198         $this->assertDatabaseHas('images', [
199             'type' => 'gallery',
200             'uploaded_to' => $imagePage->id,
201             'name' => 'my-cool-image.png',
202         ]);
203     }
204
205     public function test_read_endpoint()
206     {
207         $this->actingAsApiAdmin();
208         $imagePage = $this->entities->page();
209         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
210         $image = Image::findOrFail($data['response']->id);
211
212         $resp = $this->getJson($this->baseEndpoint . "/{$image->id}");
213         $resp->assertStatus(200);
214
215         $expectedUser = [
216             'id' => $this->users->admin()->id,
217             'name' => $this->users->admin()->name,
218             'slug' => $this->users->admin()->slug,
219         ];
220
221         $displayUrl = $image->getThumb(1680, null, true);
222         $resp->assertJson([
223             'id' => $image->id,
224             'name' => $image->name,
225             'url' => $image->url,
226             'path' => $image->path,
227             'type' => 'gallery',
228             'uploaded_to' => $imagePage->id,
229             'created_by' => $expectedUser,
230             'updated_by' => $expectedUser,
231             'content' => [
232                 'html' => "<a href=\"{$image->url}\" target=\"_blank\"><img src=\"{$displayUrl}\" alt=\"{$image->name}\"></a>",
233                 'markdown' => "![{$image->name}]({$displayUrl})",
234             ],
235             'created_at' => $image->created_at->toISOString(),
236             'updated_at' => $image->updated_at->toISOString(),
237         ]);
238         $this->assertStringStartsWith('http://', $resp->json('thumbs.gallery'));
239         $this->assertStringStartsWith('http://', $resp->json('thumbs.display'));
240     }
241
242     public function test_read_endpoint_provides_different_content_for_drawings()
243     {
244         $this->actingAsApiAdmin();
245         $imagePage = $this->entities->page();
246         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
247         $image = Image::findOrFail($data['response']->id);
248
249         $image->type = 'drawio';
250         $image->save();
251
252         $resp = $this->getJson($this->baseEndpoint . "/{$image->id}");
253         $resp->assertStatus(200);
254
255         $drawing = "<div drawio-diagram=\"{$image->id}\"><img src=\"{$image->url}\"></div>";
256         $resp->assertJson([
257             'id' => $image->id,
258             'content' => [
259                 'html' => $drawing,
260                 'markdown' => $drawing,
261             ],
262         ]);
263     }
264
265     public function test_read_endpoint_does_not_show_if_no_permissions_for_related_page()
266     {
267         $this->actingAsApiEditor();
268         $imagePage = $this->entities->page();
269         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
270         $image = Image::findOrFail($data['response']->id);
271
272         $this->permissions->disableEntityInheritedPermissions($imagePage);
273
274         $resp = $this->getJson($this->baseEndpoint . "/{$image->id}");
275         $resp->assertStatus(404);
276     }
277
278     public function test_update_endpoint()
279     {
280         $this->actingAsApiAdmin();
281         $imagePage = $this->entities->page();
282         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
283         $image = Image::findOrFail($data['response']->id);
284
285         $resp = $this->putJson($this->baseEndpoint . "/{$image->id}", [
286             'name' => 'My updated image name!',
287         ]);
288
289         $resp->assertStatus(200);
290         $resp->assertJson([
291             'id' => $image->id,
292             'name' => 'My updated image name!',
293         ]);
294         $this->assertDatabaseHas('images', [
295             'id' => $image->id,
296             'name' => 'My updated image name!',
297         ]);
298     }
299
300     public function test_update_existing_image_file()
301     {
302         $this->actingAsApiAdmin();
303         $imagePage = $this->entities->page();
304         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
305         $image = Image::findOrFail($data['response']->id);
306
307         $this->assertFileEquals($this->files->testFilePath('test-image.png'), public_path($data['path']));
308
309         $resp = $this->call('PUT', $this->baseEndpoint . "/{$image->id}", [], [], [
310             'image' => $this->files->uploadedImage('my-cool-image.png', 'compressed.png'),
311         ]);
312
313         $resp->assertStatus(200);
314         $this->assertFileEquals($this->files->testFilePath('compressed.png'), public_path($data['path']));
315     }
316
317     public function test_update_endpoint_requires_image_update_permission()
318     {
319         $user = $this->users->editor();
320         $this->actingAsForApi($user);
321         $imagePage = $this->entities->page();
322         $this->permissions->removeUserRolePermissions($user, ['image-update-all', 'image-update-own']);
323         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
324         $image = Image::findOrFail($data['response']->id);
325
326         $resp = $this->putJson($this->baseEndpoint . "/{$image->id}", ['name' => 'My new name']);
327         $resp->assertStatus(403);
328         $resp->assertJson($this->permissionErrorResponse());
329
330         $this->permissions->grantUserRolePermissions($user, ['image-update-all']);
331         $resp = $this->putJson($this->baseEndpoint . "/{$image->id}", ['name' => 'My new name']);
332         $resp->assertStatus(200);
333     }
334
335     public function test_delete_endpoint()
336     {
337         $this->actingAsApiAdmin();
338         $imagePage = $this->entities->page();
339         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
340         $image = Image::findOrFail($data['response']->id);
341         $this->assertDatabaseHas('images', ['id' => $image->id]);
342
343         $resp = $this->deleteJson($this->baseEndpoint . "/{$image->id}");
344
345         $resp->assertStatus(204);
346         $this->assertDatabaseMissing('images', ['id' => $image->id]);
347     }
348
349     public function test_delete_endpoint_requires_image_delete_permission()
350     {
351         $user = $this->users->editor();
352         $this->actingAsForApi($user);
353         $imagePage = $this->entities->page();
354         $this->permissions->removeUserRolePermissions($user, ['image-delete-all', 'image-delete-own']);
355         $data = $this->files->uploadGalleryImageToPage($this, $imagePage);
356         $image = Image::findOrFail($data['response']->id);
357
358         $resp = $this->deleteJson($this->baseEndpoint . "/{$image->id}");
359         $resp->assertStatus(403);
360         $resp->assertJson($this->permissionErrorResponse());
361
362         $this->permissions->grantUserRolePermissions($user, ['image-delete-all']);
363         $resp = $this->deleteJson($this->baseEndpoint . "/{$image->id}");
364         $resp->assertStatus(204);
365     }
366 }