]> BookStack Code Mirror - bookstack/blob - tests/Exports/ZipImportTest.php
ZIP Imports: Fleshed out continue page, Added testing
[bookstack] / tests / Exports / ZipImportTest.php
1 <?php
2
3 namespace Tests\Exports;
4
5 use BookStack\Activity\ActivityType;
6 use BookStack\Exports\Import;
7 use Illuminate\Http\UploadedFile;
8 use Illuminate\Testing\TestResponse;
9 use Tests\TestCase;
10 use ZipArchive;
11
12 class ZipImportTest extends TestCase
13 {
14     public function test_import_page_view()
15     {
16         $resp = $this->asAdmin()->get('/import');
17         $resp->assertSee('Import');
18         $this->withHtml($resp)->assertElementExists('form input[type="file"][name="file"]');
19     }
20
21     public function test_permissions_needed_for_import_page()
22     {
23         $user = $this->users->viewer();
24         $this->actingAs($user);
25
26         $resp = $this->get('/books');
27         $this->withHtml($resp)->assertLinkNotExists(url('/import'));
28         $resp = $this->get('/import');
29         $resp->assertRedirect('/');
30
31         $this->permissions->grantUserRolePermissions($user, ['content-import']);
32
33         $resp = $this->get('/books');
34         $this->withHtml($resp)->assertLinkExists(url('/import'));
35         $resp = $this->get('/import');
36         $resp->assertOk();
37         $resp->assertSeeText('Select ZIP file to upload');
38     }
39
40     public function test_import_page_pending_import_visibility_limited()
41     {
42         $user = $this->users->viewer();
43         $admin = $this->users->admin();
44         $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
45         $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
46         $this->permissions->grantUserRolePermissions($user, ['content-import']);
47
48         $resp = $this->actingAs($user)->get('/import');
49         $resp->assertSeeText('MySuperUserImport');
50         $resp->assertDontSeeText('MySuperAdminImport');
51
52         $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
53
54         $resp = $this->actingAs($user)->get('/import');
55         $resp->assertSeeText('MySuperUserImport');
56         $resp->assertSeeText('MySuperAdminImport');
57     }
58
59     public function test_zip_read_errors_are_shown_on_validation()
60     {
61         $invalidUpload = $this->files->uploadedImage('image.zip');
62
63         $this->asAdmin();
64         $resp = $this->runImportFromFile($invalidUpload);
65         $resp->assertRedirect('/import');
66
67         $resp = $this->followRedirects($resp);
68         $resp->assertSeeText('Could not read ZIP file');
69     }
70
71     public function test_error_shown_if_missing_data()
72     {
73         $zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
74         $zip = new ZipArchive();
75         $zip->open($zipFile, ZipArchive::CREATE);
76         $zip->addFromString('beans', 'cat');
77         $zip->close();
78
79         $this->asAdmin();
80         $upload = new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);
81         $resp = $this->runImportFromFile($upload);
82         $resp->assertRedirect('/import');
83
84         $resp = $this->followRedirects($resp);
85         $resp->assertSeeText('Could not find and decode ZIP data.json content.');
86     }
87
88     public function test_error_shown_if_no_importable_key()
89     {
90         $this->asAdmin();
91         $resp = $this->runImportFromFile($this->zipUploadFromData([
92             'instance' => []
93         ]));
94
95         $resp->assertRedirect('/import');
96         $resp = $this->followRedirects($resp);
97         $resp->assertSeeText('ZIP file data has no expected book, chapter or page content.');
98     }
99
100     public function test_zip_data_validation_messages_shown()
101     {
102         $this->asAdmin();
103         $resp = $this->runImportFromFile($this->zipUploadFromData([
104             'book' => [
105                 'id' => 4,
106                 'pages' => [
107                     'cat',
108                     [
109                         'name' => 'My inner page',
110                         'tags' => [
111                             [
112                                 'value' => 5
113                             ]
114                         ],
115                     ]
116                 ],
117             ]
118         ]));
119
120         $resp->assertRedirect('/import');
121         $resp = $this->followRedirects($resp);
122
123         $resp->assertSeeText('[book.name]: The name field is required.');
124         $resp->assertSeeText('[book.pages.0.0]: Data object expected but "string" found.');
125         $resp->assertSeeText('[book.pages.1.tags.0.name]: The name field is required.');
126         $resp->assertSeeText('[book.pages.1.tags.0.value]: The value must be a string.');
127     }
128
129     public function test_import_upload_success()
130     {
131         $admin = $this->users->admin();
132         $this->actingAs($admin);
133         $resp = $this->runImportFromFile($this->zipUploadFromData([
134             'book' => [
135                 'name' => 'My great book name',
136                 'chapters' => [
137                     [
138                         'name' => 'my chapter',
139                         'pages' => [
140                             [
141                                 'name' => 'my chapter page',
142                             ]
143                         ]
144                     ]
145                 ],
146                 'pages' => [
147                     [
148                         'name' => 'My page',
149                     ]
150                 ],
151             ],
152         ]));
153
154         $this->assertDatabaseHas('imports', [
155             'name' => 'My great book name',
156             'book_count' => 1,
157             'chapter_count' => 1,
158             'page_count' => 2,
159             'created_by' => $admin->id,
160         ]);
161
162         /** @var Import $import */
163         $import = Import::query()->latest()->first();
164         $resp->assertRedirect("/import/{$import->id}");
165         $this->assertFileExists(storage_path($import->path));
166         $this->assertActivityExists(ActivityType::IMPORT_CREATE);
167     }
168
169     public function test_import_show_page()
170     {
171         $import = Import::factory()->create(['name' => 'MySuperAdminImport']);
172
173         $resp = $this->asAdmin()->get("/import/{$import->id}");
174         $resp->assertOk();
175         $resp->assertSee('MySuperAdminImport');
176     }
177
178     public function test_import_show_page_access_limited()
179     {
180         $user = $this->users->viewer();
181         $admin = $this->users->admin();
182         $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
183         $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
184         $this->actingAs($user);
185
186         $this->get("/import/{$userImport->id}")->assertRedirect('/');
187         $this->get("/import/{$adminImport->id}")->assertRedirect('/');
188
189         $this->permissions->grantUserRolePermissions($user, ['content-import']);
190
191         $this->get("/import/{$userImport->id}")->assertOk();
192         $this->get("/import/{$adminImport->id}")->assertStatus(404);
193
194         $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
195
196         $this->get("/import/{$userImport->id}")->assertOk();
197         $this->get("/import/{$adminImport->id}")->assertOk();
198     }
199
200     public function test_import_delete()
201     {
202         $this->asAdmin();
203         $this->runImportFromFile($this->zipUploadFromData([
204             'book' => [
205                 'name' => 'My great book name'
206             ],
207         ]));
208
209         /** @var Import $import */
210         $import = Import::query()->latest()->first();
211         $this->assertDatabaseHas('imports', [
212             'id' => $import->id,
213             'name' => 'My great book name'
214         ]);
215         $this->assertFileExists(storage_path($import->path));
216
217         $resp = $this->delete("/import/{$import->id}");
218
219         $resp->assertRedirect('/import');
220         $this->assertActivityExists(ActivityType::IMPORT_DELETE);
221         $this->assertDatabaseMissing('imports', [
222             'id' => $import->id,
223         ]);
224         $this->assertFileDoesNotExist(storage_path($import->path));
225     }
226
227     public function test_import_delete_access_limited()
228     {
229         $user = $this->users->viewer();
230         $admin = $this->users->admin();
231         $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
232         $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
233         $this->actingAs($user);
234
235         $this->delete("/import/{$userImport->id}")->assertRedirect('/');
236         $this->delete("/import/{$adminImport->id}")->assertRedirect('/');
237
238         $this->permissions->grantUserRolePermissions($user, ['content-import']);
239
240         $this->delete("/import/{$userImport->id}")->assertRedirect('/import');
241         $this->delete("/import/{$adminImport->id}")->assertStatus(404);
242
243         $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
244
245         $this->delete("/import/{$adminImport->id}")->assertRedirect('/import');
246     }
247
248     protected function runImportFromFile(UploadedFile $file): TestResponse
249     {
250         return $this->call('POST', '/import', [], [], ['file' => $file]);
251     }
252
253     protected function zipUploadFromData(array $data): UploadedFile
254     {
255         $zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
256
257         $zip = new ZipArchive();
258         $zip->open($zipFile, ZipArchive::CREATE);
259         $zip->addFromString('data.json', json_encode($data));
260         $zip->close();
261
262         return new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);
263     }
264 }