Merge remote-tracking branch 'php_packages_testing/REWRITE'

This commit is contained in:
Alexander Skvortsov 2022-03-11 18:01:06 -05:00
commit 108d130354
30 changed files with 1401 additions and 0 deletions

View File

@ -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

15
php-packages/testing/.gitattributes vendored Normal file
View File

@ -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

View File

@ -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

10
php-packages/testing/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
/vendor
composer.lock
composer.phar
node_modules
.DS_Store
Thumbs.db
/src/integration/tmp
.vagrant
.idea/*
.vscode

View File

@ -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"

21
php-packages/testing/LICENSE Executable file
View File

@ -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.

View File

@ -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"
}
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Testing\integration;
use Carbon\Carbon;
use Dflydev\FigCookies\SetCookie;
use Illuminate\Support\Str;
use Laminas\Diactoros\CallbackStream;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* A collection of helpers for building PSR-7 requests for integration tests.
*/
trait BuildsHttpRequests
{
protected function requestWithJsonBody(Request $req, array $json): Request
{
return $req
->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);
}
}

View File

@ -0,0 +1,44 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Testing\integration;
use Flarum\Foundation\Application;
use Symfony\Component\Console\Application as ConsoleApplication;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
abstract class ConsoleTestCase extends TestCase
{
protected $console;
protected function console()
{
if (is_null($this->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());
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace Flarum\Testing\integration\Extend;
use Flarum\Extend\ExtenderInterface;
use Flarum\Extension\Extension;
use Illuminate\Contracts\Container\Container;
use Illuminate\Database\ConnectionInterface;
class BeginTransactionAndSetDatabase implements ExtenderInterface
{
/**
* A callback to set the database connection object on the test case.
*/
protected $setDbOnTestCase;
public function __construct(callable $setDbOnTestCase)
{
$this->setDbOnTestCase = $setDbOnTestCase;
}
public function extend(Container $container, Extension $extension = null)
{
$db = $container->make(ConnectionInterface::class);
$db->beginTransaction();
($this->setDbOnTestCase)($db);
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Flarum\Testing\integration\Extend;
use Flarum\Extend\ExtenderInterface;
use Flarum\Extension\Extension;
use Flarum\Extension\ExtensionManager;
use Flarum\Testing\integration\Extension\ExtensionManagerIncludeCurrent;
use Illuminate\Contracts\Container\Container;
class OverrideExtensionManagerForTests implements ExtenderInterface
{
/**
* IDs of extensions to boot
*/
protected $extensions;
public function __construct($extensions)
{
$this->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);
}
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Flarum\Testing\integration\Extend;
use Flarum\Extend\ExtenderInterface;
use Flarum\Extension\Extension;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Container\Container;
class SetSettingsBeforeBoot implements ExtenderInterface
{
/**
* IDs of extensions to boot
*/
protected $settings;
public function __construct($settings)
{
$this->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);
}
}
}
}

View File

@ -0,0 +1,113 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Testing\integration\Extension;
use Flarum\Database\Migrator;
use Flarum\Extension\Extension;
use Flarum\Extension\ExtensionManager;
use Flarum\Foundation\Paths;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Filesystem\Cloud;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Arr;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem as FlysystemFilesystem;
class ExtensionManagerIncludeCurrent extends ExtensionManager
{
/**
* @var array
*/
protected $enabledIds;
/**
* @var bool
*/
public $booted = false;
public function __construct(
SettingsRepositoryInterface $config,
Paths $paths,
Container $container,
Migrator $migrator,
Dispatcher $dispatcher,
Filesystem $filesystem,
array $enabledIds
) {
parent::__construct($config, $paths, $container, $migrator, $dispatcher, $filesystem);
$this->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']));
}
}

View File

@ -0,0 +1,24 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Testing\integration;
trait RetrievesAuthorizedUsers
{
protected function normalUser(): array
{
return [
'id' => 2,
'username' => 'normal',
'password' => '$2y$10$LO59tiT7uggl6Oe23o/O6.utnF6ipngYjvMvaxo1TciKqBttDNKim', // BCrypt hash for "too-obscure"
'email' => 'normal@machine.local',
'is_email_confirmed' => 1,
];
}
}

View File

@ -0,0 +1,149 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Testing\integration\Setup;
use Flarum\Foundation\Paths;
use Flarum\Install\AdminUser;
use Flarum\Install\BaseUrl;
use Flarum\Install\DatabaseConfig;
use Flarum\Install\Installation;
use Flarum\Install\Steps\ConnectToDatabase;
use Flarum\Testing\integration\UsesTmpDir;
class SetupScript
{
use UsesTmpDir;
/**
* Test database host.
*
* @var string
*/
protected $host;
/**
* Test database port.
*
* @var int
*/
protected $port;
/**
* Test database name.
*
* @var string
*/
protected $name;
/**
* Test database username.
*
* @var string
*/
protected $user;
/**
* Test database password.
*
* @var string
*/
protected $pass;
/**
* Test database prefix.
*
* @var string
*/
protected $pref;
/**
* @var DatabaseConfig
*/
private $dbConfig;
public function __construct()
{
$this->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();
}
}

View File

@ -0,0 +1,295 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Testing\integration;
use Flarum\Extend\ExtenderInterface;
use Flarum\Foundation\Config;
use Flarum\Foundation\InstalledSite;
use Flarum\Foundation\Paths;
use Flarum\Testing\integration\Extend\BeginTransactionAndSetDatabase;
use Flarum\Testing\integration\Extend\OverrideExtensionManagerForTests;
use Flarum\Testing\integration\Extend\SetSettingsBeforeBoot;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Arr;
use Laminas\Diactoros\ServerRequest;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
abstract class TestCase extends \PHPUnit\Framework\TestCase
{
use BuildsHttpRequests;
use UsesTmpDir;
/**
* @inheritDoc
*/
protected function tearDown(): void
{
parent::tearDown();
$this->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;
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Flarum\Testing\integration;
trait UsesTmpDir
{
public function tmpDir() {
return getenv('FLARUM_TEST_TMP_DIR_LOCAL') ?: getenv('FLARUM_TEST_TMP_DIR') ?: __DIR__.'/tmp';
}
public function setupTmpDir() {
$DIRS_NEEDED = [
'/',
'/public',
'/public/assets',
'/storage',
'/storage/formatter',
'/storage/sessions',
'/storage/views',
'/vendor',
'/vendor/composer'
];
$FILES_NEEDED = [
'/vendor/composer/installed.json' => '{}'
];
$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);
}
}
}
}

View File

@ -0,0 +1,18 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Testing\unit;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
abstract class TestCase extends \PHPUnit\Framework\TestCase
{
// Ensure Mockery is always torn down automatically after each test.
use MockeryPHPUnitIntegration;
}

1
php-packages/testing/tests/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vendor/*

View File

@ -0,0 +1 @@
A minimal extension skeleton to test the flarum/testing package.

View File

@ -0,0 +1,35 @@
{
"name": "flarum/testing-tests",
"description": "Minimal extension to test the flarum/testing package",
"type": "flarum-extension",
"require": {
"flarum/core": "^0.1.0@dev"
},
"require-dev": {
"flarum/testing": "*@dev"
},
"autoload-dev": {
"psr-4": {
"Flarum\\Testing\\Tests\\": "tests/"
}
},
"scripts": {
"test": [
"@test:unit",
"@test:integration"
],
"test:unit": "phpunit -c tests/phpunit.unit.xml",
"test:integration": "phpunit -c tests/phpunit.integration.xml",
"test:setup": "@php tests/integration/setup.php"
},
"scripts-descriptions": {
"test": "Runs all tests.",
"test:unit": "Runs all unit tests.",
"test:integration": "Runs all integration tests.",
"test:setup": "Sets up a database for use with integration tests. Execute this only once."
},
"repositories": [{
"type": "path",
"url": "../"
}]
}

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of flarum/testing-tests.
*
* Copyright (c) 2021 .
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/
namespace Flarum\Testing;
use Flarum\Extend;
return [
(new Extend\Settings)->serializeToForum('notARealSetting', 'not.a.real.setting'),
(new Extend\Frontend('forum'))->route('/added-by-extension', 'added-by-extension')
];

View File

@ -0,0 +1,10 @@
<?php
namespace Flarum\Testing;
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
return Migration::createTable('testing_table', function (Blueprint $table) {
$table->string('id', 100)->primary();
});

View File

@ -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;}}}

View File

View File

@ -0,0 +1,211 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Testing\Tests\integration;
use Flarum\Extend;
use Flarum\Foundation\Config;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\Testing\integration\TestCase;
use Flarum\User\User;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
class TestCaseTest extends TestCase
{
/**
* @test
*/
public function admin_user_created_as_part_of_default_state()
{
$this->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);
}
}

View File

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
use Flarum\Testing\integration\Setup\SetupScript;
require __DIR__.'/../../vendor/autoload.php';
$setup = new SetupScript();
$setup->run();

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="true"
stopOnFailure="false"
>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">../src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Flarum Integration Tests">
<directory suffix="Test.php">./integration</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">../src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Flarum Unit Tests">
<directory suffix="Test.php">./unit</directory>
</testsuite>
</testsuites>
<listeners>
<listener class="\Mockery\Adapter\Phpunit\TestListener" />
</listeners>
</phpunit>