diff --git a/php-packages/testing/.editorconfig b/php-packages/testing/.editorconfig new file mode 100644 index 000000000..a61a3ab36 --- /dev/null +++ b/php-packages/testing/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.{diff,md}] +trim_trailing_whitespace = false + +[*.{php,xml,json}] +indent_size = 4 diff --git a/php-packages/testing/.gitattributes b/php-packages/testing/.gitattributes new file mode 100644 index 000000000..61036520c --- /dev/null +++ b/php-packages/testing/.gitattributes @@ -0,0 +1,15 @@ +.gitattributes export-ignore +.gitignore export-ignore +.gitmodules export-ignore +.github export-ignore +.travis export-ignore +.travis.yml export-ignore +.editorconfig export-ignore +.styleci.yml export-ignore + +phpunit.xml export-ignore +tests export-ignore + +js/dist/* -diff + +* text=auto eol=lf diff --git a/php-packages/testing/.github/workflows/test.yml b/php-packages/testing/.github/workflows/test.yml new file mode 100644 index 000000000..b7ba28c30 --- /dev/null +++ b/php-packages/testing/.github/workflows/test.yml @@ -0,0 +1,83 @@ +name: Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + defaults: + run: + shell: bash + working-directory: tests + + strategy: + matrix: + php: [7.3, 7.4, '8.0'] + service: ['mysql:5.7', mariadb] + prefix: ['', flarum_] + + include: + - service: 'mysql:5.7' + db: MySQL + - service: mariadb + db: MariaDB + - prefix: flarum_ + prefixStr: (prefix) + + exclude: + - php: 7.3 + service: 'mysql:5.7' + prefix: flarum_ + - php: 7.3 + service: mariadb + prefix: flarum_ + - php: 8.0 + service: 'mysql:5.7' + prefix: flarum_ + - php: 8.0 + service: mariadb + prefix: flarum_ + + services: + mysql: + image: ${{ matrix.service }} + ports: + - 13306:3306 + + name: 'PHP ${{ matrix.php }} / ${{ matrix.db }} ${{ matrix.prefixStr }}' + + steps: + - uses: actions/checkout@master + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: xdebug + extensions: curl, dom, gd, json, mbstring, openssl, pdo_mysql, tokenizer, zip + tools: phpunit, composer:v2 + + # The authentication alter is necessary because newer mysql versions use the `caching_sha2_password` driver, + # which isn't supported prior to PHP7.4 + # When we drop support for PHP7.3, we should remove this from the setup. + - name: Create MySQL Database + run: | + sudo systemctl start mysql + mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 13306 + mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';" --port 13306 + + - name: Install Composer dependencies + run: composer install + + - name: Setup Composer tests + run: composer test:setup + env: + DB_PORT: 13306 + DB_PASSWORD: root + DB_PREFIX: ${{ matrix.prefix }} + + - name: Run Composer tests + run: composer test + env: + COMPOSER_PROCESS_TIMEOUT: 600 diff --git a/php-packages/testing/.gitignore b/php-packages/testing/.gitignore new file mode 100644 index 000000000..38a8fa98d --- /dev/null +++ b/php-packages/testing/.gitignore @@ -0,0 +1,10 @@ +/vendor +composer.lock +composer.phar +node_modules +.DS_Store +Thumbs.db +/src/integration/tmp +.vagrant +.idea/* +.vscode diff --git a/php-packages/testing/.styleci.yml b/php-packages/testing/.styleci.yml new file mode 100644 index 000000000..5d07e31ea --- /dev/null +++ b/php-packages/testing/.styleci.yml @@ -0,0 +1,18 @@ +preset: recommended + +enabled: + - logical_not_operators_with_successor_space + +disabled: + - align_double_arrow + - blank_line_after_opening_tag + - multiline_array_trailing_comma + - new_with_braces + - phpdoc_align + - phpdoc_order + - phpdoc_separation + - phpdoc_types + +finder: + exclude: + - "stubs" diff --git a/php-packages/testing/LICENSE b/php-packages/testing/LICENSE new file mode 100755 index 000000000..9fe7ef1c7 --- /dev/null +++ b/php-packages/testing/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020-2021 Stichting Flarum (Flarum Foundation) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/php-packages/testing/composer.json b/php-packages/testing/composer.json new file mode 100644 index 000000000..831dcd88c --- /dev/null +++ b/php-packages/testing/composer.json @@ -0,0 +1,33 @@ +{ + "name": "flarum/testing", + "description": "Automated testing infrastructure for Flarum core and extensions.", + "keywords": [ + "forum", + "discussion" + ], + "homepage": "https://flarum.org/", + "license": "MIT", + "require": { + "ext-json": "*", + "mockery/mockery": "^1.4", + "phpunit/phpunit": "^9.0" + }, + "require-dev": { + "flarum/core": "*@dev" + }, + "autoload": { + "psr-4": { + "Flarum\\Testing\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Flarum\\Testing\\Tests\\": "src/tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + } +} diff --git a/php-packages/testing/src/integration/BuildsHttpRequests.php b/php-packages/testing/src/integration/BuildsHttpRequests.php new file mode 100644 index 000000000..6b379702a --- /dev/null +++ b/php-packages/testing/src/integration/BuildsHttpRequests.php @@ -0,0 +1,69 @@ +withHeader('Content-Type', 'application/json') + ->withBody( + new CallbackStream(function () use ($json) { + return json_encode($json); + }) + ); + } + + protected function requestAsUser(Request $req, int $userId): Request + { + $token = Str::random(40); + + /** + * We insert this directly instead of via `prepareDatabase` + * so that requests can be created/sent after the app is booted. + */ + $this->database()->table('access_tokens')->insert([ + 'token' => $token, + 'user_id' => $userId, + 'created_at' => Carbon::now()->toDateTimeString(), + 'last_activity_at' => Carbon::now()->toDateTimeString(), + 'type' => 'session' + ]); + + return $req->withAddedHeader('Authorization', "Token {$token}"); + } + + protected function requestWithCookiesFrom(Request $req, Response $previous): Request + { + $cookies = array_reduce( + $previous->getHeader('Set-Cookie'), + function ($memo, $setCookieString) { + $setCookie = SetCookie::fromSetCookieString($setCookieString); + $memo[$setCookie->getName()] = $setCookie->getValue(); + + return $memo; + }, + [] + ); + + return $req->withCookieParams($cookies); + } +} diff --git a/php-packages/testing/src/integration/ConsoleTestCase.php b/php-packages/testing/src/integration/ConsoleTestCase.php new file mode 100644 index 000000000..a29db0895 --- /dev/null +++ b/php-packages/testing/src/integration/ConsoleTestCase.php @@ -0,0 +1,44 @@ +console)) { + $this->console = new ConsoleApplication('Flarum', Application::VERSION); + $this->console->setAutoExit(false); + + foreach ($this->app()->getConsoleCommands() as $command) { + $this->console->add($command); + } + } + + return $this->console; + } + + protected function runCommand(array $inputArray) + { + $input = new ArrayInput($inputArray); + $output = new BufferedOutput(); + + $this->console()->run($input, $output); + + return trim($output->fetch()); + } +} diff --git a/php-packages/testing/src/integration/Extend/BeginTransactionAndSetDatabase.php b/php-packages/testing/src/integration/Extend/BeginTransactionAndSetDatabase.php new file mode 100644 index 000000000..cc088686f --- /dev/null +++ b/php-packages/testing/src/integration/Extend/BeginTransactionAndSetDatabase.php @@ -0,0 +1,30 @@ +setDbOnTestCase = $setDbOnTestCase; + } + + public function extend(Container $container, Extension $extension = null) + { + $db = $container->make(ConnectionInterface::class); + + $db->beginTransaction(); + + ($this->setDbOnTestCase)($db); + } +} diff --git a/php-packages/testing/src/integration/Extend/OverrideExtensionManagerForTests.php b/php-packages/testing/src/integration/Extend/OverrideExtensionManagerForTests.php new file mode 100644 index 000000000..8d66e7319 --- /dev/null +++ b/php-packages/testing/src/integration/Extend/OverrideExtensionManagerForTests.php @@ -0,0 +1,39 @@ +extensions = $extensions; + } + + public function extend(Container $container, Extension $extension = null) + { + $container->when(ExtensionManagerIncludeCurrent::class)->needs('$enabledIds')->give($this->extensions); + if (count($this->extensions)) { + $container->singleton(ExtensionManager::class, ExtensionManagerIncludeCurrent::class); + $extensionManager = $container->make(ExtensionManager::class); + + foreach ($this->extensions as $extension) { + $extensionManager->enable($extension); + } + + $extensionManager->booted = true; + + $extensionManager->extend($container); + } + } +} diff --git a/php-packages/testing/src/integration/Extend/SetSettingsBeforeBoot.php b/php-packages/testing/src/integration/Extend/SetSettingsBeforeBoot.php new file mode 100644 index 000000000..51957b19a --- /dev/null +++ b/php-packages/testing/src/integration/Extend/SetSettingsBeforeBoot.php @@ -0,0 +1,32 @@ +settings = $settings; + } + + public function extend(Container $container, Extension $extension = null) + { + if (count($this->settings)) { + $settings = $container->make(SettingsRepositoryInterface::class); + + foreach ($this->settings as $key => $value) { + $settings->set($key, $value); + } + } + } +} diff --git a/php-packages/testing/src/integration/Extension/ExtensionManagerIncludeCurrent.php b/php-packages/testing/src/integration/Extension/ExtensionManagerIncludeCurrent.php new file mode 100644 index 000000000..1a3dbc8f0 --- /dev/null +++ b/php-packages/testing/src/integration/Extension/ExtensionManagerIncludeCurrent.php @@ -0,0 +1,113 @@ +enabledIds = $enabledIds; + } + + /** + * @{@inheritDoc} + */ + public function getExtensions() + { + $extensions = parent::getExtensions(); + + $package = json_decode($this->filesystem->get($this->paths->vendor . '/../composer.json'), true); + + if (Arr::get($package, 'type') === 'flarum-extension') { + $current = new Extension($this->paths->vendor . '/../', $package); + $current->setInstalled(true); + $current->setVersion(Arr::get($package, 'version')); + $current->calculateDependencies([], []); + + $extensions->put($current->getId(), $current); + + $this->extensions = $extensions->sortBy(function ($extension, $name) { + return $extension->composerJsonAttribute('extra.flarum-extension.title'); + }); + } + + return $this->extensions; + } + + /** + * We assume it's not enabled during boot. + * However, since some logic needs this, as soon as we enable extensions + * we'll switch booted to on. + */ + public function isEnabled($extension) + { + if (!$this->booted) return false; + + return parent::isEnabled($extension); + } + + /** + * In test cases, enabled extensions are determined by the test case, not the database. + */ + public function getEnabled() + { + return $this->enabledIds; + } + + /** + * Enabled extensions must be specified by the test case, so this should do nothing. + */ + protected function setEnabledExtensions(array $enabledExtensions) + { + } + + /** + * Get an instance of the assets filesystem. + * This is resolved dynamically because Flarum's filesystem configuration + * might not be booted yet when the ExtensionManager singleton initializes. + */ + protected function getAssetsFilesystem(): Cloud + { + return new FilesystemAdapter(new FlysystemFilesystem(new Local($this->paths->public . '/assets'), ['url' => resolve('flarum.config')->url() . '/assets'])); + } +} diff --git a/php-packages/testing/src/integration/RetrievesAuthorizedUsers.php b/php-packages/testing/src/integration/RetrievesAuthorizedUsers.php new file mode 100644 index 000000000..8eefc75f6 --- /dev/null +++ b/php-packages/testing/src/integration/RetrievesAuthorizedUsers.php @@ -0,0 +1,24 @@ + 2, + 'username' => 'normal', + 'password' => '$2y$10$LO59tiT7uggl6Oe23o/O6.utnF6ipngYjvMvaxo1TciKqBttDNKim', // BCrypt hash for "too-obscure" + 'email' => 'normal@machine.local', + 'is_email_confirmed' => 1, + ]; + } +} diff --git a/php-packages/testing/src/integration/Setup/SetupScript.php b/php-packages/testing/src/integration/Setup/SetupScript.php new file mode 100644 index 000000000..0767db596 --- /dev/null +++ b/php-packages/testing/src/integration/Setup/SetupScript.php @@ -0,0 +1,149 @@ +host = getenv('DB_HOST') ?: 'localhost'; + $this->port = intval(getenv('DB_PORT') ?: 3306); + $this->name = getenv('DB_DATABASE') ?: 'flarum_test'; + $this->user = getenv('DB_USERNAME') ?: 'root'; + $this->pass = getenv('DB_PASSWORD') ?? 'root'; + $this->pref = getenv('DB_PREFIX') ?: ''; + } + + public function run() + { + $tmp = $this->tmpDir(); + + echo "Connecting to database $this->name at $this->host:$this->port.\n"; + echo "Warning: all tables will be dropped to ensure clean state. DO NOT use your production database!\n"; + echo "Logging in as $this->user with password '$this->pass'.\n"; + echo "Table prefix: '$this->pref'\n"; + echo "\nStoring test config in '$tmp'\n"; + + echo "\n\nCancel now if that's not what you want...\n"; + echo "Use the following environment variables for configuration:\n"; + echo "Database: DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD, DB_PREFIX\n"; + echo "Test Config: FLARUM_TEST_TMP_DIR or FLARUM_TEST_TMP_DIR_LOCAL\n"; + + + sleep(4); + + echo "\nOff we go...\n"; + + $this->dbConfig = new DatabaseConfig('mysql', $this->host, $this->port, $this->name, $this->user, $this->pass, $this->pref); + + echo "\nWiping DB to ensure clean state\n"; + $this->wipeDb(); + echo "Success! Proceeding to installation...\n"; + + $this->setupTmpDir(); + + $installation = new Installation( + new Paths([ + 'base' => $tmp, + 'public' => "$tmp/public", + 'storage' => "$tmp/storage", + 'vendor' => getcwd() . '/vendor', + ]) + ); + + $pipeline = $installation + ->configPath('config.php') + ->debugMode(true) + ->baseUrl(BaseUrl::fromString('http://localhost')) + ->databaseConfig($this->dbConfig) + ->adminUser(new AdminUser( + 'admin', + 'password', + 'admin@machine.local' + )) + ->settings(['mail_driver' => 'log']) + ->extensions([]) + ->build(); + + // Run the actual configuration + $pipeline->run(); + + echo "Installation complete\n"; + } + + protected function wipeDb() + { + // Reuse the connection step to include version checks + (new ConnectToDatabase($this->dbConfig, function ($db) { + // Inspired by Laravel's db:wipe + $builder = $db->getSchemaBuilder(); + + $builder->dropAllTables(); + $builder->dropAllViews(); + }))->run(); + } +} diff --git a/php-packages/testing/src/integration/TestCase.php b/php-packages/testing/src/integration/TestCase.php new file mode 100644 index 000000000..034db0f3f --- /dev/null +++ b/php-packages/testing/src/integration/TestCase.php @@ -0,0 +1,295 @@ +database()->rollBack(); + } + + /** + * @var \Flarum\Foundation\InstalledApp + */ + protected $app; + + /** + * @return \Flarum\Foundation\InstalledApp + */ + protected function app() + { + if (is_null($this->app)) { + $tmp = $this->tmpDir(); + + $config = include "$tmp/config.php"; + + foreach ($this->config as $key => $value) { + Arr::set($config, $key, $value); + } + + $site = new InstalledSite( + new Paths([ + 'base' => $tmp, + 'public' => "$tmp/public", + 'storage' => "$tmp/storage", + 'vendor' => getcwd().'/vendor', + ]), + new Config($config) + ); + + $extenders = array_merge([ + new OverrideExtensionManagerForTests($this->extensions), + new BeginTransactionAndSetDatabase(function (ConnectionInterface $db) { + $this->database = $db; + }), + new SetSettingsBeforeBoot($this->settings), + ], $this->extenders); + + $site->extendWith($extenders); + + $this->app = $site->bootApp(); + + $this->populateDatabase(); + } + + return $this->app; + } + + /** + * @var ExtenderInterface[] + */ + protected $extenders = []; + + /** + * Each argument should be an instance of an extender that should + * be applied at application boot. + * + * Note that this method will have no effect if called after the + * application is booted. + */ + protected function extend(ExtenderInterface ...$extenders) + { + $this->extenders = array_merge($this->extenders, $extenders); + } + + /** + * @var string[] + */ + protected $extensions = []; + + /** + * Each argument should be an ID of an extension to be enabled. + * Extensions other than the one currently being tested must be + * listed in this extension's `composer.json` under `require` or + * `require-dev`. + * + * Note that this method will have no effect if called after the + * application is booted. + */ + protected function extension(string ...$extensions) + { + $this->extensions = array_merge($this->extensions, $extensions); + } + + /** + * @var array + */ + protected $config = []; + + /** + * Some Flarum code depends on config.php values. Flarum doesn't + * offer a way to set them at runtime, so this method lets you + * add/override them before boot. + * + * You can use dot-separated syntax to assign values to subarrays. + * + * For example: + * + * `$this->config('a.b.c', 'value');` will result in the following: + * + * [ + * 'a' => [ + * 'b' => ['c' => 'value'] + * ] + * ] + * + * Note that this method will have no effect if called after the + * application is booted. + */ + protected function config(string $key, $value) + { + $this->config[$key] = $value; + } + + /** + * @var array + */ + protected $settings = []; + + /** + * Some settings are used during application boot, so setting + * them via `prepareDatabase` will be too late for the desired + * effect. For instance, in core the active display name driver + * is configured based on the `display_name_driver` setting. + * That setting should be registered using this method. + * + * Note that this method will have no effect if called after the + * application is booted. + */ + protected function setting(string $key, $value) + { + $this->settings[$key] = $value; + } + + /** + * @var RequestHandlerInterface + */ + protected $server; + + protected function server(): RequestHandlerInterface + { + if (is_null($this->server)) { + $this->server = $this->app()->getRequestHandler(); + } + + return $this->server; + } + + protected $database; + + protected function database(): ConnectionInterface + { + $this->app(); + // Set in `BeginTransactionAndSetDatabase` extender. + return $this->database; + } + + protected $databaseContent = []; + + protected function prepareDatabase(array $tableData) + { + $this->databaseContent = array_merge_recursive( + $this->databaseContent, + $tableData + ); + } + + protected function populateDatabase() + { + // We temporarily disable foreign key checks to simplify this process. + $this->database()->getSchemaBuilder()->disableForeignKeyConstraints(); + + // Then, insert all rows required for this test case. + foreach ($this->databaseContent as $table => $rows) { + foreach ($rows as $row) { + if ($table === 'settings') { + $this->database()->table($table)->updateOrInsert( + ['key' => $row['key']], + $row + ); + } else { + $this->database()->table($table)->updateOrInsert( + isset($row['id']) ? ['id' => $row['id']] : $row, + $row + ); + } + } + } + + // And finally, turn on foreign key checks again. + $this->database()->getSchemaBuilder()->enableForeignKeyConstraints(); + } + + /** + * Send a full HTTP request through Flarum's middleware stack. + */ + protected function send(ServerRequestInterface $request): ResponseInterface + { + return $this->server()->handle($request); + } + + /** + * Build a HTTP request that can be passed through middleware. + * + * This method simplifies building HTTP requests for use in our HTTP-level + * integration tests. It provides options for all features repeatedly being + * used in those tests. + * + * @param string $method + * @param string $path + * @param array $options + * An array of optional request properties. + * Currently supported: + * - "json" should point to a JSON-serializable object that will be + * serialized and used as request body. The corresponding Content-Type + * header will be set automatically. + * - "authenticatedAs" should identify an *existing* user by ID. This will + * cause an access token to be created for this user, which will be used + * to authenticate the request via the "Authorization" header. + * - "cookiesFrom" should hold a response object from a previous HTTP + * interaction. All cookies returned from the server in that response + * (via the "Set-Cookie" header) will be copied to the cookie params of + * the new request. + * @return ServerRequestInterface + */ + protected function request(string $method, string $path, array $options = []): ServerRequestInterface + { + $request = new ServerRequest([], [], $path, $method); + + // Do we want a JSON request body? + if (isset($options['json'])) { + $request = $this->requestWithJsonBody( + $request, + $options['json'] + ); + } + + // Authenticate as a given user + if (isset($options['authenticatedAs'])) { + $request = $this->requestAsUser( + $request, + $options['authenticatedAs'] + ); + } + + // Let's copy the cookies from a previous response + if (isset($options['cookiesFrom'])) { + $request = $this->requestWithCookiesFrom( + $request, + $options['cookiesFrom'] + ); + } + + return $request; + } +} diff --git a/php-packages/testing/src/integration/UsesTmpDir.php b/php-packages/testing/src/integration/UsesTmpDir.php new file mode 100644 index 000000000..23f2eb279 --- /dev/null +++ b/php-packages/testing/src/integration/UsesTmpDir.php @@ -0,0 +1,44 @@ + '{}' + ]; + + $tmpDir = $this->tmpDir(); + + foreach ($DIRS_NEEDED as $path) { + $fullPath = $tmpDir.$path; + if (!file_exists($fullPath)) { + mkdir($fullPath); + } + } + + foreach ($FILES_NEEDED as $path => $contents) { + $fullPath = $tmpDir.$path; + if (!file_exists($fullPath)) { + file_put_contents($fullPath, $contents); + } + } + } +} diff --git a/php-packages/testing/src/unit/TestCase.php b/php-packages/testing/src/unit/TestCase.php new file mode 100644 index 000000000..ae04b4cac --- /dev/null +++ b/php-packages/testing/src/unit/TestCase.php @@ -0,0 +1,18 @@ +serializeToForum('notARealSetting', 'not.a.real.setting'), + (new Extend\Frontend('forum'))->route('/added-by-extension', 'added-by-extension') +]; diff --git a/php-packages/testing/tests/migrations/2021_05_11_000000_create_table_so_we_can_check_existence.php b/php-packages/testing/tests/migrations/2021_05_11_000000_create_table_so_we_can_check_existence.php new file mode 100644 index 000000000..649dff1b9 --- /dev/null +++ b/php-packages/testing/tests/migrations/2021_05_11_000000_create_table_so_we_can_check_existence.php @@ -0,0 +1,10 @@ +string('id', 100)->primary(); +}); \ No newline at end of file diff --git a/php-packages/testing/tests/tests/.phpunit.result.cache b/php-packages/testing/tests/tests/.phpunit.result.cache new file mode 100644 index 000000000..05fbec964 --- /dev/null +++ b/php-packages/testing/tests/tests/.phpunit.result.cache @@ -0,0 +1 @@ +C:37:"PHPUnit\Runner\DefaultTestResultCache":219:{a:2:{s:7:"defects";a:1:{s:74:"Flarum\Testing\Tests\integration\TestCaseTest::can_add_settings_via_method";i:4;}s:5:"times";a:1:{s:74:"Flarum\Testing\Tests\integration\TestCaseTest::can_add_settings_via_method";d:0.09;}}} \ No newline at end of file diff --git a/php-packages/testing/tests/tests/fixtures/.gitkeep b/php-packages/testing/tests/tests/fixtures/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/php-packages/testing/tests/tests/integration/TestCaseTest.php b/php-packages/testing/tests/tests/integration/TestCaseTest.php new file mode 100644 index 000000000..2b7c3814e --- /dev/null +++ b/php-packages/testing/tests/tests/integration/TestCaseTest.php @@ -0,0 +1,211 @@ +app(); + + $this->assertEquals(1, User::query()->count()); + + $user = User::find(1); + + $this->assertEquals('admin', $user->username); + $this->assertEquals('admin@machine.local', $user->email); + $this->assertTrue($user->isAdmin()); + } + + /** + * @test + */ + public function can_add_settings_via_method() + { + $this->setting('hello', 'world'); + $this->setting('display_name_driver', 'something_other_than_username'); + + $settings = $this->app()->getContainer()->make(SettingsRepositoryInterface::class); + + $this->assertEquals('world', $settings->get('hello')); + $this->assertEquals('something_other_than_username', $settings->get('display_name_driver')); + } + + /** + * @test + */ + public function settings_cleaned_up_from_previous_method() + { + $settings = $this->app()->getContainer()->make(SettingsRepositoryInterface::class); + + $this->assertEquals(null, $settings->get('hello')); + $this->assertEquals(null, $settings->get('display_name_driver')); + } + + /** + * @test + */ + public function can_add_config_via_method() + { + $this->config('hello', 'world'); + $this->config('url', 'https://flarum.org'); + $this->config('level1.level2', 'value'); + + $config = $this->app()->getContainer()->make(Config::class); + + $this->assertEquals('world', $config['hello']); + $this->assertEquals('https://flarum.org', $config['url']); + $this->assertEquals('value', $config['level1']['level2']); + } + + /** + * @test + */ + public function config_cleaned_up_from_previous_method() + { + $config = $this->app()->getContainer()->make(Config::class); + + $this->assertEquals(null, $config['hello']); + $this->assertEquals('http://localhost', $config['url']); + $this->assertFalse(isset($config['level1']['level2'])); + } + + /** + * @test + */ + public function current_extension_not_applied_by_default() + { + $response = $this->send( + $this->request('GET', '/') + ); + + $this->assertStringNotContainsString('notARealSetting', $response->getBody()->getContents()); + } + + /** + * @test + */ + public function current_extension_applied_if_specified() + { + $this->extension('flarum-testing-tests'); + + $response = $this->send( + $this->request('GET', '/') + ); + + $this->assertStringContainsString('notARealSetting', $response->getBody()->getContents()); + } + + /** + * @test + */ + public function current_extension_migrations_applied_if_specified() + { + $this->extension('flarum-testing-tests'); + + $tableExists = $this->app()->getContainer()->make(Builder::class)->hasTable('testing_table'); + $this->assertTrue($tableExists); + } + + /** + * @test + */ + public function current_extension_considered_enabled_after_boot() + { + $this->extension('flarum-testing-tests'); + + $enabled = $this->app()->getContainer()->make('flarum.extensions')->isEnabled('flarum-testing-tests'); + $this->assertTrue($enabled); + } + + /** + * @test + */ + public function can_apply_extenders() + { + $this->extend( + (new Extend\Settings)->serializeToForum('notARealSetting', 'not.a.real.setting') + ); + + $response = $this->send( + $this->request('GET', '/') + ); + + $this->assertStringContainsString('notARealSetting', $response->getBody()->getContents()); + } + + /** + * @test + */ + public function can_apply_route_extenders() + { + $this->extend( + (new Extend\Frontend('forum'))->route('/arbitrary', 'arbitrary') + ); + + $response = $this->send( + $this->request('GET', '/arbitrary') + ); + + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @test + */ + public function routes_added_by_current_extension_not_accessible_by_default() + { + $response = $this->send( + $this->request('GET', '/added-by-extension') + ); + + $this->assertEquals(404, $response->getStatusCode()); + } + + /** + * @test + */ + public function routes_added_by_current_extension_accessible() + { + $this->extension('flarum-testing-tests'); + + $response = $this->send( + $this->request('GET', '/added-by-extension') + ); + + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @test + */ + public function extension_url_correct() + { + $this->extension('flarum-testing-tests'); + $expected = $this->app()->getContainer()->make('filesystem')->disk('flarum-assets')->url('/flarum-testing-tests/'); + // We need to test this since we override it. + $extensions = $this->app()->getContainer()->make('flarum.extensions'); + $currExtension = $extensions->getExtension('flarum-testing-tests'); + $baseAssetsUrl = $extensions->getAsset($currExtension, ''); + + $this->assertEquals($expected, $baseAssetsUrl); + } +} \ No newline at end of file diff --git a/php-packages/testing/tests/tests/integration/setup.php b/php-packages/testing/tests/tests/integration/setup.php new file mode 100644 index 000000000..67039c083 --- /dev/null +++ b/php-packages/testing/tests/tests/integration/setup.php @@ -0,0 +1,16 @@ +run(); diff --git a/php-packages/testing/tests/tests/phpunit.integration.xml b/php-packages/testing/tests/tests/phpunit.integration.xml new file mode 100644 index 000000000..23afc237d --- /dev/null +++ b/php-packages/testing/tests/tests/phpunit.integration.xml @@ -0,0 +1,24 @@ + + + + + ../src/ + + + + + ./integration + + + diff --git a/php-packages/testing/tests/tests/phpunit.unit.xml b/php-packages/testing/tests/tests/phpunit.unit.xml new file mode 100644 index 000000000..d3a4a3e3d --- /dev/null +++ b/php-packages/testing/tests/tests/phpunit.unit.xml @@ -0,0 +1,27 @@ + + + + + ../src/ + + + + + ./unit + + + + + + diff --git a/php-packages/testing/tests/tests/unit/.gitkeep b/php-packages/testing/tests/tests/unit/.gitkeep new file mode 100644 index 000000000..e69de29bb