diff --git a/app/Http/Controllers/ClosetController.php b/app/Http/Controllers/ClosetController.php index 7e35742c..6886a73d 100644 --- a/app/Http/Controllers/ClosetController.php +++ b/app/Http/Controllers/ClosetController.php @@ -61,11 +61,6 @@ class ClosetController extends Controller ]); } - public function info() - { - return json($this->closet->getItems()); - } - public function add(Request $request) { $this->validate($request, [ @@ -77,8 +72,13 @@ class ClosetController extends Controller return json(trans('user.closet.add.lack-score'), 7); } - if ($this->closet->add($request->tid, $request->name)) { - $t = Texture::find($request->tid); + $tid = $request->tid; + if (!Texture::find($tid)) { + return json(trans('user.closet.add.not-found'), 1); + } + + if ($this->closet->add($tid, $request->name)) { + $t = Texture::find($tid); $t->likes += 1; $t->save(); @@ -100,7 +100,7 @@ class ClosetController extends Controller if ($this->closet->rename($request->tid, $request->new_name)) { return json(trans('user.closet.rename.success', ['name' => $request->new_name]), 0); } else { - return json(trans('user.closet.remove.non-existent'), 0); + return json(trans('user.closet.remove.non-existent'), 1); } } @@ -120,7 +120,7 @@ class ClosetController extends Controller return json(trans('user.closet.remove.success'), 0); } else { - return json(trans('user.closet.remove.non-existent'), 0); + return json(trans('user.closet.remove.non-existent'), 1); } } diff --git a/app/Models/Closet.php b/app/Models/Closet.php index 0cfb9b05..6b129ba1 100644 --- a/app/Models/Closet.php +++ b/app/Models/Closet.php @@ -158,10 +158,14 @@ class Closet * * @param integer $tid * @param string $new_name - * @return void + * @return bool */ public function rename($tid, $new_name) { + if (!$this->has($tid)) { + return false; + } + $offset = 0; foreach ($this->textures as $item) { if ($item['tid'] == $tid) { diff --git a/app/Models/User.php b/app/Models/User.php index 7e2c52c4..3531a20b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -230,8 +230,9 @@ class User extends Model /** * Set user score. * - * @param int $score - * @param string $mode What operation should be done, set, plus or minus. + * @param int $score + * @param string $mode What operation should be done, set, plus or minus. + * @return bool */ public function setScore($score, $mode = "set") { diff --git a/database/factories/ClosetModelFactory.php b/database/factories/ClosetModelFactory.php new file mode 100644 index 00000000..02428712 --- /dev/null +++ b/database/factories/ClosetModelFactory.php @@ -0,0 +1,11 @@ +define(Closet::class, function (Faker\Generator $faker) { + return [ + 'uid' => factory(User::class)->create()->uid, + 'textures' => '[]' + ]; +}); diff --git a/resources/lang/en/user.yml b/resources/lang/en/user.yml index 7947b308..6d20d0bc 100644 --- a/resources/lang/en/user.yml +++ b/resources/lang/en/user.yml @@ -45,6 +45,7 @@ closet: add: success: Added :name to closet successfully~ repeated: You have already added this texture. + not-found: We cannot find this texture. lack-score: You don't have enough score to add it to closet. rename: diff --git a/resources/lang/zh_CN/user.yml b/resources/lang/zh_CN/user.yml index 8c3a5628..439ab380 100644 --- a/resources/lang/zh_CN/user.yml +++ b/resources/lang/zh_CN/user.yml @@ -52,6 +52,7 @@ closet: add: success: 材质 :name 收藏成功~ repeated: 你已经收藏过这个材质啦 + not-found: 并没有这个材质 lack-score: 积分不够添加收藏啦 rename: diff --git a/tests/ClosetControllerTest.php b/tests/ClosetControllerTest.php new file mode 100644 index 00000000..05d58172 --- /dev/null +++ b/tests/ClosetControllerTest.php @@ -0,0 +1,302 @@ +user = factory(User::class)->create(); + return $this->actAs($this->user); + } + + public function testIndex() + { + $this->visit('/user/closet')->assertViewHas('user'); + } + + public function testGetClosetData() + { + $textures = factory(Texture::class, 5)->create(); + $closet = new Closet($this->user->uid); + $textures->each(function ($texture) use ($closet) { + $closet->add($texture->tid, $texture->name); + }); + $closet->save(); + + // Use default query parameters + $this->get('/user/closet-data') + ->seeJson([ + 'category' => 'skin', + 'total_pages' => 1, + 'items' => $textures->map(function ($item) { + $item->upload_at = $item->upload_at->format('Y-m-d H:i:s'); + $item->public = $item->public ? 1 : 0; + return $item; + })->take(6)->toArray() + ]); + + // Get capes + $cape = factory(Texture::class, 'cape')->create(); + $closet->add($cape->tid, $cape->name); + $closet->save(); + $this->get('/user/closet-data?category=cape') + ->seeJson([ + 'category' => 'cape', + 'total_pages' => 1, + 'items' => collect([$cape])->map(function ($item) { + $item->upload_at = $item->upload_at->format('Y-m-d H:i:s'); + $item->public = $item->public ? 1 : 0; + return $item; + })->take(6)->toArray() + ]); + + // Search by keyword + $random = $textures->random(); + $this->get('/user/closet-data?q='.$random->name) + ->seeJson([ + 'category' => 'skin', + 'total_pages' => 1, + 'items' => collect([$random])->take(6)->toArray() + ]); + } + + public function testAdd() + { + $texture = factory(Texture::class)->create(); + $name = 'my'; + option(['score_per_closet_item' => 10]); + + // Missing `tid` field + $this->post('/user/closet/add', [], ['X-Requested-With' => 'XMLHttpRequest']) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'tid']) + ]); + + // `tid` is not a integer + $this->post( + '/user/closet/add', + ['tid' => 'string'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.integer', ['attribute' => 'tid']) + ]); + + // Missing `name` field + $this->post( + '/user/closet/add', + ['tid' => 0], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'Name']) + ]); + + // `name` field has special characters + $this->post( + '/user/closet/add', + ['tid' => 0, 'name' => '\\'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.no_special_chars', ['attribute' => 'Name']) + ]); + + // The user doesn't have enough score to add a texture + $this->user->setScore(0); + $this->post( + '/user/closet/add', + ['tid' => $texture->tid, 'name' => $name] + )->seeJson([ + 'errno' => 7, + 'msg' => trans('user.closet.add.lack-score') + ]); + + // Add a not-existed texture + $this->user->setScore(100); + $this->post( + '/user/closet/add', + ['tid' => -1, 'name' => 'my'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('user.closet.add.not-found') + ]); + + // Add a texture successfully + $this->post( + '/user/closet/add', + ['tid' => $texture->tid, 'name' => $name] + )->seeJson([ + 'errno' => 0, + 'msg' => trans('user.closet.add.success', ['name' => $name]) + ]); + $this->assertEquals($texture->likes + 1, Texture::find($texture->tid)->likes); + $this->user = User::find($this->user->uid); + $this->assertEquals(90, $this->user->score); + $closet = new Closet($this->user->uid); + $this->assertTrue($closet->has($texture->tid)); + + // If the texture is duplicated, should be warned + $this->post( + '/user/closet/add', + ['tid' => $texture->tid, 'name' => $name] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('user.closet.add.repeated') + ]); + } + + public function testRename() + { + $texture = factory(Texture::class)->create(); + $name = 'new'; + + // Missing `tid` field + $this->post('/user/closet/rename', [], ['X-Requested-With' => 'XMLHttpRequest']) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'tid']) + ]); + + // `tid` is not a integer + $this->post( + '/user/closet/rename', + ['tid' => 'string'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.integer', ['attribute' => 'tid']) + ]); + + // Missing `new_name` field + $this->post( + '/user/closet/rename', + ['tid' => 0], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'new name']) + ]); + + // `new_name` field has special characters + $this->post( + '/user/closet/rename', + ['tid' => 0, 'new_name' => '\\'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.no_special_chars', ['attribute' => 'new name']) + ]); + + // Rename a not-existed texture + $this->post( + '/user/closet/rename', + ['tid' => -1, 'new_name' => $name] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('user.closet.remove.non-existent') + ]); + + // Rename a closet item successfully + $closet = new Closet($this->user->uid); + $closet->add($texture->tid, 'name'); + $closet->save(); + $closet = new Closet($this->user->uid); + $this->post( + '/user/closet/rename', + ['tid' => $texture->tid, 'new_name' => $name] + )->seeJson([ + 'errno' => 0, + 'msg' => trans('user.closet.rename.success', ['name' => 'new']) + ]); + $closet->save(); + $closet = new Closet($this->user->uid); + $this->assertTrue( + in_array($name, array_map(function ($item) { + return $item->name; + }, $closet->getItems())) + ); + } + + public function testRemove() + { + $texture = factory(Texture::class)->create(); + + // Missing `tid` field + $this->post('/user/closet/remove', [], ['X-Requested-With' => 'XMLHttpRequest']) + ->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.required', ['attribute' => 'tid']) + ]); + + // `tid` is not a integer + $this->post( + '/user/closet/remove', + ['tid' => 'string'], + ['X-Requested-With' => 'XMLHttpRequest'] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('validation.integer', ['attribute' => 'tid']) + ]); + + // Rename a not-existed texture + $this->post( + '/user/closet/remove', + ['tid' => -1] + )->seeJson([ + 'errno' => 1, + 'msg' => trans('user.closet.remove.non-existent') + ]); + + // Should return score if `return_score` is true + $closet = new Closet($this->user->uid); + $closet->add($texture->tid, 'name'); + $closet->save(); + $score = $this->user->score; + $this->post( + '/user/closet/remove', + ['tid' => $texture->tid] + )->seeJson([ + 'errno' => 0, + 'msg' => trans('user.closet.remove.success') + ]); + $closet = new Closet($this->user->uid); + $this->assertEquals($texture->likes - 1, Texture::find($texture->tid)->likes); + $this->assertEquals($score + option('score_per_closet_item'), $this->user->score); + $this->assertFalse($closet->has($texture->tid)); + + $texture = Texture::find($texture->tid); + // Should not return score if `return_score` is false + option(['return_score' => false]); + $closet = new Closet($this->user->uid); + $closet->add($texture->tid, 'name'); + $closet->save(); + $score = $this->user->score; + $this->post( + '/user/closet/remove', + ['tid' => $texture->tid] + )->seeJson([ + 'errno' => 0, + 'msg' => trans('user.closet.remove.success') + ]); + $closet = new Closet($this->user->uid); + $this->assertEquals($texture->likes - 1, Texture::find($texture->tid)->likes); + $this->assertEquals($score, $this->user->score); + $this->assertFalse($closet->has($texture->tid)); + } +}