184e2cf4eSGreg Roach<?php 284e2cf4eSGreg Roach/** 384e2cf4eSGreg Roach * webtrees: online genealogy 48fcd0d32SGreg Roach * Copyright (C) 2019 webtrees development team 584e2cf4eSGreg Roach * This program is free software: you can redistribute it and/or modify 684e2cf4eSGreg Roach * it under the terms of the GNU General Public License as published by 784e2cf4eSGreg Roach * the Free Software Foundation, either version 3 of the License, or 884e2cf4eSGreg Roach * (at your option) any later version. 984e2cf4eSGreg Roach * This program is distributed in the hope that it will be useful, 1084e2cf4eSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 1184e2cf4eSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1284e2cf4eSGreg Roach * GNU General Public License for more details. 1384e2cf4eSGreg Roach * You should have received a copy of the GNU General Public License 1484e2cf4eSGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 1584e2cf4eSGreg Roach */ 16e7f56f2aSGreg Roachdeclare(strict_types=1); 17e7f56f2aSGreg Roach 1884e2cf4eSGreg Roachnamespace Fisharebest\Webtrees; 1984e2cf4eSGreg Roach 206ccdf4f0SGreg Roachuse Fig\Http\Message\StatusCodeInterface; 21bd1e4e13SGreg Roachuse Fisharebest\Localization\Locale\LocaleEnUs; 22bd1e4e13SGreg Roachuse Fisharebest\Localization\Locale\LocaleInterface; 23e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 24126654d7SGreg Roachuse Fisharebest\Webtrees\Http\Controllers\GedcomFileController; 256ccdf4f0SGreg Roachuse Fisharebest\Webtrees\Http\Request; 268136679eSGreg Roachuse Fisharebest\Webtrees\Module\ModuleThemeInterface; 278136679eSGreg Roachuse Fisharebest\Webtrees\Module\WebtreesTheme; 288c3e1068SGreg Roachuse Fisharebest\Webtrees\Services\MigrationService; 29126654d7SGreg Roachuse Fisharebest\Webtrees\Services\TimeoutService; 30e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Services\UserService; 318b67c11aSGreg Roachuse Illuminate\Cache\ArrayStore; 328b67c11aSGreg Roachuse Illuminate\Cache\Repository; 330115bc16SGreg Roachuse Illuminate\Database\Capsule\Manager as DB; 34e16a1bfdSGreg Roachuse Illuminate\Database\Query\Builder; 357def76c7SGreg Roachuse League\Flysystem\Filesystem; 367def76c7SGreg Roachuse League\Flysystem\Memory\MemoryAdapter; 376ccdf4f0SGreg Roachuse Nyholm\Psr7\Factory\Psr17Factory; 386ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseFactoryInterface; 396ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestFactoryInterface; 406ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface; 416ccdf4f0SGreg Roachuse Psr\Http\Message\StreamFactoryInterface; 426ccdf4f0SGreg Roachuse Psr\Http\Message\UploadedFileFactoryInterface; 436ccdf4f0SGreg Roachuse Psr\Http\Message\UploadedFileInterface; 446ccdf4f0SGreg Roachuse Psr\Http\Message\UriFactoryInterface; 456ccdf4f0SGreg Roachuse function app; 467def76c7SGreg Roachuse function basename; 476ccdf4f0SGreg Roachuse function define; 486ccdf4f0SGreg Roachuse function defined; 496ccdf4f0SGreg Roachuse function filesize; 506ccdf4f0SGreg Roachuse function http_build_query; 516ccdf4f0SGreg Roachuse const UPLOAD_ERR_OK; 520115bc16SGreg Roach 5384e2cf4eSGreg Roach/** 5484e2cf4eSGreg Roach * Base class for unit tests 5584e2cf4eSGreg Roach */ 566ccdf4f0SGreg Roachclass TestCase extends \PHPUnit\Framework\TestCase implements StatusCodeInterface 5784e2cf4eSGreg Roach{ 5874d6dc0eSGreg Roach /** @var bool */ 59061b43d7SGreg Roach protected static $uses_database = false; 60061b43d7SGreg Roach 6174d6dc0eSGreg Roach /** @var object */ 6274d6dc0eSGreg Roach public static $mock_functions; 6374d6dc0eSGreg Roach 64061b43d7SGreg Roach /** 65061b43d7SGreg Roach * Things to run once, before all the tests. 66061b43d7SGreg Roach */ 67061b43d7SGreg Roach public static function setUpBeforeClass() 68061b43d7SGreg Roach { 69061b43d7SGreg Roach parent::setUpBeforeClass(); 70061b43d7SGreg Roach 716ccdf4f0SGreg Roach // Use nyholm as our PSR7 factory 726ccdf4f0SGreg Roach app()->bind(ResponseFactoryInterface::class, Psr17Factory::class); 736ccdf4f0SGreg Roach app()->bind(ServerRequestFactoryInterface::class, Psr17Factory::class); 746ccdf4f0SGreg Roach app()->bind(StreamFactoryInterface::class, Psr17Factory::class); 756ccdf4f0SGreg Roach app()->bind(UploadedFileFactoryInterface::class, Psr17Factory::class); 766ccdf4f0SGreg Roach app()->bind(UriFactoryInterface::class, Psr17Factory::class); 776ccdf4f0SGreg Roach 786ccdf4f0SGreg Roach // Use an array cache for database calls, etc. 796ccdf4f0SGreg Roach app()->instance('cache.array', new Repository(new ArrayStore())); 806ccdf4f0SGreg Roach 816ccdf4f0SGreg Roach app()->bind(Tree::class, static function () { 826ccdf4f0SGreg Roach return null; 836ccdf4f0SGreg Roach }); 846ccdf4f0SGreg Roach 856ccdf4f0SGreg Roach app()->instance(UserService::class, new UserService()); 866ccdf4f0SGreg Roach app()->instance(UserInterface::class, new GuestUser()); 876ccdf4f0SGreg Roach 886ccdf4f0SGreg Roach app()->instance(ServerRequestInterface::class, Request::create('http://localhost/index.php')); 896ccdf4f0SGreg Roach app()->instance(Filesystem::class, new Filesystem(new MemoryAdapter())); 906ccdf4f0SGreg Roach 916ccdf4f0SGreg Roach app()->bind(ModuleThemeInterface::class, WebtreesTheme::class); 926ccdf4f0SGreg Roach app()->bind(LocaleInterface::class, LocaleEnUs::class); 936ccdf4f0SGreg Roach 946ccdf4f0SGreg Roach defined('WT_BASE_URL') || define('WT_BASE_URL', 'http://localhost/'); 95f397d0fdSGreg Roach defined('WT_DATA_DIR') || define('WT_DATA_DIR', Webtrees::ROOT_DIR . 'data/'); 966ccdf4f0SGreg Roach defined('WT_LOCALE') || define('WT_LOCALE', I18N::init('en-US', null, true)); 976ccdf4f0SGreg Roach 98061b43d7SGreg Roach if (static::$uses_database) { 99061b43d7SGreg Roach static::createTestDatabase(); 100061b43d7SGreg Roach } 101061b43d7SGreg Roach } 102061b43d7SGreg Roach 103061b43d7SGreg Roach /** 1046ccdf4f0SGreg Roach * Create an SQLite in-memory database for testing 1056ccdf4f0SGreg Roach */ 1066ccdf4f0SGreg Roach protected static function createTestDatabase(): void 1076ccdf4f0SGreg Roach { 1086ccdf4f0SGreg Roach $capsule = new DB(); 1096ccdf4f0SGreg Roach $capsule->addConnection([ 1106ccdf4f0SGreg Roach 'driver' => 'sqlite', 1116ccdf4f0SGreg Roach 'database' => ':memory:', 1126ccdf4f0SGreg Roach ]); 1136ccdf4f0SGreg Roach $capsule->setAsGlobal(); 114e16a1bfdSGreg Roach 115e16a1bfdSGreg Roach Builder::macro('whereContains', function ($column, string $search, string $boolean = 'and'): Builder { 116e16a1bfdSGreg Roach $search = strtr($search, ['\\' => '\\\\', '%' => '\\%', '_' => '\\_', ' ' => '%']); 117e16a1bfdSGreg Roach 118e16a1bfdSGreg Roach return $this->where($column, 'LIKE', '%' . $search . '%', $boolean); 119e16a1bfdSGreg Roach }); 1206ccdf4f0SGreg Roach 1216ccdf4f0SGreg Roach // Migrations create logs, which requires an IP address, which requires a request 1226ccdf4f0SGreg Roach self::createRequest(); 1236ccdf4f0SGreg Roach 1246ccdf4f0SGreg Roach // Create tables 1256ccdf4f0SGreg Roach $migration_service = new MigrationService; 1266ccdf4f0SGreg Roach $migration_service->updateSchema('\Fisharebest\Webtrees\Schema', 'WT_SCHEMA_VERSION', Webtrees::SCHEMA_VERSION); 1276ccdf4f0SGreg Roach 1286ccdf4f0SGreg Roach // Create config data 1296ccdf4f0SGreg Roach $migration_service->seedDatabase(); 1306ccdf4f0SGreg Roach } 1316ccdf4f0SGreg Roach 1326ccdf4f0SGreg Roach /** 133061b43d7SGreg Roach * Things to run once, AFTER all the tests. 134061b43d7SGreg Roach */ 135061b43d7SGreg Roach public static function tearDownAfterClass() 136061b43d7SGreg Roach { 137061b43d7SGreg Roach if (static::$uses_database) { 138061b43d7SGreg Roach $pdo = DB::connection()->getPdo(); 13932f20c14SGreg Roach unset($pdo); 140061b43d7SGreg Roach } 141061b43d7SGreg Roach 142061b43d7SGreg Roach parent::tearDownAfterClass(); 143061b43d7SGreg Roach } 144061b43d7SGreg Roach 145061b43d7SGreg Roach /** 146061b43d7SGreg Roach * Things to run before every test. 147061b43d7SGreg Roach */ 148*5c48bcd6SGreg Roach protected function setUp(): void 1490115bc16SGreg Roach { 1500115bc16SGreg Roach parent::setUp(); 1510115bc16SGreg Roach 152061b43d7SGreg Roach if (static::$uses_database) { 153061b43d7SGreg Roach DB::connection()->beginTransaction(); 154061b43d7SGreg Roach } 155061b43d7SGreg Roach } 156061b43d7SGreg Roach 157061b43d7SGreg Roach /** 158061b43d7SGreg Roach * Things to run after every test 159061b43d7SGreg Roach */ 160a49feabaSGreg Roach protected function tearDown() 161a49feabaSGreg Roach { 16232f20c14SGreg Roach if (static::$uses_database) { 163061b43d7SGreg Roach DB::connection()->rollBack(); 164061b43d7SGreg Roach } 16532f20c14SGreg Roach 1668b67c11aSGreg Roach app('cache.array')->flush(); 1678b67c11aSGreg Roach 16832f20c14SGreg Roach Site::$preferences = []; 16932f20c14SGreg Roach Tree::$trees = []; 170bec87e94SGreg Roach GedcomRecord::$gedcom_record_cache = null; 171bec87e94SGreg Roach GedcomRecord::$pending_record_cache = null; 17232f20c14SGreg Roach 17332f20c14SGreg Roach Auth::logout(); 1740115bc16SGreg Roach } 1750115bc16SGreg Roach 1760115bc16SGreg Roach /** 1770115bc16SGreg Roach * Import a GEDCOM file into the test database. 1780115bc16SGreg Roach * 1790115bc16SGreg Roach * @param string $gedcom_file 180061b43d7SGreg Roach * 181061b43d7SGreg Roach * @return Tree 1820115bc16SGreg Roach */ 183061b43d7SGreg Roach protected function importTree(string $gedcom_file): Tree 1840115bc16SGreg Roach { 185061b43d7SGreg Roach $tree = Tree::create(basename($gedcom_file), basename($gedcom_file)); 1866ccdf4f0SGreg Roach 1876ccdf4f0SGreg Roach $stream = app(StreamFactoryInterface::class)->createStreamFromFile(__DIR__ . '/data/' . $gedcom_file); 1886ccdf4f0SGreg Roach $tree->importGedcomFile($stream, $gedcom_file); 1890115bc16SGreg Roach 1901ad2dde6SGreg Roach View::share('tree', $tree); 191126654d7SGreg Roach $gedcom_file_controller = new GedcomFileController(); 192126654d7SGreg Roach 193126654d7SGreg Roach do { 1941ad2dde6SGreg Roach $gedcom_file_controller->import(new TimeoutService(microtime(true)), $tree); 195126654d7SGreg Roach 196126654d7SGreg Roach $imported = $tree->getPreference('imported'); 197126654d7SGreg Roach } while (!$imported); 198061b43d7SGreg Roach 199061b43d7SGreg Roach return $tree; 2000115bc16SGreg Roach } 2016ccdf4f0SGreg Roach 2026ccdf4f0SGreg Roach /** 2036ccdf4f0SGreg Roach * Create a request and bind it into the container. 2046ccdf4f0SGreg Roach * 2056ccdf4f0SGreg Roach * @param string $method 2066ccdf4f0SGreg Roach * @param string[] $query 2076ccdf4f0SGreg Roach * @param string[] $params 2086ccdf4f0SGreg Roach * @param UploadedFileInterface[] $files 2096ccdf4f0SGreg Roach * 2106ccdf4f0SGreg Roach * @return ServerRequestInterface 2116ccdf4f0SGreg Roach */ 2126ccdf4f0SGreg Roach protected static function createRequest(string $method = 'GET', array $query = [], array $params = [], array $files = []): ServerRequestInterface 2136ccdf4f0SGreg Roach { 2146ccdf4f0SGreg Roach /** @var ServerRequestFactoryInterface */ 2156ccdf4f0SGreg Roach $server_request_factory = app(ServerRequestFactoryInterface::class); 2166ccdf4f0SGreg Roach 2176ccdf4f0SGreg Roach $uri = 'http://localhost/index.php?' . http_build_query($query); 2186ccdf4f0SGreg Roach 2196ccdf4f0SGreg Roach /** @var ServerRequestInterface $request */ 2206ccdf4f0SGreg Roach $request = $server_request_factory 2216ccdf4f0SGreg Roach ->createServerRequest($method, $uri) 2226ccdf4f0SGreg Roach ->withQueryParams($query) 2236ccdf4f0SGreg Roach ->withParsedBody($params) 2246ccdf4f0SGreg Roach ->withUploadedFiles($files); 2256ccdf4f0SGreg Roach 2266ccdf4f0SGreg Roach app()->instance(ServerRequestInterface::class, $request); 2276ccdf4f0SGreg Roach 2286ccdf4f0SGreg Roach return $request; 2296ccdf4f0SGreg Roach } 2306ccdf4f0SGreg Roach 2316ccdf4f0SGreg Roach /** 2326ccdf4f0SGreg Roach * Create an uploaded file for a request. 2336ccdf4f0SGreg Roach * 2346ccdf4f0SGreg Roach * @param string $filename 2356ccdf4f0SGreg Roach * @param string $mime_type 2366ccdf4f0SGreg Roach * 2376ccdf4f0SGreg Roach * @return UploadedFileInterface 2386ccdf4f0SGreg Roach */ 2396ccdf4f0SGreg Roach protected function createUploadedFile(string $filename, string $mime_type): UploadedFileInterface 2406ccdf4f0SGreg Roach { 2416ccdf4f0SGreg Roach /** @var StreamFactoryInterface */ 2426ccdf4f0SGreg Roach $stream_factory = app(StreamFactoryInterface::class); 2436ccdf4f0SGreg Roach 2446ccdf4f0SGreg Roach /** @var UploadedFileFactoryInterface */ 2456ccdf4f0SGreg Roach $uploaded_file_factory = app(UploadedFileFactoryInterface::class); 2466ccdf4f0SGreg Roach 2476ccdf4f0SGreg Roach $stream = $stream_factory->createStreamFromFile($filename); 2486ccdf4f0SGreg Roach 2496ccdf4f0SGreg Roach $size = filesize($filename); 2506ccdf4f0SGreg Roach 2516ccdf4f0SGreg Roach $status = UPLOAD_ERR_OK; 2526ccdf4f0SGreg Roach 2536ccdf4f0SGreg Roach $client_name = basename($filename); 2546ccdf4f0SGreg Roach 2556ccdf4f0SGreg Roach return $uploaded_file_factory->createUploadedFile($stream, $size, $status, $client_name, $mime_type); 2566ccdf4f0SGreg Roach } 25784e2cf4eSGreg Roach} 258