Skip to content

Commit 7681e32

Browse files
committed
ZIP Imports: Added high level import run tests
1 parent b7476a9 commit 7681e32

File tree

5 files changed

+192
-15
lines changed

5 files changed

+192
-15
lines changed

app/Exports/Controllers/ImportController.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ public function show(int $id)
7070
]);
7171
}
7272

73+
/**
74+
* Run the import process against an uploaded import ZIP.
75+
*/
7376
public function run(int $id, Request $request)
7477
{
75-
// TODO - Test access/visibility
7678
$import = $this->imports->findVisible($id);
7779
$parent = null;
7880

database/factories/Exports/ImportFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class ImportFactory extends Factory
2121
public function definition(): array
2222
{
2323
return [
24-
'path' => 'uploads/imports/' . Str::random(10) . '.zip',
24+
'path' => 'uploads/files/imports/' . Str::random(10) . '.zip',
2525
'name' => $this->faker->words(3, true),
2626
'type' => 'book',
2727
'metadata' => '{"name": "My book"}',

tests/Exports/ZipImportRunnerTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Tests\Exports;
4+
5+
use BookStack\Exports\ZipExports\ZipImportRunner;
6+
use Tests\TestCase;
7+
8+
class ZipImportRunnerTest extends TestCase
9+
{
10+
protected ZipImportRunner $runner;
11+
12+
protected function setUp(): void
13+
{
14+
parent::setUp();
15+
$this->runner = app()->make(ZipImportRunner::class);
16+
}
17+
18+
// TODO - Test full book import
19+
// TODO - Test full chapter import
20+
// TODO - Test full page import
21+
}

tests/Exports/ZipImportTest.php

Lines changed: 120 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Tests\Exports;
44

55
use BookStack\Activity\ActivityType;
6+
use BookStack\Entities\Models\Book;
67
use BookStack\Exports\Import;
78
use BookStack\Exports\ZipExports\Models\ZipExportBook;
89
use BookStack\Exports\ZipExports\Models\ZipExportChapter;
@@ -91,7 +92,7 @@ public function test_error_shown_if_missing_data()
9192
public function test_error_shown_if_no_importable_key()
9293
{
9394
$this->asAdmin();
94-
$resp = $this->runImportFromFile($this->zipUploadFromData([
95+
$resp = $this->runImportFromFile(ZipTestHelper::zipUploadFromData([
9596
'instance' => []
9697
]));
9798

@@ -103,7 +104,7 @@ public function test_error_shown_if_no_importable_key()
103104
public function test_zip_data_validation_messages_shown()
104105
{
105106
$this->asAdmin();
106-
$resp = $this->runImportFromFile($this->zipUploadFromData([
107+
$resp = $this->runImportFromFile(ZipTestHelper::zipUploadFromData([
107108
'book' => [
108109
'id' => 4,
109110
'pages' => [
@@ -154,7 +155,7 @@ public function test_import_upload_success()
154155
],
155156
];
156157

157-
$resp = $this->runImportFromFile($this->zipUploadFromData($data));
158+
$resp = $this->runImportFromFile(ZipTestHelper::zipUploadFromData($data));
158159

159160
$this->assertDatabaseHas('imports', [
160161
'name' => 'My great book name',
@@ -217,7 +218,7 @@ public function test_import_show_page_access_limited()
217218
public function test_import_delete()
218219
{
219220
$this->asAdmin();
220-
$this->runImportFromFile($this->zipUploadFromData([
221+
$this->runImportFromFile(ZipTestHelper::zipUploadFromData([
221222
'book' => [
222223
'name' => 'My great book name'
223224
],
@@ -262,20 +263,126 @@ public function test_import_delete_access_limited()
262263
$this->delete("/import/{$adminImport->id}")->assertRedirect('/import');
263264
}
264265

265-
protected function runImportFromFile(UploadedFile $file): TestResponse
266+
public function test_run_simple_success_scenario()
266267
{
267-
return $this->call('POST', '/import', [], [], ['file' => $file]);
268+
$import = ZipTestHelper::importFromData([], [
269+
'book' => [
270+
'name' => 'My imported book',
271+
'pages' => [
272+
[
273+
'name' => 'My imported book page',
274+
'html' => '<p>Hello there from child page!</p>'
275+
]
276+
],
277+
]
278+
]);
279+
280+
$resp = $this->asAdmin()->post("/import/{$import->id}");
281+
$book = Book::query()->where('name', '=', 'My imported book')->latest()->first();
282+
$resp->assertRedirect($book->getUrl());
283+
284+
$resp = $this->followRedirects($resp);
285+
$resp->assertSee('My imported book page');
286+
$resp->assertSee('Hello there from child page!');
287+
288+
$this->assertDatabaseMissing('imports', ['id' => $import->id]);
289+
$this->assertFileDoesNotExist(storage_path($import->path));
290+
$this->assertActivityExists(ActivityType::IMPORT_RUN, null, $import->logDescriptor());
268291
}
269292

270-
protected function zipUploadFromData(array $data): UploadedFile
293+
public function test_import_run_access_limited()
271294
{
272-
$zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
295+
$user = $this->users->editor();
296+
$admin = $this->users->admin();
297+
$userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
298+
$adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
299+
$this->actingAs($user);
273300

274-
$zip = new ZipArchive();
275-
$zip->open($zipFile, ZipArchive::CREATE);
276-
$zip->addFromString('data.json', json_encode($data));
277-
$zip->close();
301+
$this->post("/import/{$userImport->id}")->assertRedirect('/');
302+
$this->post("/import/{$adminImport->id}")->assertRedirect('/');
303+
304+
$this->permissions->grantUserRolePermissions($user, ['content-import']);
305+
306+
$this->post("/import/{$userImport->id}")->assertRedirect($userImport->getUrl()); // Getting validation response instead of access issue response
307+
$this->post("/import/{$adminImport->id}")->assertStatus(404);
308+
309+
$this->permissions->grantUserRolePermissions($user, ['settings-manage']);
310+
311+
$this->post("/import/{$adminImport->id}")->assertRedirect($adminImport->getUrl()); // Getting validation response instead of access issue response
312+
}
313+
314+
public function test_run_revalidates_content()
315+
{
316+
$import = ZipTestHelper::importFromData([], [
317+
'book' => [
318+
'id' => 'abc',
319+
]
320+
]);
321+
322+
$resp = $this->asAdmin()->post("/import/{$import->id}");
323+
$resp->assertRedirect($import->getUrl());
324+
325+
$resp = $this->followRedirects($resp);
326+
$resp->assertSeeText('The name field is required.');
327+
$resp->assertSeeText('The id must be an integer.');
328+
}
329+
330+
public function test_run_checks_permissions_on_import()
331+
{
332+
$viewer = $this->users->viewer();
333+
$this->permissions->grantUserRolePermissions($viewer, ['content-import']);
334+
$import = ZipTestHelper::importFromData(['created_by' => $viewer->id], [
335+
'book' => ['name' => 'My import book'],
336+
]);
337+
338+
$resp = $this->asViewer()->post("/import/{$import->id}");
339+
$resp->assertRedirect($import->getUrl());
340+
341+
$resp = $this->followRedirects($resp);
342+
$resp->assertSeeText('You are lacking the required permissions to create books.');
343+
}
344+
345+
public function test_run_requires_parent_for_chapter_and_page_imports()
346+
{
347+
$book = $this->entities->book();
348+
$pageImport = ZipTestHelper::importFromData([], [
349+
'page' => ['name' => 'My page', 'html' => '<p>page test!</p>'],
350+
]);
351+
$chapterImport = ZipTestHelper::importFromData([], [
352+
'chapter' => ['name' => 'My chapter'],
353+
]);
354+
355+
$resp = $this->asAdmin()->post("/import/{$pageImport->id}");
356+
$resp->assertRedirect($pageImport->getUrl());
357+
$this->followRedirects($resp)->assertSee('The parent field is required.');
358+
359+
$resp = $this->asAdmin()->post("/import/{$pageImport->id}", ['parent' => "book:{$book->id}"]);
360+
$resp->assertRedirectContains($book->getUrl());
361+
362+
$resp = $this->asAdmin()->post("/import/{$chapterImport->id}");
363+
$resp->assertRedirect($chapterImport->getUrl());
364+
$this->followRedirects($resp)->assertSee('The parent field is required.');
365+
366+
$resp = $this->asAdmin()->post("/import/{$chapterImport->id}", ['parent' => "book:{$book->id}"]);
367+
$resp->assertRedirectContains($book->getUrl());
368+
}
369+
370+
public function test_run_validates_correct_parent_type()
371+
{
372+
$chapter = $this->entities->chapter();
373+
$import = ZipTestHelper::importFromData([], [
374+
'chapter' => ['name' => 'My chapter'],
375+
]);
376+
377+
$resp = $this->asAdmin()->post("/import/{$import->id}", ['parent' => "chapter:{$chapter->id}"]);
378+
$resp->assertRedirect($import->getUrl());
379+
380+
$resp = $this->followRedirects($resp);
381+
$resp->assertSee('Parent book required for chapter import.');
382+
}
278383

279-
return new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);
384+
protected function runImportFromFile(UploadedFile $file): TestResponse
385+
{
386+
return $this->call('POST', '/import', [], [], ['file' => $file]);
280387
}
281388
}

tests/Exports/ZipTestHelper.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Tests\Exports;
4+
5+
use BookStack\Exports\Import;
6+
use Illuminate\Http\UploadedFile;
7+
use ZipArchive;
8+
9+
class ZipTestHelper
10+
{
11+
public static function importFromData(array $importData, array $zipData): Import
12+
{
13+
if (isset($zipData['book'])) {
14+
$importData['type'] = 'book';
15+
} else if (isset($zipData['chapter'])) {
16+
$importData['type'] = 'chapter';
17+
} else if (isset($zipData['page'])) {
18+
$importData['type'] = 'page';
19+
}
20+
21+
$import = Import::factory()->create($importData);
22+
$zip = static::zipUploadFromData($zipData);
23+
rename($zip->getRealPath(), storage_path($import->path));
24+
25+
return $import;
26+
}
27+
28+
public static function deleteZipForImport(Import $import): void
29+
{
30+
$path = storage_path($import->path);
31+
if (file_exists($path)) {
32+
unlink($path);
33+
}
34+
}
35+
36+
public static function zipUploadFromData(array $data): UploadedFile
37+
{
38+
$zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
39+
40+
$zip = new ZipArchive();
41+
$zip->open($zipFile, ZipArchive::CREATE);
42+
$zip->addFromString('data.json', json_encode($data));
43+
$zip->close();
44+
45+
return new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);
46+
}
47+
}

0 commit comments

Comments
 (0)