From e18033ec1ae181a8977d23d14090d0706f3cc05b Mon Sep 17 00:00:00 2001 From: Dan Brown <ssddanbrown@googlemail.com> Date: Mon, 26 Sep 2022 21:25:32 +0100 Subject: [PATCH 1/2] Added initial support for parallel testing --- app/Providers/AppServiceProvider.php | 7 +++++++ composer.json | 1 + resources/js/wysiwyg/config.js | 1 + tests/TestCase.php | 8 ++++++++ tests/ThemeTest.php | 12 ++++++------ 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3c1212e32..02c545db2 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -17,7 +17,9 @@ use GuzzleHttp\Client; use Illuminate\Contracts\Cache\Repository; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Pagination\Paginator; +use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Blade; +use Illuminate\Support\Facades\ParallelTesting; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\View; @@ -64,6 +66,11 @@ class AppServiceProvider extends ServiceProvider // Set paginator to use bootstrap-style pagination Paginator::useBootstrap(); + + // Setup database upon parallel testing database creation + ParallelTesting::setUpTestDatabase(function ($database, $token) { + Artisan::call('db:seed --class=DummyContentSeeder'); + }); } /** diff --git a/composer.json b/composer.json index 64630833d..44bbf2b99 100644 --- a/composer.json +++ b/composer.json @@ -44,6 +44,7 @@ "ssddanbrown/htmldiff": "^1.0.2" }, "require-dev": { + "brianium/paratest": "^6.6", "fakerphp/faker": "^1.16", "itsgoingd/clockwork": "^5.1", "mockery/mockery": "^1.4", diff --git a/resources/js/wysiwyg/config.js b/resources/js/wysiwyg/config.js index 52c52592c..d2f813cfb 100644 --- a/resources/js/wysiwyg/config.js +++ b/resources/js/wysiwyg/config.js @@ -252,6 +252,7 @@ export function build(options) { document_base_url: window.baseUrl('/'), end_container_on_empty_block: true, remove_trailing_brs: false, + keep_styles: false, statusbar: false, menubar: false, paste_data_images: false, diff --git a/tests/TestCase.php b/tests/TestCase.php index f17d27a1a..0926b0dcc 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -26,6 +26,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; use Illuminate\Http\JsonResponse; use Illuminate\Support\Env; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Illuminate\Testing\Assert as PHPUnit; use Monolog\Handler\TestHandler; @@ -299,6 +300,8 @@ abstract class TestCase extends BaseTestCase /** * Run a set test with the given env variable. * Remembers the original and resets the value after test. + * Database config is juggled so the value can be restored when + * parallel testing are used, where multiple databases exist. */ protected function runWithEnv(string $name, $value, callable $callback) { @@ -311,7 +314,12 @@ abstract class TestCase extends BaseTestCase $_SERVER[$name] = $value; } + $database = config('database.connections.mysql_testing.database'); $this->refreshApplication(); + + DB::purge(); + config()->set('database.connections.mysql_testing.database', $database); + $callback(); if (is_null($originalVal)) { diff --git a/tests/ThemeTest.php b/tests/ThemeTest.php index e83758a95..ac4b35de2 100644 --- a/tests/ThemeTest.php +++ b/tests/ThemeTest.php @@ -322,8 +322,8 @@ class ThemeTest extends TestCase public function test_export_body_start_and_end_template_files_can_be_used() { - $bodyStartStr = 'barry-fought-against-the-panther'; - $bodyEndStr = 'barry-lost-his-fight-with-grace'; + $bodyStartStr = 'garry-fought-against-the-panther'; + $bodyEndStr = 'garry-lost-his-fight-with-grace'; /** @var Page $page */ $page = Page::query()->first(); @@ -342,18 +342,18 @@ class ThemeTest extends TestCase protected function usingThemeFolder(callable $callback) { // Create a folder and configure a theme - $themeFolderName = 'testing_theme_' . rtrim(base64_encode(time()), '='); + $themeFolderName = 'testing_theme_' . str_shuffle(rtrim(base64_encode(time()), '=')); config()->set('view.theme', $themeFolderName); $themeFolderPath = theme_path(''); + + // Create theme folder and clean it up on application tear-down File::makeDirectory($themeFolderPath); + $this->beforeApplicationDestroyed(fn() => File::deleteDirectory($themeFolderPath)); // Run provided callback with theme env option set $this->runWithEnv('APP_THEME', $themeFolderName, function () use ($callback, $themeFolderName) { call_user_func($callback, $themeFolderName); }); - - // Cleanup the custom theme folder we created - File::deleteDirectory($themeFolderPath); } } From f21669c0c966f3dadeac2024a382b8a7cd831a8a Mon Sep 17 00:00:00 2001 From: Dan Brown <ssddanbrown@googlemail.com> Date: Tue, 27 Sep 2022 01:27:51 +0100 Subject: [PATCH 2/2] Cleaned testing service provider usage Moved testing content out of AppServiceProvider, to a testing-specific service provider. Updated docs and added composer commands to support parallel testing. Also reverted unintentional change to wysiwyg/config.js. --- app/Providers/AppServiceProvider.php | 7 ------- composer.json | 2 ++ readme.md | 9 ++------- resources/js/wysiwyg/config.js | 1 - tests/TestCase.php | 16 ++++++++++++++++ tests/TestServiceProvider.php | 26 ++++++++++++++++++++++++++ 6 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 tests/TestServiceProvider.php diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 02c545db2..3c1212e32 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -17,9 +17,7 @@ use GuzzleHttp\Client; use Illuminate\Contracts\Cache\Repository; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Pagination\Paginator; -use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Blade; -use Illuminate\Support\Facades\ParallelTesting; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\View; @@ -66,11 +64,6 @@ class AppServiceProvider extends ServiceProvider // Set paginator to use bootstrap-style pagination Paginator::useBootstrap(); - - // Setup database upon parallel testing database creation - ParallelTesting::setUpTestDatabase(function ($database, $token) { - Artisan::call('db:seed --class=DummyContentSeeder'); - }); } /** diff --git a/composer.json b/composer.json index 44bbf2b99..81896f8f8 100644 --- a/composer.json +++ b/composer.json @@ -74,6 +74,8 @@ "format": "phpcbf", "lint": "phpcs", "test": "phpunit", + "t": "@php artisan test --parallel", + "t-reset": "@php artisan test --recreate-databases", "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "@php artisan package:discover --ansi" diff --git a/readme.md b/readme.md index d0ae1b4f6..16992341d 100644 --- a/readme.md +++ b/readme.md @@ -108,14 +108,9 @@ npm run dev BookStack has many integration tests that use Laravel's built-in testing capabilities which makes use of PHPUnit. There is a `mysql_testing` database defined within the app config which is what is used by PHPUnit. This database is set with the database name, user name and password all defined as `bookstack-test`. You will have to create that database and that set of credentials before testing. -The testing database will also need migrating and seeding beforehand. This can be done with the following commands: +The testing database will also need migrating and seeding beforehand. This can be done by running `composer refresh-test-database`. -``` bash -php artisan migrate --database=mysql_testing -php artisan db:seed --class=DummyContentSeeder --database=mysql_testing -``` - -Once done you can run `composer test` in the application root directory to run all tests. +Once done you can run `composer test` in the application root directory to run all tests. Tests can be ran in parallel by running them via `composer t`. This will use Laravel's built-in parallel testing functionality, and attempt to create and seed a database instance for each testing thread. If required these parallel testing instances can be reset, before testing again, by running `composer t-reset`. ### 📜 Code Standards diff --git a/resources/js/wysiwyg/config.js b/resources/js/wysiwyg/config.js index d2f813cfb..52c52592c 100644 --- a/resources/js/wysiwyg/config.js +++ b/resources/js/wysiwyg/config.js @@ -252,7 +252,6 @@ export function build(options) { document_base_url: window.baseUrl('/'), end_container_on_empty_block: true, remove_trailing_brs: false, - keep_styles: false, statusbar: false, menubar: false, paste_data_images: false, diff --git a/tests/TestCase.php b/tests/TestCase.php index 0926b0dcc..594194168 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -22,6 +22,7 @@ use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; +use Illuminate\Contracts\Console\Kernel; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; use Illuminate\Http\JsonResponse; @@ -48,6 +49,21 @@ abstract class TestCase extends BaseTestCase */ protected string $baseUrl = 'http://localhost'; + /** + * Creates the application. + * + * @return \Illuminate\Foundation\Application + */ + public function createApplication() + { + /** @var \Illuminate\Foundation\Application $app */ + $app = require __DIR__ . '/../bootstrap/app.php'; + $app->register(TestServiceProvider::class); + $app->make(Kernel::class)->bootstrap(); + + return $app; + } + /** * Set the current user context to be an admin. */ diff --git a/tests/TestServiceProvider.php b/tests/TestServiceProvider.php new file mode 100644 index 000000000..9ad48c442 --- /dev/null +++ b/tests/TestServiceProvider.php @@ -0,0 +1,26 @@ +<?php + +namespace Tests; + +use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\ParallelTesting; +use Illuminate\Support\ServiceProvider; + +class TestServiceProvider extends ServiceProvider +{ + /** + * Bootstrap services. + * + * @return void + */ + public function boot() + { + // Tell Laravel's parallel testing functionality to seed the test + // databases with the DummyContentSeeder upon creation. + // This is only done for initial database creation. Seeding + // won't occur on every run. + ParallelTesting::setUpTestDatabase(function ($database, $token) { + Artisan::call('db:seed --class=DummyContentSeeder'); + }); + } +}