Refactor setup

This commit is contained in:
Pig Fang 2019-09-06 23:53:47 +08:00
parent 5d1dce347f
commit 13a2cd9b18
24 changed files with 226 additions and 433 deletions

1
.gitignore vendored
View File

@ -20,4 +20,5 @@ storage/*.sqlite
.vscode .vscode
storage/oauth-public.key storage/oauth-public.key
storage/oauth-private.key storage/oauth-private.key
storage/install.lock
.phpunit.result.cache .phpunit.result.cache

View File

@ -4,6 +4,7 @@ namespace App\Console\Commands;
use App\Models\User; use App\Models\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
class BsInstallCommand extends Command class BsInstallCommand extends Command
{ {
@ -11,9 +12,9 @@ class BsInstallCommand extends Command
protected $description = 'Execute installation and create a super administrator.'; protected $description = 'Execute installation and create a super administrator.';
public function handle() public function handle(Filesystem $filesystem)
{ {
if (\App\Http\Controllers\SetupController::checkTablesExist()) { if ($filesystem->exists(storage_path('install.lock'))) {
$this->info('You have installed Blessing Skin Server. Nothing to do.'); $this->info('You have installed Blessing Skin Server. Nothing to do.');
return; return;
@ -44,6 +45,8 @@ class BsInstallCommand extends Command
$admin->verified = true; $admin->verified = true;
$admin->save(); $admin->save();
$filesystem->put(storage_path('install.lock'), '');
$this->info('Installation completed!'); $this->info('Installation completed!');
$this->info('We recommend to modify your "Site URL" option if incorrect.'); $this->info('We recommend to modify your "Site URL" option if incorrect.');
} }

View File

@ -6,23 +6,22 @@ use DB;
use Log; use Log;
use File; use File;
use Option; use Option;
use Schema;
use Artisan; use Artisan;
use Storage;
use App\Models\User; use App\Models\User;
use Illuminate\Support\Str;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Composer\Semver\Comparator; use Composer\Semver\Comparator;
use Illuminate\Filesystem\Filesystem;
use App\Exceptions\PrettyPageException; use App\Exceptions\PrettyPageException;
use Symfony\Component\Finder\SplFileInfo;
class SetupController extends Controller class SetupController extends Controller
{ {
public function welcome() public function welcome(Filesystem $filesystem)
{ {
// @codeCoverageIgnoreStart if (! $filesystem->exists(base_path('.env'))) {
if (! File::exists(base_path('.env'))) { $filesystem->copy(base_path('.env.example'), base_path('.env'));
File::copy(base_path('.env.example'), base_path('.env'));
} }
// @codeCoverageIgnoreEnd
return view('setup.wizard.welcome'); return view('setup.wizard.welcome');
} }
@ -104,31 +103,7 @@ class SetupController extends Controller
return redirect('setup/info'); return redirect('setup/info');
} }
public function info() public function finish(Request $request, Filesystem $filesystem)
{
$existingTables = static::checkTablesExist([], true);
// Not installed completely
if (count($existingTables) > 0) {
Log::info('Remaining tables detected, exit setup wizard now', [$existingTables]);
$existingTables = array_map(function ($item) {
return get_db_config()['prefix'].$item;
}, $existingTables);
throw new PrettyPageException(trans('setup.database.table-already-exists', ['tables' => json_encode($existingTables)]), 1);
}
// @codeCoverageIgnoreStart
if (! function_exists('escapeshellarg')) {
throw new PrettyPageException(trans('setup.disabled-functions.escapeshellarg'), 1);
}
// @codeCoverageIgnoreEnd
return view('setup.wizard.info');
}
public function finish(Request $request)
{ {
$data = $this->validate($request, [ $data = $this->validate($request, [
'email' => 'required|email', 'email' => 'required|email',
@ -158,7 +133,7 @@ class SetupController extends Controller
$siteUrl = url('/'); $siteUrl = url('/');
if (ends_with($siteUrl, '/index.php')) { if (Str::endsWith($siteUrl, '/index.php')) {
$siteUrl = substr($siteUrl, 0, -10); // @codeCoverageIgnore $siteUrl = substr($siteUrl, 0, -10); // @codeCoverageIgnore
} }
@ -179,7 +154,7 @@ class SetupController extends Controller
$user->save(); $user->save();
$this->createDirectories(); $filesystem->put(storage_path('install.lock'), '');
return view('setup.wizard.finish')->with([ return view('setup.wizard.finish')->with([
'email' => $request->input('email'), 'email' => $request->input('email'),
@ -187,102 +162,22 @@ class SetupController extends Controller
]); ]);
} }
public function update() public function update(Filesystem $filesystem)
{ {
if (Comparator::lessThanOrEqualTo(config('app.version'), option('version'))) { collect($filesystem->files(database_path('update_scripts')))
// No updates available ->filter(function (SplFileInfo $file) {
return view('setup.locked'); $name = $file->getFilenameWithoutExtension();
} return preg_match('/^\d+\.\d+\.\d+$/', $name) > 0
&& Comparator::greaterThanOrEqualTo($name, option('version'));
return view('setup.updates.welcome'); })
} ->each(function (SplFileInfo $file) use ($filesystem) {
$filesystem->getRequire($file->getPathname());
public function doUpdate() });
{
$resource = opendir(database_path('update_scripts'));
$updateScriptExist = false;
while ($filename = @readdir($resource)) {
if ($filename != '.' && $filename != '..') {
preg_match('/update-(.*)-to-(.*).php/', $filename, $matches);
// Skip if the file is not valid or expired
if (! isset($matches[2]) ||
Comparator::lessThan($matches[2], config('app.version'))) {
continue;
}
$tips = require database_path('update_scripts')."/$filename";
$updateScriptExist = true;
}
}
closedir($resource);
foreach (config('options') as $key => $value) {
if (! Option::has($key)) {
Option::set($key, $value);
}
}
Option::set('version', config('app.version'));
option(['version' => config('app.version')]);
Artisan::call('view:clear'); Artisan::call('view:clear');
$filesystem->put(storage_path('install.lock'), '');
return view('setup.updates.success', ['tips' => $tips ?? []]); return view('setup.updates.success');
}
/**
* Check if the given tables exist in current database.
*
* @param array $tables
* @param bool $returnExisting
* @return bool|array
*/
public static function checkTablesExist($tables = [], $returnExistingTables = false)
{
$existingTables = [];
$tables = $tables ?: [
'users',
'user_closet',
'players',
'textures',
'options',
'reports',
];
foreach ($tables as $tableName) {
if (Schema::hasTable($tableName)) {
$existingTables[] = $tableName;
}
}
if (count($existingTables) == count($tables)) {
return $returnExistingTables ? $existingTables : true;
} else {
return $returnExistingTables ? $existingTables : false;
}
}
public static function checkDirectories()
{
$directories = ['storage/textures', 'plugins'];
try {
foreach ($directories as $dir) {
if (! Storage::disk('root')->has($dir)) {
if (! Storage::disk('root')->makeDirectory($dir)) {
return false; // @codeCoverageIgnore
}
}
}
return true;
} catch (\Exception $e) {
return false;
}
}
protected function createDirectories()
{
return self::checkDirectories();
} }
} }

