]> BookStack Code Mirror - bookstack/blob - tests/Api/AttachmentsApiTest.php
Merge branch 'master' of https://github.com/theodor-franke/BookStack into theodor...
[bookstack] / tests / Api / AttachmentsApiTest.php
1 <?php
2
3 namespace Tests\Api;
4
5 use BookStack\Entities\Models\Page;
6 use BookStack\Uploads\Attachment;
7 use Illuminate\Http\UploadedFile;
8 use Tests\TestCase;
9
10 class AttachmentsApiTest extends TestCase
11 {
12     use TestsApi;
13
14     protected $baseEndpoint = '/api/attachments';
15
16     public function test_index_endpoint_returns_expected_book()
17     {
18         $this->actingAsApiEditor();
19         $page = Page::query()->first();
20         $attachment = $this->createAttachmentForPage($page, [
21             'name'     => 'My test attachment',
22             'external' => true,
23         ]);
24
25         $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
26         $resp->assertJson(['data' => [
27             [
28                 'id'          => $attachment->id,
29                 'name'        => 'My test attachment',
30                 'uploaded_to' => $page->id,
31                 'external'    => true,
32             ],
33         ]]);
34     }
35
36     public function test_attachments_listing_based_upon_page_visibility()
37     {
38         $this->actingAsApiEditor();
39         /** @var Page $page */
40         $page = Page::query()->first();
41         $attachment = $this->createAttachmentForPage($page, [
42             'name'     => 'My test attachment',
43             'external' => true,
44         ]);
45
46         $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
47         $resp->assertJson(['data' => [
48             [
49                 'id' => $attachment->id,
50             ],
51         ]]);
52
53         $page->restricted = true;
54         $page->save();
55         $this->regenEntityPermissions($page);
56
57         $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
58         $resp->assertJsonMissing(['data' => [
59             [
60                 'id' => $attachment->id,
61             ],
62         ]]);
63     }
64
65     public function test_create_endpoint_for_link_attachment()
66     {
67         $this->actingAsApiAdmin();
68         /** @var Page $page */
69         $page = Page::query()->first();
70
71         $details = [
72             'name'        => 'My attachment',
73             'uploaded_to' => $page->id,
74             'link'        => 'https://cats.example.com',
75         ];
76
77         $resp = $this->postJson($this->baseEndpoint, $details);
78         $resp->assertStatus(200);
79         /** @var Attachment $newItem */
80         $newItem = Attachment::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
81         $resp->assertJson(['id' => $newItem->id, 'external' => true, 'name' => $details['name'], 'uploaded_to' => $page->id]);
82     }
83
84     public function test_create_endpoint_for_upload_attachment()
85     {
86         $this->actingAsApiAdmin();
87         /** @var Page $page */
88         $page = Page::query()->first();
89         $file = $this->getTestFile('textfile.txt');
90
91         $details = [
92             'name'        => 'My attachment',
93             'uploaded_to' => $page->id,
94         ];
95
96         $resp = $this->call('POST', $this->baseEndpoint, $details, [], ['file' => $file]);
97         $resp->assertStatus(200);
98         /** @var Attachment $newItem */
99         $newItem = Attachment::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
100         $resp->assertJson(['id' => $newItem->id, 'external' => false, 'extension' => 'txt', 'name' => $details['name'], 'uploaded_to' => $page->id]);
101         $this->assertTrue(file_exists(storage_path($newItem->path)));
102         unlink(storage_path($newItem->path));
103     }
104
105     public function test_name_needed_to_create()
106     {
107         $this->actingAsApiAdmin();
108         /** @var Page $page */
109         $page = Page::query()->first();
110
111         $details = [
112             'uploaded_to' => $page->id,
113             'link'        => 'https://example.com',
114         ];
115
116         $resp = $this->postJson($this->baseEndpoint, $details);
117         $resp->assertStatus(422);
118         $resp->assertJson([
119             'error' => [
120                 'message'    => 'The given data was invalid.',
121                 'validation' => [
122                     'name' => ['The name field is required.'],
123                 ],
124                 'code' => 422,
125             ],
126         ]);
127     }
128
129     public function test_link_or_file_needed_to_create()
130     {
131         $this->actingAsApiAdmin();
132         /** @var Page $page */
133         $page = Page::query()->first();
134
135         $details = [
136             'name'        => 'my attachment',
137             'uploaded_to' => $page->id,
138         ];
139
140         $resp = $this->postJson($this->baseEndpoint, $details);
141         $resp->assertStatus(422);
142         $resp->assertJson([
143             'error' => [
144                 'message'    => 'The given data was invalid.',
145                 'validation' => [
146                     'file' => ['The file field is required when link is not present.'],
147                     'link' => ['The link field is required when file is not present.'],
148                 ],
149                 'code' => 422,
150             ],
151         ]);
152     }
153
154     public function test_read_endpoint_for_link_attachment()
155     {
156         $this->actingAsApiAdmin();
157         /** @var Page $page */
158         $page = Page::query()->first();
159
160         $attachment = $this->createAttachmentForPage($page, [
161             'name'  => 'my attachment',
162             'path'  => 'https://example.com',
163             'order' => 1,
164         ]);
165
166         $resp = $this->getJson("{$this->baseEndpoint}/{$attachment->id}");
167
168         $resp->assertStatus(200);
169         $resp->assertJson([
170             'id'          => $attachment->id,
171             'content'     => 'https://example.com',
172             'external'    => true,
173             'uploaded_to' => $page->id,
174             'order'       => 1,
175             'created_by'  => [
176                 'name' => $attachment->createdBy->name,
177             ],
178             'updated_by' => [
179                 'name' => $attachment->createdBy->name,
180             ],
181             'links' => [
182                 'html'     => "<a target=\"_blank\" href=\"http://localhost/attachments/{$attachment->id}\">my attachment</a>",
183                 'markdown' => "[my attachment](http://localhost/attachments/{$attachment->id})",
184             ],
185         ]);
186     }
187
188     public function test_read_endpoint_for_file_attachment()
189     {
190         $this->actingAsApiAdmin();
191         /** @var Page $page */
192         $page = Page::query()->first();
193         $file = $this->getTestFile('textfile.txt');
194
195         $details = [
196             'name'        => 'My file attachment',
197             'uploaded_to' => $page->id,
198         ];
199         $this->call('POST', $this->baseEndpoint, $details, [], ['file' => $file]);
200         /** @var Attachment $attachment */
201         $attachment = Attachment::query()->orderByDesc('id')->where('name', '=', $details['name'])->firstOrFail();
202
203         $resp = $this->getJson("{$this->baseEndpoint}/{$attachment->id}");
204
205         $resp->assertStatus(200);
206         $resp->assertJson([
207             'id'          => $attachment->id,
208             'content'     => base64_encode(file_get_contents(storage_path($attachment->path))),
209             'external'    => false,
210             'uploaded_to' => $page->id,
211             'order'       => 1,
212             'created_by'  => [
213                 'name' => $attachment->createdBy->name,
214             ],
215             'updated_by' => [
216                 'name' => $attachment->updatedBy->name,
217             ],
218             'links' => [
219                 'html'     => "<a target=\"_blank\" href=\"http://localhost/attachments/{$attachment->id}\">My file attachment</a>",
220                 'markdown' => "[My file attachment](http://localhost/attachments/{$attachment->id})",
221             ],
222         ]);
223
224         unlink(storage_path($attachment->path));
225     }
226
227     public function test_update_endpoint()
228     {
229         $this->actingAsApiAdmin();
230         /** @var Page $page */
231         $page = Page::query()->first();
232         $attachment = $this->createAttachmentForPage($page);
233
234         $details = [
235             'name' => 'My updated API attachment',
236         ];
237
238         $resp = $this->putJson("{$this->baseEndpoint}/{$attachment->id}", $details);
239         $attachment->refresh();
240
241         $resp->assertStatus(200);
242         $resp->assertJson(['id' => $attachment->id, 'name' => 'My updated API attachment']);
243     }
244
245     public function test_update_link_attachment_to_file()
246     {
247         $this->actingAsApiAdmin();
248         /** @var Page $page */
249         $page = Page::query()->first();
250         $attachment = $this->createAttachmentForPage($page);
251         $file = $this->getTestFile('textfile.txt');
252
253         $resp = $this->call('PUT', "{$this->baseEndpoint}/{$attachment->id}", ['name' => 'My updated file'], [], ['file' => $file]);
254         $resp->assertStatus(200);
255
256         $attachment->refresh();
257         $this->assertFalse($attachment->external);
258         $this->assertEquals('txt', $attachment->extension);
259         $this->assertStringStartsWith('uploads/files/', $attachment->path);
260         $this->assertFileExists(storage_path($attachment->path));
261
262         unlink(storage_path($attachment->path));
263     }
264
265     public function test_update_file_attachment_to_link()
266     {
267         $this->actingAsApiAdmin();
268         /** @var Page $page */
269         $page = Page::query()->first();
270         $file = $this->getTestFile('textfile.txt');
271         $this->call('POST', $this->baseEndpoint, ['name' => 'My file attachment', 'uploaded_to' => $page->id], [], ['file' => $file]);
272         /** @var Attachment $attachment */
273         $attachment = Attachment::query()->where('name', '=', 'My file attachment')->firstOrFail();
274
275         $filePath = storage_path($attachment->path);
276         $this->assertFileExists($filePath);
277
278         $details = [
279             'name' => 'My updated API attachment',
280             'link' => 'https://cats.example.com',
281         ];
282
283         $resp = $this->putJson("{$this->baseEndpoint}/{$attachment->id}", $details);
284         $resp->assertStatus(200);
285         $attachment->refresh();
286
287         $this->assertFileDoesNotExist($filePath);
288         $this->assertTrue($attachment->external);
289         $this->assertEquals('https://cats.example.com', $attachment->path);
290         $this->assertEquals('', $attachment->extension);
291     }
292
293     public function test_delete_endpoint()
294     {
295         $this->actingAsApiAdmin();
296         /** @var Page $page */
297         $page = Page::query()->first();
298         $attachment = $this->createAttachmentForPage($page);
299
300         $resp = $this->deleteJson("{$this->baseEndpoint}/{$attachment->id}");
301
302         $resp->assertStatus(204);
303         $this->assertDatabaseMissing('attachments', ['id' => $attachment->id]);
304     }
305
306     protected function createAttachmentForPage(Page $page, $attributes = []): Attachment
307     {
308         $admin = $this->getAdmin();
309         /** @var Attachment $attachment */
310         $attachment = $page->attachments()->forceCreate(array_merge([
311             'uploaded_to' => $page->id,
312             'name'        => 'test attachment',
313             'external'    => true,
314             'order'       => 1,
315             'created_by'  => $admin->id,
316             'updated_by'  => $admin->id,
317             'path'        => 'https://attachment.example.com',
318         ], $attributes));
319
320         return $attachment;
321     }
322
323     /**
324      * Get a test file that can be uploaded.
325      */
326     protected function getTestFile(string $fileName): UploadedFile
327     {
328         return new UploadedFile(base_path('tests/test-data/test-file.txt'), $fileName, 'text/plain', 55, null, true);
329     }
330 }