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
storage/oauth-public.key
storage/oauth-private.key
storage/install.lock
.phpunit.result.cache

View File

@ -4,6 +4,7 @@ namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
class BsInstallCommand extends Command
{
@ -11,9 +12,9 @@ class BsInstallCommand extends Command
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.');
return;
@ -44,6 +45,8 @@ class BsInstallCommand extends Command
$admin->verified = true;
$admin->save();
$filesystem->put(storage_path('install.lock'), '');
$this->info('Installation completed!');
$this->info('We recommend to modify your "Site URL" option if incorrect.');
}

View File

@ -6,23 +6,22 @@ use DB;
use Log;
use File;
use Option;
use Schema;
use Artisan;
use Storage;
use App\Models\User;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Composer\Semver\Comparator;
use Illuminate\Filesystem\Filesystem;
use App\Exceptions\PrettyPageException;
use Symfony\Component\Finder\SplFileInfo;
class SetupController extends Controller
{
public function welcome()
public function welcome(Filesystem $filesystem)
{
// @codeCoverageIgnoreStart
if (! File::exists(base_path('.env'))) {
File::copy(base_path('.env.example'), base_path('.env'));
if (! $filesystem->exists(base_path('.env'))) {
$filesystem->copy(base_path('.env.example'), base_path('.env'));
}
// @codeCoverageIgnoreEnd
return view('setup.wizard.welcome');
}
@ -104,31 +103,7 @@ class SetupController extends Controller
return redirect('setup/info');
}
public function info()
{
$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)
public function finish(Request $request, Filesystem $filesystem)
{
$data = $this->validate($request, [
'email' => 'required|email',
@ -158,7 +133,7 @@ class SetupController extends Controller
$siteUrl = url('/');
if (ends_with($siteUrl, '/index.php')) {
if (Str::endsWith($siteUrl, '/index.php')) {
$siteUrl = substr($siteUrl, 0, -10); // @codeCoverageIgnore
}
@ -179,7 +154,7 @@ class SetupController extends Controller
$user->save();
$this->createDirectories();
$filesystem->put(storage_path('install.lock'), '');
return view('setup.wizard.finish')->with([
'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'))) {
// No updates available
return view('setup.locked');
}
return view('setup.updates.welcome');
}
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'));
collect($filesystem->files(database_path('update_scripts')))
->filter(function (SplFileInfo $file) {
$name = $file->getFilenameWithoutExtension();
return preg_match('/^\d+\.\d+\.\d+$/', $name) > 0
&& Comparator::greaterThanOrEqualTo($name, option('version'));
})
->each(function (SplFileInfo $file) use ($filesystem) {
$filesystem->getRequire($file->getPathname());
});
option(['version' => config('app.version')]);
Artisan::call('view:clear');
$filesystem->put(storage_path('install.lock'), '');
return view('setup.updates.success', ['tips' => $tips ?? []]);
}
/**
* 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();
return view('setup.updates.success');
}
}

View File

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

View File

@ -2,17 +2,15 @@
namespace App\Http\Middleware;
use Illuminate\Filesystem\Filesystem;
use App\Http\Controllers\SetupController;
class CheckInstallation
{
public function handle($request, \Closure $next)
{
if (config('database.default') == 'dummy') {
return $next($request); // @codeCoverageIgnore
}
if (SetupController::checkTablesExist()) {
$hasLock = resolve(Filesystem::class)->exists(storage_path('install.lock'));
if ($hasLock && ! $request->is('setup/*update')) {
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';
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/**
* Define the routes for the application.
*
@ -38,8 +28,6 @@ class RouteServiceProvider extends ServiceProvider
*/
public function map(Router $router)
{
$this->mapSetupRoutes($router);
$this->mapStaticRoutes($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.
*

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...
*/
App\Providers\RuntimeCheckServiceProvider::class,
App\Providers\AppServiceProvider::class,
App\Providers\EventServiceProvider::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:
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
updates:

View File

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

View File

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

View File

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

View File

@ -8,16 +8,6 @@
<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">
<a href="{{ url('/setup/changelog') }}" class="button button-large">@lang('setup.updates.welcome.button')</a>
</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');
});
});
/**
* 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');
$files = $app->make('files');
if (! $files->exists(storage_path('install.lock'))) {
$files->put(storage_path('install.lock'), '');
}
return $app;
}

View File

@ -4,6 +4,7 @@ namespace Tests;
use Schema;
use App\Models\User;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class BsInstallCommandTest extends TestCase
@ -15,25 +16,15 @@ class BsInstallCommandTest extends TestCase
$this->artisan('bs:install ibara.mayaka@hyouka.test 12345678 mayaka')
->expectsOutput('You have installed Blessing Skin Server. Nothing to do.');
$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',
'language_lines',
];
array_walk($tables, function ($table) {
Schema::dropIfExists($table);
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->once()
->andReturn(false);
$mock->shouldReceive('put')
->with(storage_path('install.lock'), '')
->once()
->andReturn(true);
});
$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\Player;
use App\Services\Facades\Option;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Schema;
use Illuminate\Foundation\Testing\DatabaseTransactions;
@ -84,16 +85,28 @@ class MiddlewareTest extends TestCase
{
$this->get('/setup')->assertSee('Already installed');
$tables = [
'user_closet', 'migrations', 'options', 'players', 'textures', 'users',
];
array_walk($tables, function ($table) {
Schema::dropIfExists($table);
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->twice()
->andReturn(false);
$mock->shouldReceive('exists')
->with(base_path('.env'))
->andReturn(true);
});
$this->get('/setup')->assertSee(trans(
'setup.wizard.welcome.text',
['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()
@ -178,6 +191,28 @@ class MiddlewareTest extends TestCase
->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()
{
$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' => '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;
use Mockery;
use Exception;
use CreateAllTables;
use Illuminate\Support\Str;
use AddVerificationToUsersTable;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Schema;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\Finder\SplFileInfo;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class SetupControllerTest extends TestCase
{
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()
{
$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');
}
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 = [
'type' => env('DB_CONNECTION'),
'host' => env('DB_HOST'),
@ -76,7 +62,17 @@ class SetupControllerTest extends TestCase
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'])
->assertSee(trans('setup.database.connection-error', [
'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()
{
$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
$this->post('/setup/finish')
->assertDontSee(trans('setup.wizard.finish.title'));
@ -183,13 +180,7 @@ class SetupControllerTest extends TestCase
],
])
->once()
->andReturnUsing(function () {
$migration = new CreateAllTables();
$migration->up();
$migration = new AddVerificationToUsersTable();
$migration->up();
});
->andReturn(true);
$this->post('/setup/finish', [
'email' => 'a@b.c',
'nickname' => 'nickname',
@ -209,47 +200,41 @@ class SetupControllerTest extends TestCase
public function testUpdate()
{
$this->actAs('superAdmin')
->get('/setup/update')
->assertSee(trans('setup.locked.text'));
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(storage_path('install.lock'))
->andReturn(true);
option(['version' => '0.1.0']);
$this->get('/setup/update')
->assertSee(trans('setup.updates.welcome.title'));
}
$mock->shouldReceive('put')
->with(storage_path('install.lock'), '')
->once()
->andReturn(true);
public function testDoUpdate()
{
$current_version = config('app.version');
$mock->shouldReceive('files')
->with(database_path('update_scripts'))
->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']);
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')->get('/setup/exec-update')->assertViewHas('tips');
$this->assertEquals('value', option('new_option'));
$this->actAs('superAdmin')
->get('/setup/exec-update')
->assertViewIs('setup.updates.success');
$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');
$files = $app->make('files');
if (! $files->exists(storage_path('install.lock'))) {
$files->put(storage_path('install.lock'), '');
}
return $app;
}

View File

@ -91,6 +91,7 @@ class UpdateControllerTest extends TestCase
});
$this->mock(\Illuminate\Filesystem\Filesystem::class, function ($mock) {
$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')
->assertJson(['code' => 0, 'message' => trans('admin.update.complete')]);