View File

@ -31,8 +31,9 @@ class Kernel extends HttpKernel
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class, \Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\ForbiddenIE::class,
\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class,
\App\Http\Middleware\ForbiddenIE::class,
\App\Http\Middleware\RedirectToSetup::class,
], ],
'api' => [ 'api' => [

View File

@ -2,17 +2,15 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Filesystem\Filesystem;
use App\Http\Controllers\SetupController; use App\Http\Controllers\SetupController;
class CheckInstallation class CheckInstallation
{ {
public function handle($request, \Closure $next) public function handle($request, \Closure $next)
{ {
if (config('database.default') == 'dummy') { $hasLock = resolve(Filesystem::class)->exists(storage_path('install.lock'));
return $next($request); // @codeCoverageIgnore if ($hasLock && ! $request->is('setup/*update')) {
}
if (SetupController::checkTablesExist()) {
return response()->view('setup.locked'); return response()->view('setup.locked');
} }

View File

@ -1,16 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use App\Models\User;
class LockUpdatePage
{
public function handle($request, Closure $next)
{
abort_if($request->user()->permission < User::SUPER_ADMIN, 503);
return $next($request);
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Composer\Semver\Comparator;
use Illuminate\Filesystem\Filesystem;
class RedirectToSetup
{
public function handle($request, Closure $next)
{
$version = config('app.version');
if (! $request->is('setup*') && Comparator::greaterThan($version, option('version', $version))) {
$user = $request->user();
if ($user && $user->isAdmin()) {
return redirect('/setup/update');
} else {
abort(503);
}
}
$hasLock = resolve(Filesystem::class)->exists(storage_path('install.lock'));
if ($hasLock || $request->is('setup*')) {
return $next($request);
}
return redirect('/setup');
}
}

View File

@ -20,16 +20,6 @@ class RouteServiceProvider extends ServiceProvider
*/ */
protected $namespace = 'App\Http\Controllers'; protected $namespace = 'App\Http\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/** /**
* Define the routes for the application. * Define the routes for the application.
* *
@ -38,8 +28,6 @@ class RouteServiceProvider extends ServiceProvider
*/ */
public function map(Router $router) public function map(Router $router)
{ {
$this->mapSetupRoutes($router);
$this->mapStaticRoutes($router); $this->mapStaticRoutes($router);
$this->mapWebRoutes($router); $this->mapWebRoutes($router);
@ -74,22 +62,6 @@ class RouteServiceProvider extends ServiceProvider
}); });
} }
/**
* Define the "setup" routes for the application.
*
* The routes for setup wizard.
*
* @param \Illuminate\Routing\Router $router
* @return void
*/
protected function mapSetupRoutes(Router $router)
{
Route::prefix('setup')
->namespace($this->namespace)
->middleware('web')
->group(base_path('routes/setup.php'));
}
/** /**
* Define the "static" routes for the application. * Define the "static" routes for the application.
* *

View File

@ -1,43 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Http\Request;
use Composer\Semver\Comparator;
use Illuminate\Support\ServiceProvider;
use App\Http\Controllers\SetupController;
class RuntimeCheckServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot(Request $request)
{
// Skip the installation check when in setup or under CLI
if ($request->is('setup*') || $this->app->runningInConsole()) {
return;
}
$this->checkInstallation(); // @codeCoverageIgnore
}
/**
* @codeCoverageIgnore
*/
protected function checkInstallation()
{
// Redirect to setup wizard
if (config('database.default') == 'dummy' || ! SetupController::checkTablesExist()) {
return redirect('/setup')->send();
}
if (Comparator::greaterThan(config('app.version'), option('version'))) {
return redirect('/setup/update')->send();
}
return true;
}
}

View File

@ -170,7 +170,6 @@ return [
/* /*
* Application Service Providers... * Application Service Providers...
*/ */
App\Providers\RuntimeCheckServiceProvider::class,
App\Providers\AppServiceProvider::class, App\Providers\AppServiceProvider::class,
App\Providers\EventServiceProvider::class, App\Providers\EventServiceProvider::class,
App\Providers\PluginServiceProvider::class, App\Providers\PluginServiceProvider::class,

View File

@ -1,45 +0,0 @@
<?php
return [
'characters' => '2346789abcdefghjmnpqrtuxyzABCDEFGHJMNPQRTUXYZ',
'default' => [
'length' => 5,
'width' => 100,
'height' => 34,
'quality' => 90,
],
'flat' => [
'length' => 6,
'width' => 160,
'height' => 46,
'quality' => 90,
'lines' => 6,
'bgImage' => false,
'bgColor' => '#ecf2f4',
'fontColors'=> ['#2c3e50', '#c0392b', '#16a085', '#c0392b', '#8e44ad', '#303f9f', '#f57c00', '#795548'],
'contrast' => -5,
],
'mini' => [
'length' => 3,
'width' => 60,
'height' => 32,
],
'inverse' => [
'length' => 5,
'width' => 120,
'height' => 36,
'quality' => 90,
'sensitive' => true,
'angle' => 12,
'sharpen' => 10,
'blur' => 2,
'invert' => true,
'contrast' => -5,
],
];

View File

@ -15,7 +15,7 @@ disabled-functions:
locked: locked:
title: Already installed title: Already installed
text: It appears that you have already installed Blessing Skin Server. To reinstall, please delete the tables in your database first, or use a new table prefix. text: It appears that you have already installed Blessing Skin Server. To reinstall, please delete the "install.lock" file under "storage" directory.
button: Back to homepage button: Back to homepage
updates: updates:

View File

@ -14,7 +14,7 @@ disabled-functions:
locked: locked:
title: 已安装过 title: 已安装过
text: Blessing Skin Server 看起来已经安装妥当。如果想重新安装,请删除数据库中的旧数据表,或者换一个数据表前缀 text: Blessing Skin Server 看起来已经安装妥当。如果想重新安装,请删除 storage 目录下的 install.lock 文件
button: 返回首页 button: 返回首页
updates: updates:

View File

@ -14,6 +14,7 @@
- Tweaked policy of retrieve CA cert for GuzzleHttp. - Tweaked policy of retrieve CA cert for GuzzleHttp.
- Refactor account system. - Refactor account system.
- PHP version requirement is increased to 7.2.0. - PHP version requirement is increased to 7.2.0.
- Use `install.lock` file to detect status of installation.
## Fixed ## Fixed

View File

@ -14,6 +14,7 @@
- 修改 GuzzleHttp 库获取 CA 证书的策略 - 修改 GuzzleHttp 库获取 CA 证书的策略
- 重构用户系统 - 重构用户系统
- PHP 版本最低要求为 7.2.0 - PHP 版本最低要求为 7.2.0
- 使用 `install.lock` 文件来检测安装状态
## 修复 ## 修复

View File

@ -8,16 +8,6 @@
<p>@lang('setup.updates.success.text', ['version' => config('app.version')])</p> <p>@lang('setup.updates.success.text', ['version' => config('app.version')])</p>
{{-- if any tip is given --}}
@if (is_array($tips) && count($tips) > 0)
<p><b>@lang('setup.updates.success.tips')</b></p>
<ul>
@foreach ($tips as $tip)
<li><p>{!! $tip !!}</p></li>
@endforeach
</ul>
@endif
<p class="step"> <p class="step">
<a href="{{ url('/setup/changelog') }}" class="button button-large">@lang('setup.updates.welcome.button')</a> <a href="{{ url('/setup/changelog') }}" class="button button-large">@lang('setup.updates.welcome.button')</a>
</p> </p>

View File

@ -1,28 +0,0 @@
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
/**
* Setup Wizard.
*/
Route::group(['middleware' => 'setup'], function () {
Route::any('/', 'SetupController@welcome');
Route::any('/database', 'SetupController@database');
Route::get('/info', 'SetupController@info');
Route::post('/finish', 'SetupController@finish');
});
Route::group(['middleware' => ['authorize', App\Http\Middleware\LockUpdatePage::class]], function () {
Route::any('/update', 'SetupController@update');
Route::any('/exec-update', 'SetupController@doUpdate');
Route::view('/changelog', 'setup.updates.changelog');
});

View File

@ -156,3 +156,21 @@ Route::group(['middleware' => ['authorize', 'admin'], 'prefix' => 'admin'], func
Route::any('/download', 'UpdateController@download'); Route::any('/download', 'UpdateController@download');
}); });
}); });
/**
* Setup and Update
*/
Route::group(['prefix' => 'setup'], function () {
Route::group(['middleware' => 'setup'], function () {
Route::any('/', 'SetupController@welcome');
Route::any('/database', 'SetupController@database');
Route::view('/info', 'setup.wizard.info');
Route::post('/finish', 'SetupController@finish');
});
Route::group(['middleware' => 'authorize'], function () {
Route::view('/update', 'setup.updates.welcome')->middleware('setup');
Route::any('/exec-update', 'SetupController@update')->middleware('setup');
Route::view('/changelog', 'setup.updates.changelog');
});
});

