]> BookStack Code Mirror - bookstack/commitdiff
ZIP Imports: Added high level import run tests
authorDan Brown <redacted>
Sat, 16 Nov 2024 13:57:41 +0000 (13:57 +0000)
committerDan Brown <redacted>
Sat, 16 Nov 2024 13:57:41 +0000 (13:57 +0000)
app/Exports/Controllers/ImportController.php
database/factories/Exports/ImportFactory.php
tests/Exports/ZipImportRunnerTest.php [new file with mode: 0644]
tests/Exports/ZipImportTest.php
tests/Exports/ZipTestHelper.php [new file with mode: 0644]

index d8dceed2f8e83aea554edc55ea926e56ad5af331..a20c341fb02424299c75f3423f7ddc97ea20ee27 100644 (file)
@@ -70,9 +70,11 @@ class ImportController extends Controller
         ]);
     }
 
+    /**
+     * Run the import process against an uploaded import ZIP.
+     */
     public function run(int $id, Request $request)
     {
-        // TODO - Test access/visibility
         $import = $this->imports->findVisible($id);
         $parent = null;
 
index 74a2bcd65f36d24ee5db2bd1b9219315793ba441..5d0b4f892997d82001e883b0af666ad2ff267ecc 100644 (file)
@@ -21,7 +21,7 @@ class ImportFactory extends Factory
     public function definition(): array
     {
         return [
-            'path' => 'uploads/imports/' . Str::random(10) . '.zip',
+            'path' => 'uploads/files/imports/' . Str::random(10) . '.zip',
             'name' => $this->faker->words(3, true),
             'type' => 'book',
             'metadata' => '{"name": "My book"}',
diff --git a/tests/Exports/ZipImportRunnerTest.php b/tests/Exports/ZipImportRunnerTest.php
new file mode 100644 (file)
index 0000000..7bdd8ec
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+namespace Tests\Exports;
+
+use BookStack\Exports\ZipExports\ZipImportRunner;
+use Tests\TestCase;
+
+class ZipImportRunnerTest extends TestCase
+{
+    protected ZipImportRunner $runner;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        $this->runner = app()->make(ZipImportRunner::class);
+    }
+
+    // TODO - Test full book import
+    // TODO - Test full chapter import
+    // TODO - Test full page import
+}
index 2b40100aabe854ec2d7f296ef3fb441756b83405..3644e9bdcb70112fd77922dea7a3d8a55793b084 100644 (file)
@@ -3,6 +3,7 @@
 namespace Tests\Exports;
 
 use BookStack\Activity\ActivityType;
+use BookStack\Entities\Models\Book;
 use BookStack\Exports\Import;
 use BookStack\Exports\ZipExports\Models\ZipExportBook;
 use BookStack\Exports\ZipExports\Models\ZipExportChapter;
@@ -91,7 +92,7 @@ class ZipImportTest extends TestCase
     public function test_error_shown_if_no_importable_key()
     {
         $this->asAdmin();
-        $resp = $this->runImportFromFile($this->zipUploadFromData([
+        $resp = $this->runImportFromFile(ZipTestHelper::zipUploadFromData([
             'instance' => []
         ]));
 
@@ -103,7 +104,7 @@ class ZipImportTest extends TestCase
     public function test_zip_data_validation_messages_shown()
     {
         $this->asAdmin();
-        $resp = $this->runImportFromFile($this->zipUploadFromData([
+        $resp = $this->runImportFromFile(ZipTestHelper::zipUploadFromData([
             'book' => [
                 'id' => 4,
                 'pages' => [
@@ -154,7 +155,7 @@ class ZipImportTest extends TestCase
             ],
         ];
 
-        $resp = $this->runImportFromFile($this->zipUploadFromData($data));
+        $resp = $this->runImportFromFile(ZipTestHelper::zipUploadFromData($data));
 
         $this->assertDatabaseHas('imports', [
             'name' => 'My great book name',
@@ -217,7 +218,7 @@ class ZipImportTest extends TestCase
     public function test_import_delete()
     {
         $this->asAdmin();
-        $this->runImportFromFile($this->zipUploadFromData([
+        $this->runImportFromFile(ZipTestHelper::zipUploadFromData([
             'book' => [
                 'name' => 'My great book name'
             ],
@@ -262,20 +263,126 @@ class ZipImportTest extends TestCase
         $this->delete("/import/{$adminImport->id}")->assertRedirect('/import');
     }
 
-    protected function runImportFromFile(UploadedFile $file): TestResponse
+    public function test_run_simple_success_scenario()
     {
-        return $this->call('POST', '/import', [], [], ['file' => $file]);
+        $import = ZipTestHelper::importFromData([], [
+            'book' => [
+                'name' => 'My imported book',
+                'pages' => [
+                    [
+                        'name' => 'My imported book page',
+                        'html' => '<p>Hello there from child page!</p>'
+                    ]
+                ],
+            ]
+        ]);
+
+        $resp = $this->asAdmin()->post("/import/{$import->id}");
+        $book = Book::query()->where('name', '=', 'My imported book')->latest()->first();
+        $resp->assertRedirect($book->getUrl());
+
+        $resp = $this->followRedirects($resp);
+        $resp->assertSee('My imported book page');
+        $resp->assertSee('Hello there from child page!');
+
+        $this->assertDatabaseMissing('imports', ['id' => $import->id]);
+        $this->assertFileDoesNotExist(storage_path($import->path));
+        $this->assertActivityExists(ActivityType::IMPORT_RUN, null, $import->logDescriptor());
     }
 
-    protected function zipUploadFromData(array $data): UploadedFile
+    public function test_import_run_access_limited()
     {
-        $zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
+        $user = $this->users->editor();
+        $admin = $this->users->admin();
+        $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
+        $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
+        $this->actingAs($user);
 
-        $zip = new ZipArchive();
-        $zip->open($zipFile, ZipArchive::CREATE);
-        $zip->addFromString('data.json', json_encode($data));
-        $zip->close();
+        $this->post("/import/{$userImport->id}")->assertRedirect('/');
+        $this->post("/import/{$adminImport->id}")->assertRedirect('/');
+
+        $this->permissions->grantUserRolePermissions($user, ['content-import']);
+
+        $this->post("/import/{$userImport->id}")->assertRedirect($userImport->getUrl()); // Getting validation response instead of access issue response
+        $this->post("/import/{$adminImport->id}")->assertStatus(404);
+
+        $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
+
+        $this->post("/import/{$adminImport->id}")->assertRedirect($adminImport->getUrl()); // Getting validation response instead of access issue response
+    }
+
+    public function test_run_revalidates_content()
+    {
+        $import = ZipTestHelper::importFromData([], [
+            'book' => [
+                'id' => 'abc',
+            ]
+        ]);
+
+        $resp = $this->asAdmin()->post("/import/{$import->id}");
+        $resp->assertRedirect($import->getUrl());
+
+        $resp = $this->followRedirects($resp);
+        $resp->assertSeeText('The name field is required.');
+        $resp->assertSeeText('The id must be an integer.');
+    }
+
+    public function test_run_checks_permissions_on_import()
+    {
+        $viewer = $this->users->viewer();
+        $this->permissions->grantUserRolePermissions($viewer, ['content-import']);
+        $import = ZipTestHelper::importFromData(['created_by' => $viewer->id], [
+            'book' => ['name' => 'My import book'],
+        ]);
+
+        $resp = $this->asViewer()->post("/import/{$import->id}");
+        $resp->assertRedirect($import->getUrl());
+
+        $resp = $this->followRedirects($resp);
+        $resp->assertSeeText('You are lacking the required permissions to create books.');
+    }
+
+    public function test_run_requires_parent_for_chapter_and_page_imports()
+    {
+        $book = $this->entities->book();
+        $pageImport = ZipTestHelper::importFromData([], [
+            'page' => ['name' => 'My page', 'html' => '<p>page test!</p>'],
+        ]);
+        $chapterImport = ZipTestHelper::importFromData([], [
+            'chapter' => ['name' => 'My chapter'],
+        ]);
+
+        $resp = $this->asAdmin()->post("/import/{$pageImport->id}");
+        $resp->assertRedirect($pageImport->getUrl());
+        $this->followRedirects($resp)->assertSee('The parent field is required.');
+
+        $resp = $this->asAdmin()->post("/import/{$pageImport->id}", ['parent' => "book:{$book->id}"]);
+        $resp->assertRedirectContains($book->getUrl());
+
+        $resp = $this->asAdmin()->post("/import/{$chapterImport->id}");
+        $resp->assertRedirect($chapterImport->getUrl());
+        $this->followRedirects($resp)->assertSee('The parent field is required.');
+
+        $resp = $this->asAdmin()->post("/import/{$chapterImport->id}", ['parent' => "book:{$book->id}"]);
+        $resp->assertRedirectContains($book->getUrl());
+    }
+
+    public function test_run_validates_correct_parent_type()
+    {
+        $chapter = $this->entities->chapter();
+        $import = ZipTestHelper::importFromData([], [
+            'chapter' => ['name' => 'My chapter'],
+        ]);
+
+        $resp = $this->asAdmin()->post("/import/{$import->id}", ['parent' => "chapter:{$chapter->id}"]);
+        $resp->assertRedirect($import->getUrl());
+
+        $resp = $this->followRedirects($resp);
+        $resp->assertSee('Parent book required for chapter import.');
+    }
 
-        return new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);
+    protected function runImportFromFile(UploadedFile $file): TestResponse
+    {
+        return $this->call('POST', '/import', [], [], ['file' => $file]);
     }
 }
diff --git a/tests/Exports/ZipTestHelper.php b/tests/Exports/ZipTestHelper.php
new file mode 100644 (file)
index 0000000..3a9b343
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+
+namespace Tests\Exports;
+
+use BookStack\Exports\Import;
+use Illuminate\Http\UploadedFile;
+use ZipArchive;
+
+class ZipTestHelper
+{
+    public static function importFromData(array $importData, array $zipData): Import
+    {
+        if (isset($zipData['book'])) {
+            $importData['type'] = 'book';
+        } else if (isset($zipData['chapter'])) {
+            $importData['type'] = 'chapter';
+        } else if (isset($zipData['page'])) {
+            $importData['type'] = 'page';
+        }
+
+        $import = Import::factory()->create($importData);
+        $zip = static::zipUploadFromData($zipData);
+        rename($zip->getRealPath(), storage_path($import->path));
+
+        return $import;
+    }
+
+    public static function deleteZipForImport(Import $import): void
+    {
+        $path = storage_path($import->path);
+        if (file_exists($path)) {
+            unlink($path);
+        }
+    }
+
+    public static function zipUploadFromData(array $data): UploadedFile
+    {
+        $zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
+
+        $zip = new ZipArchive();
+        $zip->open($zipFile, ZipArchive::CREATE);
+        $zip->addFromString('data.json', json_encode($data));
+        $zip->close();
+
+        return new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);
+    }
+}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.