From a413927980ce5cee19ab392e0fcc0fe6a3668dc5 Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Fri, 17 Nov 2017 07:40:29 +0800 Subject: [PATCH] Add tests for `TextureController` --- app/Http/Controllers/TextureController.php | 48 ++-- app/Models/Player.php | 1 - storage/testing/.gitignore | 3 + storage/testing/textures/.gitignore | 2 + tests/TextureControllerTest.php | 273 +++++++++++++++++++++ 5 files changed, 308 insertions(+), 19 deletions(-) create mode 100644 storage/testing/.gitignore create mode 100644 storage/testing/textures/.gitignore create mode 100644 tests/TextureControllerTest.php diff --git a/app/Http/Controllers/TextureController.php b/app/Http/Controllers/TextureController.php index a47a0712..e3562074 100644 --- a/app/Http/Controllers/TextureController.php +++ b/app/Http/Controllers/TextureController.php @@ -58,7 +58,7 @@ class TextureController extends Controller 'Content-Length' => Storage::disk('textures')->size($hash), ]); } else { - abort(404); + return abort(404); } } @@ -88,7 +88,7 @@ class TextureController extends Controller return $player->getBinaryTexture('cape'); } - public function avatar($base64_email, $size = 128, UserRepository $users) + public function avatar($base64_email, UserRepository $users, $size = 128) { $user = $users->get(base64_decode($base64_email), 'email'); @@ -97,33 +97,39 @@ class TextureController extends Controller if ($t = Texture::find($tid)) { if (Storage::disk('textures')->has($t->hash)) { - $responses = Event::fire(new GetAvatarPreview($t, $size)); + $responses = event(new GetAvatarPreview($t, $size)); if (isset($responses[0]) && $responses[0] instanceof \Symfony\Component\HttpFoundation\Response) { - return $responses[0]; + return $responses[0]; // @codeCoverageIgnore } else { - $filename = storage_path("textures/{$t->hash}"); + $filename = config('filesystems.disks.textures.root').'/'.$t->hash; $png = Minecraft::generateAvatarFromSkin($filename, $size); + ob_start(); imagepng($png); imagedestroy($png); + $image = ob_get_contents(); + ob_end_clean(); - return Response::png(); + return Response::png($image); } } } } $png = imagecreatefromstring(base64_decode(static::getDefaultAvatar())); + ob_start(); imagepng($png); imagedestroy($png); + $image = ob_get_contents(); + ob_end_clean(); - return Response::png(); + return Response::png($image); } public function avatarWithSize($size, $base64_email, UserRepository $users) { - return $this->avatar($base64_email, $size, $users); + return $this->avatar($base64_email, $users, $size); } public function preview($tid, $size = 250) @@ -131,33 +137,42 @@ class TextureController extends Controller // output image directly if ($t = Texture::find($tid)) { if (Storage::disk('textures')->has($t->hash)) { - $responses = Event::fire(new GetSkinPreview($t, $size)); + $responses = event(new GetSkinPreview($t, $size)); if (isset($responses[0]) && $responses[0] instanceof \Symfony\Component\HttpFoundation\Response) { - return $responses[0]; + return $responses[0]; // @codeCoverageIgnore } else { - $filename = storage_path("textures/{$t->hash}"); + $filename = config('filesystems.disks.textures.root').'/'.$t->hash; if ($t->type == "cape") { $png = Minecraft::generatePreviewFromCape($filename, $size); + ob_start(); imagepng($png); imagedestroy($png); + $image = ob_get_contents(); + ob_end_clean(); } else { $png = Minecraft::generatePreviewFromSkin($filename, $size); + ob_start(); imagepng($png); imagedestroy($png); + $image = ob_get_contents(); + ob_end_clean(); } - return Response::png(); + return Response::png($image); } } } $png = imagecreatefromstring(base64_decode(static::getBrokenPreview())); + ob_start(); imagepng($png); imagedestroy($png); + $image = ob_get_contents(); + ob_end_clean(); - return Response::png(); + return Response::png($image); } public function previewWithSize($size, $tid) @@ -171,10 +186,10 @@ class TextureController extends Controller if (Storage::disk('textures')->has($t->hash)) { return Response::png(Storage::disk('textures')->get($t->hash)); } else { - abort(404, trans('general.texture-deleted')); + return abort(404, trans('general.texture-deleted')); } } else { - abort(404, trans('skinlib.non-existent')); + return abort(404, trans('skinlib.non-existent')); } } @@ -183,9 +198,6 @@ class TextureController extends Controller { $player = Player::where('player_name', $player_name)->first(); - if (!$player) - abort(404, trans('general.unexistent-player')); - if ($player->isBanned()) abort(404, trans('general.player-banned')); diff --git a/app/Models/Player.php b/app/Models/Player.php index 2b50742f..f5bf9583 100644 --- a/app/Models/Player.php +++ b/app/Models/Player.php @@ -146,7 +146,6 @@ class Player extends Model { if ($this->getTexture($type)) { $hash = $this->getTexture($type); - $path = storage_path("textures/$hash"); if (Storage::disk('textures')->has($hash)) { // Cache friendly diff --git a/storage/testing/.gitignore b/storage/testing/.gitignore new file mode 100644 index 00000000..e8e3c93f --- /dev/null +++ b/storage/testing/.gitignore @@ -0,0 +1,3 @@ +* +!textures +!.gitignore diff --git a/storage/testing/textures/.gitignore b/storage/testing/textures/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/storage/testing/textures/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/TextureControllerTest.php b/tests/TextureControllerTest.php new file mode 100644 index 00000000..c497695a --- /dev/null +++ b/tests/TextureControllerTest.php @@ -0,0 +1,273 @@ + storage_path('testing/textures')]); + } + + protected function tearDown() + { + collect(Storage::disk('textures')->files()) + ->reject(function ($filename) { + return $filename == '.gitignore'; + }) + ->each(function ($filename) { + Storage::disk('textures')->delete($filename); + }); + parent::tearDown(); + } + + public function testJson() + { + $steve = factory(Texture::class)->create(); + + // Player is not existed + $this->get('/nope.json') + ->see(trans('general.unexistent-player')) + ->assertResponseStatus(404); + + // Player is banned + $player = factory(Player::class)->create(['tid_steve' => $steve->tid]); + User::find($player->uid)->setPermission(User::BANNED); + $this->get("/{$player->player_name}.json") + ->see(trans('general.player-banned')) + ->assertResponseStatus(404); + + User::find($player->uid)->setPermission(User::NORMAL); + + // Default API is CSL API + $this->get("/{$player->player_name}.json") + ->seeJson([ + 'username' => $player->player_name, + 'skins' => [ + 'default' => $steve->hash, + 'slim' => null + ], + 'cape' => null + ])->seeHeader('Last-Modified'); + } + + public function testJsonWithApi() + { + $steve = factory(Texture::class)->create(); + $player = factory(Player::class)->create(['tid_steve' => $steve->tid]); + + // CSL API + $this->get("/csl/{$player->player_name}.json") + ->seeJson([ + 'username' => $player->player_name, + 'skins' => [ + 'default' => $steve->hash, + 'slim' => null + ], + 'cape' => null + ])->seeHeader('Last-Modified'); + + // USM API + $this->get("/usm/{$player->player_name}.json") + ->seeJson([ + 'player_name' => $player->player_name, + 'model_preference' => ['default', 'slim'], + 'skins' => [ + 'default' => $steve->hash, + 'slim' => null + ], + 'cape' => null + ])->seeHeader('Last-Modified'); + } + + public function testTexture() + { + $steve = factory(Texture::class)->create(); + Storage::disk('textures')->put($steve->hash, ''); + $this->get('/textures/nope') + ->see('404'); + + $this->get('/textures/'.$steve->hash) + ->seeHeader('Content-Type', 'image/png') + ->seeHeader('Last-Modified') + ->seeHeader('Accept-Ranges', 'bytes') + ->seeHeader('Content-Length', Storage::disk('textures')->size($steve->hash)) + ->assertResponseStatus(200); + } + + public function testTextureWithApi() + { + $steve = factory(Texture::class)->create(); + Storage::disk('textures')->put($steve->hash, ''); + + $this->get('/csl/textures/'.$steve->hash) + ->seeHeader('Content-Type', 'image/png') + ->seeHeader('Last-Modified') + ->seeHeader('Accept-Ranges', 'bytes') + ->seeHeader('Content-Length', Storage::disk('textures')->size($steve->hash)) + ->assertResponseStatus(200); + + $this->get('/usm/textures/'.$steve->hash) + ->seeHeader('Content-Type', 'image/png') + ->seeHeader('Last-Modified') + ->seeHeader('Accept-Ranges', 'bytes') + ->seeHeader('Content-Length', Storage::disk('textures')->size($steve->hash)) + ->assertResponseStatus(200); + } + + public function testSkin() + { + $steve = factory(Texture::class)->create(); + $player = factory(Player::class)->create([ + 'preference' => 'slim', + 'tid_steve' => $steve->tid, + 'tid_alex' => 0 + ]); + + $this->get("/skin/{$player->player_name}.png") + ->see(trans('general.texture-not-uploaded', ['type' => 'alex'])); + + $player->setPreference('default'); + $this->get("/skin/{$player->player_name}.png") + ->see(trans('general.texture-deleted')); + + Storage::disk('textures')->put($steve->hash, ''); + $this->get("/skin/{$player->player_name}.png") + ->seeHeader('Content-Type', 'image/png') + ->seeHeader('Last-Modified') + ->seeHeader('Accept-Ranges', 'bytes') + ->seeHeader('Content-Length', Storage::disk('textures')->size($steve->hash)) + ->assertResponseStatus(200); + } + + public function testSkinWithModel() + { + $steve = factory(Texture::class)->create(); + $player = factory(Player::class)->create([ + 'preference' => 'slim', + 'tid_steve' => $steve->tid, + 'tid_alex' => 0 + ]); + + $this->get("/skin/alex/{$player->player_name}.png") + ->see(trans('general.texture-not-uploaded', ['type' => 'alex'])); + + $player->setPreference('default'); + $this->get("/skin/steve/{$player->player_name}.png") + ->see(trans('general.texture-deleted')); + } + + public function testCape() + { + $cape = factory(Texture::class, 'cape')->create(); + $player = factory(Player::class)->create([ + 'tid_cape' => $cape->tid + ]); + + $this->get("/cape/{$player->player_name}.png") + ->see(trans('general.texture-deleted')); + } + + public function testAvatar() + { + $base64_email = base64_encode('a@b.c'); + $this->get("/avatar/$base64_email.png") + ->seeHeader('Content-Type', 'image/png'); + + $steve = factory(Texture::class)->create(); + $png = base64_decode(\App\Http\Controllers\TextureController::getDefaultSkin()); + Storage::disk('textures')->put($steve->hash, $png); + + $user = factory(User::class)->create(['avatar' => $steve->tid]); + + $mock = Mockery::mock('overload:Minecraft'); + $mock->shouldReceive('generateAvatarFromSkin') + ->once() + ->andReturn(imagecreatefromstring($png)); + + $this->expectsEvents(\App\Events\GetAvatarPreview::class); + $this->get('/avatar/'.base64_encode($user->email).'.png') + ->seeHeader('Content-Type', 'image/png'); + } + + public function testAvatarWithSize() + { + $steve = factory(Texture::class)->create(); + $png = base64_decode(\App\Http\Controllers\TextureController::getDefaultSkin()); + Storage::disk('textures')->put($steve->hash, $png); + + $user = factory(User::class)->create(['avatar' => $steve->tid]); + + $mock = Mockery::mock('overload:Minecraft'); + $mock->shouldReceive('generateAvatarFromSkin') + ->once() + ->andReturn(imagecreatefromstring($png)); + + $this->expectsEvents(\App\Events\GetAvatarPreview::class); + $this->get('/avatar/50/'.base64_encode($user->email).'.png') + ->seeHeader('Content-Type', 'image/png'); + } + + public function testPreview() + { + $steve = factory(Texture::class)->create(); + $cape = factory(Texture::class, 'cape')->create(); + + $this->get('/preview/0.png') + ->seeHeader('Content-Type', 'image/png'); + + $this->get("/preview/{$steve->tid}.png") + ->seeHeader('Content-Type', 'image/png'); + + $png = base64_decode(\App\Http\Controllers\TextureController::getDefaultSkin()); + Storage::disk('textures')->put($steve->hash, $png); + Storage::disk('textures')->put($cape->hash, $png); + + $mock = Mockery::mock('overload:Minecraft'); + $mock->shouldReceive('generatePreviewFromSkin') + ->once() + ->andReturn(imagecreatefromstring($png)); + $this->expectsEvents(\App\Events\GetSkinPreview::class); + $this->get("/preview/{$steve->tid}.png") + ->seeHeader('Content-Type', 'image/png'); + + $mock->shouldReceive('generatePreviewFromCape') + ->once() + ->andReturn(imagecreatefromstring($png)); + $this->get("/preview/{$cape->tid}.png") + ->seeHeader('Content-Type', 'image/png'); + } + + public function testPreviewWithSize() + { + $this->get('/preview/200/0.png') + ->seeHeader('Content-Type', 'image/png'); + } + + public function testRaw() + { + $steve = factory(Texture::class)->create(); + Storage::disk('textures')->put($steve->hash, ''); + + // Not found + $this->get('/raw/0.png') + ->see(trans('skinlib.non-existent')); + + // Success + $this->get("/raw/{$steve->tid}.png") + ->seeHeader('Content-Type', 'image/png'); + + // Texture is deleted + Storage::disk('textures')->delete($steve->hash); + $this->get("/raw/{$steve->tid}.png") + ->see(trans('general.texture-deleted')); + } +}