View File

@ -28,6 +28,11 @@ class BrowserKitTestCase extends TestCase
Artisan::call('migrate:refresh'); Artisan::call('migrate:refresh');
$files = $app->make('files');
if (! $files->exists(storage_path('install.lock'))) {
$files->put(storage_path('install.lock'), '');
}
return $app; return $app;
} }

View File

@ -4,6 +4,7 @@ namespace Tests;
use Schema; use Schema;
use App\Models\User; use App\Models\User;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
class BsInstallCommandTest extends TestCase class BsInstallCommandTest extends TestCase
@ -15,25 +16,15 @@ class BsInstallCommandTest extends TestCase
$this->artisan('bs:install ibara.mayaka@hyouka.test 12345678 mayaka') $this->artisan('bs:install ibara.mayaka@hyouka.test 12345678 mayaka')
->expectsOutput('You have installed Blessing Skin Server. Nothing to do.'); ->expectsOutput('You have installed Blessing Skin Server. Nothing to do.');
$tables = [ $this->mock(Filesystem::class, function ($mock) {
'user_closet', $mock->shouldReceive('exists')
'migrations', ->with(storage_path('install.lock'))
'options', ->once()
'players', ->andReturn(false);
'textures', $mock->shouldReceive('put')
'users', ->with(storage_path('install.lock'), '')
'reports', ->once()
'oauth_auth_codes', ->andReturn(true);
'oauth_access_tokens',
'oauth_clients',
'oauth_personal_access_clients',
'oauth_refresh_tokens',
'notifications',
'jobs',
'language_lines',
];
array_walk($tables, function ($table) {
Schema::dropIfExists($table);
}); });
$this->artisan('bs:install ibara.mayaka@hyouka.test 12345678 mayaka') $this->artisan('bs:install ibara.mayaka@hyouka.test 12345678 mayaka')

