From b6ffa970cf8ae975ecc91983477b2e1b6fee59d3 Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Thu, 2 Nov 2017 16:50:00 +0800 Subject: [PATCH] Add tests for `AdminController` --- app/Http/Controllers/AdminController.php | 40 +- app/Services/OptionForm.php | 65 +- database/factories/TextureModelFactory.php | 42 ++ resources/lang/en/admin.yml | 1 + resources/lang/zh_CN/admin.yml | 1 + tests/AdminControllerTest.php | 750 +++++++++++++++++++++ 6 files changed, 875 insertions(+), 24 deletions(-) create mode 100644 database/factories/TextureModelFactory.php create mode 100644 tests/AdminControllerTest.php diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index 4cbd818f..c1cd44e8 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -88,16 +88,13 @@ class AdminController extends Controller $form->group('sign_gap_time')->text('sign_gap_time')->addon(); $form->checkbox('sign_after_zero')->label()->hint(); - })->handle(function() { - $sign_score = $_POST['sign_score_from'].','.$_POST['sign_score_to']; + })->after(function() { + $sign_score = request('sign_score_from').','.request('sign_score_to'); Option::set('sign_score', $sign_score); - - unset($_POST['sign_score_from']); - unset($_POST['sign_score_to']); })->with([ 'sign_score_from' => @explode(',', option('sign_score'))[0], 'sign_score_to' => @explode(',', option('sign_score'))[1] - ]); + ])->handle(); return view('admin.score', ['forms' => compact('rate', 'sign')]); } @@ -108,7 +105,13 @@ class AdminController extends Controller { $form->text('site_name'); $form->text('site_description'); - $form->text('site_url')->hint(); + $form->text('site_url') + ->hint() + ->format(function ($url) { + if (ends_with($url, '/')) + $url = substr($url, 0, -1); + return $url; + }); $form->checkbox('user_can_register')->label(); @@ -135,10 +138,7 @@ class AdminController extends Controller $form->checkbox('allow_sending_statistics')->label()->hint(); - })->handle(function() { - if (substr($_POST['site_url'], -1) == "/") - $_POST['site_url'] = substr($_POST['site_url'], 0, -1); - }); + })->handle(); $announ = Option::form('announ', OptionForm::AUTO_DETECT, function($form) { @@ -220,7 +220,7 @@ class AdminController extends Controller * Handle ajax request from /admin/users * * @param Request $request - * @return void + * @return Illuminate\Http\JsonResponse */ public function userAjaxHandler(Request $request, UserRepository $users) { @@ -305,6 +305,8 @@ class AdminController extends Controller $user->delete(); return json(trans('admin.users.operations.delete.success'), 0); + } else { + return json(trans('admin.users.operations.invalid'), 1); } } @@ -313,12 +315,12 @@ class AdminController extends Controller */ public function playerAjaxHandler(Request $request, UserRepository $users) { - $action = isset($_GET['action']) ? $_GET['action'] : ""; + $action = $request->input('action'); $player = Player::find($request->input('pid')); if (! $player) { - abort(404, trans('general.unexistent-player')); + return json(trans('general.unexistent-player'), 1); } if ($player->user()->first()->uid !== app('user.current')->uid) { @@ -351,7 +353,6 @@ class AdminController extends Controller } elseif ($action == "owner") { $this->validate($request, [ - 'pid' => 'required|integer', 'uid' => 'required|integer' ]); @@ -369,9 +370,15 @@ class AdminController extends Controller return json(trans('admin.players.delete.success'), 0); } elseif ($action == "name") { + $this->validate($request, [ + 'name' => 'required' + ]); + $player->rename($request->input('name')); return json(trans('admin.players.name.success', ['player' => $player->player_name]), 0, ['name' => $player->player_name]); + } else { + return json(trans('admin.users.operations.invalid'), 1); } } @@ -379,7 +386,8 @@ class AdminController extends Controller * Get one user information * * @param string $uid - * @return JsonResponse + * @param UserRepository $users + * @return \Illuminate\Http\JsonResponse */ public function getOneUser($uid, UserRepository $users) { diff --git a/app/Services/OptionForm.php b/app/Services/OptionForm.php index edc25922..43acd54d 100644 --- a/app/Services/OptionForm.php +++ b/app/Services/OptionForm.php @@ -28,6 +28,8 @@ class OptionForm protected $buttons = []; protected $messages = []; + protected $hookBefore; + protected $hookAfter; protected $alwaysCallback = null; protected $renderWithOutTable = false; @@ -175,6 +177,32 @@ class OptionForm return $this; } + /** + * Add callback which will be executed before handling options + * + * @param callable $callback + * @return $this + */ + public function before(callable $callback) + { + $this->hookBefore = $callback; + + return $this; + } + + /** + * Add callback which will be executed after handling options + * + * @param callable $callback + * @return $this + */ + public function after(callable $callback) + { + $this->hookAfter = $callback; + + return $this; + } + /** * Add callback which will be always executed. * @@ -216,11 +244,18 @@ class OptionForm */ public function handle(callable $callback = null) { - if (Arr::get($_POST, 'option') == $this->id) { + $request = app('request'); + $allPostData = $request->all(); + + if ($request->isMethod('POST') && Arr::get($allPostData, 'option') == $this->id) { if (!is_null($callback)) { call_user_func($callback, $this); } + if (!is_null($this->hookBefore)) { + call_user_func($this->hookBefore, $this); + } + $postOptionQueue = []; $arrayOptionQueue = []; @@ -238,23 +273,24 @@ class OptionForm } foreach ($postOptionQueue as $item) { - if ($item instanceof OptionFormCheckbox && !isset($_POST[$item->id])) { + if ($item instanceof OptionFormCheckbox && !isset($allPostData[$item->id])) { // preset value for checkboxes which are not checked - $_POST[$item->id] = "false"; + $allPostData[$item->id] = "false"; } // Str::is('*[*]', $item->id) if (false !== ($result = $this->parseIdWithOffset($item->id))) { // Push array option value to cache. - // Values of post ids like *[*] is collected as arrays in $_POST + // Values of post ids like *[*] is collected as arrays in $allPostData // automatically by Laravel. - $arrayOptionQueue[$result['id']] = $_POST[$result['id']]; + $arrayOptionQueue[$result['id']] = $allPostData[$result['id']]; continue; } // compare with raw option value - if (($data = Arr::get($_POST, $item->id)) != option($item->id, null, true)) { - Option::set($item->id, $data); + if (($data = Arr::get($allPostData, $item->id)) != option($item->id, null, true)) { + $formatted = is_null($item->format) ? $data : call_user_func($item->format, $data); + Option::set($item->id, $formatted); } } @@ -262,6 +298,10 @@ class OptionForm Option::set($key, serialize($value)); } + if (!is_null($this->hookAfter)) { + call_user_func($this->hookAfter, $this); + } + $this->addMessage(trans('options.option-saved'), 'success'); } @@ -352,7 +392,7 @@ class OptionForm 'style' => 'primary', 'text' => trans('general.submit'), 'type' => 'submit', - 'name' => 'submit' + 'name' => 'submit_'.$this->id ]); } @@ -380,6 +420,8 @@ class OptionFormItem public $hint; + public $format; + public $value = null; public $disabled; @@ -419,6 +461,13 @@ class OptionFormItem return $this; } + public function format(callable $callback) + { + $this->format = $callback; + + return $this; + } + public function disabled($disabled = "disabled") { $this->disabled = "disabled=\"$disabled\""; diff --git a/database/factories/TextureModelFactory.php b/database/factories/TextureModelFactory.php new file mode 100644 index 00000000..970d5a28 --- /dev/null +++ b/database/factories/TextureModelFactory.php @@ -0,0 +1,42 @@ +define(Texture::class, function (Faker\Generator $faker) { + return [ + 'name' => $faker->firstName, + 'type' => 'steve', + 'likes' => rand(0, 50), + 'hash' => $faker->sha256, + 'size' => rand(1, 2048), + 'uploader' => factory(App\Models\User::class)->create()->uid, + 'public' => true, + 'upload_at' => $faker->dateTime + ]; +}); + +$factory->defineAs(Texture::class, 'alex', function (Faker\Generator $faker) { + return [ + 'name' => $faker->firstName, + 'type' => 'alex', + 'likes' => rand(0, 50), + 'hash' => $faker->sha256, + 'size' => rand(1, 2048), + 'uploader' => factory(App\Models\User::class)->create()->uid, + 'public' => true, + 'upload_at' => $faker->dateTime + ]; +}); + +$factory->defineAs(Texture::class, 'cape', function (Faker\Generator $faker) { + return [ + 'name' => $faker->firstName, + 'type' => 'cape', + 'likes' => rand(0, 50), + 'hash' => $faker->sha256, + 'size' => rand(1, 2048), + 'uploader' => factory(App\Models\User::class)->create()->uid, + 'public' => true, + 'upload_at' => $faker->dateTime + ]; +}); diff --git a/resources/lang/en/admin.yml b/resources/lang/en/admin.yml index 153f9858..27a81d73 100644 --- a/resources/lang/en/admin.yml +++ b/resources/lang/en/admin.yml @@ -18,6 +18,7 @@ users: title: Operations non-existent: No such user. no-permission: You have no permission to operate this user. + invalid: Invalid action. email: change: Edit Email existed: :email is existed. diff --git a/resources/lang/zh_CN/admin.yml b/resources/lang/zh_CN/admin.yml index 37cf113d..8b4318dd 100644 --- a/resources/lang/zh_CN/admin.yml +++ b/resources/lang/zh_CN/admin.yml @@ -18,6 +18,7 @@ users: title: 更多操作 non-existent: 用户不存在 no-permission: 你无权操作此用户 + invalid: 无效 action email: change: 修改邮箱 existed: :email 已被占用 diff --git a/tests/AdminControllerTest.php b/tests/AdminControllerTest.php new file mode 100644 index 00000000..c5ca9be5 --- /dev/null +++ b/tests/AdminControllerTest.php @@ -0,0 +1,750 @@ +actAs('admin'); + } + + public function testIndex() + { + $this->visit('/admin')->seePageIs('/admin'); + } + + public function testCustomize() + { + // Check if `color_scheme` is existed or not + $this->get('/admin/customize?action=color', [ + 'X-Requested-With' => 'XMLHttpRequest' + ])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'color scheme']) + ]); + + // Change color + $this->get('/admin/customize?action=color&color_scheme=purple') + ->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.customize.change-color.success') + ]); + $this->assertEquals('purple', option('color_scheme')); + + // View + $this->visit('/admin/customize') + ->see('Change theme color'); + + $this->visit('/admin/customize') + ->type('url', 'home_pic_url') + ->type('url', 'favicon_url') + ->select('1', 'copyright_prefer') + ->type('copyright', 'copyright_text') + ->press('submit_homepage'); + $this->assertEquals('url', option('home_pic_url')); + $this->assertEquals('url', option('favicon_url')); + $this->assertEquals('1', option('copyright_prefer')); + $this->assertEquals('copyright', option('copyright_text')); + + $this->visit('/admin/customize') + ->type('css', 'custom_css') + ->type('js', 'custom_js') + ->press('submit_customJsCss'); + $this->assertEquals('css', option('custom_css')); + $this->assertEquals('js', option('custom_js')); + } + + public function testScore() + { + $this->visit('/admin/score') + ->type('4', 'score_per_storage') + ->type('6', 'private_score_per_storage') + ->type('8', 'score_per_closet_item') + ->uncheck('return_score') + ->type('12', 'score_per_player') + ->type('500', 'user_initial_score') + ->press('submit_rate'); + $this->assertEquals('4', option('score_per_storage')); + $this->assertEquals('6', option('private_score_per_storage')); + $this->assertEquals('8', option('score_per_closet_item')); + $this->assertFalse(option('return_score')); + $this->assertEquals('12', option('score_per_player')); + $this->assertEquals('500', option('user_initial_score')); + + $this->visit('/admin/score') + ->type('233', 'sign_score_from') + ->type('666', 'sign_score_to') + ->type('7', 'sign_gap_time') + ->check('sign_after_zero') + ->press('submit_sign'); + $this->assertEquals('233,666', option('sign_score')); + $this->assertEquals('7', option('sign_gap_time')); + $this->assertTrue(option('sign_after_zero')); + } + + public function testOptions() + { + $this->visit('/admin/options') + ->type('My Site', 'site_name') + ->type('hi', 'site_description') + ->type('http://blessing.skin/', 'site_url') + ->uncheck('user_can_register') + ->type('8', 'regs_per_ip') + ->select('1', 'ip_get_method') + ->type('2048', 'max_upload_file_size') + ->see(trans( + 'options.general.max_upload_file_size.hint', + ['size' => ini_get('upload_max_filesize')] + )) + ->uncheck('allow_chinese_playername') + ->select('1', 'api_type') + ->check('auto_del_invalid_texture') + ->type('code', 'comment_script') + ->uncheck('allow_sending_statistics') + ->press('submit_general'); + $this->assertEquals('My Site', option('site_name')); + $this->assertEquals('hi', option('site_description')); + $this->assertEquals('http://blessing.skin', option('site_url')); + $this->assertFalse(option('user_can_register')); + $this->assertEquals('8', option('regs_per_ip')); + $this->assertEquals('1', option('ip_get_method')); + $this->assertEquals('2048', option('max_upload_file_size')); + $this->assertFalse(option('allow_chinese_playername')); + $this->assertEquals('1', option('api_type')); + $this->assertTrue(option('auto_del_invalid_texture')); + $this->assertEquals('code', option('comment_script')); + $this->assertFalse(option('allow_sending_statistics')); + + $this->visit('/admin/options') + ->type('announcement', 'announcement') + ->press('submit_announ'); + $this->assertEquals('announcement', option('announcement')); + + $this->visit('/admin/options') + ->check('force_ssl') + ->uncheck('auto_detect_asset_url') + ->check('return_200_when_notfound') + ->type('0', 'cache_expire_time') + ->press('submit_resources'); + $this->assertTrue(option('force_ssl')); + $this->assertFalse(option('auto_detect_asset_url')); + $this->assertTrue(option('return_200_when_notfound')); + $this->assertEquals('0', option('cache_expire_time')); + } + + public function testUsers() + { + $this->visit('/admin/users')->see(trans('general.user-manage')); + } + + public function testGetUserData() + { + $this->visit('/admin/user-data') + ->seeJsonStructure([ + 'data' => [[ + 'uid', + 'email', + 'nickname', + 'score', + 'permission', + 'register_at', + 'operations', + 'players_count' + ]] + ]); + + $user = factory(User::class)->create(); + $this->visit('/admin/user-data?uid='.$user->uid) + ->seeJsonSubset([ + 'data' => [[ + 'uid' => $user->uid, + 'email' => $user->email, + 'nickname' => $user->nickname, + 'score' => $user->score, + 'permission' => $user->permission, + 'players_count' => 0 + ]] + ]); + } + + public function testPlayers() + { + $this->visit('/admin/players')->see(trans('general.player-manage')); + } + + public function testGetPlayerData() + { + $player = factory(Player::class)->create(); + $user = User::find($player->uid); + + $this->visit('/admin/player-data') + ->seeJsonStructure([ + 'data' => [[ + 'pid', + 'uid', + 'player_name', + 'preference', + 'tid_steve', + 'tid_alex', + 'tid_cape', + 'last_modified' + ]] + ]); + + $this->visit('/admin/player-data?uid='.$user->uid) + ->seeJsonSubset([ + 'data' => [[ + 'pid' => $player->pid, + 'uid' => $user->uid, + 'player_name' => $player->player_name, + 'preference' => $player->preference, + 'tid_steve' => $player->tid_steve, + 'tid_alex' => $player->tid_alex, + 'tid_cape' => $player->tid_cape + ]] + ]); + } + + public function testUserAjaxHandler() + { + // Operate on an not-existed user + $this->post('/admin/users') + ->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.users.operations.non-existent') + ]); + + $user = factory(User::class)->create(); + + // Operate without `action` field + $this->post('/admin/users', ['uid' => $user->uid]) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.users.operations.invalid') + ]); + + // An admin operating on a super admin should be forbidden + $superAdmin = factory(User::class, 'superAdmin')->create(); + $this->post('/admin/users', ['uid' => $superAdmin->uid]) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.users.operations.no-permission') + ]); + + // Action is `email` but without `email` field + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'email'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'email']) + ]); + + // Action is `email` but with an invalid email address + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'email', 'email' => 'invalid'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.email', ['attribute' => 'email']) + ]); + + // Using an existed email address + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'email', 'email' => $superAdmin->email] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.users.operations.email.existed', ['email' => $superAdmin->email]) + ]); + + // Set email successfully + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'email', 'email' => 'a@b.c'] + )->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.users.operations.email.success') + ]); + $this->seeInDatabase('users', [ + 'uid' => $user->uid, + 'email' => 'a@b.c' + ]); + + // Action is `nickname` but without `nickname` field + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'nickname'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'nickname']) + ]); + + // Action is `email` but with an invalid nickname + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'nickname', 'nickname' => '\\'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.nickname', ['attribute' => 'nickname']) + ]); + + // Set nickname successfully + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'nickname', 'nickname' => 'nickname'] + )->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.users.operations.nickname.success', ['new' => 'nickname']) + ]); + $this->seeInDatabase('users', [ + 'uid' => $user->uid, + 'nickname' => 'nickname' + ]); + + // Action is `password` but without `password` field + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'password'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'password']) + ]); + + // Set a too short password + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'password', 'password' => '1'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.min.string', ['attribute' => 'password', 'min' => 8]) + ]); + + // Set a too long password + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'password', 'password' => str_random(17)], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.max.string', ['attribute' => 'password', 'max' => 16]) + ]); + + // Set password successfully + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'password', 'password' => '12345678'] + )->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.users.operations.password.success') + ]); + $user = User::find($user->uid); + $this->assertTrue($user->verifyPassword('12345678')); + + // Action is `score` but without `score` field + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'score'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'score']) + ]); + + // Action is `score` but with an not-an-integer value + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'score', 'score' => 'string'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.integer', ['attribute' => 'score']) + ]); + + // Set score successfully + $this->post( + '/admin/users', + ['uid' => $user->uid, 'action' => 'score', 'score' => 123] + )->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.users.operations.score.success') + ]); + $this->seeInDatabase('users', [ + 'uid' => $user->uid, + 'score' => 123 + ]); + + // Ban a user + $this->post('/admin/users', ['uid' => $user->uid, 'action' => 'ban']) + ->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.users.operations.ban.ban.success'), + 'permission' => User::BANNED + ]); + $user = User::find($user->uid); + $this->assertEquals(User::BANNED, $user->getPermission()); + + // Unban a user + $this->post('/admin/users', ['uid' => $user->uid, 'action' => 'ban']) + ->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.users.operations.ban.unban.success'), + 'permission' => User::NORMAL + ]); + $user = User::find($user->uid); + $this->assertEquals(User::NORMAL, $user->getPermission()); + + // Set a user to be an admin + $this->post('/admin/users', ['uid' => $user->uid, 'action' => 'admin']) + ->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.users.operations.admin.set.success'), + 'permission' => User::ADMIN + ]); + $user = User::find($user->uid); + $this->assertEquals(User::ADMIN, $user->getPermission()); + + // An admin cannot set another admin to be a normal user + $this->post('/admin/users', ['uid' => $user->uid]) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.users.operations.no-permission') + ]); + + // Set an admin to be a normal user + $this->actAs('superAdmin') + ->post('/admin/users', ['uid' => $user->uid, 'action' => 'admin']) + ->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.users.operations.admin.unset.success'), + 'permission' => User::NORMAL + ]); + $user = User::find($user->uid); + $this->assertEquals(User::NORMAL, $user->getPermission()); + + // Delete a user + $this->post('/admin/users', ['uid' => $user->uid, 'action' => 'delete']) + ->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.users.operations.delete.success') + ]); + $this->assertNull(User::find($user->uid)); + } + + public function testPlayerAjaxHandler() + { + $player = factory(Player::class)->create(); + + // Operate on a not-existed player + $this->post('/admin/players', ['pid' => -1]) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('general.unexistent-player') + ]); + + // An admin cannot operate another admin's player + $admin = factory(User::class, 'admin')->create(); + $this->post( + '/admin/players', + ['pid' => factory(Player::class)->create(['uid' => $admin->uid])->pid] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.players.no-permission') + ]); + $superAdmin = factory(User::class, 'superAdmin')->create(); + $this->post( + '/admin/players', + ['pid' => factory(Player::class)->create(['uid' => $superAdmin->uid])->pid] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.players.no-permission') + ]); + // For self is OK + $this->actAs($admin)->post( + '/admin/players', + ['pid' => factory(Player::class)->create(['uid' => $admin->uid])->pid] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.users.operations.invalid') + ]); + + // Change preference without `preference` field + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'preference' + ], ['X-Requested-With' => 'XMLHttpRequest'])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'preference']) + ]); + + // Change preference but neither `default` nor `slim` + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'preference', + 'preference' => 'steve' + ], ['X-Requested-With' => 'XMLHttpRequest'])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.preference', ['attribute' => 'preference']) + ]); + + // Set preference successfully + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'preference', + 'preference' => 'slim' + ])->seeJson([ + 'errno' => 0, + 'msg' => trans( + 'admin.players.preference.success', + ['player' => $player->player_name, 'preference' => 'slim'] + ) + ]); + $player = Player::find($player->pid); + $this->assertEquals('slim', $player->preference); + + // Change texture without `model` field + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture' + ], ['X-Requested-With' => 'XMLHttpRequest'])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'model']) + ]); + + // Change texture with invalid model name + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'slim' + ], ['X-Requested-With' => 'XMLHttpRequest'])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.model', ['attribute' => 'model']) + ]); + + // Change texture without `tid` field + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'steve' + ], ['X-Requested-With' => 'XMLHttpRequest'])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'tid']) + ]); + + // Change texture with a not-integer value + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'steve', + 'tid' => 'string' + ], ['X-Requested-With' => 'XMLHttpRequest'])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.integer', ['attribute' => 'tid']) + ]); + + // Invalid texture + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'steve', + 'tid' => -1 + ])->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.players.textures.non-existent', ['tid' => -1]) + ]); + + $steve = factory(Texture::class)->create(); + $alex = factory(Texture::class, 'alex')->create(); + $cape = factory(Texture::class, 'cape')->create(); + + // Steve + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'steve', + 'tid' => $steve->tid + ])->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.players.textures.success', ['player' => $player->player_name]) + ]); + $player = Player::find($player->pid); + $this->assertEquals($steve->tid, $player->tid_steve); + + // Alex + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'alex', + 'tid' => $alex->tid + ])->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.players.textures.success', ['player' => $player->player_name]) + ]); + $player = Player::find($player->pid); + $this->assertEquals($alex->tid, $player->tid_alex); + + // Cape + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'cape', + 'tid' => $cape->tid + ])->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.players.textures.success', ['player' => $player->player_name]) + ]); + $player = Player::find($player->pid); + $this->assertEquals($cape->tid, $player->tid_cape); + + // Reset texture + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'steve', + 'tid' => 0 + ])->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.players.textures.success', ['player' => $player->player_name]) + ]); + $player = Player::find($player->pid); + $this->assertEquals(0, $player->tid_steve); + $this->assertNotEquals(0, $player->tid_alex); + $this->assertNotEquals(0, $player->tid_cape); + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'alex', + 'tid' => 0 + ])->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.players.textures.success', ['player' => $player->player_name]) + ]); + $player = Player::find($player->pid); + $this->assertEquals(0, $player->tid_alex); + $this->assertNotEquals(0, $player->tid_cape); + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'texture', + 'model' => 'cape', + 'tid' => 0 + ])->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.players.textures.success', ['player' => $player->player_name]) + ]); + $player = Player::find($player->pid); + $this->assertEquals(0, $player->tid_cape); + + // Change owner without `uid` field + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'owner' + ], ['X-Requested-With' => 'XMLHttpRequest'])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'uid']) + ]); + + // Change owner with a not-integer `uid` value + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'owner', + 'uid' => 'string' + ], ['X-Requested-With' => 'XMLHttpRequest'])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.integer', ['attribute' => 'uid']) + ]); + + // Change owner to a not-existed user + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'owner', + 'uid' => -1 + ])->seeJson([ + 'errno' => 1, + 'msg' => trans('admin.users.operations.non-existent') + ]); + + // Change owner successfully + $user = factory(User::class)->create(); + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'owner', + 'uid' => $user->uid + ])->seeJson([ + 'errno' => 0, + 'msg' => trans( + 'admin.players.owner.success', + ['player' => $player->player_name, 'user' => $user->nickname] + ) + ]); + + // Rename a player without `name` field + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'name' + ], ['X-Requested-With' => 'XMLHttpRequest'])->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'Name']) + ]); + + // Rename a player successfully + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'name', + 'name' => 'new_name' + ])->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.players.name.success', ['player' => 'new_name']), + 'name' => 'new_name' + ]); + + // Delete a player + $this->post('/admin/players', [ + 'pid' => $player->pid, + 'action' => 'delete' + ])->seeJson([ + 'errno' => 0, + 'msg' => trans('admin.players.delete.success') + ]); + $this->assertNull(Player::find($player->pid)); + } + + public function testGetOneUser() + { + $user = factory(User::class)->create(); + $this->get('/admin/user/'.$user->uid) + ->seeJson([ + 'errno' => 0, + 'msg' => 'success', + 'user' => [ + 'uid' => $user->uid, + 'email' => $user->email, + 'nickname' => $user->nickname, + 'score' => $user->score, + 'avatar' => $user->avatar, + 'permission' => $user->permission + ] + ]); + + $this->get('/admin/user/-1') + ->seeJson([ + 'errno' => 1, + 'msg' => 'No such user.' + ]); + } +}