View File

@ -6,6 +6,7 @@ use Event;
use App\Models\User; use App\Models\User;
use App\Models\Player; use App\Models\Player;
use App\Services\Facades\Option; use App\Services\Facades\Option;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
@ -84,16 +85,28 @@ class MiddlewareTest extends TestCase
{ {
$this->get('/setup')->assertSee('Already installed'); $this->get('/setup')->assertSee('Already installed');
$tables = [ $this->mock(Filesystem::class, function ($mock) {
'user_closet', 'migrations', 'options', 'players', 'textures', 'users', $mock->shouldReceive('exists')
]; ->with(storage_path('install.lock'))
array_walk($tables, function ($table) { ->twice()
Schema::dropIfExists($table); ->andReturn(false);
$mock->shouldReceive('exists')
->with(base_path('.env'))
->andReturn(true);
}); });
$this->get('/setup')->assertSee(trans( $this->get('/setup')->assertSee(trans(
'setup.wizard.welcome.text', 'setup.wizard.welcome.text',
['version' => config('app.version')] ['version' => config('app.version')]
)); ));
$this->actAs('superAdmin');
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->andReturn(true);
});
config(['app.version' => '100.0.0']);
$this->get('/setup/update')->assertSee(trans('setup.updates.welcome.title'));
} }
public function testCheckPlayerExist() public function testCheckPlayerExist()
@ -178,6 +191,28 @@ class MiddlewareTest extends TestCase
->assertRedirect('/user'); ->assertRedirect('/user');
} }
public function testRedirectToSetup()
{
$current = config('app.version');
config(['app.version' => '100.0.0']);
$this->get('/')->assertStatus(503);
$this->actAs('superAdmin')->get('/')->assertRedirect('/setup/update');
config(['app.version' => $current]);
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->andReturn(true, false, false);
$mock->shouldReceive('exists')
->with(base_path('.env'))
->andReturn(true);
});
$this->get('/')->assertViewIs('index');
$this->get('/setup')->assertViewIs('setup.wizard.welcome');
$this->get('/')->assertRedirect('/setup');
}
public function testRejectBannedUser() public function testRejectBannedUser()
{ {
$user = factory(User::class, 'banned')->create(); $user = factory(User::class, 'banned')->create();
@ -210,10 +245,4 @@ class MiddlewareTest extends TestCase
$this->get('/', ['user-agent' => 'MSIE'])->assertSee(trans('errors.http.ie')); $this->get('/', ['user-agent' => 'MSIE'])->assertSee(trans('errors.http.ie'));
$this->get('/', ['user-agent' => 'Trident'])->assertSee(trans('errors.http.ie')); $this->get('/', ['user-agent' => 'Trident'])->assertSee(trans('errors.http.ie'));
} }
public function testLockUpdatePage()
{
$this->actAs('admin')->get('/setup/changelog')->assertStatus(503);
$this->actAs('superAdmin')->get('/setup/changelog')->assertStatus(200);
}
} }

View File

@ -2,62 +2,48 @@
namespace Tests; namespace Tests;
use Mockery;
use Exception;
use CreateAllTables;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use AddVerificationToUsersTable;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Schema; use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage; use Symfony\Component\Finder\SplFileInfo;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
class SetupControllerTest extends TestCase class SetupControllerTest extends TestCase
{ {
use DatabaseTransactions; use DatabaseTransactions;
protected function tearDown(): void
{
$this->dropAllTables();
Mockery::close();
parent::tearDown();
}
protected function dropAllTables()
{
$tables = [
'user_closet',
'migrations',
'options',
'players',
'textures',
'users',
'reports',
'oauth_auth_codes',
'oauth_access_tokens',
'oauth_clients',
'oauth_personal_access_clients',
'oauth_refresh_tokens',
'notifications',
'jobs',
];
array_walk($tables, function ($table) {
Schema::dropIfExists($table);
});
return $this;
}
public function testWelcome() public function testWelcome()
{ {
$this->dropAllTables(); $this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->andReturn(false);
$mock->shouldReceive('exists')
->with(base_path('.env'))
->once()
->andReturn(false);
$mock->shouldReceive('copy')
->with(base_path('.env.example'), base_path('.env'))
->once()
->andReturn(true);
});
$this->get('/setup')->assertViewIs('setup.wizard.welcome'); $this->get('/setup')->assertViewIs('setup.wizard.welcome');
} }
public function testDatabase() public function testDatabase()
{ {
$this->dropAllTables(); $this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->atLeast(1)
->andReturn(false);
$mock->shouldReceive('put')
->with(storage_path('install.lock'), '')
->atLeast(1)
->andReturn(true);
});
$fake = [ $fake = [
'type' => env('DB_CONNECTION'), 'type' => env('DB_CONNECTION'),
'host' => env('DB_HOST'), 'host' => env('DB_HOST'),
@ -76,7 +62,17 @@ class SetupControllerTest extends TestCase
public function testReportDatabaseConnectionError() public function testReportDatabaseConnectionError()
{ {
$this->dropAllTables(); $this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->atLeast(1)
->andReturn(false);
$mock->shouldReceive('put')
->with(storage_path('install.lock'), '')
->atLeast(1)
->andReturn(true);
});
$this->post('/setup/database', ['type' => 'sqlite', 'host' => 'placeholder', 'db' => 'test']) $this->post('/setup/database', ['type' => 'sqlite', 'host' => 'placeholder', 'db' => 'test'])
->assertSee(trans('setup.database.connection-error', [ ->assertSee(trans('setup.database.connection-error', [
'type' => 'SQLite', 'type' => 'SQLite',
@ -84,18 +80,19 @@ class SetupControllerTest extends TestCase
])); ]));
} }
public function testInfo()
{
$this->dropAllTables();
$this->get('/setup/info')->assertViewIs('setup.wizard.info');
Artisan::call('migrate:refresh');
Schema::drop('users');
$this->get('/setup/info')->assertSee('already exist');
}
public function testFinish() public function testFinish()
{ {
$this->dropAllTables(); $this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->atLeast(1)
->andReturn(false);
$mock->shouldReceive('put')
->with(storage_path('install.lock'), '')
->atLeast(1)
->andReturn(true);
});
// Without `email` field // Without `email` field
$this->post('/setup/finish') $this->post('/setup/finish')
->assertDontSee(trans('setup.wizard.finish.title')); ->assertDontSee(trans('setup.wizard.finish.title'));
@ -183,13 +180,7 @@ class SetupControllerTest extends TestCase
], ],
]) ])
->once() ->once()
->andReturnUsing(function () { ->andReturn(true);
$migration = new CreateAllTables();
$migration->up();
$migration = new AddVerificationToUsersTable();
$migration->up();
});
$this->post('/setup/finish', [ $this->post('/setup/finish', [
'email' => 'a@b.c', 'email' => 'a@b.c',
'nickname' => 'nickname', 'nickname' => 'nickname',
@ -209,47 +200,41 @@ class SetupControllerTest extends TestCase
public function testUpdate() public function testUpdate()
{ {
$this->actAs('superAdmin') $this->mock(Filesystem::class, function ($mock) {
->get('/setup/update') $mock->shouldReceive('exists')
->assertSee(trans('setup.locked.text')); ->with(storage_path('install.lock'))
->andReturn(true);
option(['version' => '0.1.0']); $mock->shouldReceive('put')
$this->get('/setup/update') ->with(storage_path('install.lock'), '')
->assertSee(trans('setup.updates.welcome.title')); ->once()
} ->andReturn(true);
public function testDoUpdate() $mock->shouldReceive('files')
{ ->with(database_path('update_scripts'))
$current_version = config('app.version'); ->once()
->andReturn([
new SplFileInfo('/1.0.0.php', '', ''),
new SplFileInfo('/99.0.0.php', '', ''),
new SplFileInfo('/100.0.0.php', '', ''),
]);
$mock->shouldNotReceive('getRequire')->with('/1.0.0.php');
$mock->shouldReceive('getRequire')
->with('/99.0.0.php')
->once();
$mock->shouldReceive('getRequire')
->with('/100.0.0.php')
->once();
});
Artisan::shouldReceive('call')->with('view:clear')->once();
config(['app.version' => '100.0.0']); config(['app.version' => '100.0.0']);
copy(
database_path('update_scripts/update-3.1-to-3.1.1.php'),
database_path("update_scripts/update-$current_version-to-100.0.0.php")
); // Just a fixture
config(['options.new_option' => 'value']); $this->actAs('superAdmin')
$this->actAs('superAdmin')->get('/setup/exec-update')->assertViewHas('tips'); ->get('/setup/exec-update')
$this->assertEquals('value', option('new_option')); ->assertViewIs('setup.updates.success');
$this->assertEquals('100.0.0', option('version')); $this->assertEquals('100.0.0', option('version'));
unlink(database_path("update_scripts/update-$current_version-to-100.0.0.php"));
option(['version' => '3.0.0']); // Fake old version
$this->get('/setup/exec-update');
$this->assertEquals('100.0.0', option('version'));
}
public function testCheckDirectories()
{
Storage::shouldReceive('disk')
->with('root')
->andReturnSelf();
Storage::shouldReceive('has')
->with('storage/textures')
->andReturn(false);
Storage::shouldReceive('makeDirectory')
->with('storage/textures')
->andThrow(new Exception());
$this->assertFalse(\App\Http\Controllers\SetupController::checkDirectories());
} }
} }

View File

@ -27,6 +27,11 @@ class TestCase extends \Illuminate\Foundation\Testing\TestCase
Artisan::call('migrate:refresh'); Artisan::call('migrate:refresh');
$files = $app->make('files');
if (! $files->exists(storage_path('install.lock'))) {
$files->put(storage_path('install.lock'), '');
}
return $app; return $app;
} }

View File

@ -91,6 +91,7 @@ class UpdateControllerTest extends TestCase
}); });
$this->mock(\Illuminate\Filesystem\Filesystem::class, function ($mock) { $this->mock(\Illuminate\Filesystem\Filesystem::class, function ($mock) {
$mock->shouldReceive('delete')->with(storage_path('options/cache.php'))->once(); $mock->shouldReceive('delete')->with(storage_path('options/cache.php'))->once();
$mock->shouldReceive('exists')->with(storage_path('install.lock'))->andReturn(true);
}); });
$this->getJson('/admin/update/download?action=download') $this->getJson('/admin/update/download?action=download')
->assertJson(['code' => 0, 'message' => trans('admin.update.complete')]); ->assertJson(['code' => 0, 'message' => trans('admin.update.complete')]);